From 8915080b64221d8b1d929175980c27d2845fc0db Mon Sep 17 00:00:00 2001 From: Hartmut Seichter Date: Tue, 24 Nov 2020 23:54:49 +0100 Subject: [PATCH] testing a ecs --- .vscode/settings.json | 3 + src/core/include/pw/core/matrix_transform.hpp | 4 +- src/deps/CMakeLists.txt | 6 +- src/deps/entt/LICENSE | 21 + src/deps/entt/entt/entt.hpp | 20185 ++++++++++++++++ src/runtime/pixwerx.cpp | 8 +- src/scene/CMakeLists.txt | 10 +- src/scene/include/pw/scene/component.hpp | 2 +- src/scene/include/pw/scene/entity.hpp | 57 + .../pw/scene/{camera.hpp => projection.hpp} | 40 +- src/scene/include/pw/scene/scene.hpp | 12 +- src/scene/src/camera.cpp | 42 - src/scene/src/entity.cpp | 12 + src/scene/src/projection.cpp | 45 + src/scene/src/scene.cpp | 17 +- src/scene/tests/CMakeLists.txt | 8 + src/scene/tests/pwscene_test_scene.cpp | 24 + src/scripts/demos/simple_000.lua | 10 +- src/system/src/window.cpp | 9 +- src/visual/src/shader/unlit_vs.glsl | 5 +- 20 files changed, 20419 insertions(+), 101 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 src/deps/entt/LICENSE create mode 100644 src/deps/entt/entt/entt.hpp create mode 100644 src/scene/include/pw/scene/entity.hpp rename src/scene/include/pw/scene/{camera.hpp => projection.hpp} (75%) delete mode 100644 src/scene/src/camera.cpp create mode 100644 src/scene/src/entity.cpp create mode 100644 src/scene/src/projection.cpp create mode 100644 src/scene/tests/pwscene_test_scene.cpp diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..b4d8c35 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools" +} \ No newline at end of file diff --git a/src/core/include/pw/core/matrix_transform.hpp b/src/core/include/pw/core/matrix_transform.hpp index a3a6520..80cd0aa 100644 --- a/src/core/include/pw/core/matrix_transform.hpp +++ b/src/core/include/pw/core/matrix_transform.hpp @@ -50,8 +50,8 @@ struct matrix_transform { frustum(0,2) = (right+left)/(right-left); //A frustum(1,2) = (top+bottom)/(top-bottom); //B - frustum(2,2) = -(z_far+z_near)/(z_far-z_near); //C - frustum(2,3) = -T(2) * z_far*z_near/(z_far-z_near); //D + frustum(2,2) = -(z_far+z_near)/(z_far-z_near); //C + frustum(2,3) = -T(2) * z_far*z_near/(z_far-z_near); //D frustum(3,2) = -T(1); diff --git a/src/deps/CMakeLists.txt b/src/deps/CMakeLists.txt index d983037..f868e9b 100644 --- a/src/deps/CMakeLists.txt +++ b/src/deps/CMakeLists.txt @@ -1,12 +1,10 @@ -set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "Build the GLFW example programs" FORCE) +set(GLFW_BUILD_EXAMPLES TRUE CACHE BOOL "Build the GLFW example programs" FORCE) set(GLFW_BUILD_TESTS OFF CACHE BOOL "Build the GLFW test programs" FORCE) set(GLFW_BUILD_DOCS OFF CACHE BOOL "Build the GLFW documentation" FORCE) set(GLFW_INSTALL OFF CACHE BOOL "Generate installation target" FORCE) -add_subdirectory(glfw-3.3.2) +add_subdirectory(glfw-3.2.1) add_subdirectory(lua-5.3.5) add_subdirectory(glad) -#add_subdirectory(arrrgh) - diff --git a/src/deps/entt/LICENSE b/src/deps/entt/LICENSE new file mode 100644 index 0000000..1c5f480 --- /dev/null +++ b/src/deps/entt/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017-2020 Michele Caini + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copy of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copy or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/deps/entt/entt/entt.hpp b/src/deps/entt/entt/entt.hpp new file mode 100644 index 0000000..b085a2d --- /dev/null +++ b/src/deps/entt/entt/entt.hpp @@ -0,0 +1,20185 @@ +// #include "core/algorithm.hpp" +#ifndef ENTT_CORE_ALGORITHM_HPP +#define ENTT_CORE_ALGORITHM_HPP + + +#include +#include +#include +#include +#include +// #include "utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + + +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_HS_SUFFIX +# define ENTT_HS_SUFFIX _hs +#endif + + +#ifndef ENTT_HWS_SUFFIX +# define ENTT_HWS_SUFFIX _hws +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifndef ENTT_PAGE_SIZE +# define ENTT_PAGE_SIZE 32768 +#endif + + +#ifndef ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty +#else +# include +# define ENTT_IS_EMPTY(Type) std::false_type +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) +# define ENTT_PRETTY_FUNCTION_CONSTEXPR +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif 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_CONSTEXPR +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + + +#ifndef ENTT_STANDALONE +# define ENTT_FAST_PATH(...) false +#else +# define ENTT_FAST_PATH(Cond) Cond +#endif + + +#endif + + + +namespace entt { + + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type && operator()(Type &&value) const ENTT_NOEXCEPT { + return std::forward(value); + } +}; + + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +[[nodiscard]] constexpr auto overload(Type Class:: *member) ENTT_NOEXCEPT { return member; } + + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { return func; } + + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + y_combinator(Func recursive): + func{std::move(recursive)} + {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + decltype(auto) operator()(Args &&... args) const { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + decltype(auto) operator()(Args &&... args) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + + +} + + +#endif + + + +namespace entt { + + +/** + * @brief Function object to wrap `std::sort` in a class type. + * + * Unfortunately, `std::sort` cannot be passed as template argument to a class + * template or a function template.
+ * This class fills the gap by wrapping some flavors of `std::sort` in a + * function object. + */ +struct std_sort { + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given binary comparison function. + * + * @tparam It Type of random access iterator. + * @tparam Compare Type of comparison function object. + * @tparam Args Types of arguments to forward to the sort function. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + * @param args Arguments to forward to the sort function, if any. + */ + template, typename... Args> + void operator()(It first, It last, Compare compare = Compare{}, Args &&... args) const { + std::sort(std::forward(args)..., std::move(first), std::move(last), std::move(compare)); + } +}; + + +/*! @brief Function object for performing insertion sort. */ +struct insertion_sort { + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given binary comparison function. + * + * @tparam It Type of random access iterator. + * @tparam Compare Type of comparison function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + */ + template> + void operator()(It first, It last, Compare compare = Compare{}) const { + if(first < last) { + for(auto it = first+1; it < last; ++it) { + auto value = std::move(*it); + auto pre = it; + + for(; pre > first && compare(value, *(pre-1)); --pre) { + *pre = std::move(*(pre-1)); + } + + *pre = std::move(value); + } + } + } +}; + + +/** + * @brief Function object for performing LSD radix sort. + * @tparam Bit Number of bits processed per pass. + * @tparam N Maximum number of bits to sort. + */ +template +struct radix_sort { + static_assert((N % Bit) == 0, "The maximum number of bits to sort must be a multiple of the number of bits processed per pass"); + + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given _getter_ to access the + * actual data to be sorted. + * + * This implementation is inspired by the online book + * [Physically Based Rendering](http://www.pbr-book.org/3ed-2018/Primitives_and_Intersection_Acceleration/Bounding_Volume_Hierarchies.html#RadixSort). + * + * @tparam It Type of random access iterator. + * @tparam Getter Type of _getter_ function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param getter A valid _getter_ function object. + */ + template + void operator()(It first, It last, Getter getter = Getter{}) const { + if(first < last) { + static constexpr auto mask = (1 << Bit) - 1; + static constexpr auto buckets = 1 << Bit; + static constexpr auto passes = N / Bit; + + using value_type = typename std::iterator_traits::value_type; + std::vector aux(std::distance(first, last)); + + auto part = [getter = std::move(getter)](auto from, auto to, auto out, auto start) { + std::size_t index[buckets]{}; + std::size_t count[buckets]{}; + + std::for_each(from, to, [&getter, &count, start](const value_type &item) { + ++count[(getter(item) >> start) & mask]; + }); + + std::for_each(std::next(std::begin(index)), std::end(index), [index = std::begin(index), count = std::begin(count)](auto &item) mutable { + item = *(index++) + *(count++); + }); + + std::for_each(from, to, [&getter, &out, &index, start](value_type &item) { + out[index[(getter(item) >> start) & mask]++] = std::move(item); + }); + }; + + for(std::size_t pass = 0; pass < (passes & ~1); pass += 2) { + part(first, last, aux.begin(), pass * Bit); + part(aux.begin(), aux.end(), first, (pass + 1) * Bit); + } + + if constexpr(passes & 1) { + part(first, last, aux.begin(), (passes - 1) * Bit); + std::move(aux.begin(), aux.end(), first); + } + } + } +}; + + +} + + +#endif + +// #include "core/attribute.h" +#ifndef ENTT_CORE_ATTRIBUTE_H +#define ENTT_CORE_ATTRIBUTE_H + + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + + +#endif + +// #include "core/family.hpp" +#ifndef ENTT_CORE_FAMILY_HPP +#define ENTT_CORE_FAMILY_HPP + + +// #include "../config/config.h" + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + + +// #include "../config/config.h" + + + +namespace entt { + + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + + +} + + +#endif + + + +namespace entt { + + +/** + * @brief Dynamic identifier generator. + * + * Utility class template that can be used to assign unique identifiers to types + * at runtime. Use different specializations to create separate sets of + * identifiers. + */ +template +class family { + inline static ENTT_MAYBE_ATOMIC(id_type) identifier{}; + +public: + /*! @brief Unsigned integer type. */ + using family_type = id_type; + + /*! @brief Statically generated unique identifier for the given type. */ + template + // at the time I'm writing, clang crashes during compilation if auto is used instead of family_type + inline static const family_type type = identifier++; +}; + + +} + + +#endif + +// #include "core/hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + + +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +struct fnv1a_traits; + + +template<> +struct fnv1a_traits { + using type = std::uint32_t; + static constexpr std::uint32_t offset = 2166136261; + static constexpr std::uint32_t prime = 16777619; +}; + + +template<> +struct fnv1a_traits { + using type = std::uint64_t; + static constexpr std::uint64_t offset = 14695981039346656037ull; + static constexpr std::uint64_t prime = 1099511628211ull; +}; + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifers in the codebase while using their numeric + * counterparts at runtime.
+ * Because of that, a hashed string can also be used in constant expressions if + * required. + * + * @tparam Char Character type. + */ +template +class basic_hashed_string { + using traits_type = internal::fnv1a_traits; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const Char *curr) ENTT_NOEXCEPT: str{curr} {} + const Char *str; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr id_type helper(const Char *curr) ENTT_NOEXCEPT { + auto value = traits_type::offset; + + while(*curr != 0) { + value = (value ^ static_cast(*(curr++))) * traits_type::prime; + } + + return value; + } + +public: + /*! @brief Character type. */ + using value_type = Char; + /*! @brief Unsigned integer type. */ + using hash_type = id_type; + + /** + * @brief Returns directly the numeric representation of a string. + * + * Forcing template resolution avoids implicit conversions. An + * human-readable identifier can be anything but a plain, old bunch of + * characters.
+ * Example of use: + * @code{.cpp} + * const auto value = basic_hashed_string::to_value("my.png"); + * @endcode + * + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifer. + * @return The numeric representation of the string. + */ + template + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { + return helper(str); + } + + /** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ + [[nodiscard]] static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { + return helper(wrapper.str); + } + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifer. + * @param size Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static hash_type value(const value_type *str, std::size_t size) ENTT_NOEXCEPT { + id_type partial{traits_type::offset}; + while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } + return partial; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() ENTT_NOEXCEPT + : str{nullptr}, hash{} + {} + + /** + * @brief Constructs a hashed string from an array of const characters. + * + * Forcing template resolution avoids implicit conversions. An + * human-readable identifier can be anything but a plain, old bunch of + * characters.
+ * Example of use: + * @code{.cpp} + * basic_hashed_string hs{"my.png"}; + * @endcode + * + * @tparam N Number of characters of the identifier. + * @param curr Human-readable identifer. + */ + template + constexpr basic_hashed_string(const value_type (&curr)[N]) ENTT_NOEXCEPT + : str{curr}, hash{helper(curr)} + {} + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT + : str{wrapper.str}, hash{helper(wrapper.str)} + {} + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the instance. + */ + [[nodiscard]] constexpr const value_type * data() const ENTT_NOEXCEPT { + return str; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the instance. + */ + [[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { + return hash; + } + + /*! @copydoc data */ + [[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { return data(); } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the instance. + */ + [[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { return value(); } + + /** + * @brief Compares two hashed strings. + * @param other Hashed string with which to compare. + * @return True if the two hashed strings are identical, false otherwise. + */ + [[nodiscard]] constexpr bool operator==(const basic_hashed_string &other) const ENTT_NOEXCEPT { + return hash == other.hash; + } + +private: + const value_type *str; + hash_type hash; +}; + + +/** + * @brief Deduction guide. + * + * It allows to deduce the character type of the hashed string directly from a + * human-readable identifer provided to the constructor. + * + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifer. + */ +template +basic_hashed_string(const Char (&str)[N]) ENTT_NOEXCEPT +-> basic_hashed_string; + + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + + +} + + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr entt::hashed_string operator"" ENTT_HS_SUFFIX(const char *str, std::size_t) ENTT_NOEXCEPT { + return entt::hashed_string{str}; +} + + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] constexpr entt::hashed_wstring operator"" ENTT_HWS_SUFFIX(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { + return entt::hashed_wstring{str}; +} + + +#endif + +// #include "core/ident.hpp" +#ifndef ENTT_CORE_IDENT_HPP +#define ENTT_CORE_IDENT_HPP + + +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Types identifiers. + * + * Variable template used to generate identifiers at compile-time for the given + * types. Use the `get` member function to know what's the identifier associated + * to the specific type. + * + * @note + * Identifiers are constant expression and can be used in any context where such + * an expression is required. As an example: + * @code{.cpp} + * using id = entt::identifier; + * + * switch(a_type_identifier) { + * case id::type: + * // ... + * break; + * case id::type: + * // ... + * break; + * default: + * // ... + * } + * @endcode + * + * @tparam Types List of types for which to generate identifiers. + */ +template +class identifier { + using tuple_type = std::tuple...>; + + template + [[nodiscard]] static constexpr id_type get(std::index_sequence) { + static_assert(std::disjunction_v...>, "Invalid type"); + return (0 + ... + (std::is_same_v> ? id_type(Indexes) : id_type{})); + } + +public: + /*! @brief Unsigned integer type. */ + using identifier_type = id_type; + + /*! @brief Statically generated unique identifier for the given type. */ + template + static constexpr identifier_type type = get>(std::index_sequence_for{}); +}; + + +} + + +#endif + +// #include "core/monostate.hpp" +#ifndef ENTT_CORE_MONOSTATE_HPP +#define ENTT_CORE_MONOSTATE_HPP + + +// #include "../config/config.h" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Minimal implementation of the monostate pattern. + * + * A minimal, yet complete configuration system built on top of the monostate + * pattern. Thread safe by design, it works only with basic types like `int`s or + * `bool`s.
+ * Multiple types and therefore more than one value can be associated with a + * single key. Because of this, users must pay attention to use the same type + * both during an assignment and when they try to read back their data. + * Otherwise, they can incur in unexpected results. + */ +template +struct monostate { + /** + * @brief Assigns a value of a specific type to a given key. + * @tparam Type Type of the value to assign. + * @param val User data to assign to the given key. + */ + template + void operator=(Type val) const ENTT_NOEXCEPT { + value = val; + } + + /** + * @brief Gets a value of a specific type for a given key. + * @tparam Type Type of the value to get. + * @return Stored value, if any. + */ + template + operator Type() const ENTT_NOEXCEPT { + return value; + } + +private: + template + inline static ENTT_MAYBE_ATOMIC(Type) value{}; +}; + + +/** + * @brief Helper variable template. + * @tparam Value Value used to differentiate between different variables. + */ +template +inline monostate monostate_v = {}; + + +} + + +#endif + +// #include "core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + + +#include +// #include "../config/config.h" + +// #include "../core/attribute.h" +#ifndef ENTT_CORE_ATTRIBUTE_H +#define ENTT_CORE_ATTRIBUTE_H + + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + + +#endif + +// #include "hashed_string.hpp" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +struct ENTT_API type_index { + [[nodiscard]] static id_type next() ENTT_NOEXCEPT { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + + +template +[[nodiscard]] constexpr auto type_name() ENTT_NOEXCEPT { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX)+1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{}; +#endif +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Type index. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() ENTT_NOEXCEPT { + static const id_type value = internal::type_index::next(); + return value; + } +}; + + +/** + * @brief Provides the member constant `value` to true if a given type is + * indexable, false otherwise. + * @tparam Type Potentially indexable type. + */ +template +struct has_type_index: std::false_type {}; + + +/*! @brief has_type_index */ +template +struct has_type_index::value())>>: std::true_type {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially indexable type. + */ +template +inline constexpr bool has_type_index_v = has_type_index::value; + + +/** + * @brief Type info. + * @tparam Type Type for which to generate information. + */ +template +struct type_info { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION_CONSTEXPR + [[nodiscard]] static constexpr id_type id() ENTT_NOEXCEPT { + constexpr auto value = hashed_string::value(ENTT_PRETTY_FUNCTION); + return value; + } +#elif defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static id_type id() ENTT_NOEXCEPT { + static const auto value = hashed_string::value(ENTT_PRETTY_FUNCTION); + return value; + } +#else + [[nodiscard]] static id_type id() ENTT_NOEXCEPT { + return type_index::value(); + } +#endif + + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION_CONSTEXPR + [[nodiscard]] static constexpr std::string_view name() ENTT_NOEXCEPT { + constexpr auto value = internal::type_name(); + return value; + } +#elif defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static std::string_view name() ENTT_NOEXCEPT { + static const auto value = internal::type_name(); + return value; + } +#else + [[nodiscard]] static constexpr std::string_view name() ENTT_NOEXCEPT { + return internal::type_name(); + } +#endif +}; + + +} + + +#endif + +// #include "core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + + +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_t = Type; + + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_v = Value; + + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + + +/** + * @brief Alias template to ease the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + + +/** + * @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 A class to use to push around lists of types, nothing more. */ +template +struct type_list {}; + + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_size; + + +/** + * @brief Compile-time number of elements in a type list. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_size> + : std::integral_constant +{}; + + +/** + * @brief Helper variable template. + * @tparam List Type list. + */ +template +inline constexpr auto type_list_size_v = type_list_size::value; + + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + std::disjunction_v...>, + typename type_list_unique>::type, + type_list_cat_t, typename type_list_unique>::type> + >; +}; + + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = type_list<>; +}; + + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type Potentially equality comparable type. + */ +template> +struct is_equality_comparable: std::false_type {}; + + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable() == std::declval())>> + : std::true_type +{}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially equality comparable type. + */ +template +inline constexpr auto is_equality_comparable_v = is_equality_comparable::value; + + +/** + * @brief Provides the member constant `value` to true if a given type is empty + * and the empty type optimization is enabled, false otherwise. + * @tparam Type Potential empty type. + */ +template +struct is_eto_eligible + : ENTT_IS_EMPTY(Type) +{}; + + +/** + * @brief Helper variable template. + * @tparam Type Potential empty type. + */ +template +inline constexpr auto is_eto_eligible_v = is_eto_eligible::value; + + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class * clazz(Ret(Class:: *)(Args...)); + + template + static Class * clazz(Ret(Class:: *)(Args...) const); + + template + static Class * clazz(Type Class:: *); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + + +} + + +#endif + +// #include "core/utility.hpp" + +// #include "entity/actor.hpp" +#ifndef ENTT_ENTITY_ACTOR_HPP +#define ENTT_ENTITY_ACTOR_HPP + + +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_HS_SUFFIX +# define ENTT_HS_SUFFIX _hs +#endif + + +#ifndef ENTT_HWS_SUFFIX +# define ENTT_HWS_SUFFIX _hws +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifndef ENTT_PAGE_SIZE +# define ENTT_PAGE_SIZE 32768 +#endif + + +#ifndef ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty +#else +# include +# define ENTT_IS_EMPTY(Type) std::false_type +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) +# define ENTT_PRETTY_FUNCTION_CONSTEXPR +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif 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_CONSTEXPR +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + + +#ifndef ENTT_STANDALONE +# define ENTT_FAST_PATH(...) false +#else +# define ENTT_FAST_PATH(Cond) Cond +#endif + + +#endif + +// #include "registry.hpp" +#ifndef ENTT_ENTITY_REGISTRY_HPP +#define ENTT_ENTITY_REGISTRY_HPP + + +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/algorithm.hpp" +#ifndef ENTT_CORE_ALGORITHM_HPP +#define ENTT_CORE_ALGORITHM_HPP + + +#include +#include +#include +#include +#include +// #include "utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + + +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_HS_SUFFIX +# define ENTT_HS_SUFFIX _hs +#endif + + +#ifndef ENTT_HWS_SUFFIX +# define ENTT_HWS_SUFFIX _hws +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifndef ENTT_PAGE_SIZE +# define ENTT_PAGE_SIZE 32768 +#endif + + +#ifndef ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty +#else +# include +# define ENTT_IS_EMPTY(Type) std::false_type +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) +# define ENTT_PRETTY_FUNCTION_CONSTEXPR +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif 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_CONSTEXPR +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + + +#ifndef ENTT_STANDALONE +# define ENTT_FAST_PATH(...) false +#else +# define ENTT_FAST_PATH(Cond) Cond +#endif + + +#endif + + + +namespace entt { + + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type && operator()(Type &&value) const ENTT_NOEXCEPT { + return std::forward(value); + } +}; + + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +[[nodiscard]] constexpr auto overload(Type Class:: *member) ENTT_NOEXCEPT { return member; } + + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { return func; } + + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + y_combinator(Func recursive): + func{std::move(recursive)} + {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + decltype(auto) operator()(Args &&... args) const { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + decltype(auto) operator()(Args &&... args) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + + +} + + +#endif + + + +namespace entt { + + +/** + * @brief Function object to wrap `std::sort` in a class type. + * + * Unfortunately, `std::sort` cannot be passed as template argument to a class + * template or a function template.
+ * This class fills the gap by wrapping some flavors of `std::sort` in a + * function object. + */ +struct std_sort { + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given binary comparison function. + * + * @tparam It Type of random access iterator. + * @tparam Compare Type of comparison function object. + * @tparam Args Types of arguments to forward to the sort function. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + * @param args Arguments to forward to the sort function, if any. + */ + template, typename... Args> + void operator()(It first, It last, Compare compare = Compare{}, Args &&... args) const { + std::sort(std::forward(args)..., std::move(first), std::move(last), std::move(compare)); + } +}; + + +/*! @brief Function object for performing insertion sort. */ +struct insertion_sort { + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given binary comparison function. + * + * @tparam It Type of random access iterator. + * @tparam Compare Type of comparison function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + */ + template> + void operator()(It first, It last, Compare compare = Compare{}) const { + if(first < last) { + for(auto it = first+1; it < last; ++it) { + auto value = std::move(*it); + auto pre = it; + + for(; pre > first && compare(value, *(pre-1)); --pre) { + *pre = std::move(*(pre-1)); + } + + *pre = std::move(value); + } + } + } +}; + + +/** + * @brief Function object for performing LSD radix sort. + * @tparam Bit Number of bits processed per pass. + * @tparam N Maximum number of bits to sort. + */ +template +struct radix_sort { + static_assert((N % Bit) == 0, "The maximum number of bits to sort must be a multiple of the number of bits processed per pass"); + + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given _getter_ to access the + * actual data to be sorted. + * + * This implementation is inspired by the online book + * [Physically Based Rendering](http://www.pbr-book.org/3ed-2018/Primitives_and_Intersection_Acceleration/Bounding_Volume_Hierarchies.html#RadixSort). + * + * @tparam It Type of random access iterator. + * @tparam Getter Type of _getter_ function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param getter A valid _getter_ function object. + */ + template + void operator()(It first, It last, Getter getter = Getter{}) const { + if(first < last) { + static constexpr auto mask = (1 << Bit) - 1; + static constexpr auto buckets = 1 << Bit; + static constexpr auto passes = N / Bit; + + using value_type = typename std::iterator_traits::value_type; + std::vector aux(std::distance(first, last)); + + auto part = [getter = std::move(getter)](auto from, auto to, auto out, auto start) { + std::size_t index[buckets]{}; + std::size_t count[buckets]{}; + + std::for_each(from, to, [&getter, &count, start](const value_type &item) { + ++count[(getter(item) >> start) & mask]; + }); + + std::for_each(std::next(std::begin(index)), std::end(index), [index = std::begin(index), count = std::begin(count)](auto &item) mutable { + item = *(index++) + *(count++); + }); + + std::for_each(from, to, [&getter, &out, &index, start](value_type &item) { + out[index[(getter(item) >> start) & mask]++] = std::move(item); + }); + }; + + for(std::size_t pass = 0; pass < (passes & ~1); pass += 2) { + part(first, last, aux.begin(), pass * Bit); + part(aux.begin(), aux.end(), first, (pass + 1) * Bit); + } + + if constexpr(passes & 1) { + part(first, last, aux.begin(), (passes - 1) * Bit); + std::move(aux.begin(), aux.end(), first); + } + } + } +}; + + +} + + +#endif + +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + + +// #include "../config/config.h" + + + +namespace entt { + + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + + +} + + +#endif + +// #include "../core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + + +#include +// #include "../config/config.h" + +// #include "../core/attribute.h" +#ifndef ENTT_CORE_ATTRIBUTE_H +#define ENTT_CORE_ATTRIBUTE_H + + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + + +#endif + +// #include "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + + +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +struct fnv1a_traits; + + +template<> +struct fnv1a_traits { + using type = std::uint32_t; + static constexpr std::uint32_t offset = 2166136261; + static constexpr std::uint32_t prime = 16777619; +}; + + +template<> +struct fnv1a_traits { + using type = std::uint64_t; + static constexpr std::uint64_t offset = 14695981039346656037ull; + static constexpr std::uint64_t prime = 1099511628211ull; +}; + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifers in the codebase while using their numeric + * counterparts at runtime.
+ * Because of that, a hashed string can also be used in constant expressions if + * required. + * + * @tparam Char Character type. + */ +template +class basic_hashed_string { + using traits_type = internal::fnv1a_traits; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const Char *curr) ENTT_NOEXCEPT: str{curr} {} + const Char *str; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr id_type helper(const Char *curr) ENTT_NOEXCEPT { + auto value = traits_type::offset; + + while(*curr != 0) { + value = (value ^ static_cast(*(curr++))) * traits_type::prime; + } + + return value; + } + +public: + /*! @brief Character type. */ + using value_type = Char; + /*! @brief Unsigned integer type. */ + using hash_type = id_type; + + /** + * @brief Returns directly the numeric representation of a string. + * + * Forcing template resolution avoids implicit conversions. An + * human-readable identifier can be anything but a plain, old bunch of + * characters.
+ * Example of use: + * @code{.cpp} + * const auto value = basic_hashed_string::to_value("my.png"); + * @endcode + * + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifer. + * @return The numeric representation of the string. + */ + template + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { + return helper(str); + } + + /** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ + [[nodiscard]] static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { + return helper(wrapper.str); + } + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifer. + * @param size Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static hash_type value(const value_type *str, std::size_t size) ENTT_NOEXCEPT { + id_type partial{traits_type::offset}; + while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } + return partial; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() ENTT_NOEXCEPT + : str{nullptr}, hash{} + {} + + /** + * @brief Constructs a hashed string from an array of const characters. + * + * Forcing template resolution avoids implicit conversions. An + * human-readable identifier can be anything but a plain, old bunch of + * characters.
+ * Example of use: + * @code{.cpp} + * basic_hashed_string hs{"my.png"}; + * @endcode + * + * @tparam N Number of characters of the identifier. + * @param curr Human-readable identifer. + */ + template + constexpr basic_hashed_string(const value_type (&curr)[N]) ENTT_NOEXCEPT + : str{curr}, hash{helper(curr)} + {} + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT + : str{wrapper.str}, hash{helper(wrapper.str)} + {} + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the instance. + */ + [[nodiscard]] constexpr const value_type * data() const ENTT_NOEXCEPT { + return str; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the instance. + */ + [[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { + return hash; + } + + /*! @copydoc data */ + [[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { return data(); } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the instance. + */ + [[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { return value(); } + + /** + * @brief Compares two hashed strings. + * @param other Hashed string with which to compare. + * @return True if the two hashed strings are identical, false otherwise. + */ + [[nodiscard]] constexpr bool operator==(const basic_hashed_string &other) const ENTT_NOEXCEPT { + return hash == other.hash; + } + +private: + const value_type *str; + hash_type hash; +}; + + +/** + * @brief Deduction guide. + * + * It allows to deduce the character type of the hashed string directly from a + * human-readable identifer provided to the constructor. + * + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifer. + */ +template +basic_hashed_string(const Char (&str)[N]) ENTT_NOEXCEPT +-> basic_hashed_string; + + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + + +} + + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr entt::hashed_string operator"" ENTT_HS_SUFFIX(const char *str, std::size_t) ENTT_NOEXCEPT { + return entt::hashed_string{str}; +} + + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] constexpr entt::hashed_wstring operator"" ENTT_HWS_SUFFIX(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { + return entt::hashed_wstring{str}; +} + + +#endif + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +struct ENTT_API type_index { + [[nodiscard]] static id_type next() ENTT_NOEXCEPT { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + + +template +[[nodiscard]] constexpr auto type_name() ENTT_NOEXCEPT { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX)+1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{}; +#endif +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Type index. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() ENTT_NOEXCEPT { + static const id_type value = internal::type_index::next(); + return value; + } +}; + + +/** + * @brief Provides the member constant `value` to true if a given type is + * indexable, false otherwise. + * @tparam Type Potentially indexable type. + */ +template +struct has_type_index: std::false_type {}; + + +/*! @brief has_type_index */ +template +struct has_type_index::value())>>: std::true_type {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially indexable type. + */ +template +inline constexpr bool has_type_index_v = has_type_index::value; + + +/** + * @brief Type info. + * @tparam Type Type for which to generate information. + */ +template +struct type_info { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION_CONSTEXPR + [[nodiscard]] static constexpr id_type id() ENTT_NOEXCEPT { + constexpr auto value = hashed_string::value(ENTT_PRETTY_FUNCTION); + return value; + } +#elif defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static id_type id() ENTT_NOEXCEPT { + static const auto value = hashed_string::value(ENTT_PRETTY_FUNCTION); + return value; + } +#else + [[nodiscard]] static id_type id() ENTT_NOEXCEPT { + return type_index::value(); + } +#endif + + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION_CONSTEXPR + [[nodiscard]] static constexpr std::string_view name() ENTT_NOEXCEPT { + constexpr auto value = internal::type_name(); + return value; + } +#elif defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static std::string_view name() ENTT_NOEXCEPT { + static const auto value = internal::type_name(); + return value; + } +#else + [[nodiscard]] static constexpr std::string_view name() ENTT_NOEXCEPT { + return internal::type_name(); + } +#endif +}; + + +} + + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + + +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_t = Type; + + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_v = Value; + + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + + +/** + * @brief Alias template to ease the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + + +/** + * @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 A class to use to push around lists of types, nothing more. */ +template +struct type_list {}; + + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_size; + + +/** + * @brief Compile-time number of elements in a type list. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_size> + : std::integral_constant +{}; + + +/** + * @brief Helper variable template. + * @tparam List Type list. + */ +template +inline constexpr auto type_list_size_v = type_list_size::value; + + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + std::disjunction_v...>, + typename type_list_unique>::type, + type_list_cat_t, typename type_list_unique>::type> + >; +}; + + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = type_list<>; +}; + + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type Potentially equality comparable type. + */ +template> +struct is_equality_comparable: std::false_type {}; + + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable() == std::declval())>> + : std::true_type +{}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially equality comparable type. + */ +template +inline constexpr auto is_equality_comparable_v = is_equality_comparable::value; + + +/** + * @brief Provides the member constant `value` to true if a given type is empty + * and the empty type optimization is enabled, false otherwise. + * @tparam Type Potential empty type. + */ +template +struct is_eto_eligible + : ENTT_IS_EMPTY(Type) +{}; + + +/** + * @brief Helper variable template. + * @tparam Type Potential empty type. + */ +template +inline constexpr auto is_eto_eligible_v = is_eto_eligible::value; + + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class * clazz(Ret(Class:: *)(Args...)); + + template + static Class * clazz(Ret(Class:: *)(Args...) const); + + template + static Class * clazz(Type Class:: *); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + + +} + + +#endif + +// #include "../signal/sigh.hpp" +#ifndef ENTT_SIGNAL_SIGH_HPP +#define ENTT_SIGNAL_SIGH_HPP + + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_HS_SUFFIX +# define ENTT_HS_SUFFIX _hs +#endif + + +#ifndef ENTT_HWS_SUFFIX +# define ENTT_HWS_SUFFIX _hws +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifndef ENTT_PAGE_SIZE +# define ENTT_PAGE_SIZE 32768 +#endif + + +#ifndef ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty +#else +# include +# define ENTT_IS_EMPTY(Type) std::false_type +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) +# define ENTT_PRETTY_FUNCTION_CONSTEXPR +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif 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_CONSTEXPR +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + + +#ifndef ENTT_STANDALONE +# define ENTT_FAST_PATH(...) false +#else +# define ENTT_FAST_PATH(Cond) Cond +#endif + + +#endif + +// #include "delegate.hpp" +#ifndef ENTT_SIGNAL_DELEGATE_HPP +#define ENTT_SIGNAL_DELEGATE_HPP + + +#include +#include +#include +#include +#include +// #include "../config/config.h" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +auto function_pointer(Ret(*)(Args...)) -> Ret(*)(Args...); + + +template +auto function_pointer(Ret(*)(Type, Args...), Other &&) -> Ret(*)(Args...); + + +template +auto function_pointer(Ret(Class:: *)(Args...), Other &&...) -> Ret(*)(Args...); + + +template +auto function_pointer(Ret(Class:: *)(Args...) const, Other &&...) -> Ret(*)(Args...); + + +template +auto function_pointer(Type Class:: *, Other &&...) -> Type(*)(); + + +template +using function_pointer_t = decltype(internal::function_pointer(std::declval()...)); + + +template +[[nodiscard]] constexpr auto index_sequence_for(Ret(*)(Args...)) { + return std::index_sequence_for{}; +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/*! @brief Used to wrap a function or a member of a specified type. */ +template +struct connect_arg_t {}; + + +/*! @brief Constant of type connect_arg_t used to disambiguate calls. */ +template +inline constexpr connect_arg_t connect_arg{}; + + +/** + * @brief Basic delegate implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + */ +template +class delegate; + + +/** + * @brief Utility class to use to send around functions and members. + * + * Unmanaged delegate for function pointers and members. Users of this class are + * in charge of disconnecting instances before deleting them. + * + * A delegate can be used as a general purpose invoker without memory overhead + * for free functions possibly with payloads and bound or unbound members. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +class delegate { + template + [[nodiscard]] auto wrap(std::index_sequence) ENTT_NOEXCEPT { + return [](const void *, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + return Ret(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + }; + } + + template + [[nodiscard]] auto wrap(Type &, std::index_sequence) ENTT_NOEXCEPT { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast, const void *, void *>>(payload)); + return Ret(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + }; + } + + template + [[nodiscard]] auto wrap(Type *, std::index_sequence) ENTT_NOEXCEPT { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast, const void *, void *>>(payload)); + return Ret(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + }; + } + +public: + /*! @brief Function type of the contained target. */ + using function_type = Ret(const void *, Args...); + /*! @brief Function type of the delegate. */ + using type = Ret(Args...); + /*! @brief Return type of the delegate. */ + using result_type = Ret; + + /*! @brief Default constructor. */ + delegate() ENTT_NOEXCEPT + : fn{nullptr}, data{nullptr} + {} + + /** + * @brief Constructs a delegate and connects a free function or an unbound + * member. + * @tparam Candidate Function or member to connect to the delegate. + */ + template + delegate(connect_arg_t) ENTT_NOEXCEPT { + connect(); + } + + /** + * @brief Constructs a delegate and connects a free function with payload or + * a bound member. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + delegate(connect_arg_t, Type &&value_or_instance) ENTT_NOEXCEPT { + connect(std::forward(value_or_instance)); + } + + /** + * @brief Constructs a delegate and connects an user defined function with + * optional payload. + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ + delegate(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT { + connect(function, payload); + } + + /** + * @brief Connects a free function or an unbound member to a delegate. + * @tparam Candidate Function or member to connect to the delegate. + */ + template + void connect() ENTT_NOEXCEPT { + data = nullptr; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *, Args... args) -> Ret { + return Ret(std::invoke(Candidate, std::forward(args)...)); + }; + } else if constexpr(std::is_member_pointer_v) { + fn = wrap(internal::index_sequence_for>>(internal::function_pointer_t{})); + } else { + fn = wrap(internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @brief Connects a free function with payload or a bound member to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of the instance overcomes + * the one of the delegate.
+ * When used to connect a free function with payload, its signature must be + * such that the instance is the first argument before the ones used to + * define the delegate itself. + * + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid reference that fits the purpose. + */ + template + void connect(Type &value_or_instance) ENTT_NOEXCEPT { + data = &value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast, const void *, void *>>(payload)); + return Ret(std::invoke(Candidate, *curr, std::forward(args)...)); + }; + } else { + fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @brief Connects a free function with payload or a bound member to a + * delegate. + * + * @sa connect(Type &) + * + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid pointer that fits the purpose. + */ + template + void connect(Type *value_or_instance) ENTT_NOEXCEPT { + data = value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast, const void *, void *>>(payload)); + return Ret(std::invoke(Candidate, curr, std::forward(args)...)); + }; + } else { + fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @brief Connects an user defined function with optional payload to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of an instance overcomes + * the one of the delegate.
+ * The payload is returned as the first argument to the target function in + * all cases. + * + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ + void connect(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT { + fn = function; + data = payload; + } + + /** + * @brief Resets a delegate. + * + * After a reset, a delegate cannot be invoked anymore. + */ + void reset() ENTT_NOEXCEPT { + fn = nullptr; + data = nullptr; + } + + /** + * @brief Returns the instance or the payload linked to a delegate, if any. + * @return An opaque pointer to the underlying data. + */ + [[nodiscard]] const void * instance() const ENTT_NOEXCEPT { + return data; + } + + /** + * @brief Triggers a delegate. + * + * The delegate invokes the underlying function and returns the result. + * + * @warning + * Attempting to trigger an invalid delegate results in undefined + * behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * delegate has not yet been set. + * + * @param args Arguments to use to invoke the underlying function. + * @return The value returned by the underlying function. + */ + Ret operator()(Args... args) const { + ENTT_ASSERT(fn); + return fn(data, std::forward(args)...); + } + + /** + * @brief Checks whether a delegate actually stores a listener. + * @return False if the delegate is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + // no need to test also data + return !(fn == nullptr); + } + + /** + * @brief Compares the contents of two delegates. + * @param other Delegate with which to compare. + * @return False if the two contents differ, true otherwise. + */ + [[nodiscard]] bool operator==(const delegate &other) const ENTT_NOEXCEPT { + return fn == other.fn && data == other.data; + } + +private: + function_type *fn; + const void *data; +}; + + +/** + * @brief Compares the contents of two delegates. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @param lhs A valid delegate object. + * @param rhs A valid delegate object. + * @return True if the two contents differ, false otherwise. + */ +template +[[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + */ +template +delegate(connect_arg_t) ENTT_NOEXCEPT +-> 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 &&) ENTT_NOEXCEPT +-> delegate>>; + + +/*! @brief Deduction guide. */ +template +delegate(Ret(*)(const void *, Args...), const void * = nullptr) ENTT_NOEXCEPT +-> delegate; + + +} + + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_SIGNAL_FWD_HPP +#define ENTT_SIGNAL_FWD_HPP + + +namespace entt { + + +template +class delegate; + + +class dispatcher; + + +template +class emitter; + + +class connection; + + +struct scoped_connection; + + +template +class sink; + + +template +class sigh; + + +} + + +#endif + + + +namespace entt { + + +/** + * @brief Sink class. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Function A valid function type. + */ +template +class sink; + + +/** + * @brief Unmanaged signal handler. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Function A valid function type. + */ +template +class sigh; + + +/** + * @brief Unmanaged signal handler. + * + * It works directly with references to classes and pointers to member functions + * as well as pointers to free functions. Users of this class are in charge of + * disconnecting instances before deleting them. + * + * This class serves mainly two purposes: + * + * * Creating signals to use later to notify a bunch of listeners. + * * Collecting results from a set of functions like in a voting system. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +class sigh { + /*! @brief A sink is allowed to modify a signal. */ + friend class sink; + +public: + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Sink type. */ + using sink_type = sink; + + /** + * @brief Instance type when it comes to connecting member functions. + * @tparam Class Type of class to which the member function belongs. + */ + template + using instance_type = Class *; + + /** + * @brief Number of listeners connected to the signal. + * @return Number of listeners currently connected. + */ + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + return calls.size(); + } + + /** + * @brief Returns false if at least a listener is connected to the signal. + * @return True if the signal has no listeners connected, false otherwise. + */ + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return calls.empty(); + } + + /** + * @brief Triggers a signal. + * + * All the listeners are notified. Order isn't guaranteed. + * + * @param args Arguments to use to invoke listeners. + */ + void publish(Args... args) const { + for(auto &&call: std::as_const(calls)) { + call(args...); + } + } + + /** + * @brief Collects return values from the listeners. + * + * The collector must expose a call operator with the following properties: + * + * * The return type is either `void` or such that it's convertible to + * `bool`. In the second case, a true value will stop the iteration. + * * The list of parameters is empty if `Ret` is `void`, otherwise it + * contains a single element such that `Ret` is convertible to it. + * + * @tparam Func Type of collector to use, if any. + * @param func A valid function object. + * @param args Arguments to use to invoke listeners. + */ + template + void collect(Func func, Args... args) const { + for(auto &&call: calls) { + if constexpr(std::is_void_v) { + if constexpr(std::is_invocable_r_v) { + call(args...); + if(func()) { break; } + } else { + call(args...); + func(); + } + } else { + if constexpr(std::is_invocable_r_v) { + if(func(call(args...))) { break; } + } else { + func(call(args...)); + } + } + } + } + +private: + std::vector> calls; +}; + + +/** + * @brief Connection class. + * + * Opaque object the aim of which is to allow users to release an already + * estabilished connection without having to keep a reference to the signal or + * the sink that generated it. + */ +class connection { + /*! @brief A sink is allowed to create connection objects. */ + template + friend class sink; + + connection(delegate fn, void *ref) + : disconnect{fn}, signal{ref} + {} + +public: + /*! @brief Default constructor. */ + connection() = default; + + /** + * @brief Checks whether a connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return static_cast(disconnect); + } + + /*! @brief Breaks the connection. */ + void release() { + if(disconnect) { + disconnect(signal); + disconnect.reset(); + } + } + +private: + delegate disconnect; + void *signal{}; +}; + + +/** + * @brief Scoped connection class. + * + * Opaque object the aim of which is to allow users to release an already + * estabilished connection without having to keep a reference to the signal or + * the sink that generated it.
+ * A scoped connection automatically breaks the link between the two objects + * when it goes out of scope. + */ +struct scoped_connection { + /*! @brief Default constructor. */ + scoped_connection() = default; + + /** + * @brief Constructs a scoped connection from a basic connection. + * @param other A valid connection object. + */ + scoped_connection(const connection &other) + : conn{other} + {} + + /*! @brief Default copy constructor, deleted on purpose. */ + scoped_connection(const scoped_connection &) = delete; + + /*! @brief Automatically breaks the link on destruction. */ + ~scoped_connection() { + conn.release(); + } + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This scoped connection. + */ + scoped_connection & operator=(const scoped_connection &) = delete; + + /** + * @brief Acquires a connection. + * @param other The connection object to acquire. + * @return This scoped connection. + */ + scoped_connection & operator=(connection other) { + conn = std::move(other); + return *this; + } + + /** + * @brief Checks whether a scoped connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return static_cast(conn); + } + + /*! @brief Breaks the connection. */ + void release() { + conn.release(); + } + +private: + connection conn; +}; + + +/** + * @brief Sink class. + * + * A sink is used to connect listeners to signals and to disconnect them.
+ * The function type for a listener is the one of the signal to which it + * belongs. + * + * The clear separation between a signal and a sink permits to store the former + * as private data member without exposing the publish functionality to the + * users of the class. + * + * @warning + * Lifetime of a sink must not overcome that of the signal to which it refers. + * In any other case, attempting to use a sink results in undefined behavior. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +class sink { + using signal_type = sigh; + using difference_type = typename std::iterator_traits::difference_type; + + template + static void release(Type value_or_instance, void *signal) { + sink{*static_cast(signal)}.disconnect(value_or_instance); + } + + template + static void release(void *signal) { + sink{*static_cast(signal)}.disconnect(); + } + +public: + /** + * @brief Constructs a sink that is allowed to modify a given signal. + * @param ref A valid reference to a signal object. + */ + sink(sigh &ref) ENTT_NOEXCEPT + : offset{}, + signal{&ref} + {} + + /** + * @brief Returns false if at least a listener is connected to the sink. + * @return True if the sink has no listeners connected, false otherwise. + */ + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return signal->calls.empty(); + } + + /** + * @brief Returns a sink that connects before a given free function or an + * unbound member. + * @tparam Function A valid free function pointer. + * @return A properly initialized sink object. + */ + template + [[nodiscard]] sink before() { + delegate call{}; + call.template connect(); + + const auto &calls = signal->calls; + const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); + + sink other{*this}; + other.offset = std::distance(it, calls.cend()); + return other; + } + + /** + * @brief Returns a sink that connects before a free function with payload + * or a bound member. + * @tparam Candidate Member or free function to look for. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized sink object. + */ + template + [[nodiscard]] sink before(Type &&value_or_instance) { + delegate call{}; + call.template connect(value_or_instance); + + const auto &calls = signal->calls; + const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); + + sink other{*this}; + other.offset = std::distance(it, calls.cend()); + return other; + } + + /** + * @brief Returns a sink that connects before a given instance or specific + * payload. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized sink object. + */ + template + [[nodiscard]] sink before(Type &value_or_instance) { + return before(&value_or_instance); + } + + /** + * @brief Returns a sink that connects before a given instance or specific + * payload. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid pointer that fits the purpose. + * @return A properly initialized sink object. + */ + template + [[nodiscard]] sink before(Type *value_or_instance) { + sink other{*this}; + + if(value_or_instance) { + const auto &calls = signal->calls; + const auto it = std::find_if(calls.cbegin(), calls.cend(), [value_or_instance](const auto &delegate) { + return delegate.instance() == value_or_instance; + }); + + other.offset = std::distance(it, calls.cend()); + } + + return other; + } + + /** + * @brief Returns a sink that connects before anything else. + * @return A properly initialized sink object. + */ + [[nodiscard]] sink before() { + sink other{*this}; + other.offset = signal->calls.size(); + return other; + } + + /** + * @brief Connects a free function or an unbound member to a signal. + * + * The signal handler performs checks to avoid multiple connections for the + * same function. + * + * @tparam Candidate Function or member to connect to the signal. + * @return A properly initialized connection object. + */ + template + connection connect() { + disconnect(); + + delegate call{}; + call.template connect(); + signal->calls.insert(signal->calls.end() - offset, std::move(call)); + + delegate conn{}; + conn.template connect<&release>(); + return { std::move(conn), signal }; + } + + /** + * @brief Connects a free function with payload or a bound member to a + * signal. + * + * The signal isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of the instance overcomes + * the one of the signal. On the other side, the signal handler performs + * checks to avoid multiple connections for the same function.
+ * When used to connect a free function with payload, its signature must be + * such that the instance is the first argument before the ones used to + * define the signal itself. + * + * @tparam Candidate Function or member to connect to the signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized connection object. + */ + template + connection connect(Type &&value_or_instance) { + disconnect(value_or_instance); + + delegate call{}; + call.template connect(value_or_instance); + signal->calls.insert(signal->calls.end() - offset, std::move(call)); + + delegate conn{}; + conn.template connect<&release>(value_or_instance); + return { std::move(conn), signal }; + } + + /** + * @brief Disconnects a free function or an unbound member from a signal. + * @tparam Candidate Function or member to disconnect from the signal. + */ + template + void disconnect() { + auto &calls = signal->calls; + delegate call{}; + call.template connect(); + calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); + } + + /** + * @brief Disconnects a free function with payload or a bound member from a + * signal. + * @tparam Candidate Function or member to disconnect from the signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type &&value_or_instance) { + auto &calls = signal->calls; + delegate call{}; + call.template connect(value_or_instance); + calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); + } + + /** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type &value_or_instance) { + disconnect(&value_or_instance); + } + + /** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type *value_or_instance) { + if(value_or_instance) { + auto &calls = signal->calls; + calls.erase(std::remove_if(calls.begin(), calls.end(), [value_or_instance](const auto &delegate) { + return delegate.instance() == value_or_instance; + }), calls.end()); + } + } + + /*! @brief Disconnects all the listeners from a signal. */ + void disconnect() { + signal->calls.clear(); + } + +private: + difference_type offset; + signal_type *signal; +}; + + +/** + * @brief Deduction guide. + * + * It allows to deduce the function type of a sink directly from the signal it + * refers to. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +sink(sigh &) ENTT_NOEXCEPT -> sink; + + +} + + +#endif + +// #include "entity.hpp" +#ifndef ENTT_ENTITY_ENTITY_HPP +#define ENTT_ENTITY_ENTITY_HPP + + +#include +#include +#include +// #include "../config/config.h" + + + +namespace entt { + + +/** + * @brief Entity traits. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is an accepted entity type. + */ +template +struct entt_traits; + + +/** + * @brief Entity traits for enumeration types. + * @tparam Type The type to check. + */ +template +struct entt_traits>> + : entt_traits> +{}; + + +/** + * @brief Entity traits for a 16 bits entity identifier. + * + * A 16 bits entity identifier guarantees: + * + * * 12 bits for the entity number (up to 4k entities). + * * 4 bit for the version (resets in [0-15]). + */ +template<> +struct entt_traits { + /*! @brief Underlying entity type. */ + using entity_type = std::uint16_t; + /*! @brief Underlying version type. */ + using version_type = std::uint8_t; + /*! @brief Difference type. */ + using difference_type = std::int16_t; + + /*! @brief Mask to use to get the entity number out of an identifier. */ + static constexpr entity_type entity_mask = 0xFFF; + /*! @brief Mask to use to get the version out of an identifier. */ + static constexpr entity_type version_mask = 0xF; + /*! @brief Extent of the entity number within an identifier. */ + static constexpr std::size_t entity_shift = 12u; +}; + + +/** + * @brief Entity traits for a 32 bits entity identifier. + * + * A 32 bits entity identifier guarantees: + * + * * 20 bits for the entity number (suitable for almost all the games). + * * 12 bit for the version (resets in [0-4095]). + */ +template<> +struct entt_traits { + /*! @brief Underlying entity type. */ + using entity_type = std::uint32_t; + /*! @brief Underlying version type. */ + using version_type = std::uint16_t; + /*! @brief Difference type. */ + using difference_type = std::int32_t; + + /*! @brief Mask to use to get the entity number out of an identifier. */ + static constexpr entity_type entity_mask = 0xFFFFF; + /*! @brief Mask to use to get the version out of an identifier. */ + static constexpr entity_type version_mask = 0xFFF; + /*! @brief Extent of the entity number within an identifier. */ + static constexpr std::size_t entity_shift = 20u; +}; + + +/** + * @brief Entity traits for a 64 bits entity identifier. + * + * A 64 bits entity identifier guarantees: + * + * * 32 bits for the entity number (an indecently large number). + * * 32 bit for the version (an indecently large number). + */ +template<> +struct entt_traits { + /*! @brief Underlying entity type. */ + using entity_type = std::uint64_t; + /*! @brief Underlying version type. */ + using version_type = std::uint32_t; + /*! @brief Difference type. */ + using difference_type = std::int64_t; + + /*! @brief Mask to use to get the entity number out of an identifier. */ + static constexpr entity_type entity_mask = 0xFFFFFFFF; + /*! @brief Mask to use to get the version out of an identifier. */ + static constexpr entity_type version_mask = 0xFFFFFFFF; + /*! @brief Extent of the entity number within an identifier. */ + static constexpr std::size_t entity_shift = 32u; +}; + + +/** + * @brief Converts an entity type to its underlying type. + * @tparam Entity The value type. + * @param entity The value to convert. + * @return The integral representation of the given value. + */ +template +[[nodiscard]] constexpr auto to_integral(const Entity entity) ENTT_NOEXCEPT { + return static_cast::entity_type>(entity); +} + + +/*! @brief Null object for all entity identifiers. */ +struct null_t { + /** + * @brief Converts the null object to identifiers of any type. + * @tparam Entity Type of entity identifier. + * @return The null representation for the given identifier. + */ + template + [[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT { + return Entity{entt_traits::entity_mask}; + } + + /** + * @brief Compares two null objects. + * @return True in all cases. + */ + [[nodiscard]] constexpr bool operator==(null_t) const ENTT_NOEXCEPT { + return true; + } + + /** + * @brief Compares two null objects. + * @return False in all cases. + */ + [[nodiscard]] constexpr bool operator!=(null_t) const ENTT_NOEXCEPT { + return false; + } + + /** + * @brief Compares a null object and an entity identifier of any type. + * @tparam Entity Type of entity identifier. + * @param entity Entity identifier with which to compare. + * @return False if the two elements differ, true otherwise. + */ + template + [[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT { + return (to_integral(entity) & entt_traits::entity_mask) == to_integral(static_cast(*this)); + } + + /** + * @brief Compares a null object and an entity identifier of any type. + * @tparam Entity Type of entity identifier. + * @param entity Entity identifier with which to compare. + * @return True if the two elements differ, false otherwise. + */ + template + [[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT { + return !(entity == *this); + } +}; + + +/** + * @brief Compares a null object and an entity identifier of any type. + * @tparam Entity Type of entity identifier. + * @param entity Entity identifier with which to compare. + * @param other A null object yet to be converted. + * @return False if the two elements differ, true otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const Entity entity, null_t other) ENTT_NOEXCEPT { + return other.operator==(entity); +} + + +/** + * @brief Compares a null object and an entity identifier of any type. + * @tparam Entity Type of entity identifier. + * @param entity Entity identifier with which to compare. + * @param other A null object yet to be converted. + * @return True if the two elements differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const Entity entity, null_t other) ENTT_NOEXCEPT { + return !(other == entity); +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Compile-time constant for null entities. + * + * There exist implicit conversions from this variable to entity identifiers of + * any allowed type. Similarly, there exist comparision operators between the + * null entity and any other entity identifier. + */ +inline constexpr null_t null{}; + + +} + + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_ENTITY_FWD_HPP +#define ENTT_ENTITY_FWD_HPP + + +// #include "../core/fwd.hpp" + + + +namespace entt { + + +template +class basic_registry; + + +template +class basic_view; + + +template +class basic_runtime_view; + + +template +class basic_group; + + +template +class basic_observer; + + +template +struct basic_actor; + + +template +struct basic_handle; + + +template +class basic_snapshot; + + +template +class basic_snapshot_loader; + + +template +class basic_continuous_loader; + + +/*! @brief Default entity identifier. */ +enum class entity: id_type {}; + + +/*! @brief Alias declaration for the most common use case. */ +using registry = basic_registry; + + +/*! @brief Alias declaration for the most common use case. */ +using observer = basic_observer; + + +/*! @brief Alias declaration for the most common use case. */ +using actor [[deprecated("Consider using the handle class instead")]] = basic_actor; + + +/*! @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. */ +using snapshot = basic_snapshot; + + +/*! @brief Alias declaration for the most common use case. */ +using snapshot_loader = basic_snapshot_loader; + + +/*! @brief Alias declaration for the most common use case. */ +using continuous_loader = basic_continuous_loader; + + +/** + * @brief Alias declaration for the most common use case. + * @tparam Types Types of components iterated by the view. + */ +template +using view = basic_view; + + +/*! @brief Alias declaration for the most common use case. */ +using runtime_view = basic_runtime_view; + + +/** + * @brief Alias declaration for the most common use case. + * @tparam Types Types of components iterated by the group. + */ +template +using group = basic_group; + + +} + + +#endif + +// #include "group.hpp" +#ifndef ENTT_ENTITY_GROUP_HPP +#define ENTT_ENTITY_GROUP_HPP + + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/type_traits.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "pool.hpp" +#ifndef ENTT_ENTITY_POOL_HPP +#define ENTT_ENTITY_POOL_HPP + + +#include +// #include "storage.hpp" +#ifndef ENTT_ENTITY_STORAGE_HPP +#define ENTT_ENTITY_STORAGE_HPP + + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/algorithm.hpp" + +// #include "../core/type_traits.hpp" + +// #include "entity.hpp" + +// #include "sparse_set.hpp" +#ifndef ENTT_ENTITY_SPARSE_SET_HPP +#define ENTT_ENTITY_SPARSE_SET_HPP + + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/algorithm.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Basic sparse set implementation. + * + * Sparse set or packed array or whatever is the name users give it.
+ * Two arrays: an _external_ one and an _internal_ one; a _sparse_ one and a + * _packed_ one; one used for direct access through contiguous memory, the other + * one used to get the data through an extra level of indirection.
+ * This is largely used by the registry to offer users the fastest access ever + * to the components. Views and groups in general are almost entirely designed + * around sparse sets. + * + * This type of data structure is widely documented in the literature and on the + * web. This is nothing more than a customized implementation suitable for the + * purpose of the framework. + * + * @note + * Internal data structures arrange elements to maximize performance. There are + * no guarantees that entities are returned in the insertion order when iterate + * a sparse set. Do not make assumption on the order in any case. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class sparse_set { + static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0), "ENTT_PAGE_SIZE must be a power of two"); + static constexpr auto entt_per_page = ENTT_PAGE_SIZE / sizeof(Entity); + + using traits_type = entt_traits; + using page_type = std::unique_ptr; + + class sparse_set_iterator final { + friend class sparse_set; + + using packed_type = std::vector; + using index_type = typename traits_type::difference_type; + + sparse_set_iterator(const packed_type &ref, const index_type idx) ENTT_NOEXCEPT + : packed{&ref}, index{idx} + {} + + public: + using difference_type = index_type; + using value_type = Entity; + using pointer = const value_type *; + using reference = const value_type &; + using iterator_category = std::random_access_iterator_tag; + + sparse_set_iterator() ENTT_NOEXCEPT = default; + + sparse_set_iterator & operator++() ENTT_NOEXCEPT { + return --index, *this; + } + + sparse_set_iterator operator++(int) ENTT_NOEXCEPT { + iterator orig = *this; + return ++(*this), orig; + } + + sparse_set_iterator & operator--() ENTT_NOEXCEPT { + return ++index, *this; + } + + sparse_set_iterator operator--(int) ENTT_NOEXCEPT { + sparse_set_iterator orig = *this; + return operator--(), orig; + } + + sparse_set_iterator & operator+=(const difference_type value) ENTT_NOEXCEPT { + index -= value; + return *this; + } + + sparse_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + sparse_set_iterator copy = *this; + return (copy += value); + } + + sparse_set_iterator & operator-=(const difference_type value) ENTT_NOEXCEPT { + return (*this += -value); + } + + sparse_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + return (*this + -value); + } + + difference_type operator-(const sparse_set_iterator &other) const ENTT_NOEXCEPT { + return other.index - index; + } + + [[nodiscard]] reference operator[](const difference_type value) const { + const auto pos = size_type(index-value-1u); + return (*packed)[pos]; + } + + [[nodiscard]] bool operator==(const sparse_set_iterator &other) const ENTT_NOEXCEPT { + return other.index == index; + } + + [[nodiscard]] bool operator!=(const sparse_set_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + [[nodiscard]] bool operator<(const sparse_set_iterator &other) const ENTT_NOEXCEPT { + return index > other.index; + } + + [[nodiscard]] bool operator>(const sparse_set_iterator &other) const ENTT_NOEXCEPT { + return index < other.index; + } + + [[nodiscard]] bool operator<=(const sparse_set_iterator &other) const ENTT_NOEXCEPT { + return !(*this > other); + } + + [[nodiscard]] bool operator>=(const sparse_set_iterator &other) const ENTT_NOEXCEPT { + return !(*this < other); + } + + [[nodiscard]] pointer operator->() const { + const auto pos = size_type(index-1u); + return &(*packed)[pos]; + } + + [[nodiscard]] reference operator*() const { + return *operator->(); + } + + private: + const packed_type *packed; + index_type index; + }; + + [[nodiscard]] auto page(const Entity entt) const ENTT_NOEXCEPT { + return size_type{(to_integral(entt) & traits_type::entity_mask) / entt_per_page}; + } + + [[nodiscard]] auto offset(const Entity entt) const ENTT_NOEXCEPT { + return size_type{to_integral(entt) & (entt_per_page - 1)}; + } + + [[nodiscard]] page_type & assure(const std::size_t pos) { + if(!(pos < sparse.size())) { + sparse.resize(pos+1); + } + + if(!sparse[pos]) { + sparse[pos].reset(new entity_type[entt_per_page]); + // null is safe in all cases for our purposes + for(auto *first = sparse[pos].get(), *last = first + entt_per_page; first != last; ++first) { + *first = null; + } + } + + return sparse[pos]; + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Random access iterator type. */ + using iterator = sparse_set_iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = const entity_type *; + + /*! @brief Default constructor. */ + sparse_set() = default; + + /*! @brief Default move constructor. */ + sparse_set(sparse_set &&) = default; + + /*! @brief Default destructor. */ + virtual ~sparse_set() = default; + + /*! @brief Default move assignment operator. @return This sparse set. */ + sparse_set & operator=(sparse_set &&) = default; + + /** + * @brief Increases the capacity of a sparse set. + * + * If the new capacity is greater than the current capacity, new storage is + * allocated, otherwise the method does nothing. + * + * @param cap Desired capacity. + */ + void reserve(const size_type cap) { + 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]] size_type capacity() const ENTT_NOEXCEPT { + return packed.capacity(); + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() { + // conservative approach + if(packed.empty()) { + sparse.clear(); + } + + sparse.shrink_to_fit(); + packed.shrink_to_fit(); + } + + /** + * @brief Returns the extent of a sparse set. + * + * The extent of a sparse set is also the size of the internal sparse array. + * There is no guarantee that the internal packed array has the same size. + * Usually the size of the internal sparse array is equal or greater than + * the one of the internal packed array. + * + * @return Extent of the sparse set. + */ + [[nodiscard]] size_type extent() const ENTT_NOEXCEPT { + return sparse.size() * entt_per_page; + } + + /** + * @brief Returns the number of elements in a sparse set. + * + * The number of elements is also the size of the internal packed array. + * There is no guarantee that the internal sparse array has the same size. + * Usually the size of the internal sparse array is equal or greater than + * the one of the internal packed array. + * + * @return Number of elements. + */ + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + return packed.size(); + } + + /** + * @brief Checks whether a sparse set is empty. + * @return True if the sparse set is empty, false otherwise. + */ + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return packed.empty(); + } + + /** + * @brief Direct access to the internal packed array. + * + * 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 internal packed array. + */ + [[nodiscard]] const entity_type * data() const ENTT_NOEXCEPT { + return packed.data(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * The returned iterator points to the first entity of the internal packed + * array. If the sparse set is empty, the returned iterator will be equal to + * `end()`. + * + * @return An iterator to the first entity of the internal packed array. + */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + const typename traits_type::difference_type pos = packed.size(); + return iterator{packed, pos}; + } + + /** + * @brief Returns an iterator to the end. + * + * The returned iterator points to the element following the last entity in + * the internal packed array. Attempting to dereference the returned + * iterator results in undefined behavior. + * + * @return An iterator to the element following the last entity of the + * internal packed array. + */ + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + return iterator{packed, {}}; + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * The returned iterator points to the first entity of the reversed internal + * packed array. If the sparse set is empty, the returned iterator will be + * equal to `rend()`. + * + * @return An iterator to the first entity of the reversed internal packed + * array. + */ + [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { + return packed.data(); + } + + /** + * @brief Returns a reverse iterator to the end. + * + * The returned iterator points to the element following the last entity in + * the reversed internal packed array. Attempting to dereference the + * returned iterator results in undefined behavior. + * + * @return An iterator to the element following the last entity of the + * reversed internal packed array. + */ + [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { + return rbegin() + packed.size(); + } + + /** + * @brief Finds an entity. + * @param entt A valid entity identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const { + return contains(entt) ? --(end() - index(entt)) : end(); + } + + /** + * @brief Checks if a sparse set contains an entity. + * @param entt A valid entity identifier. + * @return True if the sparse set contains the entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const { + const auto curr = page(entt); + // testing against null permits to avoid accessing the packed array + return (curr < sparse.size() && sparse[curr] && sparse[curr][offset(entt)] != null); + } + + /** + * @brief Returns the position of an entity in a sparse set. + * + * @warning + * Attempting to get the position of an entity that doesn't belong to the + * sparse set results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * sparse set doesn't contain the given entity. + * + * @param entt A valid entity identifier. + * @return The position of the entity in the sparse set. + */ + [[nodiscard]] size_type index(const entity_type entt) const { + ENTT_ASSERT(contains(entt)); + return size_type{to_integral(sparse[page(entt)][offset(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.
+ * An assertion will abort the execution at runtime in debug mode if the + * sparse set already contains the given entity. + * + * @param entt A valid entity identifier. + */ + void emplace(const entity_type entt) { + ENTT_ASSERT(!contains(entt)); + assure(page(entt))[offset(entt)] = entity_type(static_cast(packed.size())); + packed.push_back(entt); + } + + /** + * @brief Assigns one or more entities to a sparse set. + * + * @warning + * Attempting to assign an entity that already belongs to the sparse set + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * sparse set already contains the given entity. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + template + void insert(It first, It last) { + auto next = static_cast(packed.size()); + packed.insert(packed.end(), first, last); + + while(first != last) { + const auto entt = *(first++); + ENTT_ASSERT(!contains(entt)); + assure(page(entt))[offset(entt)] = entity_type(next++); + } + } + + /** + * @brief Removes an entity from a sparse set. + * + * @warning + * Attempting to remove an entity that doesn't belong to the sparse set + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * sparse set doesn't contain the given entity. + * + * @param entt A valid entity identifier. + */ + void erase(const entity_type entt) { + ENTT_ASSERT(contains(entt)); + const auto curr = page(entt); + const auto pos = offset(entt); + packed[size_type{to_integral(sparse[curr][pos])}] = packed.back(); + sparse[page(packed.back())][offset(packed.back())] = sparse[curr][pos]; + sparse[curr][pos] = null; + packed.pop_back(); + } + + /** + * @brief Swaps two entities in the internal packed array. + * + * For what it's worth, this function affects both the internal sparse array + * and the internal packed array. Users should not care of that anyway. + * + * @warning + * Attempting to swap entities that don't belong to the sparse set results + * in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * sparse set doesn't contain the given entities. + * + * @param lhs A valid entity identifier. + * @param rhs A valid entity identifier. + */ + virtual void swap(const entity_type lhs, const entity_type rhs) { + auto &from = sparse[page(lhs)][offset(lhs)]; + auto &to = sparse[page(rhs)][offset(rhs)]; + std::swap(packed[size_type{to_integral(from)}], packed[size_type{to_integral(to)}]); + std::swap(from, to); + } + + /** + * @brief Sort elements according to the given comparison function. + * + * Sort the elements so that iterating the range with a couple of iterators + * returns them in the expected order. See `begin` and `end` for more + * details. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to the following: + * + * @code{.cpp} + * bool(const Entity, const Entity); + * @endcode + * + * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function oject must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort(iterator first, iterator last, Compare compare, Sort algo = Sort{}, Args &&... args) { + ENTT_ASSERT(!(last < first)); + ENTT_ASSERT(!(last > end())); + + const auto length = std::distance(first, last); + const auto skip = std::distance(last, end()); + const auto to = packed.rend() - skip; + const auto from = to - length; + + algo(from, to, std::move(compare), std::forward(args)...); + + for(size_type pos = skip, end = skip+length; pos < end; ++pos) { + sparse[page(packed[pos])][offset(packed[pos])] = entity_type(static_cast(pos)); + } + } + + /** + * @brief Sort elements according to the given comparison function. + * + * @sa sort + * + * This function is a slightly slower version of `sort` that invokes the + * caller to indicate which entities are swapped.
+ * It's recommended when the caller wants to sort its own data structures to + * align them with the order induced in the sparse set. + * + * The signature of the callback should be equivalent to the following: + * + * @code{.cpp} + * bool(const Entity, const Entity); + * @endcode + * + * @tparam Apply Type of function object to invoke to notify the caller. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param apply A valid function object to use as a callback. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void arrange(iterator first, iterator last, Apply apply, Compare compare, Sort algo = Sort{}, Args &&... args) { + ENTT_ASSERT(!(last < first)); + ENTT_ASSERT(!(last > end())); + + const auto length = std::distance(first, last); + const auto skip = std::distance(last, end()); + const auto to = packed.rend() - skip; + const auto from = to - length; + + algo(from, to, std::move(compare), std::forward(args)...); + + for(size_type pos = skip, end = skip+length; pos < end; ++pos) { + auto curr = pos; + auto next = index(packed[curr]); + + while(curr != next) { + apply(packed[curr], packed[next]); + sparse[page(packed[curr])][offset(packed[curr])] = entity_type(static_cast(curr)); + + curr = next; + next = index(packed[curr]); + } + } + } + + /** + * @brief Sort entities according to their order in another sparse set. + * + * Entities that are part of both the sparse sets are ordered internally + * according to the order they have in `other`. All the other entities goes + * to the end of the list and there are no guarantees on their order.
+ * In other terms, this function can be used to impose the same order on two + * sets by using one of them as a master and the other one as a slave. + * + * Iterating the sparse set with a couple of iterators returns elements in + * the expected order after a call to `respect`. See `begin` and `end` for + * more details. + * + * @param other The sparse sets that imposes the order of the entities. + */ + void respect(const sparse_set &other) { + const auto to = other.end(); + auto from = other.begin(); + + size_type pos = packed.size() - 1; + + while(pos && from != to) { + if(contains(*from)) { + if(*from != packed[pos]) { + swap(packed[pos], *from); + } + + --pos; + } + + ++from; + } + } + + /** + * @brief Clears a sparse set. + */ + void clear() ENTT_NOEXCEPT { + sparse.clear(); + packed.clear(); + } + +private: + std::vector sparse; + std::vector packed; +}; + + +} + + +#endif + + + +namespace entt { + + +/** + * @brief Basic storage implementation. + * + * This class is a refinement of a sparse set that associates an object to an + * entity. The main purpose of this class is to extend sparse sets to store + * components in a registry. It guarantees fast access both to the elements and + * to the entities. + * + * @note + * Entities and objects have the same order. It's guaranteed both in case of raw + * access (either to entities or objects) and when using random or input access + * iterators. + * + * @note + * Internal data structures arrange elements to maximize performance. There are + * no guarantees that objects are returned in the insertion order when iterate + * a storage. Do not make assumption on the order in any case. + * + * @warning + * Empty types aren't explicitly instantiated. Therefore, many of the functions + * normally available for non-empty types will not be available for empty ones. + * + * @sa sparse_set + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Type Type of objects assigned to the entities. + */ +template> +class storage: public sparse_set { + static_assert(std::is_move_constructible_v && std::is_move_assignable_v, "The managed type must be at least move constructible and assignable"); + + using underlying_type = sparse_set; + using traits_type = entt_traits; + + template + class storage_iterator final { + friend class storage; + + using instance_type = std::conditional_t, std::vector>; + using index_type = typename traits_type::difference_type; + + storage_iterator(instance_type &ref, const index_type idx) ENTT_NOEXCEPT + : instances{&ref}, index{idx} + {} + + public: + using difference_type = index_type; + using value_type = Type; + using pointer = std::conditional_t; + using reference = std::conditional_t; + using iterator_category = std::random_access_iterator_tag; + + storage_iterator() ENTT_NOEXCEPT = default; + + storage_iterator & operator++() ENTT_NOEXCEPT { + return --index, *this; + } + + storage_iterator operator++(int) ENTT_NOEXCEPT { + storage_iterator orig = *this; + return ++(*this), orig; + } + + storage_iterator & operator--() ENTT_NOEXCEPT { + return ++index, *this; + } + + storage_iterator operator--(int) ENTT_NOEXCEPT { + storage_iterator orig = *this; + return operator--(), orig; + } + + storage_iterator & operator+=(const difference_type value) ENTT_NOEXCEPT { + index -= value; + return *this; + } + + storage_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + storage_iterator copy = *this; + return (copy += value); + } + + storage_iterator & operator-=(const difference_type value) ENTT_NOEXCEPT { + return (*this += -value); + } + + storage_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + return (*this + -value); + } + + difference_type operator-(const storage_iterator &other) const ENTT_NOEXCEPT { + return other.index - index; + } + + [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { + const auto pos = size_type(index-value-1); + return (*instances)[pos]; + } + + [[nodiscard]] bool operator==(const storage_iterator &other) const ENTT_NOEXCEPT { + return other.index == index; + } + + [[nodiscard]] bool operator!=(const storage_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + [[nodiscard]] bool operator<(const storage_iterator &other) const ENTT_NOEXCEPT { + return index > other.index; + } + + [[nodiscard]] bool operator>(const storage_iterator &other) const ENTT_NOEXCEPT { + return index < other.index; + } + + [[nodiscard]] bool operator<=(const storage_iterator &other) const ENTT_NOEXCEPT { + return !(*this > other); + } + + [[nodiscard]] bool operator>=(const storage_iterator &other) const ENTT_NOEXCEPT { + return !(*this < other); + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + const auto pos = size_type(index-1u); + return &(*instances)[pos]; + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return *operator->(); + } + + private: + instance_type *instances; + index_type index; + }; + +public: + /*! @brief Type of the objects associated with the entities. */ + using object_type = Type; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Random access iterator type. */ + using iterator = storage_iterator; + /*! @brief Constant random access iterator type. */ + using const_iterator = storage_iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = Type *; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = const Type *; + + + /** + * @brief Increases the capacity of a storage. + * + * If the new capacity is greater than the current capacity, new storage is + * allocated, otherwise the method does nothing. + * + * @param cap Desired capacity. + */ + void reserve(const size_type cap) { + underlying_type::reserve(cap); + instances.reserve(cap); + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() { + underlying_type::shrink_to_fit(); + instances.shrink_to_fit(); + } + + /** + * @brief Direct access to the array of objects. + * + * The returned pointer is such that range `[raw(), raw() + size()]` is + * always a valid range, even if the container is empty. + * + * @note + * Objects are in the reverse order as returned by the `begin`/`end` + * iterators. + * + * @return A pointer to the array of objects. + */ + [[nodiscard]] const object_type * raw() const ENTT_NOEXCEPT { + return instances.data(); + } + + /*! @copydoc raw */ + [[nodiscard]] object_type * raw() ENTT_NOEXCEPT { + return const_cast(std::as_const(*this).raw()); + } + + /** + * @brief Returns an iterator to the beginning. + * + * The returned iterator points to the first instance of the internal array. + * If the storage is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { + const typename traits_type::difference_type pos = underlying_type::size(); + return const_iterator{instances, pos}; + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() ENTT_NOEXCEPT { + const typename traits_type::difference_type pos = underlying_type::size(); + return iterator{instances, pos}; + } + + /** + * @brief Returns an iterator to the end. + * + * The returned iterator points to the element following the last instance + * of the internal array. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { + return const_iterator{instances, {}}; + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() ENTT_NOEXCEPT { + return iterator{instances, {}}; + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * The returned iterator points to the first instance of the reversed + * internal array. If the storage is empty, the returned iterator will be + * equal to `rend()`. + * + * @return An iterator to the first instance of the reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crbegin() const ENTT_NOEXCEPT { + return instances.data(); + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const ENTT_NOEXCEPT { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() ENTT_NOEXCEPT { + return instances.data(); + } + + /** + * @brief Returns a reverse iterator to the end. + * + * The returned iterator points to the element following the last instance + * of the reversed internal array. Attempting to dereference the returned + * iterator results in undefined behavior. + * + * @return An iterator to the element following the last instance of the + * reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crend() const ENTT_NOEXCEPT { + return crbegin() + instances.size(); + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const ENTT_NOEXCEPT { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() ENTT_NOEXCEPT { + return rbegin() + instances.size(); + } + + /** + * @brief Returns the object associated with an entity. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * storage doesn't contain the given entity. + * + * @param entt A valid entity identifier. + * @return The object associated with the entity. + */ + [[nodiscard]] const object_type & get(const entity_type entt) const { + return instances[underlying_type::index(entt)]; + } + + /*! @copydoc get */ + [[nodiscard]] object_type & get(const entity_type entt) { + return const_cast(std::as_const(*this).get(entt)); + } + + /** + * @brief Returns a pointer to the object associated with an entity, if any. + * @param entt A valid entity identifier. + * @return The object associated with the entity, if any. + */ + [[nodiscard]] const object_type * try_get(const entity_type entt) const { + return underlying_type::contains(entt) ? (instances.data() + underlying_type::index(entt)) : nullptr; + } + + /*! @copydoc try_get */ + [[nodiscard]] object_type * try_get(const entity_type entt) { + return const_cast(std::as_const(*this).try_get(entt)); + } + + /** + * @brief Assigns an entity to a storage and constructs its object. + * + * This version accept both types that can be constructed in place directly + * and types like aggregates that do not work well with a placement new as + * performed usually under the hood during an _emplace back_. + * + * @warning + * Attempting to use an entity that already belongs to the storage results + * in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * storage already contains the given entity. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entt A valid entity identifier. + * @param args Parameters to use to construct an object for the entity. + */ + template + void emplace(const entity_type entt, Args &&... args) { + if constexpr(std::is_aggregate_v) { + instances.push_back(Type{std::forward(args)...}); + } else { + instances.emplace_back(std::forward(args)...); + } + + // entity goes after component in case constructor throws + underlying_type::emplace(entt); + } + + /** + * @brief Assigns one or more entities to a storage and constructs their + * objects from a given instance. + * + * @warning + * Attempting to assign an entity that already belongs to the storage + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * storage already contains the given entity. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param value An instance of the object to construct. + */ + template + void insert(It first, It last, const object_type &value = {}) { + instances.insert(instances.end(), std::distance(first, last), value); + // entities go after components in case constructors throw + underlying_type::insert(first, last); + } + + /** + * @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. + * @param to An iterator past the last element of the range of objects. + */ + template + void insert(EIt first, EIt last, CIt from, CIt to) { + instances.insert(instances.end(), from, to); + // entities go after components in case constructors throw + underlying_type::insert(first, last); + } + + /** + * @brief Removes an entity from a storage and destroys its object. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * storage doesn't contain the given entity. + * + * @param entt A valid entity identifier. + */ + void erase(const entity_type entt) { + auto other = std::move(instances.back()); + instances[underlying_type::index(entt)] = std::move(other); + instances.pop_back(); + underlying_type::erase(entt); + } + + /** + * @brief Swaps entities and objects in the internal packed arrays. + * + * @warning + * Attempting to swap entities that don't belong to the sparse set results + * in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * sparse set doesn't contain the given entities. + * + * @param lhs A valid entity identifier. + * @param rhs A valid entity identifier. + */ + void swap(const entity_type lhs, const entity_type rhs) override { + std::swap(instances[underlying_type::index(lhs)], instances[underlying_type::index(rhs)]); + underlying_type::swap(lhs, rhs); + } + + /** + * @brief Sort elements according to the given comparison function. + * + * Sort the elements so that iterating the range with a couple of iterators + * returns them in the expected order. See `begin` and `end` for more + * details. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(const Entity, const Entity); + * bool(const Type &, const Type &); + * @endcode + * + * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function oject must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * @warning + * Empty types are never instantiated. Therefore, only comparison function + * objects that require to return entities rather than components are + * accepted. + * + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort(iterator first, iterator last, Compare compare, Sort algo = Sort{}, Args &&... args) { + ENTT_ASSERT(!(last < first)); + ENTT_ASSERT(!(last > end())); + + const auto from = underlying_type::begin() + std::distance(begin(), first); + const auto to = from + std::distance(first, last); + + const auto apply = [this](const auto lhs, const auto rhs) { + std::swap(instances[underlying_type::index(lhs)], instances[underlying_type::index(rhs)]); + }; + + if constexpr(std::is_invocable_v) { + underlying_type::arrange(from, to, std::move(apply), [this, compare = std::move(compare)](const auto lhs, const auto rhs) { + return compare(std::as_const(instances[underlying_type::index(lhs)]), std::as_const(instances[underlying_type::index(rhs)])); + }, std::move(algo), std::forward(args)...); + } else { + underlying_type::arrange(from, to, std::move(apply), std::move(compare), std::move(algo), std::forward(args)...); + } + } + + /*! @brief Clears a storage. */ + void clear() { + underlying_type::clear(); + instances.clear(); + } + +private: + std::vector instances; +}; + + +/*! @copydoc storage */ +template +class storage>>: public sparse_set { + using underlying_type = sparse_set; + +public: + /*! @brief Type of the objects associated with the entities. */ + using object_type = Type; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + + /** + * @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.
+ * An assertion will abort the execution at runtime in debug mode if the + * storage already contains the given entity. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entt A valid entity identifier. + * @param args Parameters to use to construct an object for the entity. + */ + template + void emplace(const entity_type entt, Args &&... args) { + [[maybe_unused]] object_type instance{std::forward(args)...}; + underlying_type::emplace(entt); + } + + /** + * @brief Assigns one or more entities to a storage. + * + * @warning + * Attempting to assign an entity that already belongs to the storage + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * storage already contains the given entity. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + template + void insert(It first, It last, const object_type & = {}) { + underlying_type::insert(first, last); + } +}; + + +} + + +#endif + + + +namespace entt { + + +/** + * @brief Applies component-to-pool conversion and defines the resulting type as + * the member typedef type. + * + * Formally: + * + * * If the component type is a non-const one, the member typedef type is the + * declared storage type. + * * If the component type is a const one, the member typedef type is the + * declared storage type, except it has a const-qualifier added. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Type Type of objects assigned to the entities. + */ +template +struct pool { + /*! @brief Resulting type after component-to-pool conversion. */ + using type = storage; +}; + + +/*! @copydoc pool */ +template +struct pool { + /*! @brief Resulting type after component-to-pool conversion. */ + using type = std::add_const_t>::type>; +}; + + +/** + * @brief Alias declaration to use to make component-to-pool conversions. + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Type Type of objects assigned to the entities. + */ +template +using pool_t = typename pool::type; + + +} + + +#endif + +// #include "sparse_set.hpp" + +// #include "utility.hpp" +#ifndef ENTT_ENTITY_UTILITY_HPP +#define ENTT_ENTITY_UTILITY_HPP + + +// #include "../core/type_traits.hpp" + + + +namespace entt { + + +/** + * @brief Alias for exclusion lists. + * @tparam Type List of types. + */ +template +struct exclude_t: type_list {}; + + +/** + * @brief Variable template for exclusion lists. + * @tparam Type List of types. + */ +template +inline constexpr exclude_t exclude{}; + + +/** + * @brief Alias for lists of observed components. + * @tparam Type List of types. + */ +template +struct get_t: type_list{}; + + +/** + * @brief Variable template for lists of observed components. + * @tparam Type List of types. + */ +template +inline constexpr get_t get{}; + + +} + + +#endif + + + +namespace entt { + + +/** + * @brief Group. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + */ +template +class basic_group; + + +/** + * @brief Non-owning group. + * + * A non-owning group returns all entities and only the entities that have at + * least the given components. Moreover, it's guaranteed that the entity list + * is tightly packed in memory for fast iterations. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators and using them results in undefined behavior. + * + * @note + * Groups share references to the underlying data structures of the registry + * that generated them. Therefore any change to the entities and to the + * components made by means of the registry are immediately reflected by all the + * groups.
+ * Moreover, sorting a non-owning group affects all the instances of the same + * group (it means that users don't have to call `sort` on each instance to sort + * all of them because they _share_ entities and components). + * + * @warning + * Lifetime of a group must not overcome that of the registry that generated it. + * In any other case, attempting to use a group results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Exclude Types of components used to filter the group. + * @tparam Get Type of components observed by the group. + */ +template +class basic_group, get_t> { + /*! @brief A registry is allowed to create groups. */ + friend class basic_registry; + + template + using pool_type = pool_t; + + class group_proxy { + friend class basic_group, get_t>; + + class proxy_iterator { + friend class group_proxy; + + using it_type = typename sparse_set::iterator; + using ref_type = decltype(std::tuple_cat(std::declval, std::tuple<>, std::tuple *>>>()...)); + + proxy_iterator(it_type from, ref_type ref) ENTT_NOEXCEPT + : it{from}, + pools{ref} + {} + + public: + using difference_type = std::ptrdiff_t; + using value_type = decltype(std::tuple_cat( + std::declval>(), + std::declval, std::tuple<>, std::tuple>>()... + )); + using pointer = void; + using reference = decltype(std::tuple_cat( + std::declval>(), + std::declval, std::tuple<>, std::tuple>>()... + )); + using iterator_category = std::input_iterator_tag; + + proxy_iterator & operator++() ENTT_NOEXCEPT { + return ++it, *this; + } + + proxy_iterator operator++(int) ENTT_NOEXCEPT { + proxy_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return std::apply([entt = *it](auto *... cpool) { return reference{entt, cpool->get(entt)...}; }, pools); + } + + [[nodiscard]] bool operator==(const proxy_iterator &other) const ENTT_NOEXCEPT { + return other.it == it; + } + + [[nodiscard]] bool operator!=(const proxy_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + private: + it_type it{}; + ref_type pools{}; + }; + + group_proxy(const sparse_set &ref, std::tuple *...> gpools) + : handler{&ref}, + pools{gpools} + {} + + public: + using iterator = proxy_iterator; + + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return proxy_iterator{handler->begin(), std::tuple_cat([](auto *cpool) { + if constexpr(is_eto_eligible_v::object_type>) { + return std::make_tuple(); + } else { + return std::make_tuple(cpool); + } + }(std::get *>(pools))...)}; + } + + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + return proxy_iterator{handler->end(), std::tuple_cat([](auto *cpool) { + if constexpr(is_eto_eligible_v::object_type>) { + return std::make_tuple(); + } else { + return std::make_tuple(cpool); + } + }(std::get *>(pools))...)}; + } + + private: + const sparse_set *handler; + std::tuple *...> pools; + }; + + basic_group(sparse_set &ref, pool_type &... gpool) ENTT_NOEXCEPT + : handler{&ref}, + pools{&gpool...} + {} + + template + void traverse(Func func, type_list) const { + for(const auto entt: *handler) { + if constexpr(std::is_invocable_v({}))...>) { + func(std::get *>(pools)->get(entt)...); + } else { + func(entt, std::get *>(pools)->get(entt)...); + } + } + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Random access iterator type. */ + using iterator = typename sparse_set::iterator; + /*! @brief Reversed iterator type. */ + using reverse_iterator = typename sparse_set::reverse_iterator; + + /** + * @brief Returns the number of existing components of the given type. + * @tparam Component Type of component of which to return the size. + * @return Number of existing components of the given type. + */ + template + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + return std::get *>(pools)->size(); + } + + /** + * @brief Returns the number of entities that have the given components. + * @return Number of entities that have the given components. + */ + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + return handler->size(); + } + + /** + * @brief Returns the number of elements that a group has currently + * allocated space for. + * @return Capacity of the group. + */ + [[nodiscard]] size_type capacity() const ENTT_NOEXCEPT { + return handler->capacity(); + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() { + handler->shrink_to_fit(); + } + + /** + * @brief Checks whether a group or some pools are empty. + * @tparam Component Types of components in which one is interested. + * @return True if the group or the pools are empty, false otherwise. + */ + template + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + if constexpr(sizeof...(Component) == 0) { + return handler->empty(); + } else { + return (std::get *>(pools)->empty() && ...); + } + } + + /** + * @brief Direct access to the list of components of a given pool. + * + * The returned pointer is such that range + * `[raw(), raw() + size()]` is always a + * valid range, even if the container is empty. + * + * @note + * Components are in the reverse order as returned by the `begin`/`end` + * iterators. + * + * @tparam Component Type of component in which one is interested. + * @return A pointer to the array of components. + */ + template + [[nodiscard]] Component * raw() const ENTT_NOEXCEPT { + return std::get *>(pools)->raw(); + } + + /** + * @brief Direct access to the list of entities of a given pool. + * + * The returned pointer is such that range + * `[data(), data() + size()]` is always a + * valid range, even if the container is empty. + * + * @note + * Entities are in the reverse order as returned by the `begin`/`end` + * iterators. + * + * @tparam Component Type of component in which one is interested. + * @return A pointer to the array of entities. + */ + template + [[nodiscard]] const entity_type * data() const ENTT_NOEXCEPT { + return std::get *>(pools)->data(); + } + + /** + * @brief Direct access to the list of entities. + * + * The returned pointer is such that range `[data(), data() + size()]` is + * always a valid range, even if the container is empty. + * + * @note + * Entities are in the reverse order as returned by the `begin`/`end` + * iterators. + * + * @return A pointer to the array of entities. + */ + [[nodiscard]] const entity_type * data() const ENTT_NOEXCEPT { + return handler->data(); + } + + /** + * @brief Returns an iterator to the first entity of the group. + * + * The returned iterator points to the first entity of the group. If the + * group is empty, the returned iterator will be equal to `end()`. + * + * @note + * Iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first entity of the group. + */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return handler->begin(); + } + + /** + * @brief Returns an iterator that is past the last entity of the group. + * + * The returned iterator points to the entity following the last entity of + * the group. Attempting to dereference the returned iterator results in + * undefined behavior. + * + * @note + * Iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the entity following the last entity of the + * group. + */ + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + return handler->end(); + } + + /** + * @brief Returns an iterator to the first entity of the reversed group. + * + * The returned iterator points to the first entity of the reversed group. + * If the group is empty, the returned iterator will be equal to `rend()`. + * + * @note + * Iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first entity of the reversed group. + */ + [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { + return handler->rbegin(); + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * group. + * + * The returned iterator points to the entity following the last entity of + * the reversed group. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the entity following the last entity of the + * reversed group. + */ + [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { + return handler->rend(); + } + + /** + * @brief Returns the first entity of the group, if any. + * @return The first entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity of the group, if any. + * @return The last entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const { + const auto it = rbegin(); + return it != rend() ? *it : null; + } + + /** + * @brief Finds an entity. + * @param entt A valid entity identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const { + const auto it = handler->find(entt); + return it != end() && *it == entt ? it : end(); + } + + /** + * @brief Returns the identifier that occupies the given position. + * @param pos Position of the element to return. + * @return The identifier that occupies the given position. + */ + [[nodiscard]] entity_type operator[](const size_type pos) const { + return begin()[pos]; + } + + /** + * @brief Checks if a group contains an entity. + * @param entt A valid entity identifier. + * @return True if the group contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const { + return handler->contains(entt); + } + + /** + * @brief Returns the components assigned to the given entity. + * + * Prefer this function instead of `registry::get` during iterations. It has + * far better performance than its counterpart. + * + * @warning + * Attempting to use an invalid component type results in a compilation + * error. Attempting to use an entity that doesn't belong to the group + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * group doesn't contain the given entity. + * + * @tparam Component Types of components to get. + * @param entt A valid entity identifier. + * @return The components assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entt) const { + ENTT_ASSERT(contains(entt)); + + if constexpr(sizeof...(Component) == 1) { + return (std::get *>(pools)->get(entt), ...); + } else { + return std::tuple({}))...>{get(entt)...}; + } + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of references to 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 { + using get_type_list = type_list_cat_t, type_list<>, type_list>...>; + traverse(std::move(func), get_type_list{}); + } + + /** + * @brief Returns an iterable object to use to _visit_ the group. + * + * The iterable object returns tuples that contain the current entity and a + * set of references to its non-empty components. The _constness_ of the + * components is as requested. + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @return An iterable object to use to _visit_ the group. + */ + [[nodiscard]] auto proxy() const ENTT_NOEXCEPT { + return group_proxy{*handler, pools}; + } + + /** + * @brief Sort a group according to the given comparison function. + * + * Sort the group so that iterating it with a couple of iterators returns + * entities and components in the expected order. See `begin` and `end` for + * more details. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(std::tuple, std::tuple); + * bool(const Component &..., const Component &...); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Component` are such that they are iterated by the group.
+ * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function oject must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * @tparam Component Optional types of components to compare. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort(Compare compare, Sort algo = Sort{}, Args &&... args) { + if constexpr(sizeof...(Component) == 0) { + static_assert(std::is_invocable_v, "Invalid comparison function"); + handler->sort(handler->begin(), handler->end(), std::move(compare), std::move(algo), std::forward(args)...); + } else if constexpr(sizeof...(Component) == 1) { + handler->sort(handler->begin(), handler->end(), [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { + return compare((std::get *>(pools)->get(lhs), ...), (std::get *>(pools)->get(rhs), ...)); + }, std::move(algo), std::forward(args)...); + } else { + handler->sort(handler->begin(), handler->end(), [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { + return compare(std::tuple({}))...>{std::get *>(pools)->get(lhs)...}, std::tuple({}))...>{std::get *>(pools)->get(rhs)...}); + }, std::move(algo), std::forward(args)...); + } + } + + /** + * @brief Sort the shared pool of entities according to the given component. + * + * Non-owning groups of the same type share with the registry a pool of + * entities with its own order that doesn't depend on the order of any pool + * of components. Users can order the underlying data structure so that it + * respects the order of the pool of the given component. + * + * @note + * The shared pool of entities and thus its order is affected by the changes + * to each and every pool that it tracks. Therefore changes to those pools + * can quickly ruin the order imposed to the pool of entities shared between + * the non-owning groups. + * + * @tparam Component Type of component to use to impose the order. + */ + template + void sort() const { + handler->respect(*std::get *>(pools)); + } + +private: + sparse_set *handler; + const std::tuple *...> pools; +}; + + +/** + * @brief Owning group. + * + * Owning groups return all entities and only the entities that have at least + * the given components. Moreover: + * + * * It's guaranteed that the entity list is tightly packed in memory for fast + * iterations. + * * It's guaranteed that the lists of owned components are tightly packed in + * memory for even faster iterations and to allow direct access. + * * They stay true to the order of the owned components and all instances have + * the same order in memory. + * + * The more types of components are owned by a group, the faster it is to + * iterate them. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators and using them results in undefined behavior. + * + * @note + * Groups share references to the underlying data structures of the registry + * that generated them. Therefore any change to the entities and to the + * components made by means of the registry are immediately reflected by all the + * groups. + * Moreover, sorting an owning group affects all the instance of the same group + * (it means that users don't have to call `sort` on each instance to sort all + * of them because they share the underlying data structure). + * + * @warning + * Lifetime of a group must not overcome that of the registry that generated it. + * In any other case, attempting to use a group results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Exclude Types of components used to filter the group. + * @tparam Get Types of components observed by the group. + * @tparam Owned Types of components owned by the group. + */ +template +class basic_group, get_t, Owned...> { + /*! @brief A registry is allowed to create groups. */ + friend class basic_registry; + + template + using pool_type = pool_t; + + template + using component_iterator = decltype(std::declval>().begin()); + + class group_proxy { + friend class basic_group, get_t, Owned...>; + + class proxy_iterator { + friend class group_proxy; + + using it_type = typename sparse_set::iterator; + using owned_type = decltype(std::tuple_cat(std::declval, std::tuple<>, std::tuple>>>()...)); + using get_type = decltype(std::tuple_cat(std::declval, std::tuple<>, std::tuple *>>>()...)); + + proxy_iterator(it_type from, owned_type oref, get_type gref) ENTT_NOEXCEPT + : it{from}, + owned{oref}, + get{gref} + {} + + public: + using difference_type = std::ptrdiff_t; + using value_type = decltype(std::tuple_cat( + std::declval>(), + std::declval, std::tuple<>, std::tuple>>()..., + std::declval, std::tuple<>, std::tuple>>()... + )); + using pointer = void; + using reference = decltype(std::tuple_cat( + std::declval>(), + std::declval, std::tuple<>, std::tuple>>()..., + std::declval, std::tuple<>, std::tuple>>()... + )); + using iterator_category = std::input_iterator_tag; + + proxy_iterator & operator++() ENTT_NOEXCEPT { + return ++it, std::apply([](auto &&... curr) { (++curr, ...); }, owned), *this; + } + + proxy_iterator operator++(int) ENTT_NOEXCEPT { + proxy_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return std::tuple_cat( + std::make_tuple(*it), + std::apply([](auto &&... curr) { return std::forward_as_tuple(*curr...); }, owned), + std::apply([entt = *it](auto &&... curr) { return std::forward_as_tuple(curr->get(entt)...); }, get) + ); + } + + [[nodiscard]] bool operator==(const proxy_iterator &other) const ENTT_NOEXCEPT { + return other.it == it; + } + + [[nodiscard]] bool operator!=(const proxy_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + private: + it_type it{}; + owned_type owned{}; + get_type get{}; + }; + + group_proxy(std::tuple *..., pool_type *...> cpools, const std::size_t &extent) + : pools{cpools}, + length{&extent} + {} + + public: + using iterator = proxy_iterator; + + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return proxy_iterator{ + std::get<0>(pools)->sparse_set::end() - *length, + std::tuple_cat([length = *length](auto *cpool) { + if constexpr(is_eto_eligible_v::object_type>) { + return std::make_tuple(); + } else { + return std::make_tuple(cpool->end() - length); + } + }(std::get *>(pools))...), + std::tuple_cat([](auto *cpool) { + if constexpr(is_eto_eligible_v::object_type>) { + return std::make_tuple(); + } else { + return std::make_tuple(cpool); + } + }(std::get *>(pools))...) + }; + } + + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + return proxy_iterator{ + std::get<0>(pools)->sparse_set::end(), + std::tuple_cat([](auto *cpool) { + if constexpr(is_eto_eligible_v::object_type>) { + return std::make_tuple(); + } else { + return std::make_tuple(cpool->end()); + } + }(std::get *>(pools))...), + std::tuple_cat([](auto *cpool) { + if constexpr(is_eto_eligible_v::object_type>) { + return std::make_tuple(); + } else { + return std::make_tuple(cpool); + } + }(std::get *>(pools))...) + }; + } + + private: + const std::tuple *..., pool_type *...> pools; + const std::size_t *length; + }; + + basic_group(const std::size_t &extent, pool_type &... opool, pool_type &... gpool) ENTT_NOEXCEPT + : pools{&opool..., &gpool...}, + length{&extent} + {} + + template + void traverse(Func func, type_list, type_list) const { + [[maybe_unused]] auto it = std::make_tuple((std::get *>(pools)->end() - *length)...); + [[maybe_unused]] auto data = std::get<0>(pools)->sparse_set::end() - *length; + + for(auto next = *length; next; --next) { + if constexpr(std::is_invocable_v({}))..., decltype(get({}))...>) { + if constexpr(sizeof...(Weak) == 0) { + func(*(std::get>(it)++)...); + } else { + const auto entt = *(data++); + func(*(std::get>(it)++)..., std::get *>(pools)->get(entt)...); + } + } else { + const auto entt = *(data++); + func(entt, *(std::get>(it)++)..., std::get *>(pools)->get(entt)...); + } + } + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Random access iterator type. */ + using iterator = typename sparse_set::iterator; + /*! @brief Reversed iterator type. */ + using reverse_iterator = typename sparse_set::reverse_iterator; + + /** + * @brief Returns the number of existing components of the given type. + * @tparam Component Type of component of which to return the size. + * @return Number of existing components of the given type. + */ + template + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + return std::get *>(pools)->size(); + } + + /** + * @brief Returns the number of entities that have the given components. + * @return Number of entities that have the given components. + */ + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + return *length; + } + + /** + * @brief Checks whether a group or some pools are empty. + * @tparam Component Types of components in which one is interested. + * @return True if the group or the pools are empty, false otherwise. + */ + template + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + if constexpr(sizeof...(Component) == 0) { + return !*length; + } else { + return (std::get *>(pools)->empty() && ...); + } + } + + /** + * @brief Direct access to the list of components of a given pool. + * + * The returned pointer is such that range + * `[raw(), raw() + size()]` is always a + * valid range, even if the container is empty.
+ * Moreover, in case the group owns the given component, the range + * `[raw(), raw() + size()]` is such that it contains + * the instances that are part of the group itself. + * + * @note + * Components are in the reverse order as returned by the `begin`/`end` + * iterators. + * + * @tparam Component Type of component in which one is interested. + * @return A pointer to the array of components. + */ + template + [[nodiscard]] Component * raw() const ENTT_NOEXCEPT { + return std::get *>(pools)->raw(); + } + + /** + * @brief Direct access to the list of entities of a given pool. + * + * The returned pointer is such that range + * `[data(), data() + size()]` is always a + * valid range, even if the container is empty.
+ * Moreover, in case the group owns the given component, the range + * `[data(), data() + size()]` is such that it + * contains the entities that are part of the group itself. + * + * @note + * Entities are in the reverse order as returned by the `begin`/`end` + * iterators. + * + * @tparam Component Type of component in which one is interested. + * @return A pointer to the array of entities. + */ + template + [[nodiscard]] const entity_type * data() const ENTT_NOEXCEPT { + return std::get *>(pools)->data(); + } + + /** + * @brief Direct access to the list of entities. + * + * The returned pointer is such that range `[data(), data() + size()]` is + * always a valid range, even if the container is empty. + * + * @note + * Entities are in the reverse order as returned by the `begin`/`end` + * iterators. + * + * @return A pointer to the array of entities. + */ + [[nodiscard]] const entity_type * data() const ENTT_NOEXCEPT { + return std::get<0>(pools)->data(); + } + + /** + * @brief Returns an iterator to the first entity of the group. + * + * The returned iterator points to the first entity of the group. If the + * group is empty, the returned iterator will be equal to `end()`. + * + * @note + * Iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first entity of the group. + */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return std::get<0>(pools)->sparse_set::end() - *length; + } + + /** + * @brief Returns an iterator that is past the last entity of the group. + * + * The returned iterator points to the entity following the last entity of + * the group. Attempting to dereference the returned iterator results in + * undefined behavior. + * + * @note + * Iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the entity following the last entity of the + * group. + */ + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + return std::get<0>(pools)->sparse_set::end(); + } + + /** + * @brief Returns an iterator to the first entity of the reversed group. + * + * The returned iterator points to the first entity of the reversed group. + * If the group is empty, the returned iterator will be equal to `rend()`. + * + * @note + * Iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first entity of the reversed group. + */ + [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { + return std::get<0>(pools)->sparse_set::rbegin(); + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * group. + * + * The returned iterator points to the entity following the last entity of + * the reversed group. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the entity following the last entity of the + * reversed group. + */ + [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { + return std::get<0>(pools)->sparse_set::rbegin() + *length; + } + + /** + * @brief Returns the first entity of the group, if any. + * @return The first entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity of the group, if any. + * @return The last entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const { + const auto it = rbegin(); + return it != rend() ? *it : null; + } + + /** + * @brief Finds an entity. + * @param entt A valid entity identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const { + const auto it = std::get<0>(pools)->find(entt); + return it != end() && it >= begin() && *it == entt ? it : end(); + } + + /** + * @brief Returns the identifier that occupies the given position. + * @param pos Position of the element to return. + * @return The identifier that occupies the given position. + */ + [[nodiscard]] entity_type operator[](const size_type pos) const { + return begin()[pos]; + } + + /** + * @brief Checks if a group contains an entity. + * @param entt A valid entity identifier. + * @return True if the group contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const { + return std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < (*length)); + } + + /** + * @brief Returns the components assigned to the given entity. + * + * Prefer this function instead of `registry::get` during iterations. It has + * far better performance than its counterpart. + * + * @warning + * Attempting to use an invalid component type results in a compilation + * error. Attempting to use an entity that doesn't belong to the group + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * group doesn't contain the given entity. + * + * @tparam Component Types of components to get. + * @param entt A valid entity identifier. + * @return The components assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entt) const { + ENTT_ASSERT(contains(entt)); + + if constexpr(sizeof...(Component) == 1) { + return (std::get *>(pools)->get(entt), ...); + } else { + return std::tuple({}))...>{get(entt)...}; + } + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of references to 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 { + using owned_type_list = type_list_cat_t, type_list<>, type_list>...>; + using get_type_list = type_list_cat_t, type_list<>, type_list>...>; + traverse(std::move(func), owned_type_list{}, get_type_list{}); + } + + /** + * @brief Returns an iterable object to use to _visit_ the group. + * + * The iterable object returns tuples that contain the current entity and a + * set of references to its non-empty components. The _constness_ of the + * components is as requested. + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @return An iterable object to use to _visit_ the group. + */ + [[nodiscard]] auto proxy() const ENTT_NOEXCEPT { + return group_proxy{pools, *length}; + } + + /** + * @brief Sort a group according to the given comparison function. + * + * Sort the group so that iterating it with a couple of iterators returns + * entities and components in the expected order. See `begin` and `end` for + * more details. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(std::tuple, std::tuple); + * bool(const Component &, const Component &); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Component` are either owned types or not but still such that they + * are iterated by the group.
+ * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function oject must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * @tparam Component Optional types of components to compare. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort(Compare compare, Sort algo = Sort{}, Args &&... args) { + auto *cpool = std::get<0>(pools); + + if constexpr(sizeof...(Component) == 0) { + static_assert(std::is_invocable_v, "Invalid comparison function"); + cpool->sort(cpool->end()-*length, cpool->end(), std::move(compare), std::move(algo), std::forward(args)...); + } else if constexpr(sizeof...(Component) == 1) { + cpool->sort(cpool->end()-*length, cpool->end(), [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { + return compare((std::get *>(pools)->get(lhs), ...), (std::get *>(pools)->get(rhs), ...)); + }, std::move(algo), std::forward(args)...); + } else { + cpool->sort(cpool->end()-*length, cpool->end(), [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { + return compare(std::tuple({}))...>{std::get *>(pools)->get(lhs)...}, std::tuple({}))...>{std::get *>(pools)->get(rhs)...}); + }, std::move(algo), std::forward(args)...); + } + + [this](auto *head, auto *... other) { + for(auto next = *length; next; --next) { + const auto pos = next - 1; + [[maybe_unused]] const auto entt = head->data()[pos]; + (other->swap(other->data()[pos], entt), ...); + } + }(std::get *>(pools)...); + } + +private: + const std::tuple *..., pool_type *...> pools; + const size_type *length; +}; + + +} + + +#endif + +// #include "runtime_view.hpp" +#ifndef ENTT_ENTITY_RUNTIME_VIEW_HPP +#define ENTT_ENTITY_RUNTIME_VIEW_HPP + + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "sparse_set.hpp" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Runtime view. + * + * Runtime views iterate over those entities that have at least all the given + * components in their bags. During initialization, a runtime view looks at the + * number of entities available for each component and picks up a reference to + * the smallest set of candidate entities in order to get a performance boost + * when iterate.
+ * Order of elements during iterations are highly dependent on the order of the + * underlying data structures. See sparse_set and its specializations for more + * details. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all the other cases, modifying the pools of the given components in any + * way invalidates all the iterators and using them results in undefined + * behavior. + * + * @note + * Views share references to the underlying data structures of the registry that + * generated them. Therefore any change to the entities and to the components + * made by means of the registry are immediately reflected by the views, unless + * a pool was missing when the view was built (in this case, the view won't + * have a valid reference and won't be updated accordingly). + * + * @warning + * Lifetime of a view must not overcome that of the registry that generated it. + * In any other case, attempting to use a view results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class basic_runtime_view { + /*! @brief A registry is allowed to create views. */ + friend class basic_registry; + + using underlying_iterator = typename sparse_set::iterator; + + class view_iterator final { + friend class basic_runtime_view; + + view_iterator(const std::vector *> &cpools, const std::vector *> &ignore, underlying_iterator curr) ENTT_NOEXCEPT + : pools{&cpools}, + filter{&ignore}, + it{curr} + { + if(it != (*pools)[0]->end() && !valid()) { + ++(*this); + } + } + + [[nodiscard]] bool valid() const { + return 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 underlying_iterator::difference_type; + using value_type = typename underlying_iterator::value_type; + using pointer = typename underlying_iterator::pointer; + using reference = typename underlying_iterator::reference; + using iterator_category = std::bidirectional_iterator_tag; + + view_iterator() ENTT_NOEXCEPT = default; + + view_iterator & operator++() { + while(++it != (*pools)[0]->end() && !valid()); + return *this; + } + + view_iterator operator++(int) { + view_iterator orig = *this; + return ++(*this), orig; + } + + view_iterator & operator--() ENTT_NOEXCEPT { + while(--it != (*pools)[0]->begin() && !valid()); + return *this; + } + + view_iterator operator--(int) ENTT_NOEXCEPT { + view_iterator orig = *this; + return operator--(), orig; + } + + [[nodiscard]] bool operator==(const view_iterator &other) const ENTT_NOEXCEPT { + return other.it == it; + } + + [[nodiscard]] bool operator!=(const view_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + [[nodiscard]] pointer operator->() const { + return it.operator->(); + } + + [[nodiscard]] reference operator*() const { + return *operator->(); + } + + private: + const std::vector *> *pools; + const std::vector *> *filter; + underlying_iterator it; + }; + + basic_runtime_view(std::vector *> cpools, std::vector *> epools) ENTT_NOEXCEPT + : pools{std::move(cpools)}, + filter{std::move(epools)} + { + const auto it = std::min_element(pools.begin(), pools.end(), [](const auto *lhs, const auto *rhs) { + return (!lhs && rhs) || (lhs && rhs && lhs->size() < rhs->size()); + }); + + // brings the best candidate (if any) on front of the vector + std::rotate(pools.begin(), it, pools.end()); + } + + [[nodiscard]] bool valid() const { + return !pools.empty() && pools.front(); + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Bidirectional iterator type. */ + using iterator = view_iterator; + + /** + * @brief Estimates the number of entities that have the given components. + * @return Estimated number of entities that have the given components. + */ + [[nodiscard]] size_type size() const { + return valid() ? pools.front()->size() : size_type{}; + } + + /** + * @brief Checks if the view is definitely empty. + * @return True if the view is definitely empty, false otherwise. + */ + [[nodiscard]] bool empty() const { + return !valid() || pools.front()->empty(); + } + + /** + * @brief Returns an iterator to the first entity that has the given + * components. + * + * The returned iterator points to the first entity that has the given + * components. If the view is empty, the returned iterator will be equal to + * `end()`. + * + * @note + * Iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first entity that has the given components. + */ + [[nodiscard]] iterator begin() const { + return valid() ? iterator{pools, filter, pools[0]->begin()} : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity that has the + * given components. + * + * The returned iterator points to the entity following the last entity that + * has the given components. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the entity following the last entity that has the + * given components. + */ + [[nodiscard]] iterator end() const { + return valid() ? iterator{pools, filter, pools[0]->end()} : iterator{}; + } + + /** + * @brief Checks if a view contains an entity. + * @param entt A valid entity identifier. + * @return True if the view contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const { + return valid() && std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *curr) { return curr->contains(entt); }) + && std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); }); + } + + /** + * @brief Iterates entities and applies the given function object to them. + * + * The function object is invoked for each entity. It is provided only with + * the entity itself. To get the components, users can use the registry with + * which the view was built.
+ * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const entity_type); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + for(const auto entity: *this) { + func(entity); + } + } + +private: + std::vector *> pools; + std::vector *> filter; +}; + + +} + + +#endif + +// #include "sparse_set.hpp" + +// #include "storage.hpp" + +// #include "utility.hpp" + +// #include "view.hpp" +#ifndef ENTT_ENTITY_VIEW_HPP +#define ENTT_ENTITY_VIEW_HPP + + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/type_traits.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "pool.hpp" + +// #include "sparse_set.hpp" + +// #include "utility.hpp" + + + +namespace entt { + + +/** + * @brief View. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + */ +template +class basic_view; + + +/** + * @brief Multi component view. + * + * Multi component views iterate over those entities that have at least all the + * given components in their bags. During initialization, a multi component view + * looks at the number of entities available for each component and uses the + * smallest set in order to get a performance boost when iterate. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the view in any way + * invalidates all the iterators and using them results in undefined behavior. + * + * @note + * Views share references to the underlying data structures of the registry that + * generated them. Therefore any change to the entities and to the components + * made by means of the registry are immediately reflected by views. + * + * @warning + * Lifetime of a view must not overcome that of the registry that generated it. + * In any other case, attempting to use a view results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Exclude Types of components used to filter the view. + * @tparam Component Types of components iterated by the view. + */ +template +class basic_view, Component...> { + /*! @brief A registry is allowed to create views. */ + friend class basic_registry; + + template + using pool_type = pool_t; + + template + using component_iterator = decltype(std::declval>().begin()); + + using unchecked_type = std::array *, (sizeof...(Component) - 1)>; + using filter_type = std::array *, sizeof...(Exclude)>; + + template + class view_iterator final { + friend class basic_view, Component...>; + + view_iterator(It from, It to, It curr, unchecked_type other, filter_type ignore) ENTT_NOEXCEPT + : first{from}, + last{to}, + it{curr}, + unchecked{other}, + filter{ignore} + { + if(it != last && !valid()) { + ++(*this); + } + } + + [[nodiscard]] bool valid() const { + return std::all_of(unchecked.cbegin(), unchecked.cend(), [entt = *it](const sparse_set *curr) { return curr->contains(entt); }) + && (sizeof...(Exclude) == 0 || std::none_of(filter.cbegin(), filter.cend(), [entt = *it](const sparse_set *cpool) { return cpool->contains(entt); })); + } + + public: + using difference_type = typename std::iterator_traits::difference_type; + using value_type = typename std::iterator_traits::value_type; + using pointer = typename std::iterator_traits::pointer; + using reference = typename std::iterator_traits::reference; + using iterator_category = std::bidirectional_iterator_tag; + + view_iterator() ENTT_NOEXCEPT = default; + + view_iterator & operator++() { + while(++it != last && !valid()); + return *this; + } + + view_iterator operator++(int) { + view_iterator orig = *this; + return ++(*this), orig; + } + + view_iterator & operator--() ENTT_NOEXCEPT { + while(--it != first && !valid()); + return *this; + } + + view_iterator operator--(int) ENTT_NOEXCEPT { + view_iterator orig = *this; + return operator--(), orig; + } + + [[nodiscard]] bool operator==(const view_iterator &other) const ENTT_NOEXCEPT { + return other.it == it; + } + + [[nodiscard]] bool operator!=(const view_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + [[nodiscard]] pointer operator->() const { + return &*it; + } + + [[nodiscard]] reference operator*() const { + return *operator->(); + } + + private: + It first; + It last; + It it; + unchecked_type unchecked; + filter_type filter; + }; + + class view_proxy { + friend class basic_view, Component...>; + + using proxy_view_iterator = view_iterator::iterator>; + + class proxy_iterator { + friend class view_proxy; + + using ref_type = decltype(std::tuple_cat(std::declval, std::tuple<>, std::tuple *>>>()...)); + + proxy_iterator(proxy_view_iterator from, ref_type ref) ENTT_NOEXCEPT + : it{from}, + pools{ref} + {} + + public: + using difference_type = std::ptrdiff_t; + using value_type = decltype(std::tuple_cat( + std::declval>(), + std::declval, std::tuple<>, std::tuple>>()... + )); + using pointer = void; + using reference = decltype(std::tuple_cat( + std::declval>(), + std::declval, std::tuple<>, std::tuple>>()... + )); + using iterator_category = std::input_iterator_tag; + + proxy_iterator & operator++() ENTT_NOEXCEPT { + return ++it, *this; + } + + proxy_iterator operator++(int) ENTT_NOEXCEPT { + proxy_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return std::apply([entt = *it](auto *... cpool) { return reference{entt, cpool->get(entt)...}; }, pools); + } + + [[nodiscard]] bool operator==(const proxy_iterator &other) const ENTT_NOEXCEPT { + return other.it == it; + } + + [[nodiscard]] bool operator!=(const proxy_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + private: + proxy_view_iterator it{}; + const ref_type pools{}; + }; + + view_proxy(proxy_view_iterator from, proxy_view_iterator to, std::tuple *...> ref) + : first{from}, + last{to}, + pools{ref} + {} + + public: + using iterator = proxy_iterator; + + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return proxy_iterator{first, std::tuple_cat([](auto *cpool) { + if constexpr(is_eto_eligible_v::object_type>) { + return std::make_tuple(); + } else { + return std::make_tuple(cpool); + } + }(std::get *>(pools))...)}; + } + + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + return proxy_iterator{last, std::tuple_cat([](auto *cpool) { + if constexpr(is_eto_eligible_v::object_type>) { + return std::make_tuple(); + } else { + return std::make_tuple(cpool); + } + }(std::get *>(pools))...)}; + } + + private: + proxy_view_iterator first; + proxy_view_iterator last; + const std::tuple *...> pools; + }; + + basic_view(pool_type &... component, unpack_as_t, Exclude> &... epool) ENTT_NOEXCEPT + : pools{&component...}, + view{candidate()}, + filter{&epool...} + {} + + [[nodiscard]] const sparse_set * candidate() const ENTT_NOEXCEPT { + return (std::min)({ static_cast *>(std::get *>(pools))... }, [](const auto *lhs, const auto *rhs) { + return lhs->size() < rhs->size(); + }); + } + + [[nodiscard]] unchecked_type unchecked(const sparse_set *cpool) const { + std::size_t pos{}; + unchecked_type other{}; + ((std::get *>(pools) == cpool ? nullptr : (other[pos] = std::get *>(pools), other[pos++])), ...); + return other; + } + + template + [[nodiscard]] decltype(auto) get([[maybe_unused]] component_iterator &it, [[maybe_unused]] pool_type *cpool, [[maybe_unused]] const Entity entt) const { + if constexpr(std::is_same_v) { + return *it; + } else { + return cpool->get(entt); + } + } + + template + void traverse(Func func, type_list) const { + if constexpr(std::disjunction_v...>) { + auto it = std::get *>(pools)->begin(); + + for(const auto entt: static_cast &>(*std::get *>(pools))) { + if(((std::is_same_v || std::get *>(pools)->contains(entt)) && ...) + && (sizeof...(Exclude) == 0 || std::none_of(filter.cbegin(), filter.cend(), [entt](const sparse_set *cpool) { return cpool->contains(entt); }))) + { + if constexpr(std::is_invocable_v({}))...>) { + func(get(it, std::get *>(pools), entt)...); + } else { + func(entt, get(it, std::get *>(pools), entt)...); + } + } + + ++it; + } + } else { + for(const auto entt: static_cast &>(*std::get *>(pools))) { + if(((std::is_same_v || std::get *>(pools)->contains(entt)) && ...) + && (sizeof...(Exclude) == 0 || std::none_of(filter.cbegin(), filter.cend(), [entt](const sparse_set *cpool) { return cpool->contains(entt); }))) + { + if constexpr(std::is_invocable_v({}))...>) { + func(std::get *>(pools)->get(entt)...); + } else { + func(entt, std::get *>(pools)->get(entt)...); + } + } + } + } + } + + template + void iterate(Func func, type_list) const { + const auto last = view->data() + view->size(); + auto first = view->data(); + + while(first != last) { + if((std::get *>(pools)->contains(*first) && ...) + && (sizeof...(Exclude) == 0 || std::none_of(filter.cbegin(), filter.cend(), [entt = *first](const sparse_set *cpool) { return cpool->contains(entt); }))) + { + const auto base = *(first++); + const auto chunk = (std::min)({ (std::get *>(pools)->size() - std::get *>(pools)->index(base))... }); + size_type length{}; + + for(++length; + length < chunk + && ((*(std::get *>(pools)->data() + std::get *>(pools)->index(base) + length) == *first) && ...) + && (sizeof...(Exclude) == 0 || std::none_of(filter.cbegin(), filter.cend(), [entt = *first](const sparse_set *cpool) { return cpool->contains(entt); })); + ++length, ++first); + + func(view->data() + view->index(base), (std::get *>(pools)->raw() + std::get *>(pools)->index(base))..., length); + } else { + ++first; + } + } + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Bidirectional iterator type. */ + using iterator = view_iterator::iterator>; + /*! @brief Reverse iterator type. */ + using reverse_iterator = view_iterator::reverse_iterator>; + + /** + * @brief Returns the number of existing components of the given type. + * + * This isn't the number of entities iterated by the view. + * + * @tparam Comp Type of component of which to return the size. + * @return Number of existing components of the given type. + */ + template + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + return std::get *>(pools)->size(); + } + + /** + * @brief Estimates the number of entities iterated by the view. + * @return Estimated number of entities iterated by the view. + */ + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + return (std::min)({ std::get *>(pools)->size()... }); + } + + /** + * @brief Checks whether a view or some pools are empty. + * + * The view is definitely empty if one of the pools it uses is empty. In all + * other cases, the view may be empty and not return entities even if this + * function returns false. + * + * @tparam Comp Types of components in which one is interested. + * @return True if the view or the pools are empty, false otherwise. + */ + template + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + if constexpr(sizeof...(Comp) == 0) { + return (std::get *>(pools)->empty() || ...); + } else { + return (std::get *>(pools)->empty() && ...); + } + } + + /** + * @brief Direct access to the list of components of a given pool. + * + * The returned pointer is such that range + * `[raw(), raw() + size()]` is always a valid range, even + * if the container is empty. + * + * @note + * Components are in the reverse order as returned by the `begin`/`end` + * iterators. + * + * @tparam Comp Type of component in which one is interested. + * @return A pointer to the array of components. + */ + template + [[nodiscard]] Comp * raw() const ENTT_NOEXCEPT { + return std::get *>(pools)->raw(); + } + + /** + * @brief Direct access to the list of entities of a given pool. + * + * The returned pointer is such that range + * `[data(), data() + size()]` is always a valid range, + * even if the container is empty. + * + * @note + * Entities are in the reverse order as returned by the `begin`/`end` + * iterators. + * + * @tparam Comp Type of component in which one is interested. + * @return A pointer to the array of entities. + */ + template + [[nodiscard]] const entity_type * data() const ENTT_NOEXCEPT { + return std::get *>(pools)->data(); + } + + /** + * @brief Returns an iterator to the first entity of the view. + * + * The returned iterator points to the first entity of the view. If the view + * is empty, the returned iterator will be equal to `end()`. + * + * @note + * Iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first entity of the view. + */ + [[nodiscard]] iterator begin() const { + return iterator{view->begin(), view->end(), view->begin(), unchecked(view), filter}; + } + + /** + * @brief Returns an iterator that is past the last entity of the view. + * + * The returned iterator points to the entity following the last entity of + * the view. Attempting to dereference the returned iterator results in + * undefined behavior. + * + * @note + * Iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the entity following the last entity of the view. + */ + [[nodiscard]] iterator end() const { + return iterator{view->begin(), view->end(), view->end(), unchecked(view), filter}; + } + + /** + * @brief Returns an iterator to the first entity of the reversed view. + * + * The returned iterator points to the first entity of the reversed view. If + * the view is empty, the returned iterator will be equal to `rend()`. + * + * @note + * Iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first entity of the reversed view. + */ + [[nodiscard]] reverse_iterator rbegin() const { + return reverse_iterator{view->rbegin(), view->rend(), view->rbegin(), unchecked(view), filter}; + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * view. + * + * The returned iterator points to the entity following the last entity of + * the reversed view. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the entity following the last entity of the + * reversed view. + */ + [[nodiscard]] reverse_iterator rend() const { + return reverse_iterator{view->rbegin(), view->rend(), view->rend(), unchecked(view), filter}; + } + + /** + * @brief Returns the first entity of the view, if any. + * @return The first entity of the view if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity of the view, if any. + * @return The last entity of the view if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const { + const auto it = rbegin(); + return it != rend() ? *it : null; + } + + /** + * @brief Finds an entity. + * @param entt A valid entity identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const { + iterator it{view->begin(), view->end(), view->find(entt), unchecked(view), filter}; + return (it != end() && *it == entt) ? it : end(); + } + + /** + * @brief Checks if a view contains an entity. + * @param entt A valid entity identifier. + * @return True if the view contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const { + return (std::get *>(pools)->contains(entt) && ...) + && (sizeof...(Exclude) == 0 || std::none_of(filter.begin(), filter.end(), [entt](const auto *cpool) { return cpool->contains(entt); })); + } + + /** + * @brief Returns the components assigned to the given entity. + * + * Prefer this function instead of `registry::get` during iterations. It has + * far better performance than its counterpart. + * + * @warning + * Attempting to use an invalid component type results in a compilation + * error. Attempting to use an entity that doesn't belong to the view + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * view doesn't contain the given entity. + * + * @tparam Comp Types of components to get. + * @param entt A valid entity identifier. + * @return The components assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entt) const { + ENTT_ASSERT(contains(entt)); + + if constexpr(sizeof...(Comp) == 0) { + static_assert(sizeof...(Component) == 1, "Invalid component type"); + return (std::get *>(pools)->get(entt), ...); + } else if constexpr(sizeof...(Comp) == 1) { + return (std::get *>(pools)->get(entt), ...); + } else { + return std::tuple({}))...>{get(entt)...}; + } + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of references to 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 { + view = candidate(); + ((std::get *>(pools) == view ? each(std::move(func)) : void()), ...); + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The pool of the suggested component is used to lead the iterations. The + * returned entities will therefore respect the order of the pool associated + * with that type.
+ * It is no longer guaranteed that the performance is the best possible, but + * there will be greater control over the order of iteration. + * + * @sa each + * + * @tparam Comp Type of component to use to enforce the iteration order. + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + using non_empty_type = type_list_cat_t, type_list<>, type_list>...>; + traverse(std::move(func), non_empty_type{}); + } + + /** + * @brief Returns an iterable object to use to _visit_ the view. + * + * The iterable object returns tuples that contain the current entity and a + * set of references to its non-empty components. The _constness_ of the + * components is as requested. + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @return An iterable object to use to _visit_ the view. + */ + [[nodiscard]] auto proxy() const ENTT_NOEXCEPT { + view = candidate(); + return view_proxy{begin(), end(), pools}; + } + + /** + * @brief Returns an iterable object to use to _visit_ the view. + * + * The pool of the suggested component is used to lead the iterations. The + * returned elements will therefore respect the order of the pool associated + * with that type.
+ * It is no longer guaranteed that the performance is the best possible, but + * there will be greater control over the order of iteration. + * + * @sa each + * + * @tparam Comp Type of component to use to enforce the iteration order. + * @return An iterable object to use to _visit_ the view. + */ + template + [[nodiscard]] auto proxy() const ENTT_NOEXCEPT { + const sparse_set *cpool = std::get *>(pools); + iterator first{cpool->begin(), cpool->end(), cpool->begin(), unchecked(cpool), filter}; + iterator last{cpool->begin(), cpool->end(), cpool->end(), unchecked(cpool), filter}; + return view_proxy{std::move(first), std::move(last), pools}; + } + + /** + * @brief Chunked iteration for entities and components + * + * Chunked iteration tries to spot chunks in the sets of entities and + * components and return them one at a time along with their sizes.
+ * This type of iteration is intended where it's known a priori that the + * creation of entities and components takes place in chunk, which is + * actually quite common. In this case, various optimizations can be applied + * downstream to obtain even better performances from the views. + * + * The signature of the function must be equivalent to the following: + * + * @code{.cpp} + * void(const entity_type *, Type *..., size_type); + * @endcode + * + * The arguments are as follows: + * + * * A pointer to the entities belonging to the chunk. + * * Pointers to the components associated with the returned entities. + * * The length of the chunk. + * + * Note that the callback can be invoked 0 or more times and no guarantee is + * given on the order of the elements. + * + * @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 chunked(Func func) const { + using non_empty_type = type_list_cat_t, type_list<>, type_list>...>; + view = candidate(); + iterate(std::move(func), non_empty_type{}); + } + +private: + const std::tuple *...> pools; + mutable const sparse_set* view; + filter_type filter; +}; + + +/** + * @brief Single component view specialization. + * + * Single component views are specialized in order to get a boost in terms of + * performance. This kind of views can access the underlying data structure + * directly and avoid superfluous checks. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given component are created and assigned to entities. + * * The entity currently pointed is modified (as an example, the given + * component is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pool iterated by the view in any way + * invalidates all the iterators and using them results in undefined behavior. + * + * @note + * Views share a reference to the underlying data structure of the registry that + * generated them. Therefore any change to the entities and to the components + * made by means of the registry are immediately reflected by views. + * + * @warning + * Lifetime of a view must not overcome that of the registry that generated it. + * In any other case, attempting to use a view results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Component Type of component iterated by the view. + */ +template +class basic_view, Component> { + /*! @brief A registry is allowed to create views. */ + friend class basic_registry; + + using pool_type = pool_t; + + class view_proxy { + friend class basic_view, Component>; + + class proxy_iterator { + friend class view_proxy; + + using it_type = std::conditional_t< + is_eto_eligible_v, + std::tuple::iterator>, + std::tuple::iterator, decltype(std::declval().begin())> + >; + + proxy_iterator(it_type from) ENTT_NOEXCEPT + : it{from} + {} + + public: + using difference_type = std::ptrdiff_t; + using value_type = std::conditional_t, std::tuple, std::tuple>; + using pointer = void; + using reference = std::conditional_t, std::tuple, std::tuple>; + using iterator_category = std::input_iterator_tag; + + proxy_iterator & operator++() ENTT_NOEXCEPT { + return std::apply([](auto &&... curr) { (++curr, ...); }, it), *this; + } + + proxy_iterator operator++(int) ENTT_NOEXCEPT { + proxy_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return std::apply([](auto &&... curr) { return reference{*curr...}; }, it); + } + + [[nodiscard]] bool operator==(const proxy_iterator &other) const ENTT_NOEXCEPT { + return std::get<0>(other.it) == std::get<0>(it); + } + + [[nodiscard]] bool operator!=(const proxy_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + private: + it_type it{}; + }; + + view_proxy(pool_type &ref) + : pool{&ref} + {} + + public: + using iterator = proxy_iterator; + + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + if constexpr(is_eto_eligible_v) { + return proxy_iterator{std::make_tuple(pool->sparse_set::begin())}; + } else { + return proxy_iterator{std::make_tuple(pool->sparse_set::begin(), pool->begin())}; + } + } + + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + if constexpr(is_eto_eligible_v) { + return proxy_iterator{std::make_tuple(pool->sparse_set::end())}; + } else { + return proxy_iterator{std::make_tuple(pool->sparse_set::end(), pool->end())}; + } + } + + private: + pool_type *pool; + }; + + basic_view(pool_type &ref) ENTT_NOEXCEPT + : pool{&ref} + {} + +public: + /*! @brief Type of component iterated by the view. */ + using raw_type = Component; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Random access iterator type. */ + using iterator = typename sparse_set::iterator; + /*! @brief Reversed iterator type. */ + using reverse_iterator = typename sparse_set::reverse_iterator; + + /** + * @brief Returns the number of entities that have the given component. + * @return Number of entities that have the given component. + */ + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + return pool->size(); + } + + /** + * @brief Checks whether a view is empty. + * @return True if the view is empty, false otherwise. + */ + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return pool->empty(); + } + + /** + * @brief Direct access to the list of components. + * + * The returned pointer is such that range `[raw(), raw() + size()]` is + * always a valid range, even if the container is empty. + * + * @note + * Components are in the reverse order as returned by the `begin`/`end` + * iterators. + * + * @return A pointer to the array of components. + */ + [[nodiscard]] raw_type * raw() const ENTT_NOEXCEPT { + return pool->raw(); + } + + /** + * @brief Direct access to the list of entities. + * + * The returned pointer is such that range `[data(), data() + size()]` is + * always a valid range, even if the container is empty. + * + * @note + * Entities are in the reverse order as returned by the `begin`/`end` + * iterators. + * + * @return A pointer to the array of entities. + */ + [[nodiscard]] const entity_type * data() const ENTT_NOEXCEPT { + return pool->data(); + } + + /** + * @brief Returns an iterator to the first entity of the view. + * + * The returned iterator points to the first entity of the view. If the view + * is empty, the returned iterator will be equal to `end()`. + * + * @note + * Iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first entity of the view. + */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return pool->sparse_set::begin(); + } + + /** + * @brief Returns an iterator that is past the last entity of the view. + * + * The returned iterator points to the entity following the last entity of + * the view. Attempting to dereference the returned iterator results in + * undefined behavior. + * + * @note + * Iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the entity following the last entity of the view. + */ + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + return pool->sparse_set::end(); + } + + /** + * @brief Returns an iterator to the first entity of the reversed view. + * + * The returned iterator points to the first entity of the reversed view. If + * the view is empty, the returned iterator will be equal to `rend()`. + * + * @note + * Iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first entity of the reversed view. + */ + [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { + return pool->sparse_set::rbegin(); + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * view. + * + * The returned iterator points to the entity following the last entity of + * the reversed view. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the entity following the last entity of the + * reversed view. + */ + [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { + return pool->sparse_set::rend(); + } + + /** + * @brief Returns the first entity of the view, if any. + * @return The first entity of the view if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity of the view, if any. + * @return The last entity of the view if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const { + const auto it = rbegin(); + return it != rend() ? *it : null; + } + + /** + * @brief Finds an entity. + * @param entt A valid entity identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const { + const auto it = pool->find(entt); + return it != end() && *it == entt ? it : end(); + } + + /** + * @brief Returns the identifier that occupies the given position. + * @param pos Position of the element to return. + * @return The identifier that occupies the given position. + */ + [[nodiscard]] entity_type operator[](const size_type pos) const { + return begin()[pos]; + } + + /** + * @brief Checks if a view contains an entity. + * @param entt A valid entity identifier. + * @return True if the view contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const { + return pool->contains(entt); + } + + /** + * @brief Returns the component assigned to the given entity. + * + * Prefer this function instead of `registry::get` during iterations. It has + * far better performance than its counterpart. + * + * @warning + * Attempting to use an entity that doesn't belong to the view results in + * undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * view doesn't contain the given entity. + * + * @param entt A valid entity identifier. + * @return The component assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + static_assert(std::is_same_v, "Invalid component type"); + ENTT_ASSERT(contains(entt)); + return pool->get(entt); + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a reference to the component if it's a non-empty one. + * The _constness_ of the component is as requested.
+ * The signature of the function must be equivalent to one of the following + * forms: + * + * @code{.cpp} + * void(const entity_type, Component &); + * void(Component &); + * @endcode + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + if constexpr(is_eto_eligible_v) { + if constexpr(std::is_invocable_v) { + for(auto pos = pool->size(); pos; --pos) { + func(); + } + } else { + for(const auto entt: *this) { + func(entt); + } + } + } else { + if constexpr(std::is_invocable_v) { + for(auto &&component: *pool) { + func(component); + } + } else { + auto raw = pool->begin(); + + for(const auto entt: *this) { + func(entt, *(raw++)); + } + } + } + } + + /** + * @brief Returns an iterable object to use to _visit_ the view. + * + * The iterable object returns tuples that contain the current entity and a + * reference to its component if it's a non-empty one. The _constness_ of + * the component is as requested. + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @return An iterable object to use to _visit_ the view. + */ + [[nodiscard]] auto proxy() const ENTT_NOEXCEPT { + return view_proxy{*pool}; + } + +private: + pool_type *pool; +}; + + +} + + +#endif + + + +namespace entt { + + +/** + * @brief Fast and reliable entity-component system. + * + * The registry is the core class of the entity-component framework.
+ * It stores entities and arranges pools of components on a per request basis. + * By means of a registry, users can manage entities and components, then create + * views or groups to iterate them. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class basic_registry { + using traits_type = entt_traits; + + template + struct pool_handler final: storage { + static_assert(std::is_same_v>, "Invalid component type"); + + [[nodiscard]] auto on_construct() ENTT_NOEXCEPT { + return sink{construction}; + } + + [[nodiscard]] auto on_update() ENTT_NOEXCEPT { + return sink{update}; + } + + [[nodiscard]] auto on_destroy() ENTT_NOEXCEPT { + return sink{destruction}; + } + + template + decltype(auto) emplace(basic_registry &owner, const Entity entt, Args &&... args) { + storage::emplace(entt, std::forward(args)...); + construction.publish(owner, entt); + + if constexpr(!is_eto_eligible_v) { + return this->get(entt); + } + } + + template + void insert(basic_registry &owner, It first, It last, Args &&... args) { + storage::insert(first, last, std::forward(args)...); + + if(!construction.empty()) { + while(first != last) { construction.publish(owner, *(first++)); } + } + } + + void remove(basic_registry &owner, const Entity entt) { + destruction.publish(owner, entt); + this->erase(entt); + } + + template + void remove(basic_registry &owner, It first, It last) { + if(std::distance(first, last) == std::distance(this->begin(), this->end())) { + if(!destruction.empty()) { + while(first != last) { destruction.publish(owner, *(first++)); } + } + + this->clear(); + } else { + while(first != last) { this->remove(owner, *(first++)); } + } + } + + template + decltype(auto) patch(basic_registry &owner, const Entity entt, [[maybe_unused]] Func &&... func) { + if constexpr(is_eto_eligible_v) { + update.publish(owner, entt); + } else { + (std::forward(func)(this->get(entt)), ...); + update.publish(owner, entt); + return this->get(entt); + } + } + + decltype(auto) replace(basic_registry &owner, const Entity entt, Component component) { + return patch(owner, entt, [&component](auto &&curr) { curr = std::move(component); }); + } + + private: + sigh construction{}; + sigh destruction{}; + sigh update{}; + }; + + struct pool_data { + id_type type_id{}; + std::unique_ptr> pool{}; + void(* remove)(sparse_set &, basic_registry &, const Entity){}; + }; + + template + struct group_handler; + + template + struct group_handler, get_t, Owned...> { + static_assert(std::conjunction_v>..., std::is_same>..., std::is_same>...>, "One or more component types are invalid"); + std::conditional_t, std::size_t> current{}; + + template + void maybe_valid_if(basic_registry &owner, const Entity entt) { + [[maybe_unused]] const auto cpools = std::forward_as_tuple(owner.assure()...); + + const auto is_valid = ((std::is_same_v || std::get &>(cpools).contains(entt)) && ...) + && ((std::is_same_v || owner.assure().contains(entt)) && ...) + && ((std::is_same_v || !owner.assure().contains(entt)) && ...); + + if constexpr(sizeof...(Owned) == 0) { + if(is_valid && !current.contains(entt)) { + current.emplace(entt); + } + } else { + if(is_valid && !(std::get<0>(cpools).index(entt) < current)) { + const auto pos = current++; + (std::get &>(cpools).swap(std::get &>(cpools).data()[pos], entt), ...); + } + } + } + + void discard_if([[maybe_unused]] basic_registry &owner, const Entity entt) { + if constexpr(sizeof...(Owned) == 0) { + if(current.contains(entt)) { + current.erase(entt); + } + } else { + if(const auto cpools = std::forward_as_tuple(owner.assure()...); std::get<0>(cpools).contains(entt) && (std::get<0>(cpools).index(entt) < current)) { + const auto pos = --current; + (std::get &>(cpools).swap(std::get &>(cpools).data()[pos], entt), ...); + } + } + } + }; + + struct group_data { + std::size_t size; + std::unique_ptr group; + bool (* owned)(const id_type) ENTT_NOEXCEPT; + bool (* get)(const id_type) ENTT_NOEXCEPT; + bool (* exclude)(const id_type) ENTT_NOEXCEPT; + }; + + struct variable_data { + id_type type_id; + std::unique_ptr value; + }; + + template + [[nodiscard]] const pool_handler & assure() const { + const sparse_set *cpool; + + if constexpr(ENTT_FAST_PATH(has_type_index_v)) { + const auto index = type_index::value(); + + if(!(index < pools.size())) { + pools.resize(size_type(index+1u)); + } + + if(auto &&pdata = pools[index]; !pdata.pool) { + pdata.type_id = type_info::id(); + pdata.pool.reset(new pool_handler()); + pdata.remove = [](sparse_set &target, basic_registry &owner, const entity_type entt) { + static_cast &>(target).remove(owner, entt); + }; + } + + cpool = pools[index].pool.get(); + } else { + if(const auto it = std::find_if(pools.cbegin(), pools.cend(), [id = type_info::id()](const auto &pdata) { return id == pdata.type_id; }); it == pools.cend()) { + cpool = pools.emplace_back(pool_data{ + type_info::id(), + std::unique_ptr>{new pool_handler()}, + [](sparse_set &target, basic_registry &owner, const entity_type entt) { + static_cast &>(target).remove(owner, entt); + } + }).pool.get(); + } else { + cpool = it->pool.get(); + } + } + + return *static_cast *>(cpool); + } + + template + [[nodiscard]] pool_handler & assure() { + return const_cast &>(std::as_const(*this).template assure()); + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Underlying version type. */ + using version_type = typename traits_type::version_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + + /*! @brief Default constructor. */ + basic_registry() = default; + + /*! @brief Default move constructor. */ + basic_registry(basic_registry &&) = default; + + /*! @brief Default move assignment operator. @return This registry. */ + basic_registry & operator=(basic_registry &&) = default; + + /** + * @brief Prepares a pool for the given type if required. + * @tparam Component Type of component for which to prepare a pool. + */ + template + void prepare() { + // suppress the warning due to the [[nodiscard]] attribute + static_cast(assure()); + } + + /** + * @brief Returns the number of existing components of the given type. + * @tparam Component Type of component of which to return the size. + * @return Number of existing components of the given type. + */ + template + [[nodiscard]] size_type size() const { + return assure().size(); + } + + /** + * @brief Returns the number of entities created so far. + * @return Number of entities created so far. + */ + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + return entities.size(); + } + + /** + * @brief Returns the number of entities still in use. + * @return Number of entities still in use. + */ + [[nodiscard]] size_type alive() const { + auto sz = entities.size(); + auto curr = destroyed; + + for(; curr != null; --sz) { + curr = entities[to_integral(curr) & traits_type::entity_mask]; + } + + return sz; + } + + /** + * @brief Increases the capacity of the registry or of the pools for the + * given components. + * + * If no components are specified, the capacity of the registry is + * increased, that is the number of entities it contains. Otherwise the + * capacity of the pools for the given components is increased.
+ * In both cases, if the new capacity is greater than the current capacity, + * new storage is allocated, otherwise the method does nothing. + * + * @tparam Component Types of components for which to reserve storage. + * @param cap Desired capacity. + */ + template + void reserve(const size_type cap) { + if constexpr(sizeof...(Component) == 0) { + entities.reserve(cap); + } else { + (assure().reserve(cap), ...); + } + } + + /** + * @brief Returns the capacity of the pool for the given component. + * @tparam Component Type of component in which one is interested. + * @return Capacity of the pool of the given component. + */ + template + [[nodiscard]] size_type capacity() const { + return assure().capacity(); + } + + /** + * @brief Returns the number of entities that a registry has currently + * allocated space for. + * @return Capacity of the registry. + */ + [[nodiscard]] size_type capacity() const ENTT_NOEXCEPT { + return entities.capacity(); + } + + /** + * @brief Requests the removal of unused capacity for the given components. + * @tparam Component Types of components for which to reclaim unused + * capacity. + */ + template + void shrink_to_fit() { + (assure().shrink_to_fit(), ...); + } + + /** + * @brief Checks whether the registry or the pools of the given components + * are empty. + * + * A registry is considered empty when it doesn't contain entities that are + * still in use. + * + * @tparam Component Types of components in which one is interested. + * @return True if the registry or the pools of the given components are + * empty, false otherwise. + */ + template + [[nodiscard]] bool empty() const { + if constexpr(sizeof...(Component) == 0) { + return !alive(); + } else { + return (assure().empty() && ...); + } + } + + /** + * @brief Direct access to the list of components of a given pool. + * + * The returned pointer is such that range + * `[raw(), raw() + size()]` is always a + * valid range, even if the container is empty. + * + * Components are in the reverse order as imposed by the sorting + * functionalities. + * + * @note + * Empty components aren't explicitly instantiated. Therefore, this function + * isn't available for them. A compilation error will occur if invoked. + * + * @tparam Component Type of component in which one is interested. + * @return A pointer to the array of components of the given type. + */ + template + [[nodiscard]] const Component * raw() const { + return assure().raw(); + } + + /*! @copydoc raw */ + template + [[nodiscard]] Component * raw() { + return const_cast(std::as_const(*this).template raw()); + } + + /** + * @brief Direct access to the list of entities of a given pool. + * + * The returned pointer is such that range + * `[data(), data() + size()]` is always a + * valid range, even if the container is empty. + * + * Entities are in the reverse order as imposed by the sorting + * functionalities. + * + * @tparam Component Type of component in which one is interested. + * @return A pointer to the array of entities. + */ + template + [[nodiscard]] const entity_type * data() const { + return assure().data(); + } + + /** + * @brief Direct access to the list of entities of a registry. + * + * The returned pointer is such that range `[data(), data() + size()]` is + * always a valid range, even if the container is empty. + * + * @warning + * This list contains both valid and destroyed entities and isn't suitable + * for direct use. + * + * @return A pointer to the array of entities. + */ + [[nodiscard]] const entity_type * data() const ENTT_NOEXCEPT { + return entities.data(); + } + + /** + * @brief Checks if an entity identifier refers to a valid entity. + * @param entity An entity identifier, either valid or not. + * @return True if the identifier is valid, false otherwise. + */ + [[nodiscard]] bool valid(const entity_type entity) const { + const auto pos = size_type(to_integral(entity) & traits_type::entity_mask); + return (pos < entities.size() && entities[pos] == entity); + } + + /** + * @brief Returns the entity identifier without the version. + * @param entity An entity identifier, either valid or not. + * @return The entity identifier without the version. + */ + [[nodiscard]] static entity_type entity(const entity_type entity) ENTT_NOEXCEPT { + return entity_type{to_integral(entity) & traits_type::entity_mask}; + } + + /** + * @brief Returns the version stored along with an entity identifier. + * @param entity An entity identifier, either valid or not. + * @return The version stored along with the given entity identifier. + */ + [[nodiscard]] static version_type version(const entity_type entity) ENTT_NOEXCEPT { + return version_type(to_integral(entity) >> traits_type::entity_shift); + } + + /** + * @brief Returns the actual version for an entity identifier. + * + * @warning + * Attempting to use an entity that doesn't belong to the registry results + * in undefined behavior. An entity belongs to the registry even if it has + * been previously destroyed and/or recycled.
+ * An assertion will abort the execution at runtime in debug mode if the + * registry doesn't own the given entity. + * + * @param entity A valid entity identifier. + * @return Actual version for the given entity identifier. + */ + [[nodiscard]] version_type current(const entity_type entity) const { + const auto pos = size_type(to_integral(entity) & traits_type::entity_mask); + ENTT_ASSERT(pos < entities.size()); + return version_type(to_integral(entities[pos]) >> traits_type::entity_shift); + } + + /** + * @brief Creates a new entity and returns it. + * + * There are two kinds of possible entity identifiers: + * + * * Newly created ones in case no entities have been previously destroyed. + * * Recycled ones with updated versions. + * + * @return A valid entity identifier. + */ + entity_type create() { + entity_type entt; + + if(destroyed == null) { + entt = entities.emplace_back(entity_type{static_cast(entities.size())}); + // traits_type::entity_mask is reserved to allow for null identifiers + ENTT_ASSERT(to_integral(entt) < traits_type::entity_mask); + } else { + const auto curr = to_integral(destroyed); + const auto version = to_integral(entities[curr]) & (traits_type::version_mask << traits_type::entity_shift); + destroyed = entity_type{to_integral(entities[curr]) & traits_type::entity_mask}; + entt = entities[curr] = entity_type{curr | version}; + } + + return entt; + } + + /** + * @brief Creates a new entity and returns it. + * + * @sa create + * + * If the requested entity isn't in use, the suggested identifier is created + * and returned. Otherwise, a new one will be generated for this purpose. + * + * @param hint A desired entity identifier. + * @return A valid entity identifier. + */ + [[nodiscard]] entity_type create(const entity_type hint) { + ENTT_ASSERT(hint != null); + entity_type entt; + + if(const auto req = (to_integral(hint) & traits_type::entity_mask); !(req < entities.size())) { + entities.reserve(req + 1); + + for(auto pos = entities.size(); pos < req; ++pos) { + entities.emplace_back(destroyed); + destroyed = entity_type{static_cast(pos)}; + } + + entt = entities.emplace_back(hint); + } else if(const auto curr = (to_integral(entities[req]) & traits_type::entity_mask); req == curr) { + entt = create(); + } else { + auto *it = &destroyed; + for(; (to_integral(*it) & traits_type::entity_mask) != req; it = &entities[to_integral(*it) & traits_type::entity_mask]); + *it = entity_type{curr | (to_integral(*it) & (traits_type::version_mask << traits_type::entity_shift))}; + entt = entities[req] = hint; + } + + return entt; + } + + /** + * @brief Assigns each element in a range an entity. + * + * @sa create + * + * @tparam It Type of forward iterator. + * @param first An iterator to the first element of the range to generate. + * @param last An iterator past the last element of the range to generate. + */ + template + void create(It first, It last) { + std::generate(first, last, [this]() { return create(); }); + } + + /** + * @brief Assigns entities to an empty registry. + * + * This function is intended for use in conjunction with `raw`.
+ * Don't try to inject ranges of randomly generated entities. There is no + * guarantee that a registry will continue to work properly in this case. + * + * @warning + * An assertion will abort the execution at runtime in debug mode if all + * pools aren't empty. + * + * @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 assign(It first, It last) { + ENTT_ASSERT(std::all_of(pools.cbegin(), pools.cend(), [](auto &&pdata) { return !pdata.pool || pdata.pool->empty(); })); + entities.assign(first, last); + destroyed = null; + + for(std::size_t pos{}, end = entities.size(); pos < end; ++pos) { + if((to_integral(entities[pos]) & traits_type::entity_mask) != pos) { + const auto version = to_integral(entities[pos]) & (traits_type::version_mask << traits_type::entity_shift); + entities[pos] = entity_type{to_integral(destroyed) | version}; + destroyed = entity_type{static_cast(pos)}; + } + } + } + + /** + * @brief Destroys an entity. + * + * When an entity is destroyed, its version is updated and the identifier + * can be recycled at any time. + * + * @sa remove_all + * + * @param entity A valid entity identifier. + */ + void destroy(const entity_type entity) { + destroy(entity, version_type((to_integral(entity) >> traits_type::entity_shift) + 1)); + } + + /** + * @brief Destroys an entity. + * + * If the entity isn't already destroyed, the suggested version is used + * instead of the implicitly generated one. + * + * @sa remove_all + * + * @param entity A valid entity identifier. + * @param version A desired version upon destruction. + */ + void destroy(const entity_type entity, const version_type version) { + remove_all(entity); + // lengthens the implicit list of destroyed entities + const auto entt = to_integral(entity) & traits_type::entity_mask; + entities[entt] = entity_type{to_integral(destroyed) | (typename traits_type::entity_type{version} << traits_type::entity_shift)}; + destroyed = entity_type{entt}; + } + + /** + * @brief Destroys all the entities in a range. + * + * @sa destroy + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + template + void destroy(It first, It last) { + while(first != last) { destroy(*(first++)); } + } + + /** + * @brief Assigns the given component to an entity. + * + * A new instance of the given component is created and initialized with the + * arguments provided (the component must have a proper constructor or be of + * aggregate type). Then the component is assigned to the given entity. + * + * @warning + * Attempting to use an invalid entity or to assign a component to an entity + * that already owns it results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity or if the entity already owns an instance of the given + * component. + * + * @tparam Component Type of component to create. + * @tparam Args Types of arguments to use to construct the component. + * @param entity A valid entity identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + decltype(auto) emplace(const entity_type entity, Args &&... args) { + ENTT_ASSERT(valid(entity)); + return assure().emplace(*this, entity, std::forward(args)...); + } + + /** + * @brief Assigns each entity in a range the given component. + * + * @sa emplace + * + * @tparam Component Type of component to create. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param value An instance of the component to assign. + */ + template + void insert(It first, It last, const Component &value = {}) { + ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); })); + assure().insert(*this, first, last, value); + } + + /** + * @brief Assigns each entity in a range the given components. + * + * @sa emplace + * + * @tparam Component Type of component to create. + * @tparam EIt Type of input iterator. + * @tparam CIt Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param from An iterator to the first element of the range of components. + * @param to An iterator past the last element of the range of components. + */ + template + void insert(EIt first, EIt last, CIt from, CIt to) { + static_assert(std::is_constructible_v::value_type>, "Invalid value type"); + ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); })); + assure().insert(*this, first, last, from, to); + } + + /** + * @brief Assigns or replaces the given component for an entity. + * + * Equivalent to the following snippet (pseudocode): + * + * @code{.cpp} + * auto &component = registry.has(entity) ? registry.replace(entity, args...) : registry.emplace(entity, args...); + * @endcode + * + * Prefer this function anyway because it has slightly better performance. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity. + * + * @tparam Component Type of component to assign or replace. + * @tparam Args Types of arguments to use to construct the component. + * @param entity A valid entity identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + decltype(auto) emplace_or_replace(const entity_type entity, Args &&... args) { + ENTT_ASSERT(valid(entity)); + auto &cpool = assure(); + + return cpool.contains(entity) + ? cpool.replace(*this, entity, Component{std::forward(args)...}) + : cpool.emplace(*this, entity, std::forward(args)...); + } + + /** + * @brief Patches the given component for an entity. + * + * The signature of the functions should be equivalent to the following: + * + * @code{.cpp} + * void(Component &); + * @endcode + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned. However, this function can be used to trigger an update signal + * for them. + * + * @warning + * Attempting to use an invalid entity or to patch a component of an entity + * that doesn't own it results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity or if the entity doesn't own an instance of the given + * component. + * + * @tparam Component Type of component to patch. + * @tparam Func Types of the function objects to invoke. + * @param entity A valid entity identifier. + * @param func Valid function objects. + * @return A reference to the patched component. + */ + template + decltype(auto) patch(const entity_type entity, Func &&... func) { + ENTT_ASSERT(valid(entity)); + return assure().patch(*this, entity, std::forward(func)...); + } + + /** + * @brief Replaces the given component for an entity. + * + * A new instance of the given component is created and initialized with the + * arguments provided (the component must have a proper constructor or be of + * aggregate type). Then the component is assigned to the given entity. + * + * @warning + * Attempting to use an invalid entity or to replace a component of an + * entity that doesn't own it results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity or if the entity doesn't own an instance of the given + * component. + * + * @tparam Component Type of component to replace. + * @tparam Args Types of arguments to use to construct the component. + * @param entity A valid entity identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the component being replaced. + */ + template + decltype(auto) replace(const entity_type entity, Args &&... args) { + return assure().replace(*this, entity, Component{std::forward(args)...}); + } + + /** + * @brief Removes the given components from an entity. + * + * @warning + * Attempting to use an invalid entity or to remove a component from an + * entity that doesn't own it results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity or if the entity doesn't own an instance of the given + * component. + * + * @tparam Component Types of components to remove. + * @param entity A valid entity identifier. + */ + template + void remove(const entity_type entity) { + ENTT_ASSERT(valid(entity)); + (assure().remove(*this, entity), ...); + } + + /** + * @brief Removes the given components from all the entities in a range. + * + * @see remove + * + * @tparam Component Types of components to remove. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + template + void remove(It first, It last) { + ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); })); + (assure().remove(*this, first, last), ...); + } + + /** + * @brief Removes the given components from an entity. + * + * Equivalent to the following snippet (pseudocode): + * + * @code{.cpp} + * if(registry.has(entity)) { registry.remove(entity) } + * @endcode + * + * Prefer this function anyway because it has slightly better performance. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity. + * + * @tparam Component Types of components to remove. + * @param entity A valid entity identifier. + * @return The number of components actually removed. + */ + template + size_type remove_if_exists(const entity_type entity) { + ENTT_ASSERT(valid(entity)); + + return ([this, entity](auto &&cpool) { + return cpool.contains(entity) ? (cpool.remove(*this, entity), true) : false; + }(assure()) + ... + size_type{}); + } + + /** + * @brief Removes all the components from an entity and makes it orphaned. + * + * @warning + * In case there are listeners that observe the destruction of components + * and assign other components to the entity in their bodies, the result of + * invoking this function may not be as expected. In the worst case, it + * could lead to undefined behavior. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity. + * + * @param entity A valid entity identifier. + */ + void remove_all(const entity_type entity) { + ENTT_ASSERT(valid(entity)); + + for(auto pos = pools.size(); pos; --pos) { + if(auto &pdata = pools[pos-1]; pdata.pool && pdata.pool->contains(entity)) { + pdata.remove(*pdata.pool, *this, entity); + } + } + } + + /** + * @brief Checks if an entity has all the given components. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity. + * + * @tparam Component Components for which to perform the check. + * @param entity A valid entity identifier. + * @return True if the entity has all the components, false otherwise. + */ + template + [[nodiscard]] bool has(const entity_type entity) const { + ENTT_ASSERT(valid(entity)); + return (assure().contains(entity) && ...); + } + + /** + * @brief Checks if an entity has at least one of the given components. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity. + * + * @tparam Component Components for which to perform the check. + * @param entity A valid entity identifier. + * @return True if the entity has at least one of the given components, + * false otherwise. + */ + template + [[nodiscard]] bool any(const entity_type entity) const { + ENTT_ASSERT(valid(entity)); + return (assure().contains(entity) || ...); + } + + /** + * @brief Returns references to the given components for an entity. + * + * @warning + * Attempting to use an invalid entity or to get a component from an entity + * that doesn't own it results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity or if the entity doesn't own an instance of the given + * component. + * + * @tparam Component Types of components to get. + * @param entity A valid entity identifier. + * @return References to the components owned by the entity. + */ + template + [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entity) const { + ENTT_ASSERT(valid(entity)); + + if constexpr(sizeof...(Component) == 1) { + return (assure().get(entity), ...); + } else { + return std::forward_as_tuple(get(entity)...); + } + } + + /*! @copydoc get */ + template + [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entity) { + ENTT_ASSERT(valid(entity)); + + if constexpr(sizeof...(Component) == 1) { + return (assure().get(entity), ...); + } else { + return std::forward_as_tuple(get(entity)...); + } + } + + /** + * @brief Returns a reference to the given component for an entity. + * + * In case the entity doesn't own the component, the parameters provided are + * used to construct it.
+ * Equivalent to the following snippet (pseudocode): + * + * @code{.cpp} + * auto &component = registry.has(entity) ? registry.get(entity) : registry.emplace(entity, args...); + * @endcode + * + * Prefer this function anyway because it has slightly better performance. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity. + * + * @tparam Component Type of component to get. + * @tparam Args Types of arguments to use to construct the component. + * @param entity A valid entity identifier. + * @param args Parameters to use to initialize the component. + * @return Reference to the component owned by the entity. + */ + template + [[nodiscard]] decltype(auto) get_or_emplace(const entity_type entity, Args &&... args) { + ENTT_ASSERT(valid(entity)); + auto &cpool = assure(); + return cpool.contains(entity) ? cpool.get(entity) : cpool.emplace(*this, entity, std::forward(args)...); + } + + /** + * @brief Returns pointers to the given components for an entity. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity. + * + * @tparam Component Types of components to get. + * @param entity A valid entity identifier. + * @return Pointers to the components owned by the entity. + */ + template + [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entity) const { + ENTT_ASSERT(valid(entity)); + + if constexpr(sizeof...(Component) == 1) { + return (assure().try_get(entity), ...); + } else { + return std::make_tuple(try_get(entity)...); + } + } + + /*! @copydoc try_get */ + template + [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entity) { + ENTT_ASSERT(valid(entity)); + + if constexpr(sizeof...(Component) == 1) { + return (assure().try_get(entity), ...); + } else { + return std::make_tuple(try_get(entity)...); + } + } + + /** + * @brief Clears a whole registry or the pools for the given components. + * @tparam Component Types of components to remove from their entities. + */ + template + void clear() { + if constexpr(sizeof...(Component) == 0) { + // useless this-> used to suppress a warning with clang + each([this](const auto entity) { this->destroy(entity); }); + } else { + ([this](auto &&cpool) { + cpool.remove(*this, cpool.sparse_set::begin(), cpool.sparse_set::end()); + }(assure()), ...); + } + } + + /** + * @brief Iterates all the entities that are still in use. + * + * The function object is invoked for each entity that is still in use.
+ * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const Entity); + * @endcode + * + * This function is fairly slow and should not be used frequently. However, + * it's useful for iterating all the entities still in use, regardless of + * their components. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + if(destroyed == null) { + for(auto pos = entities.size(); pos; --pos) { + func(entities[pos-1]); + } + } else { + for(auto pos = entities.size(); pos; --pos) { + if(const auto entt = entities[pos - 1]; (to_integral(entt) & traits_type::entity_mask) == (pos - 1)) { + func(entt); + } + } + } + } + + /** + * @brief Checks if an entity has components assigned. + * @param entity A valid entity identifier. + * @return True if the entity has no components assigned, false otherwise. + */ + [[nodiscard]] bool orphan(const entity_type entity) const { + ENTT_ASSERT(valid(entity)); + return std::none_of(pools.cbegin(), pools.cend(), [entity](auto &&pdata) { return pdata.pool && pdata.pool->contains(entity); }); + } + + /** + * @brief Iterates orphans and applies them the given function object. + * + * The function object is invoked for each entity that is still in use and + * has no components assigned.
+ * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const Entity); + * @endcode + * + * This function can be very slow and should not be used frequently. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void orphans(Func func) const { + each([this, &func](const auto entity) { + if(orphan(entity)) { + func(entity); + } + }); + } + + /** + * @brief Returns a sink object for the given component. + * + * A sink is an opaque object used to connect listeners to components.
+ * The sink returned by this function can be used to receive notifications + * whenever a new instance of the given component is created and assigned to + * an entity. + * + * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(registry &, Entity); + * @endcode + * + * Listeners are invoked **after** the component has been assigned to the + * entity. + * + * @sa sink + * + * @tparam Component Type of component of which to get the sink. + * @return A temporary sink object. + */ + template + [[nodiscard]] auto on_construct() { + return assure().on_construct(); + } + + /** + * @brief Returns a sink object for the given component. + * + * A sink is an opaque object used to connect listeners to components.
+ * The sink returned by this function can be used to receive notifications + * whenever an instance of the given component is explicitly updated. + * + * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(registry &, Entity); + * @endcode + * + * Listeners are invoked **after** the component has been updated. + * + * @sa sink + * + * @tparam Component Type of component of which to get the sink. + * @return A temporary sink object. + */ + template + [[nodiscard]] auto on_update() { + return assure().on_update(); + } + + /** + * @brief Returns a sink object for the given component. + * + * A sink is an opaque object used to connect listeners to components.
+ * The sink returned by this function can be used to receive notifications + * whenever an instance of the given component is removed from an entity and + * thus destroyed. + * + * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(registry &, Entity); + * @endcode + * + * Listeners are invoked **before** the component has been removed from the + * entity. + * + * @sa sink + * + * @tparam Component Type of component of which to get the sink. + * @return A temporary sink object. + */ + template + [[nodiscard]] auto on_destroy() { + return assure().on_destroy(); + } + + /** + * @brief Returns a view for the given components. + * + * This kind of objects are created on the fly and share with the registry + * its internal data structures.
+ * Feel free to discard a view after the use. Creating and destroying a view + * is an incredibly cheap operation because they do not require any type of + * initialization.
+ * As a rule of thumb, storing a view should never be an option. + * + * Views do their best to iterate the smallest set of candidate entities. + * In particular: + * + * * Single component views are incredibly fast and iterate a packed array + * of entities, all of which has the given component. + * * Multi component views look at the number of entities available for each + * component and pick up a reference to the smallest set of candidates to + * test for the given components. + * + * Views in no way affect the functionalities of the registry nor those of + * the underlying pools. + * + * @note + * Multi component views are pretty fast. However their performance tend to + * degenerate when the number of components to iterate grows up and the most + * of the entities have all the given components.
+ * To get a performance boost, consider using a group instead. + * + * @tparam Component Type of components used to construct the view. + * @tparam Exclude Types of components used to filter the view. + * @return A newly created view. + */ + template + [[nodiscard]] basic_view, Component...> view(exclude_t = {}) const { + static_assert(sizeof...(Component) > 0, "Exclusion-only views are not supported"); + return { assure>()..., assure()... }; + } + + /*! @copydoc view */ + template + [[nodiscard]] basic_view, Component...> view(exclude_t = {}) { + static_assert(sizeof...(Component) > 0, "Exclusion-only views are not supported"); + return { assure>()..., assure()... }; + } + + /** + * @brief Returns a runtime view for the given components. + * + * This kind of objects are created on the fly and share with the registry + * its internal data structures.
+ * Users should throw away the view after use. Fortunately, creating and + * destroying a runtime view is an incredibly cheap operation because they + * do not require any type of initialization.
+ * As a rule of thumb, storing a view should never be an option. + * + * Runtime views are to be used when users want to construct a view from + * some external inputs and don't know at compile-time what are the required + * components. + * + * @tparam ItComp Type of input iterator for the components to use to + * construct the view. + * @tparam ItExcl Type of input iterator for the components to use to filter + * the view. + * @param first An iterator to the first element of the range of components + * to use to construct the view. + * @param last An iterator past the last element of the range of components + * to use to construct the view. + * @param from An iterator to the first element of the range of components + * to use to filter the view. + * @param to An iterator past the last element of the range of components to + * use to filter the view. + * @return A newly created runtime view. + */ + template + [[nodiscard]] basic_runtime_view runtime_view(ItComp first, ItComp last, ItExcl from = {}, ItExcl to = {}) const { + std::vector *> component(std::distance(first, last)); + std::vector *> filter(std::distance(from, to)); + + std::transform(first, last, component.begin(), [this](const auto ctype) { + const auto it = std::find_if(pools.cbegin(), pools.cend(), [ctype](auto &&pdata) { return pdata.pool && pdata.type_id == ctype; }); + return it == pools.cend() ? nullptr : it->pool.get(); + }); + + std::transform(from, to, filter.begin(), [this](const auto ctype) { + const auto it = std::find_if(pools.cbegin(), pools.cend(), [ctype](auto &&pdata) { return pdata.pool && pdata.type_id == ctype; }); + return it == pools.cend() ? nullptr : it->pool.get(); + }); + + return { std::move(component), std::move(filter) }; + } + + /** + * @brief Returns a group for the given components. + * + * This kind of objects are created on the fly and share with the registry + * its internal data structures.
+ * Feel free to discard a group after the use. Creating and destroying a + * group is an incredibly cheap operation because they do not require any + * type of initialization, but for the first time they are requested.
+ * As a rule of thumb, storing a group should never be an option. + * + * Groups support exclusion lists and can own types of components. The more + * types are owned by a group, the faster it is to iterate entities and + * components.
+ * However, groups also affect some features of the registry such as the + * creation and destruction of components, which will consequently be + * slightly slower (nothing that can be noticed in most cases). + * + * @note + * Pools of components that are owned by a group cannot be sorted anymore. + * The group takes the ownership of the pools and arrange components so as + * to iterate them as fast as possible. + * + * @tparam Owned Types of components owned by the group. + * @tparam Get Types of components observed by the group. + * @tparam Exclude Types of components used to filter the group. + * @return A newly created group. + */ + template + [[nodiscard]] basic_group, get_t, Owned...> group(get_t, exclude_t = {}) { + static_assert(sizeof...(Owned) + sizeof...(Get) > 0, "Exclusion-only views are not supported"); + static_assert(sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude) > 1, "Single component groups are not allowed"); + + using handler_type = group_handler, get_t...>, std::decay_t...>; + + const auto cpools = std::forward_as_tuple(assure>()..., assure>()...); + constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude); + handler_type *handler = nullptr; + + if(auto it = std::find_if(groups.cbegin(), groups.cend(), [size](const auto &gdata) { + return gdata.size == size + && (gdata.owned(type_info>::id()) && ...) + && (gdata.get(type_info>::id()) && ...) + && (gdata.exclude(type_info::id()) && ...); + }); it != groups.cend()) + { + handler = static_cast(it->group.get()); + } + + if(!handler) { + group_data candidate = { + size, + { new handler_type{}, [](void *instance) { delete static_cast(instance); } }, + []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_info>::id()) || ...); }, + []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_info>::id()) || ...); }, + []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_info::id()) || ...); }, + }; + + handler = static_cast(candidate.group.get()); + + const void *maybe_valid_if = nullptr; + const void *discard_if = nullptr; + + if constexpr(sizeof...(Owned) == 0) { + groups.push_back(std::move(candidate)); + } else { + ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), [size](const auto &gdata) { + const auto overlapping = (0u + ... + gdata.owned(type_info>::id())); + const auto sz = overlapping + (0u + ... + gdata.get(type_info>::id())) + (0u + ... + gdata.exclude(type_info::id())); + return !overlapping || ((sz == size) || (sz == gdata.size)); + })); + + const auto next = std::find_if_not(groups.cbegin(), groups.cend(), [size](const auto &gdata) { + return !(0u + ... + gdata.owned(type_info>::id())) || (size > gdata.size); + }); + + const auto prev = std::find_if(std::make_reverse_iterator(next), groups.crend(), [](const auto &gdata) { + return (0u + ... + gdata.owned(type_info>::id())); + }); + + maybe_valid_if = (next == groups.cend() ? maybe_valid_if : next->group.get()); + discard_if = (prev == groups.crend() ? discard_if : prev->group.get()); + groups.insert(next, std::move(candidate)); + } + + (on_construct>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>>(*handler), ...); + (on_construct>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>>(*handler), ...); + (on_destroy().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>(*handler), ...); + + (on_destroy>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); + (on_destroy>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); + (on_construct().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); + + if constexpr(sizeof...(Owned) == 0) { + for(const auto entity: view(exclude)) { + handler->current.emplace(entity); + } + } else { + // we cannot iterate backwards because we want to leave behind valid entities in case of owned types + for(auto *first = std::get<0>(cpools).data(), *last = first + std::get<0>(cpools).size(); first != last; ++first) { + handler->template maybe_valid_if...>>>(*this, *first); + } + } + } + + if constexpr(sizeof...(Owned) == 0) { + return { handler->current, std::get> &>(cpools)... }; + } else { + return { handler->current, std::get> &>(cpools)... , std::get> &>(cpools)... }; + } + } + + /** + * @brief Returns a group for the given components. + * + * @sa group + * + * @tparam Owned Types of components owned by the group. + * @tparam Get Types of components observed by the group. + * @tparam Exclude Types of components used to filter the group. + * @return A newly created group. + */ + template + [[nodiscard]] basic_group, get_t, Owned...> group(get_t, exclude_t = {}) const { + static_assert(std::conjunction_v..., std::is_const...>, "Invalid non-const type"); + return const_cast(this)->group(get_t{}, exclude); + } + + /** + * @brief Returns a group for the given components. + * + * @sa group + * + * @tparam Owned Types of components owned by the group. + * @tparam Exclude Types of components used to filter the group. + * @return A newly created group. + */ + template + [[nodiscard]] basic_group, get_t<>, Owned...> group(exclude_t = {}) { + return group(get_t<>{}, exclude); + } + + /** + * @brief Returns a group for the given components. + * + * @sa group + * + * @tparam Owned Types of components owned by the group. + * @tparam Exclude Types of components used to filter the group. + * @return A newly created group. + */ + template + [[nodiscard]] basic_group, get_t<>, Owned...> group(exclude_t = {}) const { + static_assert(std::conjunction_v...>, "Invalid non-const type"); + return const_cast(this)->group(exclude); + } + + /** + * @brief Checks whether the given components belong to any group. + * @tparam Component Types of components in which one is interested. + * @return True if the pools of the given components are sortable, false + * otherwise. + */ + template + [[nodiscard]] bool sortable() const { + return std::none_of(groups.cbegin(), groups.cend(), [](auto &&gdata) { return (gdata.owned(type_info>::id()) || ...); }); + } + + /** + * @brief Checks whether a group can be sorted. + * @tparam Owned Types of components owned by the group. + * @tparam Get Types of components observed by the group. + * @tparam Exclude Types of components used to filter the group. + * @return True if the group can be sorted, false otherwise. + */ + template + [[nodiscard]] bool sortable(const basic_group, get_t, Owned...> &) ENTT_NOEXCEPT { + constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude); + return std::find_if(groups.cbegin(), groups.cend(), [size](const auto &gdata) { + return (0u + ... + gdata.owned(type_info>::id())) && (size < gdata.size); + }) == groups.cend(); + } + + /** + * @brief Sorts the pool of entities for the given component. + * + * The order of the elements in a pool is highly affected by assignments + * of components to entities and deletions. Components are arranged to + * maximize the performance during iterations and users should not make any + * assumption on the order.
+ * This function can be used to impose an order to the elements in the pool + * of the given component. The order is kept valid until a component of the + * given type is assigned or removed from an entity. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(const Entity, const Entity); + * bool(const Component &, const Component &); + * @endcode + * + * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function oject must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * The comparison funtion object received by the sort function object hasn't + * necessarily the type of the one passed along with the other parameters to + * this member function. + * + * @warning + * Pools of components owned by a group cannot be sorted.
+ * An assertion will abort the execution at runtime in debug mode in case + * the pool is owned by a group. + * + * @tparam Component Type of components to sort. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort(Compare compare, Sort algo = Sort{}, Args &&... args) { + ENTT_ASSERT(sortable()); + auto &cpool = assure(); + cpool.sort(cpool.begin(), cpool.end(), std::move(compare), std::move(algo), std::forward(args)...); + } + + /** + * @brief Sorts two pools of components in the same way. + * + * The order of the elements in a pool is highly affected by assignments + * of components to entities and deletions. Components are arranged to + * maximize the performance during iterations and users should not make any + * assumption on the order. + * + * It happens that different pools of components must be sorted the same way + * because of runtime and/or performance constraints. This function can be + * used to order a pool of components according to the order between the + * entities in another pool of components. + * + * @b How @b it @b works + * + * Being `A` and `B` the two sets where `B` is the master (the one the order + * of which rules) and `A` is the slave (the one to sort), after a call to + * this function an iterator for `A` will return the entities according to + * the following rules: + * + * * All the entities in `A` that are also in `B` are returned first + * according to the order they have in `B`. + * * All the entities in `A` that are not in `B` are returned in no + * particular order after all the other entities. + * + * Any subsequent change to `B` won't affect the order in `A`. + * + * @warning + * Pools of components owned by a group cannot be sorted.
+ * An assertion will abort the execution at runtime in debug mode in case + * the pool is owned by a group. + * + * @tparam To Type of components to sort. + * @tparam From Type of components to use to sort. + */ + template + void sort() { + ENTT_ASSERT(sortable()); + assure().respect(assure()); + } + + /** + * @brief Visits an entity and returns the types for its components. + * + * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const id_type); + * @endcode + * + * Returned identifiers are those of the components owned by the entity. + * + * @sa type_info + * + * @warning + * It's not specified whether a component attached to or removed from the + * given entity during the visit is returned or not to the caller. + * + * @tparam Func Type of the function object to invoke. + * @param entity A valid entity identifier. + * @param func A valid function object. + */ + template + void visit(entity_type entity, Func func) const { + for(auto pos = pools.size(); pos; --pos) { + if(const auto &pdata = pools[pos-1]; pdata.pool && pdata.pool->contains(entity)) { + func(pdata.type_id); + } + } + } + + /** + * @brief Visits a registry and returns the types for its components. + * + * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const id_type); + * @endcode + * + * Returned identifiers are those of the components managed by the registry. + * + * @sa type_info + * + * @warning + * It's not specified whether a component for which a pool is created during + * the visit is returned or not to the caller. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void visit(Func func) const { + for(auto pos = pools.size(); pos; --pos) { + if(const auto &pdata = pools[pos-1]; pdata.pool) { + func(pdata.type_id); + } + } + } + + /** + * @brief Binds an object to the context of the registry. + * + * If the value already exists it is overwritten, otherwise a new instance + * of the given type is created and initialized with the arguments provided. + * + * @tparam Type Type of object to set. + * @tparam Args Types of arguments to use to construct the object. + * @param args Parameters to use to initialize the value. + * @return A reference to the newly created object. + */ + template + Type & set(Args &&... args) { + unset(); + vars.push_back(variable_data{type_info::id(), { new Type{std::forward(args)...}, [](void *instance) { delete static_cast(instance); } }}); + return *static_cast(vars.back().value.get()); + } + + /** + * @brief Unsets a context variable if it exists. + * @tparam Type Type of object to set. + */ + template + void unset() { + vars.erase(std::remove_if(vars.begin(), vars.end(), [](auto &&var) { + return var.type_id == type_info::id(); + }), vars.end()); + } + + /** + * @brief Binds an object to the context of the registry. + * + * In case the context doesn't contain the given object, the parameters + * provided are used to construct it. + * + * @tparam Type Type of object to set. + * @tparam Args Types of arguments to use to construct the object. + * @param args Parameters to use to initialize the object. + * @return A reference to the object in the context of the registry. + */ + template + [[nodiscard]] Type & ctx_or_set(Args &&... args) { + auto *value = try_ctx(); + return value ? *value : set(std::forward(args)...); + } + + /** + * @brief Returns a pointer to an object in the context of the registry. + * @tparam Type Type of object to get. + * @return A pointer to the object if it exists in the context of the + * registry, a null pointer otherwise. + */ + template + [[nodiscard]] const Type * try_ctx() const { + auto it = std::find_if(vars.cbegin(), vars.cend(), [](auto &&var) { return var.type_id == type_info::id(); }); + return it == vars.cend() ? nullptr : static_cast(it->value.get()); + } + + /*! @copydoc try_ctx */ + template + [[nodiscard]] Type * try_ctx() { + return const_cast(std::as_const(*this).template try_ctx()); + } + + /** + * @brief Returns a reference to an object in the context of the registry. + * + * @warning + * Attempting to get a context variable that doesn't exist results in + * undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid requests. + * + * @tparam Type Type of object to get. + * @return A valid reference to the object in the context of the registry. + */ + template + [[nodiscard]] const Type & ctx() const { + const auto *instance = try_ctx(); + ENTT_ASSERT(instance); + return *instance; + } + + /*! @copydoc ctx */ + template + [[nodiscard]] Type & ctx() { + return const_cast(std::as_const(*this).template ctx()); + } + + /** + * @brief Visits a registry and returns the types for its context variables. + * + * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const id_type); + * @endcode + * + * Returned identifiers are those of the context variables currently set. + * + * @sa type_info + * + * @warning + * It's not specified whether a context variable created during the visit is + * returned or not to the caller. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void ctx(Func func) const { + for(auto pos = vars.size(); pos; --pos) { + func(vars[pos-1].type_id); + } + } + +private: + std::vector groups{}; + mutable std::vector pools{}; + std::vector entities{}; + std::vector vars{}; + entity_type destroyed{null}; +}; + + +} + + +#endif + +// #include "entity.hpp" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Dedicated to those who aren't confident with the + * entity-component-system architecture. + * + * Tiny wrapper around a registry, for all those users that aren't confident + * with entity-component-system architecture and prefer to iterate objects + * directly. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +struct [[deprecated("Consider using the handle class instead")]] basic_actor { + /*! @brief Type of registry used internally. */ + using registry_type = basic_registry; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + + basic_actor() ENTT_NOEXCEPT + : entt{null}, reg{nullptr} + {} + + /** + * @brief Move constructor. + * + * After actor move construction, instances that have been moved from are + * placed in a valid but unspecified state. It's highly discouraged to + * continue using them. + * + * @param other The instance to move from. + */ + basic_actor(basic_actor &&other) ENTT_NOEXCEPT + : entt{other.entt}, reg{other.reg} + { + other.entt = null; + } + + /** + * @brief Constructs an actor from a given registry. + * @param ref An instance of the registry class. + */ + explicit basic_actor(registry_type &ref) + : entt{ref.create()}, reg{&ref} + {} + + /** + * @brief Constructs an actor from a given entity. + * @param entity A valid entity identifier. + * @param ref An instance of the registry class. + */ + explicit basic_actor(entity_type entity, registry_type &ref) ENTT_NOEXCEPT + : entt{entity}, reg{&ref} + { + ENTT_ASSERT(ref.valid(entity)); + } + + /*! @brief Default destructor. */ + virtual ~basic_actor() { + if(*this) { + reg->destroy(entt); + } + } + + /** + * @brief Move assignment operator. + * + * After actor move assignment, instances that have been moved from are + * placed in a valid but unspecified state. It's highly discouraged to + * continue using them. + * + * @param other The instance to move from. + * @return This actor. + */ + basic_actor & operator=(basic_actor &&other) ENTT_NOEXCEPT { + if(this != &other) { + auto tmp{std::move(other)}; + std::swap(reg, tmp.reg); + std::swap(entt, tmp.entt); + } + + return *this; + } + + /** + * @brief Assigns the given component to an actor. + * + * A new instance of the given component is created and initialized with the + * arguments provided (the component must have a proper constructor or be of + * aggregate type). Then the component is assigned to the actor.
+ * In case the actor already has a component of the given type, it's + * replaced with the new one. + * + * @tparam Component Type of the component to create. + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + decltype(auto) assign(Args &&... args) { + return reg->template emplace_or_replace(entt, std::forward(args)...); + } + + /** + * @brief Removes the given component from an actor. + * @tparam Component Type of the component to remove. + */ + template + void remove() { + reg->template remove(entt); + } + + /** + * @brief Checks if an actor has the given components. + * @tparam Component Components for which to perform the check. + * @return True if the actor has all the components, false otherwise. + */ + template + [[nodiscard]] bool has() const { + return reg->template has(entt); + } + + /** + * @brief Returns references to the given components for an actor. + * @tparam Component Types of components to get. + * @return References to the components owned by the actor. + */ + template + [[nodiscard]] decltype(auto) get() const { + return std::as_const(*reg).template get(entt); + } + + /*! @copydoc get */ + template + [[nodiscard]] decltype(auto) get() { + return reg->template get(entt); + } + + /** + * @brief Returns pointers to the given components for an actor. + * @tparam Component Types of components to get. + * @return Pointers to the components owned by the actor. + */ + template + [[nodiscard]] auto try_get() const { + return std::as_const(*reg).template try_get(entt); + } + + /*! @copydoc try_get */ + template + [[nodiscard]] auto try_get() { + return reg->template try_get(entt); + } + + /** + * @brief Returns a reference to the underlying registry. + * @return A reference to the underlying registry. + */ + [[nodiscard]] const registry_type & backend() const ENTT_NOEXCEPT { + return *reg; + } + + /*! @copydoc backend */ + [[nodiscard]] registry_type & backend() ENTT_NOEXCEPT { + return const_cast(std::as_const(*this).backend()); + } + + /** + * @brief Returns the entity associated with an actor. + * @return The entity associated with the actor. + */ + [[nodiscard]] entity_type entity() const ENTT_NOEXCEPT { + return entt; + } + + /** + * @brief Checks if an actor refers to a valid entity or not. + * @return True if the actor refers to a valid entity, false otherwise. + */ + [[nodiscard]] explicit operator bool() const { + return reg && reg->valid(entt); + } + +private: + entity_type entt; + registry_type *reg; +}; + + +} + + +#endif + +// #include "entity/entity.hpp" + +// #include "entity/group.hpp" + +// #include "entity/handle.hpp" +#ifndef ENTT_ENTITY_HANDLE_HPP +#define ENTT_ENTITY_HANDLE_HPP + + +// #include "registry.hpp" + + + +namespace entt { + + +/** + * @brief Non-owning handle to an entity. + * + * Tiny wrapper around a registry and an entity. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +struct basic_handle { + /*! @brief Underlying entity identifier. */ + using entity_type = std::remove_const_t; + + /*! @brief Type of registry accepted by the handle. */ + using registry_type = std::conditional_t< + std::is_const_v, + const basic_registry, + basic_registry + >; + + /** + * @brief Constructs a handle from a given registry and entity. + * @param ref An instance of the registry class. + * @param value An entity identifier. + */ + basic_handle(registry_type &ref, entity_type value = null) ENTT_NOEXCEPT + : reg{&ref}, entt{value} + {} + + /** + * @brief Assigns an entity to a handle. + * @param value An entity identifier. + * @return This handle. + */ + basic_handle & operator=(const entity_type value) ENTT_NOEXCEPT { + entt = value; + return *this; + } + + /** + * @brief Assigns the null object to a handle. + * @return This handle. + */ + basic_handle & operator=(null_t) ENTT_NOEXCEPT { + return (*this = static_cast(null)); + } + + /** + * @brief Constructs a const handle from a non-const one. + * @return A const handle referring to the same entity. + */ + [[nodiscard]] operator basic_handle() const ENTT_NOEXCEPT { + return {*reg, entt}; + } + + /** + * @brief Converts a handle to its underlying entity. + * @return An entity identifier. + */ + [[nodiscard]] operator entity_type() const ENTT_NOEXCEPT { + return entity(); + } + + /** + * @brief Checks if a handle refers to a valid entity or not. + * @return True if the handle refers to a valid entity, false otherwise. + */ + [[nodiscard]] explicit operator bool() const { + return reg->valid(entt); + } + + /** + * @brief Returns a reference to the underlying registry. + * @return A reference to the underlying registry. + */ + [[nodiscard]] registry_type & registry() const ENTT_NOEXCEPT { + return *reg; + } + + /** + * @brief Returns the entity associated with a handle. + * @return The entity associated with the handle. + */ + [[nodiscard]] entity_type entity() const ENTT_NOEXCEPT { + return entt; + } + + /** + * @brief Assigns the given component to a handle. + * @sa basic_registry::emplace + * @tparam Component Type of component to create. + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + decltype(auto) emplace(Args &&... args) const { + return reg->template emplace(entt, std::forward(args)...); + } + + /** + * @brief Assigns or replaces the given component for a handle. + * @sa basic_registry::emplace_or_replace + * @tparam Component Type of component to assign or replace. + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + decltype(auto) emplace_or_replace(Args &&... args) const { + return reg->template emplace_or_replace(entt, std::forward(args)...); + } + + /** + * @brief Patches the given component for a handle. + * @sa basic_registry::patch + * @tparam Component Type of component to patch. + * @tparam Func Types of the function objects to invoke. + * @param func Valid function objects. + * @return A reference to the patched component. + */ + template + decltype(auto) patch(Func &&... func) const { + return reg->template patch(entt, std::forward(func)...); + } + + /** + * @brief Replaces the given component for a handle. + * @sa basic_registry::replace + * @tparam Component Type of component to replace. + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return A reference to the component being replaced. + */ + template + decltype(auto) replace(Args &&... args) const { + return reg->template replace(entt, std::forward(args)...); + } + + /** + * @brief Removes the given components from a handle. + * @sa basic_registry::remove + * @tparam Component Types of components to remove. + */ + template + void remove() const { + reg->template remove(entt); + } + + /** + * @brief Removes the given components from a handle. + * @sa basic_registry::remove_if_exists + * @tparam Component Types of components to remove. + * @return The number of components actually removed. + */ + template + decltype(auto) remove_if_exists() const { + return reg->template remove_if_exists(entt); + } + + /** + * @brief Removes all the components from a handle and makes it orphaned. + * @sa basic_registry::remove_all + */ + void remove_all() const { + reg->remove_all(entt); + } + + /** + * @brief Checks if a handle has all the given components. + * @sa basic_registry::has + * @tparam Component Components for which to perform the check. + * @return True if the handle has all the components, false otherwise. + */ + template + [[nodiscard]] decltype(auto) has() const { + return reg->template has(entt); + } + + /** + * @brief Checks if a handle has at least one of the given components. + * @sa basic_registry::any + * @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() const { + return reg->template any(entt); + } + + /** + * @brief Returns references to the given components for a handle. + * @sa basic_registry::get + * @tparam Component Types of components to get. + * @return References to the components owned by the handle. + */ + template + [[nodiscard]] decltype(auto) get() const { + return reg->template get(entt); + } + + /** + * @brief Returns a reference to the given component for a handle. + * @sa basic_registry::get_or_emplace + * @tparam Component Type of component to get. + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return Reference to the component owned by the handle. + */ + template + [[nodiscard]] decltype(auto) get_or_emplace(Args &&... args) const { + return reg->template get_or_emplace(entt, std::forward(args)...); + } + + /** + * @brief Returns pointers to the given components for a handle. + * @sa basic_registry::try_get + * @tparam Component Types of components to get. + * @return Pointers to the components owned by the handle. + */ + template + [[nodiscard]] decltype(auto) try_get() const { + return reg->template try_get(entt); + } + + /** + * @brief Checks if a handle has components assigned. + * @return True if the handle has no components assigned, false otherwise. + */ + [[nodiscard]] bool orphan() const { + return reg->orphan(entt); + } + + /** + * @brief Visits a handle and returns the types for its components. + * @sa basic_registry::visit + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void visit(Func &&func) const { + reg->visit(entt, std::forward(func)); + } + +private: + registry_type *reg; + entity_type entt; +}; + + +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +basic_handle(basic_registry &, Entity) -> basic_handle; + + +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +basic_handle(const basic_registry &, Entity) -> basic_handle; + + +} + + +#endif + +// #include "entity/helper.hpp" +#ifndef ENTT_ENTITY_HELPER_HPP +#define ENTT_ENTITY_HELPER_HPP + + +#include +// #include "../config/config.h" + +// #include "../core/type_traits.hpp" + +// #include "../signal/delegate.hpp" + +// #include "registry.hpp" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Converts a registry to a view. + * @tparam Const Constness of the accepted registry. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +struct as_view { + /*! @brief Type of registry to convert. */ + using registry_type = std::conditional_t, basic_registry>; + + /** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ + as_view(registry_type &source) ENTT_NOEXCEPT: reg{source} {} + + /** + * @brief Conversion function from a registry to a view. + * @tparam Exclude Types of components used to filter the view. + * @tparam Component Type of components used to construct the view. + * @return A newly created view. + */ + template + operator basic_view() const { + return reg.template view(Exclude{}); + } + +private: + registry_type ® +}; + + +/** + * @brief Deduction guide. + * + * It allows to deduce the constness of a registry directly from the instance + * provided to the constructor. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +as_view(basic_registry &) ENTT_NOEXCEPT -> as_view; + + +/*! @copydoc as_view */ +template +as_view(const basic_registry &) ENTT_NOEXCEPT -> as_view; + + +/** + * @brief Converts a registry to a group. + * @tparam Const Constness of the accepted registry. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +struct as_group { + /*! @brief Type of registry to convert. */ + using registry_type = std::conditional_t, basic_registry>; + + /** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ + as_group(registry_type &source) ENTT_NOEXCEPT: reg{source} {} + + /** + * @brief Conversion function from a registry to a group. + * @tparam Exclude Types of components used to filter the group. + * @tparam Get Types of components observed by the group. + * @tparam Owned Types of components owned by the group. + * @return A newly created group. + */ + template + operator basic_group() const { + return reg.template group(Get{}, Exclude{}); + } + +private: + registry_type ® +}; + + +/** + * @brief Deduction guide. + * + * It allows to deduce the constness of a registry directly from the instance + * provided to the constructor. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +as_group(basic_registry &) ENTT_NOEXCEPT -> as_group; + + +/*! @copydoc as_group */ +template +as_group(const basic_registry &) ENTT_NOEXCEPT -> as_group; + + + +/** + * @brief Helper to create a listener that directly invokes a member function. + * @tparam Member Member function to invoke on a component of the given type. + * @tparam Entity A valid entity type (see entt_traits for more details). + * @param reg A registry that contains the given entity and its components. + * @param entt Entity from which to get the component. + */ +template +void invoke(basic_registry ®, const Entity entt) { + static_assert(std::is_member_function_pointer_v, "Invalid pointer to non-static member function"); + delegate &, const Entity)> func; + func.template connect(reg.template get>(entt)); + func(reg, entt); +} + + +/** + * @brief Returns the entity associated with a given component. + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Component Type of component. + * @param reg A registry that contains the given entity and its components. + * @param component A valid component instance. + * @return The entity associated with the given component. + */ +template +Entity to_entity(const basic_registry ®, const Component &component) { + return *(reg.template data() + (&component - reg.template raw())); +} + + +} + + +#endif + +// #include "entity/observer.hpp" +#ifndef ENTT_ENTITY_OBSERVER_HPP +#define ENTT_ENTITY_OBSERVER_HPP + + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/type_traits.hpp" + +// #include "registry.hpp" + +// #include "storage.hpp" + +// #include "utility.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + + + +namespace entt { + + +/*! @brief Grouping matcher. */ +template +struct matcher {}; + + +/** + * @brief Collector. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + */ +template +struct basic_collector; + + +/** + * @brief Collector. + * + * A collector contains a set of rules (literally, matchers) to use to track + * entities.
+ * Its main purpose is to generate a descriptor that allows an observer to know + * how to connect to a registry. + */ +template<> +struct basic_collector<> { + /** + * @brief Adds a grouping matcher to the collector. + * @tparam AllOf Types of components tracked by the matcher. + * @tparam NoneOf Types of components used to filter out entities. + * @return The updated collector. + */ + template + static constexpr auto group(exclude_t = {}) ENTT_NOEXCEPT { + return basic_collector, type_list<>, type_list, AllOf...>>{}; + } + + /** + * @brief Adds an observing matcher to the collector. + * @tparam AnyOf Type of component for which changes should be detected. + * @return The updated collector. + */ + template + static constexpr auto update() ENTT_NOEXCEPT { + return basic_collector, type_list<>, AnyOf>>{}; + } +}; + +/** + * @brief Collector. + * @copydetails basic_collector<> + * @tparam Reject Untracked types used to filter out entities. + * @tparam Require Untracked types required by the matcher. + * @tparam Rule Specific details of the current matcher. + * @tparam Other Other matchers. + */ +template +struct basic_collector, type_list, Rule...>, Other...> { + /*! @brief Current matcher. */ + using current_type = matcher, type_list, Rule...>; + + /** + * @brief Adds a grouping matcher to the collector. + * @tparam AllOf Types of components tracked by the matcher. + * @tparam NoneOf Types of components used to filter out entities. + * @return The updated collector. + */ + template + static constexpr auto group(exclude_t = {}) ENTT_NOEXCEPT { + return basic_collector, type_list<>, type_list, AllOf...>, current_type, Other...>{}; + } + + /** + * @brief Adds an observing matcher to the collector. + * @tparam AnyOf Type of component for which changes should be detected. + * @return The updated collector. + */ + template + static constexpr auto update() ENTT_NOEXCEPT { + return basic_collector, type_list<>, AnyOf>, current_type, Other...>{}; + } + + /** + * @brief Updates the filter of the last added matcher. + * @tparam AllOf Types of components required by the matcher. + * @tparam NoneOf Types of components used to filter out entities. + * @return The updated collector. + */ + template + static constexpr auto where(exclude_t = {}) ENTT_NOEXCEPT { + using extended_type = matcher, type_list, Rule...>; + return basic_collector{}; + } +}; + + +/*! @brief Variable template used to ease the definition of collectors. */ +inline constexpr basic_collector<> collector{}; + + +/** + * @brief Observer. + * + * An observer returns all the entities and only the entities that fit the + * requirements of at least one matcher. Moreover, it's guaranteed that the + * entity list is tightly packed in memory for fast iterations.
+ * In general, observers don't stay true to the order of any set of components. + * + * Observers work mainly with two types of matchers, provided through a + * collector: + * + * * Observing matcher: an observer will return at least all the living entities + * for which one or more of the given components have been updated and not yet + * destroyed. + * * Grouping matcher: an observer will return at least all the living entities + * that would have entered the given group if it existed and that would have + * not yet left it. + * + * If an entity respects the requirements of multiple matchers, it will be + * returned once and only once by the observer in any case. + * + * Matchers support also filtering by means of a _where_ clause that accepts + * both a list of types and an exclusion list.
+ * Whenever a matcher finds that an entity matches its requirements, the + * condition of the filter is verified before to register the entity itself. + * Moreover, a registered entity isn't returned by the observer if the condition + * set by the filter is broken in the meantime. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all the other cases, modifying the pools of the given components in any + * way invalidates all the iterators and using them results in undefined + * behavior. + * + * @warning + * Lifetime of an observer doesn't necessarily have to overcome that of the + * registry to which it is connected. However, the observer must be disconnected + * from the registry before being destroyed to avoid crashes due to dangling + * pointers. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class basic_observer { + using payload_type = std::uint32_t; + + template + struct matcher_handler; + + template + struct matcher_handler, type_list, AnyOf>> { + template + static void maybe_valid_if(basic_observer &obs, const basic_registry ®, const Entity entt) { + if(reg.template has(entt) && !reg.template any(entt)) { + if(auto *comp = obs.view.try_get(entt); !comp) { + obs.view.emplace(entt); + } + + obs.view.get(entt) |= (1 << Index); + } + } + + template + static void discard_if(basic_observer &obs, const basic_registry &, const Entity entt) { + if(auto *value = obs.view.try_get(entt); value && !(*value &= (~(1 << Index)))) { + obs.view.erase(entt); + } + } + + template + static void connect(basic_observer &obs, basic_registry ®) { + (reg.template on_destroy().template connect<&discard_if>(obs), ...); + (reg.template on_construct().template connect<&discard_if>(obs), ...); + reg.template on_update().template connect<&maybe_valid_if>(obs); + reg.template on_destroy().template connect<&discard_if>(obs); + } + + static void disconnect(basic_observer &obs, basic_registry ®) { + (reg.template on_destroy().disconnect(obs), ...); + (reg.template on_construct().disconnect(obs), ...); + reg.template on_update().disconnect(obs); + reg.template on_destroy().disconnect(obs); + } + }; + + template + struct matcher_handler, type_list, type_list, AllOf...>> { + template + static void maybe_valid_if(basic_observer &obs, const basic_registry ®, const Entity entt) { + if(reg.template has(entt) && !reg.template any(entt)) { + if(auto *comp = obs.view.try_get(entt); !comp) { + obs.view.emplace(entt); + } + + obs.view.get(entt) |= (1 << Index); + } + } + + template + static void discard_if(basic_observer &obs, const basic_registry &, const Entity entt) { + if(auto *value = obs.view.try_get(entt); value && !(*value &= (~(1 << Index)))) { + obs.view.erase(entt); + } + } + + template + static void connect(basic_observer &obs, basic_registry ®) { + (reg.template on_destroy().template connect<&discard_if>(obs), ...); + (reg.template on_construct().template connect<&discard_if>(obs), ...); + (reg.template on_construct().template connect<&maybe_valid_if>(obs), ...); + (reg.template on_destroy().template connect<&maybe_valid_if>(obs), ...); + (reg.template on_destroy().template connect<&discard_if>(obs), ...); + (reg.template on_construct().template connect<&discard_if>(obs), ...); + } + + static void disconnect(basic_observer &obs, basic_registry ®) { + (reg.template on_destroy().disconnect(obs), ...); + (reg.template on_construct().disconnect(obs), ...); + (reg.template on_construct().disconnect(obs), ...); + (reg.template on_destroy().disconnect(obs), ...); + (reg.template on_destroy().disconnect(obs), ...); + (reg.template on_construct().disconnect(obs), ...); + } + }; + + template + static void disconnect(basic_observer &obs, basic_registry ®) { + (matcher_handler::disconnect(obs, reg), ...); + } + + template + void connect(basic_registry ®, std::index_sequence) { + static_assert(sizeof...(Matcher) < std::numeric_limits::digits, "Too many matchers"); + (matcher_handler::template connect(*this, reg), ...); + release = &basic_observer::disconnect; + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Random access iterator type. */ + using iterator = typename sparse_set::iterator; + + /*! @brief Default constructor. */ + basic_observer() + : target{}, release{}, view{} + {} + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_observer(const basic_observer &) = delete; + /*! @brief Default move constructor, deleted on purpose. */ + basic_observer(basic_observer &&) = delete; + + /** + * @brief Creates an observer and connects it to a given registry. + * @tparam Matcher Types of matchers to use to initialize the observer. + * @param reg A valid reference to a registry. + */ + template + basic_observer(basic_registry ®, basic_collector) + : target{®}, + release{}, + view{} + { + connect(reg, std::index_sequence_for{}); + } + + /*! @brief Default destructor. */ + ~basic_observer() = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This observer. + */ + basic_observer & operator=(const basic_observer &) = delete; + + /** + * @brief Default move assignment operator, deleted on purpose. + * @return This observer. + */ + basic_observer & operator=(basic_observer &&) = delete; + + /** + * @brief Connects an observer to a given registry. + * @tparam Matcher Types of matchers to use to initialize the observer. + * @param reg A valid reference to a registry. + */ + template + void connect(basic_registry ®, basic_collector) { + disconnect(); + connect(reg, std::index_sequence_for{}); + target = ® + view.clear(); + } + + /*! @brief Disconnects an observer from the registry it keeps track of. */ + void disconnect() { + if(release) { + release(*this, *target); + release = nullptr; + } + } + + /** + * @brief Returns the number of elements in an observer. + * @return Number of elements. + */ + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + return view.size(); + } + + /** + * @brief Checks whether an observer is empty. + * @return True if the observer is empty, false otherwise. + */ + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return view.empty(); + } + + /** + * @brief Direct access to the list of entities of the observer. + * + * The returned pointer is such that range `[data(), data() + size()]` is + * always a valid range, even if the container is empty. + * + * @note + * Entities are in the reverse order as returned by the `begin`/`end` + * iterators. + * + * @return A pointer to the array of entities. + */ + [[nodiscard]] const entity_type * data() const ENTT_NOEXCEPT { + return view.data(); + } + + /** + * @brief Returns an iterator to the first entity of the observer. + * + * The returned iterator points to the first entity of the observer. If the + * container is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the observer. + */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return view.sparse_set::begin(); + } + + /** + * @brief Returns an iterator that is past the last entity of the observer. + * + * The returned iterator points to the entity following the last entity of + * the observer. Attempting to dereference the returned iterator results in + * undefined behavior. + * + * @return An iterator to the entity following the last entity of the + * observer. + */ + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + return view.sparse_set::end(); + } + + /*! @brief Clears the underlying container. */ + void clear() ENTT_NOEXCEPT { + view.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: + basic_registry *target; + void(* release)(basic_observer &, basic_registry &); + storage view; +}; + + +} + + +#endif + +// #include "entity/pool.hpp" + +// #include "entity/registry.hpp" + +// #include "entity/runtime_view.hpp" + +// #include "entity/snapshot.hpp" +#ifndef ENTT_ENTITY_SNAPSHOT_HPP +#define ENTT_ENTITY_SNAPSHOT_HPP + + +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "entity.hpp" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Utility class to create snapshots from a registry. + * + * A _snapshot_ can be either a dump of the entire registry or a narrower + * selection of components of interest.
+ * This type can be used in both cases if provided with a correctly configured + * output archive. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class basic_snapshot { + /*! @brief A registry is allowed to create snapshots. */ + friend class basic_registry; + + using traits_type = entt_traits; + + template + void get(Archive &archive, std::size_t sz, It first, It last) const { + archive(typename traits_type::entity_type(sz)); + + while(first != last) { + const auto entt = *(first++); + + if(reg->template has(entt)) { + if constexpr(std::is_empty_v) { + archive(entt); + } else { + archive(entt, reg->template get(entt)); + } + } + } + } + + template + void component(Archive &archive, It first, It last, std::index_sequence) const { + std::array size{}; + auto begin = first; + + while(begin != last) { + const auto entt = *(begin++); + ((reg->template has(entt) ? ++size[Indexes] : size[Indexes]), ...); + } + + (get(archive, size[Indexes], first, last), ...); + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + + /** + * @brief Constructs an instance that is bound to a given registry. + * @param source A valid reference to a registry. + */ + basic_snapshot(const basic_registry &source) ENTT_NOEXCEPT + : reg{&source} + {} + + /*! @brief Default move constructor. */ + basic_snapshot(basic_snapshot &&) = default; + + /*! @brief Default move assignment operator. @return This snapshot. */ + basic_snapshot & operator=(basic_snapshot &&) = default; + + /** + * @brief Puts aside all the entities from the underlying registry. + * + * Entities are serialized along with their versions. Destroyed entities are + * taken in consideration as well by this function. + * + * @tparam Archive Type of output archive. + * @param archive A valid reference to an output archive. + * @return An object of this type to continue creating the snapshot. + */ + template + const basic_snapshot & entities(Archive &archive) const { + const auto sz = reg->size(); + auto first = reg->data(); + const auto last = first + sz; + + archive(typename traits_type::entity_type(sz)); + + while(first != last) { + archive(*(first++)); + } + + return *this; + } + + /** + * @brief Puts aside the given components. + * + * Each instance is serialized together with the entity to which it belongs. + * Entities are serialized along with their versions. + * + * @tparam Component Types of components to serialize. + * @tparam Archive Type of output archive. + * @param archive A valid reference to an output archive. + * @return An object of this type to continue creating the snapshot. + */ + template + const basic_snapshot & component(Archive &archive) const { + (component(archive, reg->template data(), reg->template data() + reg->template size()), ...); + return *this; + } + + /** + * @brief Puts aside the given components for the entities in a range. + * + * Each instance is serialized together with the entity to which it belongs. + * Entities are serialized along with their versions. + * + * @tparam Component Types of components to serialize. + * @tparam Archive Type of output archive. + * @tparam It Type of input iterator. + * @param archive A valid reference to an output archive. + * @param first An iterator to the first element of the range to serialize. + * @param last An iterator past the last element of the range to serialize. + * @return An object of this type to continue creating the snapshot. + */ + template + const basic_snapshot & component(Archive &archive, It first, It last) const { + component(archive, first, last, std::index_sequence_for{}); + return *this; + } + +private: + const basic_registry *reg; +}; + + +/** + * @brief Utility class to restore a snapshot as a whole. + * + * A snapshot loader requires that the destination registry be empty and loads + * all the data at once while keeping intact the identifiers that the entities + * originally had.
+ * An example of use is the implementation of a save/restore utility. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class basic_snapshot_loader { + /*! @brief A registry is allowed to create snapshot loaders. */ + friend class basic_registry; + + using traits_type = entt_traits; + + template + void assign(Archive &archive) const { + typename traits_type::entity_type length{}; + archive(length); + + entity_type entt{}; + + if constexpr(std::is_empty_v) { + while(length--) { + archive(entt); + const auto entity = reg->valid(entt) ? entt : reg->create(entt); + ENTT_ASSERT(entity == entt); + reg->template emplace(entity); + } + } else { + Type instance{}; + + while(length--) { + archive(entt, instance); + const auto entity = reg->valid(entt) ? entt : reg->create(entt); + ENTT_ASSERT(entity == entt); + reg->template emplace(entity, std::move(instance)); + } + } + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + + /** + * @brief Constructs an instance that is bound to a given registry. + * @param source A valid reference to a registry. + */ + basic_snapshot_loader(basic_registry &source) ENTT_NOEXCEPT + : reg{&source} + { + // restoring a snapshot as a whole requires a clean registry + ENTT_ASSERT(reg->empty()); + } + + /*! @brief Default move constructor. */ + basic_snapshot_loader(basic_snapshot_loader &&) = default; + + /*! @brief Default move assignment operator. @return This loader. */ + basic_snapshot_loader & operator=(basic_snapshot_loader &&) = default; + + /** + * @brief Restores entities that were in use during serialization. + * + * This function restores the entities that were in use during serialization + * and gives them the versions they originally had. + * + * @tparam Archive Type of input archive. + * @param archive A valid reference to an input archive. + * @return A valid loader to continue restoring data. + */ + template + const basic_snapshot_loader & entities(Archive &archive) const { + typename traits_type::entity_type length{}; + + archive(length); + std::vector all(length); + + for(decltype(length) pos{}; pos < length; ++pos) { + archive(all[pos]); + } + + reg->assign(all.cbegin(), all.cend()); + + return *this; + } + + /** + * @brief Restores components and assigns them to the right entities. + * + * The template parameter list must be exactly the same used during + * serialization. In the event that the entity to which the component is + * assigned doesn't exist yet, the loader will take care to create it with + * the version it originally had. + * + * @tparam Component Types of components to restore. + * @tparam Archive Type of input archive. + * @param archive A valid reference to an input archive. + * @return A valid loader to continue restoring data. + */ + template + const basic_snapshot_loader & component(Archive &archive) const { + (assign(archive), ...); + return *this; + } + + /** + * @brief Destroys those entities that have no components. + * + * In case all the entities were serialized but only part of the components + * was saved, it could happen that some of the entities have no components + * once restored.
+ * This functions helps to identify and destroy those entities. + * + * @return A valid loader to continue restoring data. + */ + const basic_snapshot_loader & orphans() const { + reg->orphans([this](const auto entt) { + reg->destroy(entt); + }); + + return *this; + } + +private: + basic_registry *reg; +}; + + +/** + * @brief Utility class for _continuous loading_. + * + * A _continuous loader_ is designed to load data from a source registry to a + * (possibly) non-empty destination. The loader can accommodate in a registry + * more than one snapshot in a sort of _continuous loading_ that updates the + * destination one step at a time.
+ * Identifiers that entities originally had are not transferred to the target. + * Instead, the loader maps remote identifiers to local ones while restoring a + * snapshot.
+ * An example of use is the implementation of a client-server applications with + * the requirement of transferring somehow parts of the representation side to + * side. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class basic_continuous_loader { + using traits_type = entt_traits; + + void destroy(Entity entt) { + const auto it = remloc.find(entt); + + if(it == remloc.cend()) { + const auto local = reg->create(); + remloc.emplace(entt, std::make_pair(local, true)); + reg->destroy(local); + } + } + + void restore(Entity entt) { + const auto it = remloc.find(entt); + + if(it == remloc.cend()) { + const auto local = reg->create(); + remloc.emplace(entt, std::make_pair(local, true)); + } else { + remloc[entt].first = reg->valid(remloc[entt].first) ? remloc[entt].first : reg->create(); + // set the dirty flag + remloc[entt].second = true; + } + } + + template + auto update(int, Container &container) + -> decltype(typename Container::mapped_type{}, void()) { + // map like container + Container other; + + for(auto &&pair: container) { + using first_type = std::remove_const_t::first_type>; + using second_type = typename std::decay_t::second_type; + + if constexpr(std::is_same_v && std::is_same_v) { + other.emplace(map(pair.first), map(pair.second)); + } else if constexpr(std::is_same_v) { + other.emplace(map(pair.first), std::move(pair.second)); + } else { + static_assert(std::is_same_v, "Neither the key nor the value are of entity type"); + other.emplace(std::move(pair.first), map(pair.second)); + } + } + + std::swap(container, other); + } + + template + auto update(char, Container &container) + -> decltype(typename Container::value_type{}, void()) { + // vector like container + static_assert(std::is_same_v, "Invalid value type"); + + for(auto &&entt: container) { + entt = map(entt); + } + } + + template + void update([[maybe_unused]] Other &instance, [[maybe_unused]] Member Type:: *member) { + if constexpr(!std::is_same_v) { + return; + } else if constexpr(std::is_same_v) { + instance.*member = map(instance.*member); + } else { + // maybe a container? let's try... + update(0, instance.*member); + } + } + + template + void remove_if_exists() { + for(auto &&ref: remloc) { + const auto local = ref.second.first; + + if(reg->valid(local)) { + reg->template remove_if_exists(local); + } + } + } + + template + void assign(Archive &archive, [[maybe_unused]] Member Type:: *... member) { + typename traits_type::entity_type length{}; + archive(length); + + entity_type entt{}; + + if constexpr(std::is_empty_v) { + while(length--) { + archive(entt); + restore(entt); + reg->template emplace_or_replace(map(entt)); + } + } else { + Other instance{}; + + while(length--) { + archive(entt, instance); + (update(instance, member), ...); + restore(entt); + reg->template emplace_or_replace(map(entt), std::move(instance)); + } + } + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + + /** + * @brief Constructs an instance that is bound to a given registry. + * @param source A valid reference to a registry. + */ + basic_continuous_loader(basic_registry &source) ENTT_NOEXCEPT + : reg{&source} + {} + + /*! @brief Default move constructor. */ + basic_continuous_loader(basic_continuous_loader &&) = default; + + /*! @brief Default move assignment operator. @return This loader. */ + basic_continuous_loader & operator=(basic_continuous_loader &&) = default; + + /** + * @brief Restores entities that were in use during serialization. + * + * This function restores the entities that were in use during serialization + * and creates local counterparts for them if required. + * + * @tparam Archive Type of input archive. + * @param archive A valid reference to an input archive. + * @return A non-const reference to this loader. + */ + template + basic_continuous_loader & entities(Archive &archive) { + typename traits_type::entity_type length{}; + entity_type entt{}; + + archive(length); + + for(decltype(length) pos{}; pos < length; ++pos) { + archive(entt); + + if(const auto entity = (to_integral(entt) & traits_type::entity_mask); entity == pos) { + restore(entt); + } else { + destroy(entt); + } + } + + return *this; + } + + /** + * @brief Restores components and assigns them to the right entities. + * + * The template parameter list must be exactly the same used during + * serialization. In the event that the entity to which the component is + * assigned doesn't exist yet, the loader will take care to create a local + * counterpart for it.
+ * Members can be either data members of type entity_type or containers of + * entities. In both cases, the loader will visit them and update the + * entities by replacing each one with its local counterpart. + * + * @tparam Component Type of component to restore. + * @tparam Archive Type of input archive. + * @tparam Type Types of components to update with local counterparts. + * @tparam Member Types of members to update with their local counterparts. + * @param archive A valid reference to an input archive. + * @param member Members to update with their local counterparts. + * @return A non-const reference to this loader. + */ + template + basic_continuous_loader & component(Archive &archive, Member Type:: *... member) { + (remove_if_exists(), ...); + (assign(archive, member...), ...); + return *this; + } + + /** + * @brief Helps to purge entities that no longer have a conterpart. + * + * Users should invoke this member function after restoring each snapshot, + * unless they know exactly what they are doing. + * + * @return A non-const reference to this loader. + */ + basic_continuous_loader & shrink() { + auto it = remloc.begin(); + + while(it != remloc.cend()) { + const auto local = it->second.first; + bool &dirty = it->second.second; + + if(dirty) { + dirty = false; + ++it; + } else { + if(reg->valid(local)) { + reg->destroy(local); + } + + it = remloc.erase(it); + } + } + + return *this; + } + + /** + * @brief Destroys those entities that have no components. + * + * In case all the entities were serialized but only part of the components + * was saved, it could happen that some of the entities have no components + * once restored.
+ * This functions helps to identify and destroy those entities. + * + * @return A non-const reference to this loader. + */ + basic_continuous_loader & orphans() { + reg->orphans([this](const auto entt) { + reg->destroy(entt); + }); + + return *this; + } + + /** + * @brief Tests if a loader knows about a given entity. + * @param entt An entity identifier. + * @return True if `entity` is managed by the loader, false otherwise. + */ + [[nodiscard]] bool contains(entity_type entt) const ENTT_NOEXCEPT { + return (remloc.find(entt) != remloc.cend()); + } + + /** + * @brief Returns the identifier to which an entity refers. + * @param entt An entity identifier. + * @return The local identifier if any, the null entity otherwise. + */ + [[nodiscard]] entity_type map(entity_type entt) const ENTT_NOEXCEPT { + const auto it = remloc.find(entt); + entity_type other = null; + + if(it != remloc.cend()) { + other = it->second.first; + } + + return other; + } + +private: + std::unordered_map> remloc; + basic_registry *reg; +}; + + +} + + +#endif + +// #include "entity/sparse_set.hpp" + +// #include "entity/storage.hpp" + +// #include "entity/utility.hpp" + +// #include "entity/view.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 + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_HS_SUFFIX +# define ENTT_HS_SUFFIX _hs +#endif + + +#ifndef ENTT_HWS_SUFFIX +# define ENTT_HWS_SUFFIX _hws +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifndef ENTT_PAGE_SIZE +# define ENTT_PAGE_SIZE 32768 +#endif + + +#ifndef ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty +#else +# include +# define ENTT_IS_EMPTY(Type) std::false_type +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) +# define ENTT_PRETTY_FUNCTION_CONSTEXPR +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif 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_CONSTEXPR +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + + +#ifndef ENTT_STANDALONE +# define ENTT_FAST_PATH(...) false +#else +# define ENTT_FAST_PATH(Cond) Cond +#endif + + +#endif + + + +namespace entt { + + +/** + * @brief Service locator, nothing more. + * + * A service locator can be 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 template + * based implementation tries to fill the gap and to get rid of the burden of + * defining a different specific locator for each application. + * + * @tparam Service Type of service managed by the locator. + */ +template +struct service_locator { + /*! @brief Type of service offered. */ + using service_type = Service; + + /*! @brief Default constructor, deleted on purpose. */ + service_locator() = delete; + /*! @brief Default destructor, deleted on purpose. */ + ~service_locator() = delete; + + /** + * @brief Tests if a valid service implementation is set. + * @return True if the service is set, false otherwise. + */ + [[nodiscard]] static bool empty() ENTT_NOEXCEPT { + return !static_cast(service); + } + + /** + * @brief Returns a weak pointer to a service implementation, if any. + * + * Clients of a service shouldn't retain references to it. The recommended + * way is to retrieve the service implementation currently set each and + * every time the need of using it arises. Otherwise users can incur in + * unexpected behaviors. + * + * @return A reference to the service implementation currently set, if any. + */ + [[nodiscard]] static std::weak_ptr get() ENTT_NOEXCEPT { + return service; + } + + /** + * @brief Returns a weak reference to a service implementation, if any. + * + * Clients of a service shouldn't retain references to it. The recommended + * way is to retrieve the service implementation currently set each and + * every time the need of using it arises. Otherwise users can incur in + * unexpected behaviors. + * + * @warning + * In case no service implementation has been set, a call to this function + * results in undefined behavior. + * + * @return A reference to the service implementation currently set, if any. + */ + [[nodiscard]] static Service & ref() ENTT_NOEXCEPT { + return *service; + } + + /** + * @brief Sets or replaces a service. + * @tparam Impl Type of the new service to use. + * @tparam Args Types of arguments to use to construct the service. + * @param args Parameters to use to construct the service. + */ + template + static void set(Args &&... args) { + service = std::make_shared(std::forward(args)...); + } + + /** + * @brief Sets or replaces a service. + * @param ptr Service to use to replace the current one. + */ + static void set(std::shared_ptr ptr) { + ENTT_ASSERT(static_cast(ptr)); + service = std::move(ptr); + } + + /** + * @brief Resets a service. + * + * The service is no longer valid after a reset. + */ + static void reset() { + service.reset(); + } + +private: + inline static std::shared_ptr service = nullptr; +}; + + +} + + +#endif + +// #include "meta/container.hpp" +#ifndef ENTT_META_CONTAINER_HPP +#define ENTT_META_CONTAINER_HPP + + +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_HS_SUFFIX +# define ENTT_HS_SUFFIX _hs +#endif + + +#ifndef ENTT_HWS_SUFFIX +# define ENTT_HWS_SUFFIX _hws +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifndef ENTT_PAGE_SIZE +# define ENTT_PAGE_SIZE 32768 +#endif + + +#ifndef ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty +#else +# include +# define ENTT_IS_EMPTY(Type) std::false_type +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) +# define ENTT_PRETTY_FUNCTION_CONSTEXPR +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif 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_CONSTEXPR +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + + +#ifndef ENTT_STANDALONE +# define ENTT_FAST_PATH(...) false +#else +# define ENTT_FAST_PATH(Cond) Cond +#endif + + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_META_TYPE_TRAITS_HPP +#define ENTT_META_TYPE_TRAITS_HPP + + +#include + + +namespace entt { + + +/** + * @brief Traits class template to be specialized to enable support for meta + * 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 support for meta + * sequence containers is enabled for the given type, false otherwise. + * @tparam Type Potentially sequence container type. + */ +template +struct has_meta_sequence_container_traits: std::false_type {}; + + +/*! @copydoc has_meta_sequence_container_traits */ +template +struct has_meta_sequence_container_traits::value_type>> + : std::true_type +{}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially sequence container type. + */ +template +inline constexpr auto has_meta_sequence_container_traits_v = has_meta_sequence_container_traits::value; + + +/** + * @brief Provides the member constant `value` to true if support for meta + * associative containers is enabled for the given type, false otherwise. + * @tparam Type Potentially associative container type. + */ +template +struct has_meta_associative_container_traits: std::false_type {}; + + +/*! @copydoc has_meta_associative_container_traits */ +template +struct has_meta_associative_container_traits::key_type>> + : std::true_type +{}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially associative container type. + */ +template +inline constexpr auto has_meta_associative_container_traits_v = has_meta_associative_container_traits::value; + + +/** + * @brief Provides the member constant `value` to true if a meta associative + * container claims to wrap a key-only type, false otherwise. + * @tparam Type Potentially key-only meta associative container type. + */ +template +struct is_key_only_meta_associative_container: std::true_type {}; + + +/*! @copydoc is_key_only_meta_associative_container */ +template +struct is_key_only_meta_associative_container::mapped_type>> + : std::false_type +{}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially key-only meta associative container type. + */ +template +inline constexpr auto is_key_only_meta_associative_container_v = is_key_only_meta_associative_container::value; + + +/** + * @brief Provides the member constant `value` to true if a given type is a + * pointer-like type from the point of view of the meta system, false otherwise. + * @tparam Type Potentially pointer-like type. + */ +template +struct is_meta_pointer_like: std::false_type {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially pointer-like type. + */ +template +inline constexpr auto is_meta_pointer_like_v = is_meta_pointer_like::value; + + +} + + +#endif + + + +namespace entt { + + +namespace internal { + + +template class... Trait> +struct container_traits: public Trait... {}; + + +/** + * @brief Basic STL-compatible container traits + * @tparam Container The type of the container. + */ +template +struct basic_container { + /*! @brief Iterator type of the container. */ + using iterator = typename Container::iterator; + /*! @brief Unsigned integer type. */ + using size_type = typename Container::size_type; + /*! @brief Value type of the container. */ + using value_type = typename Container::value_type; + + /** + * @brief Returns the size of the given container. + * @param cont The container for which to return the size. + * @return The size of the given container. + */ + [[nodiscard]] static size_type size(const Container &cont) ENTT_NOEXCEPT { + return cont.size(); + } + + /** + * @brief Returns an iterator to the first element of the given container. + * @param cont The container for which to return the iterator. + * @return An iterator to the first element of the given container. + */ + [[nodiscard]] static iterator begin(Container &cont) { + return cont.begin(); + } + + /** + * @brief Returns an iterator past the last element of the given container. + * @param cont The container for which to return the iterator. + * @return An iterator past the last element of the given container. + */ + [[nodiscard]] static iterator end(Container &cont) { + return cont.end(); + } +}; + + +/** + * @brief Basic STL-compatible associative container traits + * @tparam Container The type of the container. + */ +template +struct basic_associative_container { + /*! @brief Key type of the sequence container. */ + using key_type = typename Container::key_type; + + /** + * @brief Returns an iterator to the element with key equivalent to the given + * one, if any. + * @param cont The container in which to search for the element. + * @param key The key of the element to search. + * @return An iterator to the element with the given key, if any. + */ + [[nodiscard]] static typename Container::iterator find(Container &cont, const key_type &key) { + return cont.find(key); + } +}; + + +/** + * @brief Basic STL-compatible dynamic container traits + * @tparam Container The type of the container. + */ +template +struct basic_dynamic_container { + /** + * @brief Clears the content of the given container. + * @param cont The container for which to clear the content. + * @return True in case of success, false otherwise. + */ + [[nodiscard]] static bool clear(Container &cont) { + return cont.clear(), true; + } +}; + + +/** + * @brief Basic STL-compatible dynamic associative container traits + * @tparam Container The type of the container. + */ +template +struct basic_dynamic_associative_container { + /** + * @brief Removes the specified element from the given container. + * @param cont The container from which to remove the element. + * @param key The element to remove. + * @return A bool denoting whether the removal took place. + */ + [[nodiscard]] static bool erase(Container &cont, const typename Container::key_type &key) { + const auto sz = cont.size(); + return cont.erase(key) != sz; + } +}; + + +/** + * @brief Basic STL-compatible sequence container traits + * @tparam Container The type of the container. + */ +template +struct basic_sequence_container { + /** + * @brief Returns a reference to the element at the specified location of the + * given container (no bounds checking is performed). + * @param cont The container from which to get the element. + * @param pos The position of the element to return. + * @return A reference to the requested element. + */ + [[nodiscard]] static typename Container::value_type & get(Container &cont, typename Container::size_type pos) { + return cont[pos]; + } +}; + + +/** + * @brief STL-compatible dynamic associative key-only container traits + * @tparam Container The type of the container. + */ +template +struct dynamic_associative_key_only_container { + /** + * @brief Inserts an element into the given container. + * @param cont The container in which to insert the element. + * @param key The element to insert. + * @return A bool denoting whether the insertion took place. + */ + [[nodiscard]] static bool insert(Container &cont, const typename Container::key_type &key) { + return cont.insert(key).second; + } +}; + + +/** + * @brief STL-compatible dynamic key-value associative container traits + * @tparam Container The type of the container. + */ +template +struct dynamic_associative_key_value_container { + /** + * @brief Inserts an element (a key/value pair) into the given container. + * @param cont The container in which to insert the element. + * @param key The key of the element to insert. + * @param value The value of the element to insert. + * @return A bool denoting whether the insertion took place. + */ + [[nodiscard]] static bool insert(Container &cont, const typename Container::key_type &key, const typename Container::mapped_type &value) { + return cont.insert(std::make_pair(key, value)).second; + } +}; + + +/** + * @brief STL-compatible dynamic sequence container traits + * @tparam Container The type of the container. + */ +template +struct dynamic_sequence_container { + /** + * @brief Resizes the given container to contain the given number of elements. + * @param cont The container to resize. + * @param sz The new size of the container. + * @return True in case of success, false otherwise. + */ + [[nodiscard]] static bool resize(Container &cont, typename Container::size_type sz) { + return (cont.resize(sz), true); + } + + /** + * @brief Inserts an element at the specified location of the given container. + * @param cont The container into which to insert the element. + * @param it Iterator before which the element will be inserted. + * @param value Element value to insert. + * @return A pair consisting of an iterator to the inserted element (in case + * of success) and a bool denoting whether the insertion took place. + */ + [[nodiscard]] static std::pair insert(Container &cont, typename Container::iterator it, const typename Container::value_type &value) { + return { cont.insert(it, value), true }; + } + + /** + * @brief Removes the element at the specified location from the given container. + * @param cont The container from which to remove the element. + * @param it Iterator to the element to remove. + * @return A pair consisting of an iterator following the last removed + * element (in case of success) and a bool denoting whether the insertion + * took place. + */ + [[nodiscard]] static std::pair erase(Container &cont, typename Container::iterator it) { + return { cont.erase(it), true }; + } +}; + + +/** + * @brief STL-compatible fixed sequence container traits + * @tparam Container The type of the container. + */ +template +struct fixed_sequence_container { + /** + * @brief Does nothing. + * @return False to indicate failure in all cases. + */ + [[nodiscard]] static bool resize(const Container &, typename Container::size_type) { + return false; + } + + /** + * @brief Does nothing. + * @return False to indicate failure in all cases. + */ + [[nodiscard]] static bool clear(const Container &) { + return false; + } + + /** + * @brief Does nothing. + * @return A pair consisting of an invalid iterator and a false value to + * indicate failure in all cases. + */ + [[nodiscard]] static std::pair insert(const Container &, typename Container::iterator, const typename Container::value_type &) { + return { {}, false }; + } + + /** + * @brief Does nothing. + * @return A pair consisting of an invalid iterator and a false value to + * indicate failure in all cases. + */ + [[nodiscard]] static std::pair erase(const Container &, typename Container::iterator) { + return { {}, false }; + } +}; + + +} + + +/** + * @brief Meta sequence container traits for `std::vector`s of any type. + * @tparam Type The type of elements. + * @tparam Args Other arguments. + */ +template +struct meta_sequence_container_traits> + : internal::container_traits< + std::vector, + internal::basic_container, + internal::basic_dynamic_container, + internal::basic_sequence_container, + internal::dynamic_sequence_container + > +{}; + + +/** + * @brief Meta sequence container traits for `std::array`s of any type. + * @tparam Type The type of elements. + * @tparam N The number of elements. + */ +template +struct meta_sequence_container_traits> + : internal::container_traits< + std::array, + internal::basic_container, + internal::basic_sequence_container, + internal::fixed_sequence_container + > +{}; + + +/** + * @brief Meta associative container traits for `std::map`s of any type. + * @tparam Key The key type of elements. + * @tparam Value The value type of elements. + * @tparam Args Other arguments. + */ +template +struct meta_associative_container_traits> + : internal::container_traits< + std::map, + internal::basic_container, + internal::basic_associative_container, + internal::basic_dynamic_container, + internal::basic_dynamic_associative_container, + internal::dynamic_associative_key_value_container + > +{ + /*! @brief Mapped type of the sequence container. */ + using mapped_type = typename std::map::mapped_type; +}; + + +/** + * @brief Meta associative container traits for `std::unordered_map`s of any + * type. + * @tparam Key The key type of elements. + * @tparam Value The value type of elements. + * @tparam Args Other arguments. + */ +template +struct meta_associative_container_traits> + : internal::container_traits< + std::unordered_map, + internal::basic_container, + internal::basic_associative_container, + internal::basic_dynamic_container, + internal::basic_dynamic_associative_container, + internal::dynamic_associative_key_value_container + > +{ + /*! @brief Mapped type of the sequence container. */ + using mapped_type = typename std::unordered_map::mapped_type; +}; + + +/** + * @brief Meta associative container traits for `std::set`s of any type. + * @tparam Key The type of elements. + * @tparam Args Other arguments. + */ +template +struct meta_associative_container_traits> + : internal::container_traits< + std::set, + internal::basic_container, + internal::basic_associative_container, + internal::basic_dynamic_container, + internal::basic_dynamic_associative_container, + internal::dynamic_associative_key_only_container + > +{}; + + +/** + * @brief Meta associative container traits for `std::unordered_set`s of any + * type. + * @tparam Key The type of elements. + * @tparam Args Other arguments. + */ +template +struct meta_associative_container_traits> + : internal::container_traits< + std::unordered_set, + internal::basic_container, + internal::basic_associative_container, + internal::basic_dynamic_container, + internal::basic_dynamic_associative_container, + internal::dynamic_associative_key_only_container + > +{}; + + +} + + +#endif + +// #include "meta/ctx.hpp" +#ifndef ENTT_META_CTX_HPP +#define ENTT_META_CTX_HPP + + +// #include "../core/attribute.h" +#ifndef ENTT_CORE_ATTRIBUTE_H +#define ENTT_CORE_ATTRIBUTE_H + + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + + +#endif + +// #include "../config/config.h" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +struct meta_type_node; + + +struct ENTT_API meta_context { + // we could use the lines below but VS2017 returns with an ICE if combined with ENTT_API despite the code being valid C++ + // inline static meta_type_node *local = nullptr; + // inline static meta_type_node **global = &local; + + [[nodiscard]] static meta_type_node * & local() ENTT_NOEXCEPT { + static meta_type_node *chain = nullptr; + return chain; + } + + [[nodiscard]] static meta_type_node ** & global() ENTT_NOEXCEPT { + static meta_type_node **chain = &local(); + return chain; + } +}; + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/*! @brief Opaque container for a meta context. */ +struct meta_ctx { + /** + * @brief Binds the meta system to a given context. + * @param other A valid context to which to bind. + */ + static void bind(meta_ctx other) ENTT_NOEXCEPT { + internal::meta_context::global() = other.ctx; + } + +private: + internal::meta_type_node **ctx{&internal::meta_context::local()}; +}; + + +} + + +#endif + +// #include "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" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + + +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_HS_SUFFIX +# define ENTT_HS_SUFFIX _hs +#endif + + +#ifndef ENTT_HWS_SUFFIX +# define ENTT_HWS_SUFFIX _hws +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifndef ENTT_PAGE_SIZE +# define ENTT_PAGE_SIZE 32768 +#endif + + +#ifndef ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty +#else +# include +# define ENTT_IS_EMPTY(Type) std::false_type +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) +# define ENTT_PRETTY_FUNCTION_CONSTEXPR +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif 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_CONSTEXPR +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + + +#ifndef ENTT_STANDALONE +# define ENTT_FAST_PATH(...) false +#else +# define ENTT_FAST_PATH(Cond) Cond +#endif + + +#endif + + + +namespace entt { + + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + + +} + + +#endif + +// #include "../core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + + +#include +// #include "../config/config.h" + +// #include "../core/attribute.h" +#ifndef ENTT_CORE_ATTRIBUTE_H +#define ENTT_CORE_ATTRIBUTE_H + + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + + +#endif + +// #include "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + + +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +struct fnv1a_traits; + + +template<> +struct fnv1a_traits { + using type = std::uint32_t; + static constexpr std::uint32_t offset = 2166136261; + static constexpr std::uint32_t prime = 16777619; +}; + + +template<> +struct fnv1a_traits { + using type = std::uint64_t; + static constexpr std::uint64_t offset = 14695981039346656037ull; + static constexpr std::uint64_t prime = 1099511628211ull; +}; + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifers in the codebase while using their numeric + * counterparts at runtime.
+ * Because of that, a hashed string can also be used in constant expressions if + * required. + * + * @tparam Char Character type. + */ +template +class basic_hashed_string { + using traits_type = internal::fnv1a_traits; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const Char *curr) ENTT_NOEXCEPT: str{curr} {} + const Char *str; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr id_type helper(const Char *curr) ENTT_NOEXCEPT { + auto value = traits_type::offset; + + while(*curr != 0) { + value = (value ^ static_cast(*(curr++))) * traits_type::prime; + } + + return value; + } + +public: + /*! @brief Character type. */ + using value_type = Char; + /*! @brief Unsigned integer type. */ + using hash_type = id_type; + + /** + * @brief Returns directly the numeric representation of a string. + * + * Forcing template resolution avoids implicit conversions. An + * human-readable identifier can be anything but a plain, old bunch of + * characters.
+ * Example of use: + * @code{.cpp} + * const auto value = basic_hashed_string::to_value("my.png"); + * @endcode + * + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifer. + * @return The numeric representation of the string. + */ + template + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { + return helper(str); + } + + /** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ + [[nodiscard]] static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { + return helper(wrapper.str); + } + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifer. + * @param size Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static hash_type value(const value_type *str, std::size_t size) ENTT_NOEXCEPT { + id_type partial{traits_type::offset}; + while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } + return partial; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() ENTT_NOEXCEPT + : str{nullptr}, hash{} + {} + + /** + * @brief Constructs a hashed string from an array of const characters. + * + * Forcing template resolution avoids implicit conversions. An + * human-readable identifier can be anything but a plain, old bunch of + * characters.
+ * Example of use: + * @code{.cpp} + * basic_hashed_string hs{"my.png"}; + * @endcode + * + * @tparam N Number of characters of the identifier. + * @param curr Human-readable identifer. + */ + template + constexpr basic_hashed_string(const value_type (&curr)[N]) ENTT_NOEXCEPT + : str{curr}, hash{helper(curr)} + {} + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT + : str{wrapper.str}, hash{helper(wrapper.str)} + {} + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the instance. + */ + [[nodiscard]] constexpr const value_type * data() const ENTT_NOEXCEPT { + return str; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the instance. + */ + [[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { + return hash; + } + + /*! @copydoc data */ + [[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { return data(); } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the instance. + */ + [[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { return value(); } + + /** + * @brief Compares two hashed strings. + * @param other Hashed string with which to compare. + * @return True if the two hashed strings are identical, false otherwise. + */ + [[nodiscard]] constexpr bool operator==(const basic_hashed_string &other) const ENTT_NOEXCEPT { + return hash == other.hash; + } + +private: + const value_type *str; + hash_type hash; +}; + + +/** + * @brief Deduction guide. + * + * It allows to deduce the character type of the hashed string directly from a + * human-readable identifer provided to the constructor. + * + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifer. + */ +template +basic_hashed_string(const Char (&str)[N]) ENTT_NOEXCEPT +-> basic_hashed_string; + + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + + +} + + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr entt::hashed_string operator"" ENTT_HS_SUFFIX(const char *str, std::size_t) ENTT_NOEXCEPT { + return entt::hashed_string{str}; +} + + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] constexpr entt::hashed_wstring operator"" ENTT_HWS_SUFFIX(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { + return entt::hashed_wstring{str}; +} + + +#endif + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +struct ENTT_API type_index { + [[nodiscard]] static id_type next() ENTT_NOEXCEPT { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + + +template +[[nodiscard]] constexpr auto type_name() ENTT_NOEXCEPT { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX)+1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{}; +#endif +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Type index. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() ENTT_NOEXCEPT { + static const id_type value = internal::type_index::next(); + return value; + } +}; + + +/** + * @brief Provides the member constant `value` to true if a given type is + * indexable, false otherwise. + * @tparam Type Potentially indexable type. + */ +template +struct has_type_index: std::false_type {}; + + +/*! @brief has_type_index */ +template +struct has_type_index::value())>>: std::true_type {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially indexable type. + */ +template +inline constexpr bool has_type_index_v = has_type_index::value; + + +/** + * @brief Type info. + * @tparam Type Type for which to generate information. + */ +template +struct type_info { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION_CONSTEXPR + [[nodiscard]] static constexpr id_type id() ENTT_NOEXCEPT { + constexpr auto value = hashed_string::value(ENTT_PRETTY_FUNCTION); + return value; + } +#elif defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static id_type id() ENTT_NOEXCEPT { + static const auto value = hashed_string::value(ENTT_PRETTY_FUNCTION); + return value; + } +#else + [[nodiscard]] static id_type id() ENTT_NOEXCEPT { + return type_index::value(); + } +#endif + + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION_CONSTEXPR + [[nodiscard]] static constexpr std::string_view name() ENTT_NOEXCEPT { + constexpr auto value = internal::type_name(); + return value; + } +#elif defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static std::string_view name() ENTT_NOEXCEPT { + static const auto value = internal::type_name(); + return value; + } +#else + [[nodiscard]] static constexpr std::string_view name() ENTT_NOEXCEPT { + return internal::type_name(); + } +#endif +}; + + +} + + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + + +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_t = Type; + + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_v = Value; + + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + + +/** + * @brief Alias template to ease the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + + +/** + * @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 A class to use to push around lists of types, nothing more. */ +template +struct type_list {}; + + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_size; + + +/** + * @brief Compile-time number of elements in a type list. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_size> + : std::integral_constant +{}; + + +/** + * @brief Helper variable template. + * @tparam List Type list. + */ +template +inline constexpr auto type_list_size_v = type_list_size::value; + + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + std::disjunction_v...>, + typename type_list_unique>::type, + type_list_cat_t, typename type_list_unique>::type> + >; +}; + + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = type_list<>; +}; + + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type Potentially equality comparable type. + */ +template> +struct is_equality_comparable: std::false_type {}; + + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable() == std::declval())>> + : std::true_type +{}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially equality comparable type. + */ +template +inline constexpr auto is_equality_comparable_v = is_equality_comparable::value; + + +/** + * @brief Provides the member constant `value` to true if a given type is empty + * and the empty type optimization is enabled, false otherwise. + * @tparam Type Potential empty type. + */ +template +struct is_eto_eligible + : ENTT_IS_EMPTY(Type) +{}; + + +/** + * @brief Helper variable template. + * @tparam Type Potential empty type. + */ +template +inline constexpr auto is_eto_eligible_v = is_eto_eligible::value; + + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class * clazz(Ret(Class:: *)(Args...)); + + template + static Class * clazz(Ret(Class:: *)(Args...) const); + + template + static Class * clazz(Type Class:: *); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + + +} + + +#endif + +// #include "internal.hpp" +#ifndef ENTT_META_INTERNAL_HPP +#define ENTT_META_INTERNAL_HPP + + +#include +#include +#include +#include +#include +// #include "../core/attribute.h" + +// #include "../config/config.h" + +// #include "../core/fwd.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "type_traits.hpp" + + + +namespace entt { + + +class meta_any; +struct meta_handle; + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +class meta_storage { + using storage_type = std::aligned_storage_t; + using copy_fn_type = void(meta_storage &, const meta_storage &); + using steal_fn_type = void(meta_storage &, meta_storage &); + using destroy_fn_type = void(meta_storage &); + + template> + struct type_traits { + template + static void instance(meta_storage &buffer, Args &&... args) { + buffer.instance = new Type{std::forward(args)...}; + new (&buffer.storage) Type *{static_cast(buffer.instance)}; + } + + static void destroy(meta_storage &buffer) { + delete static_cast(buffer.instance); + } + + static void copy(meta_storage &to, const meta_storage &from) { + to.instance = new Type{*static_cast(from.instance)}; + new (&to.storage) Type *{static_cast(to.instance)}; + } + + static void steal(meta_storage &to, meta_storage &from) { + new (&to.storage) Type *{static_cast(from.instance)}; + to.instance = from.instance; + } + }; + + template + struct type_traits>> { + template + static void instance(meta_storage &buffer, Args &&... args) { + buffer.instance = new (&buffer.storage) Type{std::forward(args)...}; + } + + static void destroy(meta_storage &buffer) { + static_cast(buffer.instance)->~Type(); + } + + static void copy(meta_storage &to, const meta_storage &from) { + to.instance = new (&to.storage) Type{*static_cast(from.instance)}; + } + + static void steal(meta_storage &to, meta_storage &from) { + to.instance = new (&to.storage) Type{std::move(*static_cast(from.instance))}; + destroy(from); + } + }; + +public: + /*! @brief Default constructor. */ + meta_storage() ENTT_NOEXCEPT + : storage{}, + instance{}, + destroy_fn{}, + copy_fn{}, + steal_fn{} + {} + + template + explicit meta_storage(std::in_place_type_t, [[maybe_unused]] Args &&... args) + : meta_storage{} + { + if constexpr(!std::is_void_v) { + type_traits::instance(*this, std::forward(args)...); + destroy_fn = &type_traits::destroy; + copy_fn = &type_traits::copy; + steal_fn = &type_traits::steal; + } + } + + template + meta_storage(std::reference_wrapper value) + : meta_storage{} + { + instance = &value.get(); + } + + template>, meta_storage>>> + meta_storage(Type &&value) + : meta_storage{std::in_place_type>>, std::forward(value)} + {} + + meta_storage(const meta_storage &other) + : meta_storage{} + { + (other.copy_fn ? other.copy_fn : [](auto &to, const auto &from) { to.instance = from.instance; })(*this, other); + destroy_fn = other.destroy_fn; + copy_fn = other.copy_fn; + steal_fn = other.steal_fn; + } + + meta_storage(meta_storage &&other) + : meta_storage{} + { + swap(*this, other); + } + + ~meta_storage() { + if(destroy_fn) { + destroy_fn(*this); + } + } + + meta_storage & operator=(meta_storage other) { + swap(other, *this); + return *this; + } + + [[nodiscard]] const void * data() const ENTT_NOEXCEPT { + return instance; + } + + [[nodiscard]] void * data() ENTT_NOEXCEPT { + return const_cast(std::as_const(*this).data()); + } + + template + void emplace(Args &&... args) { + *this = meta_storage{std::in_place_type, std::forward(args)...}; + } + + [[nodiscard]] meta_storage ref() const ENTT_NOEXCEPT { + meta_storage other{}; + other.instance = instance; + return other; + } + + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return !(instance == nullptr); + } + + friend void swap(meta_storage &lhs, meta_storage &rhs) { + using std::swap; + + if(lhs.steal_fn && rhs.steal_fn) { + meta_storage buffer{}; + lhs.steal_fn(buffer, lhs); + rhs.steal_fn(lhs, rhs); + lhs.steal_fn(rhs, buffer); + } else if(lhs.steal_fn) { + lhs.steal_fn(rhs, lhs); + } else if(rhs.steal_fn) { + rhs.steal_fn(lhs, rhs); + } else { + swap(lhs.instance, rhs.instance); + } + + swap(lhs.destroy_fn, rhs.destroy_fn); + swap(lhs.copy_fn, rhs.copy_fn); + swap(lhs.steal_fn, rhs.steal_fn); + } + +private: + storage_type storage; + void *instance; + destroy_fn_type *destroy_fn; + copy_fn_type *copy_fn; + steal_fn_type *steal_fn; +}; + + +struct meta_type_node; + + +struct meta_prop_node { + meta_prop_node * next; + meta_any(* const key)(); + meta_any(* const value)(); +}; + + +struct meta_base_node { + meta_type_node * const parent; + meta_base_node * next; + meta_type_node *(* const type)() ENTT_NOEXCEPT; + const void *(* const cast)(const void *) ENTT_NOEXCEPT; +}; + + +struct meta_conv_node { + meta_type_node * const parent; + meta_conv_node * next; + meta_type_node *(* const type)() ENTT_NOEXCEPT; + meta_any(* const conv)(const void *); +}; + + +struct meta_ctor_node { + using size_type = std::size_t; + meta_type_node * const parent; + meta_ctor_node * next; + meta_prop_node * prop; + const size_type size; + meta_type_node *(* const arg)(size_type) ENTT_NOEXCEPT; + meta_any(* const invoke)(meta_any * const); +}; + + +struct meta_data_node { + id_type id; + meta_type_node * const parent; + meta_data_node * next; + meta_prop_node * prop; + const bool is_static; + meta_type_node *(* const type)() ENTT_NOEXCEPT; + bool(* const set)(meta_handle, meta_any); + meta_any(* const get)(meta_handle); +}; + + +struct meta_func_node { + using size_type = std::size_t; + id_type id; + meta_type_node * const parent; + meta_func_node * next; + meta_prop_node * prop; + const size_type size; + const bool is_const; + const bool is_static; + meta_type_node *(* const ret)() ENTT_NOEXCEPT; + meta_type_node *(* const arg)(size_type) ENTT_NOEXCEPT; + meta_any(* const invoke)(meta_handle, meta_any *); +}; + + +struct meta_type_node { + using size_type = std::size_t; + const id_type type_id; + id_type id; + meta_type_node * next; + meta_prop_node * prop; + const bool is_void; + const bool is_integral; + const bool is_floating_point; + const bool is_array; + const bool is_enum; + const bool is_union; + const bool is_class; + const bool is_pointer; + const bool is_function_pointer; + const bool is_member_object_pointer; + const bool is_member_function_pointer; + const bool is_pointer_like; + const bool is_sequence_container; + const bool is_associative_container; + const size_type rank; + size_type(* const extent)(size_type); + bool(* const compare)(const void *, const void *); + meta_type_node *(* const remove_pointer)() ENTT_NOEXCEPT; + meta_type_node *(* const remove_extent)() ENTT_NOEXCEPT; + meta_base_node *base{nullptr}; + meta_conv_node *conv{nullptr}; + meta_ctor_node *ctor{nullptr}; + meta_data_node *data{nullptr}; + meta_func_node *func{nullptr}; + void(* dtor)(void *){nullptr}; +}; + + +template +class meta_range { + struct range_iterator { + using difference_type = std::ptrdiff_t; + using value_type = Node; + using pointer = value_type *; + using reference = value_type &; + using iterator_category = std::forward_iterator_tag; + + range_iterator() ENTT_NOEXCEPT = default; + + range_iterator(Node *head) ENTT_NOEXCEPT + : node{head} + {} + + range_iterator & operator++() ENTT_NOEXCEPT { + return node = node->next, *this; + } + + range_iterator operator++(int) ENTT_NOEXCEPT { + range_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] bool operator==(const range_iterator &other) const ENTT_NOEXCEPT { + return other.node == node; + } + + [[nodiscard]] bool operator!=(const range_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + return node; + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return *operator->(); + } + + private: + Node *node{nullptr}; + }; + +public: + using iterator = range_iterator; + + meta_range() ENTT_NOEXCEPT = default; + + meta_range(Node *head) + : node{head} + {} + + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return iterator{node}; + } + + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + return iterator{}; + } + +private: + Node *node{nullptr}; +}; + + +template +auto find_if(const Op &op, const meta_type_node *node) +-> std::decay_t*Member)> { + std::decay_t*Member)> ret = nullptr; + + for(auto &&curr: meta_range{node->*Member}) { + if(op(&curr)) { + ret = &curr; + break; + } + } + + if(!ret) { + for(auto &&curr: meta_range{node->base}) { + if(ret = find_if(op, curr.type()); ret) { + break; + } + } + } + + return ret; +} + + +template +class ENTT_API meta_node { + static_assert(std::is_same_v>>, "Invalid type"); + + [[nodiscard]] static bool compare(const void *lhs, const void *rhs) { + if constexpr(!std::is_function_v && is_equality_comparable_v) { + return *static_cast(lhs) == *static_cast(rhs); + } else { + return lhs == rhs; + } + } + + template + [[nodiscard]] static auto extent(meta_type_node::size_type dim, std::index_sequence) { + meta_type_node::size_type ext{}; + ((ext = (dim == Index ? std::extent_v : ext)), ...); + return ext; + } + +public: + [[nodiscard]] static meta_type_node * resolve() ENTT_NOEXCEPT { + static meta_type_node node{ + type_info::id(), + {}, + nullptr, + nullptr, + std::is_void_v, + std::is_integral_v, + std::is_floating_point_v, + std::is_array_v, + std::is_enum_v, + std::is_union_v, + std::is_class_v, + std::is_pointer_v, + std::is_pointer_v && std::is_function_v>, + std::is_member_object_pointer_v, + std::is_member_function_pointer_v, + is_meta_pointer_like_v, + has_meta_sequence_container_traits_v, + has_meta_associative_container_traits_v, + std::rank_v, + [](meta_type_node::size_type dim) { + return extent(dim, std::make_index_sequence>{}); + }, + &compare, // workaround for an issue with VS2017 + &meta_node>>::resolve, + &meta_node>>::resolve + }; + + return &node; + } +}; + + +template +struct meta_info: meta_node>...> {}; + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +} + + +#endif + +// #include "meta.hpp" +#ifndef ENTT_META_META_HPP +#define ENTT_META_META_HPP + + +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/fwd.hpp" + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + + +#include +// #include "../config/config.h" + + + +namespace entt { + + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type && operator()(Type &&value) const ENTT_NOEXCEPT { + return std::forward(value); + } +}; + + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +[[nodiscard]] constexpr auto overload(Type Class:: *member) ENTT_NOEXCEPT { return member; } + + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { return func; } + + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + y_combinator(Func recursive): + func{std::move(recursive)} + {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + decltype(auto) operator()(Args &&... args) const { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + decltype(auto) operator()(Args &&... args) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + + +} + + +#endif + +// #include "ctx.hpp" + +// #include "internal.hpp" + +// #include "range.hpp" +#ifndef ENTT_META_RANGE_HPP +#define ENTT_META_RANGE_HPP + + +// #include "internal.hpp" + + + +namespace entt { + + +/** + * @brief Iterable range to use to iterate all types of meta objects. + * @tparam Type Type of meta objects iterated. + */ +template +class meta_range { + struct range_iterator { + using difference_type = std::ptrdiff_t; + using value_type = Type; + using pointer = void; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + using node_type = typename Type::node_type; + + range_iterator() ENTT_NOEXCEPT = default; + + range_iterator(node_type *head) ENTT_NOEXCEPT + : it{head} + {} + + range_iterator & operator++() ENTT_NOEXCEPT { + return ++it, *this; + } + + range_iterator operator++(int) ENTT_NOEXCEPT { + range_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return it.operator->(); + } + + [[nodiscard]] bool operator==(const range_iterator &other) const ENTT_NOEXCEPT { + return other.it == it; + } + + [[nodiscard]] bool operator!=(const range_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + private: + typename internal::meta_range::iterator it{}; + }; + +public: + /*! @brief Node type. */ + using node_type = typename Type::node_type; + /*! @brief Input iterator type. */ + using iterator = range_iterator; + + /*! @brief Default constructor. */ + meta_range() ENTT_NOEXCEPT = default; + + /** + * @brief Constructs a meta range from a given node. + * @param head The underlying node with which to construct the range. + */ + meta_range(node_type *head) + : node{head} + {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first meta object of the range. + */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return iterator{node}; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last meta object of the + * range. + */ + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + return iterator{}; + } + +private: + node_type *node{nullptr}; +}; + + +} + + +#endif + +// #include "type_traits.hpp" + + + +namespace entt { + + +class meta_type; +class meta_any; + + +/*! @brief Proxy object for sequence containers. */ +class meta_sequence_container { + template + struct meta_sequence_container_proxy; + + class meta_iterator; + +public: + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Meta iterator type. */ + using iterator = meta_iterator; + + /*! @brief Default constructor. */ + meta_sequence_container() ENTT_NOEXCEPT + : instance{nullptr} + {} + + /** + * @brief Construct a proxy object for sequence containers. + * @tparam Type Type of container to wrap. + * @param container The container to wrap. + */ + template + meta_sequence_container(Type *container) ENTT_NOEXCEPT + : value_type_fn{&meta_sequence_container_proxy::value_type}, + size_fn{&meta_sequence_container_proxy::size}, + resize_fn{&meta_sequence_container_proxy::resize}, + clear_fn{&meta_sequence_container_proxy::clear}, + begin_fn{&meta_sequence_container_proxy::begin}, + end_fn{&meta_sequence_container_proxy::end}, + insert_fn{&meta_sequence_container_proxy::insert}, + erase_fn{&meta_sequence_container_proxy::erase}, + get_fn{&meta_sequence_container_proxy::get}, + instance{container} + {} + + [[nodiscard]] inline meta_type value_type() const ENTT_NOEXCEPT; + [[nodiscard]] inline size_type size() const ENTT_NOEXCEPT; + inline bool resize(size_type) const; + inline bool clear(); + [[nodiscard]] inline iterator begin(); + [[nodiscard]] inline iterator end(); + inline std::pair insert(iterator, meta_any); + inline std::pair erase(iterator); + [[nodiscard]] inline meta_any operator[](size_type); + [[nodiscard]] inline explicit operator bool() const ENTT_NOEXCEPT; + +private: + meta_type(* value_type_fn)() ENTT_NOEXCEPT; + size_type(* size_fn)(const void *) ENTT_NOEXCEPT; + bool(* resize_fn)(void *, size_type); + bool(* clear_fn)(void *); + iterator(* begin_fn)(void *); + iterator(* end_fn)(void *); + std::pair(* insert_fn)(void *, iterator, meta_any); + std::pair(* erase_fn)(void *, iterator); + meta_any(* get_fn)(void *, size_type); + void *instance; +}; + + +/*! @brief Proxy object for associative containers. */ +class meta_associative_container { + template + struct meta_associative_container_proxy; + + class meta_iterator; + +public: + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Meta iterator type. */ + using iterator = meta_iterator; + + /*! @brief Default constructor. */ + meta_associative_container() ENTT_NOEXCEPT + : instance{nullptr} + {} + + /** + * @brief Construct a proxy object for associative containers. + * @tparam Type Type of container to wrap. + * @param container The container to wrap. + */ + template + meta_associative_container(Type *container) ENTT_NOEXCEPT + : key_only_container{is_key_only_meta_associative_container_v}, + key_type_fn{&meta_associative_container_proxy::key_type}, + mapped_type_fn{&meta_associative_container_proxy::mapped_type}, + value_type_fn{&meta_associative_container_proxy::value_type}, + size_fn{&meta_associative_container_proxy::size}, + clear_fn{&meta_associative_container_proxy::clear}, + begin_fn{&meta_associative_container_proxy::begin}, + end_fn{&meta_associative_container_proxy::end}, + insert_fn{&meta_associative_container_proxy::insert}, + erase_fn{&meta_associative_container_proxy::erase}, + find_fn{&meta_associative_container_proxy::find}, + instance{container} + {} + + [[nodiscard]] inline bool key_only() const ENTT_NOEXCEPT; + [[nodiscard]] inline meta_type key_type() const ENTT_NOEXCEPT; + [[nodiscard]] inline meta_type mapped_type() const ENTT_NOEXCEPT; + [[nodiscard]] inline meta_type value_type() const ENTT_NOEXCEPT; + [[nodiscard]] inline size_type size() const ENTT_NOEXCEPT; + inline bool clear(); + [[nodiscard]] inline iterator begin(); + [[nodiscard]] inline iterator end(); + inline bool insert(meta_any, meta_any); + inline bool erase(meta_any); + [[nodiscard]] inline iterator find(meta_any); + [[nodiscard]] inline explicit operator bool() const ENTT_NOEXCEPT; + +private: + bool key_only_container; + meta_type(* key_type_fn)() ENTT_NOEXCEPT; + meta_type(* mapped_type_fn)() ENTT_NOEXCEPT; + meta_type(* value_type_fn)() ENTT_NOEXCEPT; + size_type(* size_fn)(const void *) ENTT_NOEXCEPT; + bool(* clear_fn)(void *); + iterator(* begin_fn)(void *); + iterator(* end_fn)(void *); + bool(* insert_fn)(void *, meta_any, meta_any); + bool(* erase_fn)(void *, meta_any); + iterator(* find_fn)(void *, meta_any); + void *instance; +}; + + +/** + * @brief Opaque wrapper for values of any type. + * + * This class uses a technique called small buffer optimization (SBO) to get rid + * of memory allocations if possible. This should improve overall performance. + */ +class meta_any { + using dereference_operator_type = meta_any(meta_any &); + + template + [[nodiscard]] static meta_any dereference_operator(meta_any &any) { + if constexpr(is_meta_pointer_like_v) { + if constexpr(std::is_const_v())>>) { + return *any.cast(); + } else { + return std::ref(*any.cast()); + } + } else { + return {}; + } + } + + template + [[nodiscard]] static meta_sequence_container meta_sequence_container_factory([[maybe_unused]] void *container) ENTT_NOEXCEPT { + if constexpr(has_meta_sequence_container_traits_v) { + return static_cast(container); + } else { + return {}; + } + } + + template + [[nodiscard]] static meta_associative_container meta_associative_container_factory([[maybe_unused]] void *container) ENTT_NOEXCEPT { + if constexpr(has_meta_associative_container_traits_v) { + return static_cast(container); + } else { + return {}; + } + } + +public: + /*! @brief Default constructor. */ + meta_any() ENTT_NOEXCEPT + : storage{}, + node{}, + deref{nullptr}, + seq_factory{nullptr}, + assoc_factory{nullptr} + {} + + /** + * @brief Constructs a meta any 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, [[maybe_unused]] Args &&... args) + : storage(std::in_place_type, std::forward(args)...), + node{internal::meta_info::resolve()}, + deref{&dereference_operator}, + seq_factory{&meta_sequence_container_factory}, + assoc_factory{&meta_associative_container_factory} + {} + + /** + * @brief Constructs a meta any that holds an unmanaged object. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template + meta_any(std::reference_wrapper value) + : storage{value}, + node{internal::meta_info::resolve()}, + deref{&dereference_operator}, + seq_factory{&meta_sequence_container_factory}, + assoc_factory{&meta_associative_container_factory} + {} + + /** + * @brief Constructs a meta any from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template>, meta_any>>> + meta_any(Type &&value) + : meta_any{std::in_place_type>>, std::forward(value)} + {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + meta_any(const meta_any &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + meta_any(meta_any &&other) + : meta_any{} + { + swap(*this, other); + } + + /*! @brief Frees the internal storage, whatever it means. */ + ~meta_any() { + if(node && node->dtor) { + node->dtor(storage.data()); + } + } + + /** + * @brief Assignment operator. + * @param other The instance to assign from. + * @return This meta any object. + */ + meta_any & operator=(meta_any other) { + swap(other, *this); + return *this; + } + + /** + * @brief Returns the meta type of the underlying object. + * @return The meta type of the underlying object, if any. + */ + [[nodiscard]] inline meta_type type() const ENTT_NOEXCEPT; + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void * data() const ENTT_NOEXCEPT { + return storage.data(); + } + + /*! @copydoc data */ + [[nodiscard]] void * data() ENTT_NOEXCEPT { + return storage.data(); + } + + /** + * @brief Tries to cast an instance to a given type. + * @tparam Type Type to which to cast the instance. + * @return A (possibly null) pointer to the contained instance. + */ + template + [[nodiscard]] const Type * try_cast() const { + if(node) { + if(const auto type_id = internal::meta_info::resolve()->type_id; node->type_id == type_id) { + return static_cast(storage.data()); + } else if(const auto *base = internal::find_if<&internal::meta_type_node::base>([type_id](const auto *curr) { return curr->type()->type_id == type_id; }, node); base) { + return static_cast(base->cast(storage.data())); + } + } + + return nullptr; + } + + /*! @copydoc try_cast */ + template + [[nodiscard]] Type * try_cast() { + return const_cast(std::as_const(*this).try_cast()); + } + + /** + * @brief Tries to cast an instance to a given type. + * + * The type of the instance must be such that the cast is possible. + * + * @warning + * Attempting to perform a cast that isn't viable results in undefined + * behavior.
+ * An assertion will abort the execution at runtime in debug mode in case + * the cast is not feasible. + * + * @tparam Type Type to which to cast the instance. + * @return A reference to the contained instance. + */ + template + [[nodiscard]] const Type & cast() const { + auto * const actual = try_cast(); + ENTT_ASSERT(actual); + return *actual; + } + + /*! @copydoc cast */ + template + [[nodiscard]] Type & cast() { + return const_cast(std::as_const(*this).cast()); + } + + /** + * @brief Tries to convert an instance to a given type and returns it. + * @tparam Type Type to which to convert the instance. + * @return A valid meta any object if the conversion is possible, an invalid + * one otherwise. + */ + template + [[nodiscard]] meta_any convert() const { + if(node) { + if(const auto type_id = internal::meta_info::resolve()->type_id; node->type_id == type_id) { + return *this; + } else if(const auto * const conv = internal::find_if<&internal::meta_type_node::conv>([type_id](const auto *curr) { return curr->type()->type_id == type_id; }, node); conv) { + return conv->conv(storage.data()); + } + } + + return {}; + } + + /** + * @brief Tries to convert an instance to a given type. + * @tparam Type Type to which to convert the instance. + * @return True if the conversion is possible, false otherwise. + */ + template + bool convert() { + bool valid = (node && node->type_id == internal::meta_info::resolve()->type_id); + + if(!valid) { + if(auto any = std::as_const(*this).convert(); any) { + swap(any, *this); + valid = true; + } + } + + return valid; + } + + /** + * @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) { + *this = meta_any{std::in_place_type, std::forward(args)...}; + } + + /** + * @brief Aliasing constructor. + * @return A meta any that shares a reference to an unmanaged object. + */ + [[nodiscard]] meta_any ref() const ENTT_NOEXCEPT { + meta_any other{}; + other.node = node; + other.storage = storage.ref(); + other.deref = deref; + other.seq_factory = seq_factory; + other.assoc_factory = assoc_factory; + return other; + } + + /** + * @brief Returns a sequence container proxy. + * @return A sequence container proxy for the underlying object. + */ + [[nodiscard]] meta_sequence_container as_sequence_container() ENTT_NOEXCEPT { + return seq_factory(storage.data()); + } + + /** + * @brief Returns an associative container proxy. + * @return An associative container proxy for the underlying object. + */ + [[nodiscard]] meta_associative_container as_associative_container() ENTT_NOEXCEPT { + return assoc_factory(storage.data()); + } + + /** + * @brief Indirection operator for dereferencing opaque objects. + * @return A meta any that shares a reference to an unmanaged object if the + * wrapped element is dereferenceable, an invalid meta any otherwise. + */ + [[nodiscard]] meta_any operator*() ENTT_NOEXCEPT { + return deref(*this); + } + + /** + * @brief Returns false if a wrapper is empty, true otherwise. + * @return False if the wrapper is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return !(node == nullptr); + } + + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return False if the two objects differ in their content, true otherwise. + */ + [[nodiscard]] bool operator==(const meta_any &other) const { + return (!node && !other.node) || (node && other.node && node->type_id == other.node->type_id && node->compare(storage.data(), other.storage.data())); + } + + /** + * @brief Swaps two meta any objects. + * @param lhs A valid meta any object. + * @param rhs A valid meta any object. + */ + friend void swap(meta_any &lhs, meta_any &rhs) { + using std::swap; + swap(lhs.storage, rhs.storage); + swap(lhs.node, rhs.node); + swap(lhs.deref, rhs.deref); + swap(lhs.seq_factory, rhs.seq_factory); + swap(lhs.assoc_factory, rhs.assoc_factory); + } + +private: + internal::meta_storage storage; + internal::meta_type_node *node; + dereference_operator_type *deref; + meta_sequence_container(* seq_factory)(void *); + meta_associative_container(* assoc_factory)(void *); +}; + + +/** + * @brief Checks if two wrappers differ in their content. + * @param lhs A meta any object, either empty or not. + * @param rhs A meta any object, either empty or not. + * @return True if the two wrappers differ in their content, false otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_any &lhs, const meta_any &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/** + * @brief Opaque pointers to instances of any type. + * + * A handle doesn't perform copies and isn't responsible for the contained + * object. It doesn't prolong the lifetime of the pointed instance.
+ * Handles are used to generate meta references to actual objects when needed. + */ +struct meta_handle { + /*! @brief Default constructor. */ + meta_handle() = default; + + /** + * @brief Creates a handle that points to an unmanaged object. + * @tparam Type Type of object to use to initialize the handle. + * @param value An instance of an object to use to initialize the handle. + */ + template>, meta_handle>>> + meta_handle(Type &&value) ENTT_NOEXCEPT + : meta_handle{} + { + if constexpr(std::is_same_v>, meta_any>) { + any = value.ref(); + } else { + static_assert(std::is_lvalue_reference_v, "Lvalue reference required"); + any = std::ref(value); + } + } + + /** + * @brief Dereference operator for accessing the contained opaque object. + * @return A meta any that shares a reference to an unmanaged object. + */ + [[nodiscard]] meta_any operator*() const { + return any; + } + + /** + * @brief Access operator for accessing the contained opaque object. + * @return A meta any that shares a reference to an unmanaged object. + */ + [[nodiscard]] meta_any * operator->() { + return &any; + } + +private: + meta_any any; +}; + + +/*! @brief Opaque wrapper for meta properties of any type. */ +struct meta_prop { + /*! @brief Node type. */ + using node_type = internal::meta_prop_node; + + /** + * @brief Constructs an instance from a given node. + * @param curr The underlying node with which to construct the instance. + */ + meta_prop(const node_type *curr = nullptr) ENTT_NOEXCEPT + : node{curr} + {} + + /** + * @brief Returns the stored key. + * @return A meta any containing the key stored with the property. + */ + [[nodiscard]] meta_any key() const { + return node->key(); + } + + /** + * @brief Returns the stored value. + * @return A meta any containing the value stored with the property. + */ + [[nodiscard]] meta_any value() const { + return node->value(); + } + + /** + * @brief Returns true if a meta object is valid, false otherwise. + * @return True if the meta object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return !(node == nullptr); + } + +private: + const node_type *node; +}; + + +/*! @brief Opaque wrapper for meta base classes. */ +struct meta_base { + /*! @brief Node type. */ + using node_type = internal::meta_base_node; + + /*! @copydoc meta_prop::meta_prop */ + meta_base(const node_type *curr = nullptr) ENTT_NOEXCEPT + : node{curr} + {} + + /** + * @brief Returns the meta type to which a meta object belongs. + * @return The meta type to which the meta object belongs. + */ + [[nodiscard]] inline meta_type parent() const ENTT_NOEXCEPT; + + /*! @copydoc meta_any::type */ + [[nodiscard]] inline meta_type type() const ENTT_NOEXCEPT; + + /** + * @brief Casts an instance from a parent type to a base type. + * @param instance The instance to cast. + * @return An opaque pointer to the base type. + */ + [[nodiscard]] const void * cast(const void *instance) const ENTT_NOEXCEPT { + return node->cast(instance); + } + + /** + * @brief Returns true if a meta object is valid, false otherwise. + * @return True if the meta object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return !(node == nullptr); + } + +private: + const node_type *node; +}; + + +/*! @brief Opaque wrapper for meta conversion functions. */ +struct meta_conv { + /*! @brief Node type. */ + using node_type = internal::meta_conv_node; + + /*! @copydoc meta_prop::meta_prop */ + meta_conv(const node_type *curr = nullptr) ENTT_NOEXCEPT + : node{curr} + {} + + /*! @copydoc meta_base::parent */ + [[nodiscard]] inline meta_type parent() const ENTT_NOEXCEPT; + + /*! @copydoc meta_any::type */ + [[nodiscard]] inline meta_type type() const ENTT_NOEXCEPT; + + /** + * @brief Converts an instance to the underlying type. + * @param instance The instance to convert. + * @return An opaque pointer to the instance to convert. + */ + [[nodiscard]] meta_any convert(const void *instance) const { + return node->conv(instance); + } + + /** + * @brief Returns true if a meta object is valid, false otherwise. + * @return True if the meta object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return !(node == nullptr); + } + +private: + const node_type *node; +}; + + +/*! @brief Opaque wrapper for meta constructors. */ +struct meta_ctor { + /*! @brief Node type. */ + using node_type = internal::meta_ctor_node; + /*! @brief Unsigned integer type. */ + using size_type = typename node_type::size_type; + + /*! @copydoc meta_prop::meta_prop */ + meta_ctor(const node_type *curr = nullptr) ENTT_NOEXCEPT + : node{curr} + {} + + /*! @copydoc meta_base::parent */ + [[nodiscard]] inline meta_type parent() const ENTT_NOEXCEPT; + + /** + * @brief Returns the number of arguments accepted by a meta constructor. + * @return The number of arguments accepted by the meta constructor. + */ + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + return node->size; + } + + /** + * @brief Returns the meta type of the i-th argument of a meta constructor. + * @param index The index of the argument of which to return the meta type. + * @return The meta type of the i-th argument of a meta constructor, if any. + */ + [[nodiscard]] meta_type arg(size_type index) const ENTT_NOEXCEPT; + + /** + * @brief Creates an instance of the underlying type, if possible. + * + * To create a valid instance, the parameters must be such that a cast or + * conversion to the required types is possible. Otherwise, an empty and + * thus invalid wrapper is returned. + * + * @param args Parameters to use to construct the instance. + * @param sz Number of parameters to use to construct the instance. + * @return A meta any containing the new instance, if any. + */ + [[nodiscard]] meta_any invoke(meta_any * const args, const std::size_t sz) const { + return sz == size() ? node->invoke(args) : meta_any{}; + } + + /** + * @copybrief invoke + * + * @sa invoke + * + * @tparam Args Types of arguments to use to construct the instance. + * @param args Parameters to use to construct the instance. + * @return A meta any containing the new instance, if any. + */ + template + [[nodiscard]] meta_any invoke([[maybe_unused]] Args &&... args) const { + std::array arguments{std::forward(args)...}; + return invoke(arguments.data(), sizeof...(Args)); + } + + /** + * @brief Returns a range to use to visit all meta properties. + * @return An iterable range to use to visit all meta properties. + */ + [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { + return node->prop; + } + + /** + * @brief Returns the property associated with a given key. + * @param key The key to use to search for a property. + * @return The property associated with the given key, if any. + */ + [[nodiscard]] meta_prop prop(meta_any key) const { + internal::meta_range range{node->prop}; + return std::find_if(range.begin(), range.end(), [&key](const auto &curr) { return curr.key() == key; }).operator->(); + } + + /** + * @brief Returns true if a meta object is valid, false otherwise. + * @return True if the meta object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return !(node == nullptr); + } + +private: + const node_type *node; +}; + + +/*! @brief Opaque wrapper for meta data. */ +struct meta_data { + /*! @brief Node type. */ + using node_type = internal::meta_data_node; + + /*! @copydoc meta_prop::meta_prop */ + meta_data(const node_type *curr = nullptr) ENTT_NOEXCEPT + : node{curr} + {} + + /*! @copydoc meta_type::id */ + [[nodiscard]] id_type id() const ENTT_NOEXCEPT { + return node->id; + } + + /*! @copydoc meta_base::parent */ + [[nodiscard]] inline meta_type parent() const ENTT_NOEXCEPT; + + /** + * @brief Indicates whether a meta data is constant or not. + * @return True if the meta data is constant, false otherwise. + */ + [[nodiscard]] bool is_const() const ENTT_NOEXCEPT { + return (node->set == nullptr); + } + + /** + * @brief Indicates whether a meta data is static or not. + * @return True if the meta data is static, false otherwise. + */ + [[nodiscard]] bool is_static() const ENTT_NOEXCEPT { + return node->is_static; + } + + /*! @copydoc meta_any::type */ + [[nodiscard]] inline meta_type type() const ENTT_NOEXCEPT; + + /** + * @brief Sets the value of a given variable. + * + * It must be possible to cast the instance to the parent type of the meta + * data. Otherwise, invoking the setter results in an undefined + * behavior.
+ * The type of the value must be such that a cast or conversion to the type + * of the variable is possible. Otherwise, invoking the setter does nothing. + * + * @tparam Type Type of value to assign. + * @param instance An opaque instance of the underlying type. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ + template + bool set(meta_handle instance, Type &&value) const { + return node->set && node->set(std::move(instance), std::forward(value)); + } + + /** + * @brief Gets the value of a given variable. + * + * It must be possible to cast the instance to the parent type of the meta + * data. Otherwise, invoking the getter results in an undefined behavior. + * + * @param instance An opaque instance of the underlying type. + * @return A meta any containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(meta_handle instance) const { + return node->get(std::move(instance)); + } + + /*! @copydoc meta_ctor::prop */ + [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { + return node->prop; + } + + /** + * @brief Returns the property associated with a given key. + * @param key The key to use to search for a property. + * @return The property associated with the given key, if any. + */ + [[nodiscard]] meta_prop prop(meta_any key) const { + internal::meta_range range{node->prop}; + return std::find_if(range.begin(), range.end(), [&key](const auto &curr) { return curr.key() == key; }).operator->(); + } + + /** + * @brief Returns true if a meta object is valid, false otherwise. + * @return True if the meta object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return !(node == nullptr); + } + +private: + const node_type *node; +}; + + +/*! @brief Opaque wrapper for meta functions. */ +struct meta_func { + /*! @brief Node type. */ + using node_type = internal::meta_func_node; + /*! @brief Unsigned integer type. */ + using size_type = typename node_type::size_type; + + /*! @copydoc meta_prop::meta_prop */ + meta_func(const node_type *curr = nullptr) ENTT_NOEXCEPT + : node{curr} + {} + + /*! @copydoc meta_type::id */ + [[nodiscard]] id_type id() const ENTT_NOEXCEPT { + return node->id; + } + + /*! @copydoc meta_base::parent */ + [[nodiscard]] inline meta_type parent() const ENTT_NOEXCEPT; + + /** + * @brief Returns the number of arguments accepted by a meta function. + * @return The number of arguments accepted by the meta function. + */ + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + return node->size; + } + + /** + * @brief Indicates whether a meta function is constant or not. + * @return True if the meta function is constant, false otherwise. + */ + [[nodiscard]] bool is_const() const ENTT_NOEXCEPT { + return node->is_const; + } + + /** + * @brief Indicates whether a meta function is static or not. + * @return True if the meta function is static, false otherwise. + */ + [[nodiscard]] bool is_static() const ENTT_NOEXCEPT { + return node->is_static; + } + + /** + * @brief Returns the meta type of the return type of a meta function. + * @return The meta type of the return type of the meta function. + */ + [[nodiscard]] inline meta_type ret() const ENTT_NOEXCEPT; + + /** + * @brief Returns the meta type of the i-th argument of a meta function. + * @param index The index of the argument of which to return the meta type. + * @return The meta type of the i-th argument of a meta function, if any. + */ + [[nodiscard]] inline meta_type arg(size_type index) const ENTT_NOEXCEPT; + + /** + * @brief Invokes the underlying function, if possible. + * + * To invoke a meta function, the parameters must be such that a cast or + * conversion to the required types is possible. Otherwise, an empty and + * thus invalid wrapper is returned.
+ * It must be possible to cast the instance to the parent type of the meta + * function. Otherwise, invoking the underlying function results in an + * undefined behavior. + * + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @param sz Number of parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ + [[nodiscard]] meta_any invoke(meta_handle instance, meta_any * const args, const std::size_t sz) const { + return sz == size() ? node->invoke(instance, args) : meta_any{}; + } + + /** + * @copybrief invoke + * + * @sa invoke + * + * @tparam Args Types of arguments to use to invoke the function. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the new instance, if any. + */ + template + meta_any invoke(meta_handle instance, Args &&... args) const { + std::array arguments{std::forward(args)...}; + return invoke(instance, arguments.data(), sizeof...(Args)); + } + + /*! @copydoc meta_ctor::prop */ + [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { + return node->prop; + } + + /** + * @brief Returns the property associated with a given key. + * @param key The key to use to search for a property. + * @return The property associated with the given key, if any. + */ + [[nodiscard]] meta_prop prop(meta_any key) const { + internal::meta_range range{node->prop}; + return std::find_if(range.begin(), range.end(), [&key](const auto &curr) { return curr.key() == key; }).operator->(); + } + + /** + * @brief Returns true if a meta object is valid, false otherwise. + * @return True if the meta object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return !(node == nullptr); + } + +private: + const node_type *node; +}; + + +/*! @brief Opaque wrapper for meta types. */ +class meta_type { + template + [[nodiscard]] auto ctor(std::index_sequence) const { + internal::meta_range range{node->ctor}; + + return std::find_if(range.begin(), range.end(), [](const auto &candidate) { + return candidate.size == sizeof...(Args) && ([](auto *from, auto *to) { + return (from->type_id == to->type_id) + || internal::find_if<&node_type::base>([to](const auto *curr) { return curr->type()->type_id == to->type_id; }, from) + || internal::find_if<&node_type::conv>([to](const auto *curr) { return curr->type()->type_id == to->type_id; }, from); + }(internal::meta_info::resolve(), candidate.arg(Indexes)) && ...); + }).operator->(); + } + +public: + /*! @brief Node type. */ + using node_type = internal::meta_type_node; + /*! @brief Unsigned integer type. */ + using size_type = typename node_type::size_type; + + /*! @copydoc meta_prop::meta_prop */ + meta_type(node_type *curr = nullptr) ENTT_NOEXCEPT + : node{curr} + {} + + /** + * @brief Returns the type id of the underlying type. + * @return The type id of the underlying type. + */ + [[nodiscard]] id_type type_id() const ENTT_NOEXCEPT { + return node->type_id; + } + + /** + * @brief Returns the identifier assigned to a meta object. + * @return The identifier assigned to the meta object. + */ + [[nodiscard]] id_type id() const ENTT_NOEXCEPT { + return node->id; + } + + /** + * @brief Checks whether a type refers to void or not. + * @return True if the underlying type is void, false otherwise. + */ + [[nodiscard]] bool is_void() const ENTT_NOEXCEPT { + return node->is_void; + } + + /** + * @brief Checks whether a type refers to an integral type or not. + * @return True if the underlying type is an integral type, false otherwise. + */ + [[nodiscard]] bool is_integral() const ENTT_NOEXCEPT { + return node->is_integral; + } + + /** + * @brief Checks whether a type refers to a floating-point type or not. + * @return True if the underlying type is a floating-point type, false + * otherwise. + */ + [[nodiscard]] bool is_floating_point() const ENTT_NOEXCEPT { + return node->is_floating_point; + } + + /** + * @brief Checks whether a type refers to an array type or not. + * @return True if the underlying type is an array type, false otherwise. + */ + [[nodiscard]] bool is_array() const ENTT_NOEXCEPT { + return node->is_array; + } + + /** + * @brief Checks whether a type refers to an enum or not. + * @return True if the underlying type is an enum, false otherwise. + */ + [[nodiscard]] bool is_enum() const ENTT_NOEXCEPT { + return node->is_enum; + } + + /** + * @brief Checks whether a type refers to an union or not. + * @return True if the underlying type is an union, false otherwise. + */ + [[nodiscard]] bool is_union() const ENTT_NOEXCEPT { + return node->is_union; + } + + /** + * @brief Checks whether a type refers to a class or not. + * @return True if the underlying type is a class, false otherwise. + */ + [[nodiscard]] bool is_class() const ENTT_NOEXCEPT { + return node->is_class; + } + + /** + * @brief Checks whether a type refers to a pointer or not. + * @return True if the underlying type is a pointer, false otherwise. + */ + [[nodiscard]] bool is_pointer() const ENTT_NOEXCEPT { + return node->is_pointer; + } + + /** + * @brief Checks whether a type refers to a function pointer or not. + * @return True if the underlying type is a function pointer, false + * otherwise. + */ + [[nodiscard]] bool is_function_pointer() const ENTT_NOEXCEPT { + return node->is_function_pointer; + } + + /** + * @brief Checks whether a type refers to a pointer to data member or not. + * @return True if the underlying type is a pointer to data member, false + * otherwise. + */ + [[nodiscard]] bool is_member_object_pointer() const ENTT_NOEXCEPT { + return node->is_member_object_pointer; + } + + /** + * @brief Checks whether a type refers to a pointer to member function or + * not. + * @return True if the underlying type is a pointer to member function, + * false otherwise. + */ + [[nodiscard]] bool is_member_function_pointer() const ENTT_NOEXCEPT { + return node->is_member_function_pointer; + } + + /** + * @brief Checks whether a type is a pointer-like type or not. + * @return True if the underlying type is a pointer-like one, false + * otherwise. + */ + [[nodiscard]] bool is_pointer_like() const ENTT_NOEXCEPT { + return node->is_pointer_like; + } + + /** + * @brief Checks whether a type refers to a sequence container or not. + * @return True if the underlying type is a sequence container, false + * otherwise. + */ + [[nodiscard]] bool is_sequence_container() const ENTT_NOEXCEPT { + return node->is_sequence_container; + } + + /** + * @brief Checks whether a type refers to an associative container or not. + * @return True if the underlying type is an associative container, false + * otherwise. + */ + [[nodiscard]] bool is_associative_container() const ENTT_NOEXCEPT { + return node->is_associative_container; + } + + /** + * @brief If a type refers to an array type, provides the number of + * dimensions of the array. + * @return The number of dimensions of the array if the underlying type is + * an array type, 0 otherwise. + */ + [[nodiscard]] size_type rank() const ENTT_NOEXCEPT { + return node->rank; + } + + /** + * @brief If a type refers to an array type, provides the number of elements + * along the given dimension of the array. + * @param dim The dimension of which to return the number of elements. + * @return The number of elements along the given dimension of the array if + * the underlying type is an array type, 0 otherwise. + */ + [[nodiscard]] size_type extent(size_type dim = {}) const ENTT_NOEXCEPT { + return node->extent(dim); + } + + /** + * @brief Provides the meta type for which the pointer is defined. + * @return The meta type for which the pointer is defined or this meta type + * if it doesn't refer to a pointer type. + */ + [[nodiscard]] meta_type remove_pointer() const ENTT_NOEXCEPT { + return node->remove_pointer(); + } + + /** + * @brief Provides the meta type for which the array is defined. + * @return The meta type for which the array is defined or this meta type + * if it doesn't refer to an array type. + */ + [[nodiscard]] meta_type remove_extent() const ENTT_NOEXCEPT { + return node->remove_extent(); + } + + /** + * @brief Returns a range to use to visit top-level meta bases. + * @return An iterable range to use to visit top-level meta bases. + */ + [[nodiscard]] meta_range base() const ENTT_NOEXCEPT { + return node->base; + } + + /** + * @brief Returns the meta base associated with a given identifier. + * @param id Unique identifier. + * @return The meta base associated with the given identifier, if any. + */ + [[nodiscard]] meta_base base(const id_type id) const { + return internal::find_if<&node_type::base>([id](const auto *curr) { + return curr->type()->id == id; + }, node); + } + + /** + * @brief Returns a range to use to visit top-level meta conversion + * functions. + * @return An iterable range to use to visit top-level meta conversion + * functions. + */ + [[nodiscard]] meta_range conv() const ENTT_NOEXCEPT { + return node->conv; + } + + /** + * @brief Returns the meta conversion function associated with a given type. + * @tparam Type The type to use to search for a meta conversion function. + * @return The meta conversion function associated with the given type, if + * any. + */ + template + [[nodiscard]] meta_conv conv() const { + return internal::find_if<&node_type::conv>([type_id = internal::meta_info::resolve()->type_id](const auto *curr) { + return curr->type()->type_id == type_id; + }, node); + } + + /** + * @brief Returns a range to use to visit top-level meta constructors. + * @return An iterable range to use to visit top-level meta constructors. + */ + [[nodiscard]] meta_range ctor() const ENTT_NOEXCEPT { + return node->ctor; + } + + /** + * @brief Returns the meta constructor that accepts a given list of types of + * arguments. + * @return The requested meta constructor, if any. + */ + template + [[nodiscard]] meta_ctor ctor() const { + return ctor(std::index_sequence_for{}); + } + + /** + * @brief Returns a range to use to visit top-level meta data. + * @return An iterable range to use to visit top-level meta data. + */ + [[nodiscard]] meta_range data() const ENTT_NOEXCEPT { + return node->data; + } + + /** + * @brief Returns the meta data associated with a given identifier. + * + * The meta data of the base classes will also be visited, if any. + * + * @param id Unique identifier. + * @return The meta data associated with the given identifier, if any. + */ + [[nodiscard]] meta_data data(const id_type id) const { + return internal::find_if<&node_type::data>([id](const auto *curr) { + return curr->id == id; + }, node); + } + + /** + * @brief Returns a range to use to visit top-level meta functions. + * @return An iterable range to use to visit top-level meta functions. + */ + [[nodiscard]] meta_range func() const ENTT_NOEXCEPT { + return node->func; + } + + /** + * @brief Returns the meta function associated with a given identifier. + * + * The meta functions of the base classes will also be visited, if any. + * + * @param id Unique identifier. + * @return The meta function associated with the given identifier, if any. + */ + [[nodiscard]] meta_func func(const id_type id) const { + return internal::find_if<&node_type::func>([id](const auto *curr) { + return curr->id == id; + }, node); + } + + /** + * @brief Creates an instance of the underlying type, if possible. + * + * To create a valid instance, the parameters must be such that a cast or + * conversion to the required types is possible. Otherwise, an empty and + * thus invalid wrapper is returned. + * + * @param args Parameters to use to construct the instance. + * @param sz Number of parameters to use to construct the instance. + * @return A meta any containing the new instance, if any. + */ + [[nodiscard]] meta_any construct(meta_any * const args, const std::size_t sz) const { + meta_any any{}; + + internal::find_if<&node_type::ctor>([args, sz, &any](const auto *curr) { + return (curr->size == sz) && (any = curr->invoke(args)); + }, node); + + return any; + } + + /** + * @copybrief construct + * + * @sa construct + * + * @tparam Args Types of arguments to use to construct the instance. + * @param args Parameters to use to construct the instance. + * @return A meta any containing the new instance, if any. + */ + template + [[nodiscard]] meta_any construct(Args &&... args) const { + std::array arguments{std::forward(args)...}; + return construct(arguments.data(), sizeof...(Args)); + } + + /** + * @brief Returns a range to use to visit top-level meta properties. + * @return An iterable range to use to visit top-level meta properties. + */ + [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { + return node->prop; + } + + /** + * @brief Returns the property associated with a given key. + * + * Properties of the base classes will also be visited, if any. + * + * @param key The key to use to search for a property. + * @return The property associated with the given key, if any. + */ + [[nodiscard]] meta_prop prop(meta_any key) const { + return internal::find_if<&node_type::prop>([key = std::move(key)](const auto *curr) { + return curr->key() == key; + }, node); + } + + /** + * @brief Returns true if a meta object is valid, false otherwise. + * @return True if the meta object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return !(node == nullptr); + } + + /** + * @brief Checks if two meta objects refer to the same type. + * @param other The meta object with which to compare. + * @return True if the two meta objects refer to the same type, false + * otherwise. + */ + [[nodiscard]] bool operator==(const meta_type &other) const ENTT_NOEXCEPT { + return (!node && !other.node) || (node && other.node && node->type_id == other.node->type_id); + } + + /** + * @brief Resets a meta type and all its parts. + * + * This function resets a meta type and all its data members, member + * functions and properties, as well as its constructors, destructors and + * conversion functions if any.
+ * Base classes aren't reset but the link between the two types is removed. + * + * The meta type is also removed from the list of searchable types. + */ + void reset() ENTT_NOEXCEPT { + auto** it = internal::meta_context::global(); + + while (*it && *it != node) { + it = &(*it)->next; + } + + if(*it) { + *it = (*it)->next; + } + + const auto unregister_all = y_combinator{ + [](auto &&self, auto **curr, auto... member) { + while(*curr) { + auto *prev = *curr; + (self(&(prev->*member)), ...); + *curr = prev->next; + prev->next = nullptr; + } + } + }; + + unregister_all(&node->prop); + unregister_all(&node->base); + unregister_all(&node->conv); + unregister_all(&node->ctor, &internal::meta_ctor_node::prop); + unregister_all(&node->data, &internal::meta_data_node::prop); + unregister_all(&node->func, &internal::meta_func_node::prop); + + node->id = {}; + node->dtor = nullptr; + } + +private: + node_type *node; +}; + + +/** + * @brief Checks if two meta objects refer to the same type. + * @param lhs A meta object, either valid or not. + * @param rhs A meta object, either valid or not. + * @return False if the two meta objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_type &lhs, const meta_type &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +[[nodiscard]] inline meta_type meta_any::type() const ENTT_NOEXCEPT { + return node; +} + + +[[nodiscard]] inline meta_type meta_base::parent() const ENTT_NOEXCEPT { + return node->parent; +} + + +[[nodiscard]] inline meta_type meta_base::type() const ENTT_NOEXCEPT { + return node->type(); +} + + +[[nodiscard]] inline meta_type meta_conv::parent() const ENTT_NOEXCEPT { + return node->parent; +} + + +[[nodiscard]] inline meta_type meta_conv::type() const ENTT_NOEXCEPT { + return node->type(); +} + + +[[nodiscard]] inline meta_type meta_ctor::parent() const ENTT_NOEXCEPT { + return node->parent; +} + + +[[nodiscard]] inline meta_type meta_ctor::arg(size_type index) const ENTT_NOEXCEPT { + return index < size() ? node->arg(index) : nullptr; +} + + +[[nodiscard]] inline meta_type meta_data::parent() const ENTT_NOEXCEPT { + return node->parent; +} + + +[[nodiscard]] inline meta_type meta_data::type() const ENTT_NOEXCEPT { + return node->type(); +} + + +[[nodiscard]] inline meta_type meta_func::parent() const ENTT_NOEXCEPT { + return node->parent; +} + + +[[nodiscard]] inline meta_type meta_func::ret() const ENTT_NOEXCEPT { + return node->ret(); +} + + +[[nodiscard]] inline meta_type meta_func::arg(size_type index) const ENTT_NOEXCEPT { + return index < size() ? node->arg(index) : nullptr; +} + + +/*! @brief Opaque iterator for meta sequence containers. */ +class meta_sequence_container::meta_iterator { + /*! @brief A meta sequence container can access the underlying iterator. */ + friend class meta_sequence_container; + + template + static void incr(meta_any any) { + ++any.cast(); + } + + template + [[nodiscard]] static meta_any deref(meta_any any) { + if constexpr(std::is_const_v())>>) { + return *any.cast(); + } else { + return std::ref(*any.cast()); + } + } + +public: + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Type of elements returned by the iterator. */ + using value_type = meta_any; + /*! @brief Pointer type, `void` on purpose. */ + using pointer = void; + /*! @brief Reference type, it is **not** an actual reference. */ + using reference = value_type; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + meta_iterator() ENTT_NOEXCEPT = default; + + /** + * @brief Constructs a meta iterator from a given iterator. + * @tparam It Type of actual iterator with which to build the meta iterator. + * @param iter The actual iterator with which to build the meta iterator. + */ + template + meta_iterator(It iter) + : next_fn{&incr}, + get_fn{&deref}, + handle{std::move(iter)} + {} + + /*! @brief Pre-increment operator. @return This iterator. */ + meta_iterator & operator++() ENTT_NOEXCEPT { + return next_fn(handle.ref()), *this; + } + + /*! @brief Post-increment operator. @return This iterator. */ + meta_iterator operator++(int) ENTT_NOEXCEPT { + meta_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Checks if two meta iterators refer to the same element. + * @param other The meta iterator with which to compare. + * @return True if the two meta iterators refer to the same element, false + * otherwise. + */ + [[nodiscard]] bool operator==(const meta_iterator &other) const ENTT_NOEXCEPT { + return handle == other.handle; + } + + /** + * @brief Checks if two meta iterators refer to the same element. + * @param other The meta iterator with which to compare. + * @return False if the two meta iterators refer to the same element, true + * otherwise. + */ + [[nodiscard]] bool operator!=(const meta_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + /** + * @brief Indirection operator. + * @return The element to which the meta pointer points. + */ + [[nodiscard]] reference operator*() const { + return get_fn(handle.ref()); + } + + /** + * @brief Returns false if an iterator is invalid, true otherwise. + * @return False if the iterator is invalid, true otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return static_cast(handle); + } + +private: + void(* next_fn)(meta_any); + meta_any(* get_fn)(meta_any); + meta_any handle; +}; + + +template +struct meta_sequence_container::meta_sequence_container_proxy { + using traits_type = meta_sequence_container_traits; + + [[nodiscard]] static meta_type value_type() ENTT_NOEXCEPT { + return internal::meta_info::resolve(); + } + + [[nodiscard]] static size_type size(const void *container) ENTT_NOEXCEPT { + return traits_type::size(*static_cast(container)); + } + + [[nodiscard]] static bool resize(void *container, size_type sz) { + return traits_type::resize(*static_cast(container), sz); + } + + [[nodiscard]] static bool clear(void *container) { + return traits_type::clear(*static_cast(container)); + } + + [[nodiscard]] static iterator begin(void *container) { + return iterator{traits_type::begin(*static_cast(container))}; + } + + [[nodiscard]] static iterator end(void *container) { + return iterator{traits_type::end(*static_cast(container))}; + } + + [[nodiscard]] static std::pair insert(void *container, iterator it, meta_any value) { + if(const auto *v_ptr = value.try_cast(); v_ptr || value.convert()) { + auto ret = traits_type::insert(*static_cast(container), it.handle.cast(), v_ptr ? *v_ptr : value.cast()); + return {iterator{std::move(ret.first)}, ret.second}; + } + + return {}; + } + + [[nodiscard]] static std::pair erase(void *container, iterator it) { + auto ret = traits_type::erase(*static_cast(container), it.handle.cast()); + return {iterator{std::move(ret.first)}, ret.second}; + } + + [[nodiscard]] static meta_any get(void *container, size_type pos) { + return std::ref(traits_type::get(*static_cast(container), pos)); + } +}; + + +/** + * @brief Returns the value meta type of the wrapped container type. + * @return The value meta type of the wrapped container type. + */ +[[nodiscard]] inline meta_type meta_sequence_container::value_type() const ENTT_NOEXCEPT { + return value_type_fn(); +} + + +/** + * @brief Returns the size of the wrapped container. + * @return The size of the wrapped container. + */ +[[nodiscard]] inline meta_sequence_container::size_type meta_sequence_container::size() const ENTT_NOEXCEPT { + return size_fn(instance); +} + + +/** + * @brief Resizes the wrapped container to contain a given number of elements. + * @param sz The new size of the container. + * @return True in case of success, false otherwise. + */ +inline bool meta_sequence_container::resize(size_type sz) const { + return resize_fn(instance, sz); +} + + +/** + * @brief Clears the content of the wrapped container. + * @return True in case of success, false otherwise. + */ +inline bool meta_sequence_container::clear() { + return clear_fn(instance); +} + + +/** + * @brief Returns a meta iterator to the first element of the wrapped container. + * @return A meta iterator to the first element of the wrapped container. + */ +[[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::begin() { + return begin_fn(instance); +} + + +/** + * @brief Returns a meta iterator that is past the last element of the wrapped + * container. + * @return A meta iterator that is past the last element of the wrapped + * container. + */ +[[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::end() { + return end_fn(instance); +} + + +/** + * @brief Inserts an element at a specified location of the wrapped container. + * @param it Meta iterator before which the element will be inserted. + * @param value Element value to insert. + * @return A pair consisting of a meta iterator to the inserted element (in + * case of success) and a bool denoting whether the insertion took place. + */ +inline std::pair meta_sequence_container::insert(iterator it, meta_any value) { + return insert_fn(instance, it, value.ref()); +} + + +/** + * @brief Removes the specified element from the wrapped container. + * @param it Meta iterator to the element to remove. + * @return A pair consisting of a meta iterator following the last removed + * element (in case of success) and a bool denoting whether the insertion + * took place. + */ +inline std::pair meta_sequence_container::erase(iterator it) { + return erase_fn(instance, it); +} + + +/** + * @brief Returns a reference to the element at a specified location of the + * wrapped container (no bounds checking is performed). + * @param pos The position of the element to return. + * @return A reference to the requested element properly wrapped. + */ +[[nodiscard]] inline meta_any meta_sequence_container::operator[](size_type pos) { + return get_fn(instance, pos); +} + + +/** + * @brief Returns false if a proxy is invalid, true otherwise. + * @return False if the proxy is invalid, true otherwise. + */ +[[nodiscard]] inline meta_sequence_container::operator bool() const ENTT_NOEXCEPT { + return (instance != nullptr); +} + + +/*! @brief Opaque iterator for meta associative containers. */ +class meta_associative_container::meta_iterator { + template + static void incr(meta_any any) { + ++any.cast(); + } + + template + [[nodiscard]] static meta_any key(meta_any any) { + if constexpr(KeyOnly) { + return *any.cast(); + } else { + return any.cast()->first; + } + } + + template + [[nodiscard]] static meta_any value([[maybe_unused]] meta_any any) { + if constexpr(KeyOnly) { + return meta_any{}; + } else { + return std::ref(any.cast()->second); + } + } + +public: + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Type of elements returned by the iterator. */ + using value_type = std::pair; + /*! @brief Pointer type, `void` on purpose. */ + using pointer = void; + /*! @brief Reference type, it is **not** an actual reference. */ + using reference = value_type; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + meta_iterator() ENTT_NOEXCEPT = default; + + /** + * @brief Constructs a meta iterator from a given iterator. + * @tparam KeyOnly True if the associative container is also key-only, false + * otherwise. + * @tparam It Type of actual iterator with which to build the meta iterator. + * @param iter The actual iterator with which to build the meta iterator. + */ + template + meta_iterator(std::integral_constant, It iter) + : next_fn{&incr}, + key_fn{&key}, + value_fn{&value}, + handle{std::move(iter)} + {} + + /*! @brief Pre-increment operator. @return This iterator. */ + meta_iterator & operator++() ENTT_NOEXCEPT { + return next_fn(handle.ref()), *this; + } + + /*! @brief Post-increment operator. @return This iterator. */ + meta_iterator operator++(int) ENTT_NOEXCEPT { + meta_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Checks if two meta iterators refer to the same element. + * @param other The meta iterator with which to compare. + * @return True if the two meta iterators refer to the same element, false + * otherwise. + */ + [[nodiscard]] bool operator==(const meta_iterator &other) const ENTT_NOEXCEPT { + return handle == other.handle; + } + + /** + * @brief Checks if two meta iterators refer to the same element. + * @param other The meta iterator with which to compare. + * @return False if the two meta iterators refer to the same element, true + * otherwise. + */ + [[nodiscard]] bool operator!=(const meta_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + /** + * @brief Indirection operator. + * @return The element to which the meta pointer points. + */ + [[nodiscard]] reference operator*() const { + return { key_fn(handle.ref()), value_fn(handle.ref()) }; + } + + /** + * @brief Returns false if an iterator is invalid, true otherwise. + * @return False if the iterator is invalid, true otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return static_cast(handle); + } + +private: + void(* next_fn)(meta_any); + meta_any(* key_fn)(meta_any); + meta_any(* value_fn)(meta_any); + meta_any handle; +}; + + +template +struct meta_associative_container::meta_associative_container_proxy { + using traits_type = meta_associative_container_traits; + + [[nodiscard]] static meta_type key_type() ENTT_NOEXCEPT { + return internal::meta_info::resolve(); + } + + [[nodiscard]] static meta_type mapped_type() ENTT_NOEXCEPT { + if constexpr(is_key_only_meta_associative_container_v) { + return meta_type{}; + } else { + return internal::meta_info::resolve(); + } + } + + [[nodiscard]] static meta_type value_type() ENTT_NOEXCEPT { + return internal::meta_info::resolve(); + } + + [[nodiscard]] static size_type size(const void *container) ENTT_NOEXCEPT { + return traits_type::size(*static_cast(container)); + } + + [[nodiscard]] static bool clear(void *container) { + return traits_type::clear(*static_cast(container)); + } + + [[nodiscard]] static iterator begin(void *container) { + return iterator{is_key_only_meta_associative_container{}, traits_type::begin(*static_cast(container))}; + } + + [[nodiscard]] static iterator end(void *container) { + return iterator{is_key_only_meta_associative_container{}, traits_type::end(*static_cast(container))}; + } + + [[nodiscard]] static bool insert(void *container, meta_any key, meta_any value) { + if(const auto *k_ptr = key.try_cast(); k_ptr || key.convert()) { + if constexpr(is_key_only_meta_associative_container_v) { + return traits_type::insert(*static_cast(container), k_ptr ? *k_ptr : key.cast()); + } else { + if(auto *m_ptr = value.try_cast(); m_ptr || value.convert()) { + return traits_type::insert(*static_cast(container), k_ptr ? *k_ptr : key.cast(), m_ptr ? *m_ptr : value.cast()); + } + } + } + + return false; + } + + [[nodiscard]] static bool erase(void *container, meta_any key) { + if(const auto *k_ptr = key.try_cast(); k_ptr || key.convert()) { + return traits_type::erase(*static_cast(container), k_ptr ? *k_ptr : key.cast()); + } + + return false; + } + + [[nodiscard]] static iterator find(void *container, meta_any key) { + if(const auto *k_ptr = key.try_cast(); k_ptr || key.convert()) { + return iterator{is_key_only_meta_associative_container{}, traits_type::find(*static_cast(container), k_ptr ? *k_ptr : key.cast())}; + } + + return {}; + } +}; + + +/** + * @brief Returns true if the associative container is also key-only, false + * otherwise. + * @return True if the associative container is also key-only, false otherwise. + */ +[[nodiscard]] inline bool meta_associative_container::key_only() const ENTT_NOEXCEPT { + return key_only_container; +} + + +/** + * @brief Returns the key meta type of the wrapped container type. + * @return The key meta type of the wrapped container type. + */ +[[nodiscard]] inline meta_type meta_associative_container::key_type() const ENTT_NOEXCEPT { + return key_type_fn(); +} + + +/** + * @brief Returns the mapped meta type of the wrapped container type. + * @return The mapped meta type of the wrapped container type. + */ +[[nodiscard]] inline meta_type meta_associative_container::mapped_type() const ENTT_NOEXCEPT { + return mapped_type_fn(); +} + + +/*! @copydoc meta_sequence_container::value_type */ +[[nodiscard]] inline meta_type meta_associative_container::value_type() const ENTT_NOEXCEPT { + return value_type_fn(); +} + + +/*! @copydoc meta_sequence_container::size */ +[[nodiscard]] inline meta_associative_container::size_type meta_associative_container::size() const ENTT_NOEXCEPT { + return size_fn(instance); +} + + +/*! @copydoc meta_sequence_container::clear */ +inline bool meta_associative_container::clear() { + return clear_fn(instance); +} + + +/*! @copydoc meta_sequence_container::begin */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::begin() { + return begin_fn(instance); +} + + +/*! @copydoc meta_sequence_container::end */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::end() { + return end_fn(instance); +} + + +/** + * @brief Inserts an element (a key/value pair) into the wrapped container. + * @param key The key of the element to insert. + * @param value The value of the element to insert. + * @return A bool denoting whether the insertion took place. + */ +inline bool meta_associative_container::insert(meta_any key, meta_any value = {}) { + return insert_fn(instance, key.ref(), value.ref()); +} + + +/** + * @brief Removes the specified element from the wrapped container. + * @param key The key of the element to remove. + * @return A bool denoting whether the removal took place. + */ +inline bool meta_associative_container::erase(meta_any key) { + return erase_fn(instance, key.ref()); +} + + +/** + * @brief Returns an iterator to the element with key equivalent to a given + * one, if any. + * @param key The key of the element to search. + * @return An iterator to the element with the given key, if any. + */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::find(meta_any key) { + return find_fn(instance, key.ref()); +} + + +/** + * @brief Returns false if a proxy is invalid, true otherwise. + * @return False if the proxy is invalid, true otherwise. + */ +[[nodiscard]] inline meta_associative_container::operator bool() const ENTT_NOEXCEPT { + return (instance != nullptr); +} + + +} + + +#endif + +// #include "policy.hpp" +#ifndef ENTT_META_POLICY_HPP +#define ENTT_META_POLICY_HPP + + +namespace entt { + + +/*! @brief Empty class type used to request the _as ref_ policy. */ +struct as_ref_t {}; + + +/*! @brief Disambiguation tag. */ +inline constexpr as_ref_t as_ref; + + +/*! @brief Empty class type used to request the _as-is_ policy. */ +struct as_is_t {}; + + +/*! @brief Empty class type used to request the _as void_ policy. */ +struct as_void_t {}; + + +} + + +#endif + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +struct meta_function_helper; + + +template +struct meta_function_helper { + using return_type = std::remove_cv_t>; + using args_type = std::tuple>...>; + + static constexpr auto is_const = Const; + + [[nodiscard]] static auto arg(typename internal::meta_func_node::size_type index) ENTT_NOEXCEPT { + return std::array{{meta_info::resolve()...}}[index]; + } +}; + + +template +constexpr meta_function_helper +to_meta_function_helper(Ret(Class:: *)(Args...) const); + + +template +constexpr meta_function_helper +to_meta_function_helper(Ret(Class:: *)(Args...)); + + +template +constexpr meta_function_helper +to_meta_function_helper(Ret(*)(Args...)); + + +constexpr void to_meta_function_helper(...); + + +template +using meta_function_helper_t = decltype(to_meta_function_helper(std::declval())); + + +template +[[nodiscard]] meta_any construct(meta_any * const args, std::index_sequence) { + [[maybe_unused]] auto direct = std::make_tuple((args+Indexes)->try_cast()...); + return ((std::get(direct) || (args+Indexes)->convert()) && ...) + ? Type{(std::get(direct) ? *std::get(direct) : (args+Indexes)->cast())...} + : meta_any{}; +} + + +template +[[nodiscard]] bool setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) { + bool accepted = false; + + if constexpr(std::is_function_v>> || std::is_member_function_pointer_v) { + using helper_type = meta_function_helper_t; + using data_type = std::tuple_element_t, typename helper_type::args_type>; + + if(auto * const clazz = instance->try_cast(); clazz) { + if(auto * const direct = value.try_cast(); direct || value.convert()) { + std::invoke(Data, *clazz, direct ? *direct : value.cast()); + accepted = true; + } + } + } else if constexpr(std::is_member_object_pointer_v) { + using data_type = std::remove_cv_t().*Data)>>; + + if constexpr(!std::is_array_v) { + if(auto * const clazz = instance->try_cast(); clazz) { + if(auto * const direct = value.try_cast(); direct || value.convert()) { + std::invoke(Data, clazz) = (direct ? *direct : value.cast()); + accepted = true; + } + } + } + } else { + using data_type = std::remove_cv_t>; + + if constexpr(!std::is_array_v) { + if(auto * const direct = value.try_cast(); direct || value.convert()) { + *Data = (direct ? *direct : value.cast()); + accepted = true; + } + } + } + + return accepted; +} + + +template +[[nodiscard]] meta_any getter([[maybe_unused]] meta_handle instance) { + [[maybe_unused]] auto dispatch = [](auto &&value) { + if constexpr(std::is_same_v) { + return meta_any{std::in_place_type, std::forward(value)}; + } else if constexpr(std::is_same_v) { + return meta_any{std::ref(std::forward(value))}; + } else { + static_assert(std::is_same_v, "Policy not supported"); + return meta_any{std::forward(value)}; + } + }; + + if constexpr(std::is_function_v>> || std::is_member_function_pointer_v) { + auto * const clazz = instance->try_cast(); + return clazz ? dispatch(std::invoke(Data, *clazz)) : meta_any{}; + } else if constexpr(std::is_member_object_pointer_v) { + if constexpr(std::is_array_v().*Data)>>>) { + return meta_any{}; + } else { + auto * const clazz = instance->try_cast(); + return clazz ? dispatch(std::invoke(Data, clazz)) : meta_any{}; + } + } else if constexpr(std::is_pointer_v>) { + if constexpr(std::is_array_v>) { + return meta_any{}; + } else { + return dispatch(*Data); + } + } else { + return dispatch(Data); + } +} + + +template +[[nodiscard]] meta_any invoke([[maybe_unused]] meta_handle instance, meta_any *args, std::index_sequence) { + using helper_type = meta_function_helper_t; + + auto dispatch = [](auto *... params) { + if constexpr(std::is_void_v || std::is_same_v) { + std::invoke(Candidate, *params...); + return meta_any{std::in_place_type}; + } else if constexpr(std::is_same_v) { + return meta_any{std::ref(std::invoke(Candidate, *params...))}; + } else { + static_assert(std::is_same_v, "Policy not supported"); + return meta_any{std::invoke(Candidate, *params...)}; + } + }; + + [[maybe_unused]] const auto direct = std::make_tuple([](meta_any *any, auto *value) { + using arg_type = std::remove_reference_t; + + if(!value && any->convert()) { + value = any->try_cast(); + } + + return value; + }(args+Indexes, (args+Indexes)->try_cast>())...); + + if constexpr(std::is_function_v>>) { + return (std::get(direct) && ...) ? dispatch(std::get(direct)...) : meta_any{}; + } else { + auto * const clazz = instance->try_cast(); + return (clazz && (std::get(direct) && ...)) ? dispatch(clazz, std::get(direct)...) : meta_any{}; + } +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Meta factory to be used for reflection purposes. + * + * The meta factory is an utility class used to reflect types, data members and + * functions of all sorts. This class ensures that the underlying web of types + * is built correctly and performs some checks in debug mode to ensure that + * there are no subtle errors at runtime. + */ +template +class meta_factory; + + +/** + * @brief Extended meta factory to be used for reflection purposes. + * @tparam Type Reflected type for which the factory was created. + * @tparam Spec Property specialization pack used to disambiguate overloads. + */ +template +class meta_factory: public meta_factory { + [[nodiscard]] bool exists(const meta_any &key, const internal::meta_prop_node *node) ENTT_NOEXCEPT { + return node && (node->key() == key || exists(key, node->next)); + } + + template + void unpack(std::index_sequence, std::tuple property, Other &&... other) { + unroll(choice<3>, std::move(std::get(property))..., std::forward(other)...); + } + + template + void unroll(choice_t<3>, std::tuple property, Other &&... other) { + unpack(std::index_sequence_for{}, std::move(property), std::forward(other)...); + } + + template + void unroll(choice_t<2>, std::pair property, Other &&... other) { + assign(std::move(property.first), std::move(property.second)); + unroll(choice<3>, std::forward(other)...); + } + + template + std::enable_if_t> + unroll(choice_t<1>, Property &&property, Other &&... other) { + assign(std::forward(property)); + unroll(choice<3>, std::forward(other)...); + } + + template + void unroll(choice_t<0>, Func &&invocable, Other &&... other) { + unroll(choice<3>, std::forward(invocable)(), std::forward(other)...); + } + + template + void unroll(choice_t<0>) {} + + template + void assign(Key &&key, Value &&... value) { + static const auto property{std::make_tuple(std::forward(key), std::forward(value)...)}; + + static internal::meta_prop_node node{ + nullptr, + []() -> meta_any { + return std::get<0>(property); + }, + []() -> meta_any { + if constexpr(sizeof...(Value) == 0) { + return {}; + } else { + return std::get<1>(property); + } + } + }; + + ENTT_ASSERT(!exists(node.key(), *curr)); + node.next = *curr; + *curr = &node; + } + +public: + /** + * @brief Constructs an extended factory from a given node. + * @param target The underlying node to which to assign the properties. + */ + meta_factory(internal::meta_prop_node **target) ENTT_NOEXCEPT + : curr{target} + {} + + /** + * @brief Assigns a property to the last meta object created. + * + * Both the key and the value (if any) must be at least copy constructible. + * + * @tparam PropertyOrKey Type of the property or property key. + * @tparam Value Optional type of the property value. + * @param property_or_key Property or property key. + * @param value Optional property value. + * @return A meta factory for the parent type. + */ + template + auto prop(PropertyOrKey &&property_or_key, Value &&... value) && { + if constexpr(sizeof...(Value) == 0) { + unroll(choice<3>, std::forward(property_or_key)); + } else { + assign(std::forward(property_or_key), std::forward(value)...); + } + + return meta_factory{curr}; + } + + /** + * @brief Assigns properties to the last meta object created. + * + * Both the keys and the values (if any) must be at least copy + * constructible. + * + * @tparam Property Types of the properties. + * @param property Properties to assign to the last meta object created. + * @return A meta factory for the parent type. + */ + template + auto props(Property... property) && { + unroll(choice<3>, std::forward(property)...); + return meta_factory{curr}; + } + +private: + internal::meta_prop_node **curr; +}; + + +/** + * @brief Basic meta factory to be used for reflection purposes. + * @tparam Type Reflected type for which the factory was created. + */ +template +class meta_factory { + template + bool exists(const Node *candidate, const Node *node) ENTT_NOEXCEPT { + return node && (node == candidate || exists(candidate, node->next)); + } + + template + bool exists(const id_type id, const Node *node) ENTT_NOEXCEPT { + return node && (node->id == id || exists(id, node->next)); + } + +public: + /** + * @brief Makes a meta type _searchable_. + * @param id Optional unique identifier. + * @return An extended meta factory for the given type. + */ + auto type(const id_type id = type_info::id()) { + auto * const node = internal::meta_info::resolve(); + + ENTT_ASSERT(!exists(id, *internal::meta_context::global())); + ENTT_ASSERT(!exists(node, *internal::meta_context::global())); + node->id = id; + node->next = *internal::meta_context::global(); + *internal::meta_context::global() = node; + + return meta_factory{&node->prop}; + } + + /** + * @brief Assigns a meta base to a meta type. + * + * A reflected base class must be a real base class of the reflected type. + * + * @tparam Base Type of the base class to assign to the meta type. + * @return A meta factory for the parent type. + */ + template + auto base() ENTT_NOEXCEPT { + static_assert(std::is_base_of_v, "Invalid base type"); + auto * const type = internal::meta_info::resolve(); + + static internal::meta_base_node node{ + type, + nullptr, + &internal::meta_info::resolve, + [](const void *instance) ENTT_NOEXCEPT -> const void * { + return static_cast(static_cast(instance)); + } + }; + + ENTT_ASSERT(!exists(&node, type->base)); + node.next = type->base; + type->base = &node; + + return meta_factory{}; + } + + /** + * @brief Assigns a meta conversion function to a meta type. + * + * The given type must be such that an instance of the reflected type can be + * converted to it. + * + * @tparam To Type of the conversion function to assign to the meta type. + * @return A meta factory for the parent type. + */ + template + auto conv() ENTT_NOEXCEPT { + static_assert(std::is_convertible_v, "Could not convert to the required type"); + auto * const type = internal::meta_info::resolve(); + + static internal::meta_conv_node node{ + type, + nullptr, + &internal::meta_info::resolve, + [](const void *instance) -> meta_any { + return static_cast(*static_cast(instance)); + } + }; + + ENTT_ASSERT(!exists(&node, type->conv)); + node.next = type->conv; + type->conv = &node; + + return meta_factory{}; + } + + /** + * @brief Assigns a meta conversion function to a meta type. + * + * Conversion functions can be either free functions or member + * functions.
+ * In case of free functions, they must accept a const reference to an + * instance of the parent type as an argument. In case of member functions, + * they should have no arguments at all. + * + * @tparam Candidate The actual function to use for the conversion. + * @return A meta factory for the parent type. + */ + template + auto conv() ENTT_NOEXCEPT { + using conv_type = std::invoke_result_t; + auto * const type = internal::meta_info::resolve(); + + static internal::meta_conv_node node{ + type, + nullptr, + &internal::meta_info::resolve, + [](const void *instance) -> meta_any { + return std::invoke(Candidate, *static_cast(instance)); + } + }; + + ENTT_ASSERT(!exists(&node, type->conv)); + node.next = type->conv; + type->conv = &node; + + return meta_factory{}; + } + + /** + * @brief Assigns a meta constructor to a meta type. + * + * Free functions can be assigned to meta types in the role of constructors. + * All that is required is that they return an instance of the underlying + * type.
+ * From a client's point of view, nothing changes if a constructor of a meta + * type is a built-in one or a free function. + * + * @tparam Func The actual function to use as a constructor. + * @tparam Policy Optional policy (no policy set by default). + * @return An extended meta factory for the parent type. + */ + template + auto ctor() ENTT_NOEXCEPT { + using helper_type = internal::meta_function_helper_t; + static_assert(std::is_same_v, "The function doesn't return an object of the required type"); + auto * const type = internal::meta_info::resolve(); + + static internal::meta_ctor_node node{ + type, + nullptr, + nullptr, + std::tuple_size_v, + &helper_type::arg, + [](meta_any * const any) { + return internal::invoke({}, any, std::make_index_sequence>{}); + } + }; + + ENTT_ASSERT(!exists(&node, type->ctor)); + node.next = type->ctor; + type->ctor = &node; + + return meta_factory>{&node.prop}; + } + + /** + * @brief Assigns a meta constructor to a meta type. + * + * A meta constructor is uniquely identified by the types of its arguments + * and is such that there exists an actual constructor of the underlying + * type that can be invoked with parameters whose types are those given. + * + * @tparam Args Types of arguments to use to construct an instance. + * @return An extended meta factory for the parent type. + */ + template + auto ctor() ENTT_NOEXCEPT { + using helper_type = internal::meta_function_helper_t; + auto * const type = internal::meta_info::resolve(); + + static internal::meta_ctor_node node{ + type, + nullptr, + nullptr, + std::tuple_size_v, + &helper_type::arg, + [](meta_any * const any) { + return internal::construct>...>(any, std::make_index_sequence>{}); + } + }; + + ENTT_ASSERT(!exists(&node, type->ctor)); + node.next = type->ctor; + type->ctor = &node; + + return meta_factory{&node.prop}; + } + + /** + * @brief Assigns a meta destructor to a meta type. + * + * Free functions can be assigned to meta types in the role of destructors. + * The signature of the function should identical to the following: + * + * @code{.cpp} + * void(Type &); + * @endcode + * + * The purpose is to give users the ability to free up resources that + * require special treatment before an object is actually destroyed. + * + * @tparam Func The actual function to use as a destructor. + * @return A meta factory for the parent type. + */ + template + auto dtor() ENTT_NOEXCEPT { + static_assert(std::is_invocable_v, "The function doesn't accept an object of the type provided"); + auto * const type = internal::meta_info::resolve(); + + ENTT_ASSERT(!type->dtor); + + type->dtor = [](void *instance) { + if(instance) { + std::invoke(Func, *static_cast(instance)); + } + }; + + return meta_factory{}; + } + + /** + * @brief Assigns a meta data to a meta type. + * + * Both data members and static and global variables, as well as constants + * of any kind, can be assigned to a meta type.
+ * From a client's point of view, all the variables associated with the + * reflected object will appear as if they were part of the type itself. + * + * @tparam Data The actual variable to attach to the meta type. + * @tparam Policy Optional policy (no policy set by default). + * @param id Unique identifier. + * @return An extended meta factory for the parent type. + */ + template + auto data(const id_type id) ENTT_NOEXCEPT { + if constexpr(std::is_member_object_pointer_v) { + return data(id); + } else { + using data_type = std::remove_pointer_t>; + auto * const type = internal::meta_info::resolve(); + + static internal::meta_data_node node{ + {}, + type, + nullptr, + nullptr, + true, + &internal::meta_info::resolve, + []() -> std::remove_const_t { + if constexpr(std::is_same_v || std::is_const_v) { + return nullptr; + } else { + return &internal::setter; + } + }(), + &internal::getter + }; + + ENTT_ASSERT(!exists(id, type->data)); + ENTT_ASSERT(!exists(&node, type->data)); + node.id = id; + node.next = type->data; + type->data = &node; + + return meta_factory>{&node.prop}; + } + } + + /** + * @brief Assigns a meta data to a meta type by means of its setter and + * getter. + * + * Setters and getters can be either free functions, member functions or a + * mix of them.
+ * In case of free functions, setters and getters must accept a reference to + * an instance of the parent type as their first argument. A setter has then + * an extra argument of a type convertible to that of the parameter to + * set.
+ * In case of member functions, getters have no arguments at all, while + * setters has an argument of a type convertible to that of the parameter to + * set. + * + * @tparam Setter The actual function to use as a setter. + * @tparam Getter The actual function to use as a getter. + * @tparam Policy Optional policy (no policy set by default). + * @param id Unique identifier. + * @return An extended meta factory for the parent type. + */ + template + auto data(const id_type id) ENTT_NOEXCEPT { + using underlying_type = std::remove_reference_t>; + auto * const type = internal::meta_info::resolve(); + + static internal::meta_data_node node{ + {}, + type, + nullptr, + nullptr, + false, + &internal::meta_info::resolve, + []() -> std::remove_const_t { + if constexpr(std::is_same_v || (std::is_member_object_pointer_v && std::is_const_v)) { + return nullptr; + } else { + return &internal::setter; + } + }(), + &internal::getter + }; + + ENTT_ASSERT(!exists(id, type->data)); + ENTT_ASSERT(!exists(&node, type->data)); + node.id = id; + node.next = type->data; + type->data = &node; + + return meta_factory, std::integral_constant>{&node.prop}; + } + + /** + * @brief Assigns a meta funcion to a meta type. + * + * Both member functions and free functions can be assigned to a meta + * type.
+ * From a client's point of view, all the functions associated with the + * reflected object will appear as if they were part of the type itself. + * + * @tparam Candidate The actual function to attach to the meta type. + * @tparam Policy Optional policy (no policy set by default). + * @param id Unique identifier. + * @return An extended meta factory for the parent type. + */ + template + auto func(const id_type id) ENTT_NOEXCEPT { + using helper_type = internal::meta_function_helper_t; + auto * const type = internal::meta_info::resolve(); + + static internal::meta_func_node node{ + {}, + type, + nullptr, + nullptr, + std::tuple_size_v, + helper_type::is_const, + !std::is_member_function_pointer_v, + &internal::meta_info, void, typename helper_type::return_type>>::resolve, + &helper_type::arg, + [](meta_handle instance, meta_any *args) { + return internal::invoke(*instance, args, std::make_index_sequence>{}); + } + }; + + ENTT_ASSERT(!exists(id, type->func)); + ENTT_ASSERT(!exists(&node, type->func)); + node.id = id; + node.next = type->func; + type->func = &node; + + return meta_factory>{&node.prop}; + } +}; + + +/** + * @brief Utility function to use for reflection. + * + * This is the point from which everything starts.
+ * By invoking this function with a type that is not yet reflected, a meta type + * is created to which it will be possible to attach meta objects through a + * dedicated factory. + * + * @tparam Type Type to reflect. + * @return A meta factory for the given type. + */ +template +[[nodiscard]] auto meta() ENTT_NOEXCEPT { + auto * const node = internal::meta_info::resolve(); + // extended meta factory to allow assigning properties to opaque meta types + return meta_factory{&node->prop}; +} + + +} + + +#endif + +// #include "meta/internal.hpp" + +// #include "meta/meta.hpp" + +// #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 Makes `std::shared_ptr`s of any type pointer-like types for the meta + * system. + * @tparam Type Element type. + */ +template +struct is_meta_pointer_like> + : std::true_type +{}; + + +/** + * @brief Makes `std::unique_ptr`s of any type pointer-like types for the meta + * system. + * @tparam Type Element type. + * @tparam Args Other arguments. + */ +template +struct is_meta_pointer_like> + : std::true_type +{}; + + +} + + +#endif + +// #include "meta/policy.hpp" + +// #include "meta/range.hpp" + +// #include "meta/resolve.hpp" +#ifndef ENTT_META_RESOLVE_HPP +#define ENTT_META_RESOLVE_HPP + + +#include +// #include "ctx.hpp" + +// #include "meta.hpp" + +// #include "range.hpp" + + + +namespace entt { + + +/** + * @brief Returns the meta type associated with a given type. + * @tparam Type Type to use to search for a meta type. + * @return The meta type associated with the given type, if any. + */ +template +[[nodiscard]] meta_type resolve() ENTT_NOEXCEPT { + return internal::meta_info::resolve(); +} + + +/** + * @brief Returns a range to use to visit all meta types. + * @return An iterable range to use to visit all meta types. + */ +[[nodiscard]] inline meta_range resolve() { + return *internal::meta_context::global(); +} + + +/** + * @brief Returns the meta type associated with a given identifier, if any. + * @param id Unique identifier. + * @return The meta type associated with the given identifier, if any. + */ +[[nodiscard]] inline meta_type resolve_id(const id_type id) ENTT_NOEXCEPT { + internal::meta_range range{*internal::meta_context::global()}; + return std::find_if(range.begin(), range.end(), [id](const auto &curr) { return curr.id == id; }).operator->(); +} + + +/** + * @brief Returns the meta type associated with a given type id, if any. + * @param id Unique identifier. + * @return The meta type associated with the given type id, if any. + */ +[[nodiscard]] inline meta_type resolve_type(const id_type id) ENTT_NOEXCEPT { + internal::meta_range range{*internal::meta_context::global()}; + return std::find_if(range.begin(), range.end(), [id](const auto &curr) { return curr.type_id == id; }).operator->(); +} + + +} + + +#endif + +// #include "meta/type_traits.hpp" + +// #include "platform/android-ndk-r17.hpp" +#ifndef ENTT_PLATFORM_ANDROID_NDK_R17_HPP +#define ENTT_PLATFORM_ANDROID_NDK_R17_HPP + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +#ifdef __ANDROID__ +#include +#if __NDK_MAJOR__ == 17 + + +#include +#include +#include + + +namespace std { + + +namespace internal { + + +template +constexpr auto is_invocable(int) -> decltype(std::invoke(std::declval(), std::declval()...), std::true_type{}); + + +template +constexpr std::false_type is_invocable(...); + + +template +constexpr auto is_invocable_r(int) +-> std::enable_if_t(), std::declval()...)), Ret>, std::true_type>; + + +template +constexpr std::false_type is_invocable_r(...); + + +} + + +template +struct is_invocable: decltype(internal::is_invocable(0)) {}; + + +template +inline constexpr bool is_invocable_v = std::is_invocable::value; + + +template +struct is_invocable_r: decltype(internal::is_invocable_r(0)) {}; + + +template +inline constexpr bool is_invocable_r_v = std::is_invocable_r::value; + + +template +struct invoke_result { + using type = decltype(std::invoke(std::declval(), std::declval()...)); +}; + + +template +using invoke_result_t = typename std::invoke_result::type; + + +} + + +#endif +#endif + + +/** + * Internal details not to be documented. + * @endcond + */ + + +#endif + +// #include "process/process.hpp" +#ifndef ENTT_PROCESS_PROCESS_HPP +#define ENTT_PROCESS_PROCESS_HPP + + +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_HS_SUFFIX +# define ENTT_HS_SUFFIX _hs +#endif + + +#ifndef ENTT_HWS_SUFFIX +# define ENTT_HWS_SUFFIX _hws +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifndef ENTT_PAGE_SIZE +# define ENTT_PAGE_SIZE 32768 +#endif + + +#ifndef ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty +#else +# include +# define ENTT_IS_EMPTY(Type) std::false_type +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) +# define ENTT_PRETTY_FUNCTION_CONSTEXPR +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif 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_CONSTEXPR +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + + +#ifndef ENTT_STANDALONE +# define ENTT_FAST_PATH(...) false +#else +# define ENTT_FAST_PATH(Cond) Cond +#endif + + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + + +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_HS_SUFFIX +# define ENTT_HS_SUFFIX _hs +#endif + + +#ifndef ENTT_HWS_SUFFIX +# define ENTT_HWS_SUFFIX _hws +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifndef ENTT_PAGE_SIZE +# define ENTT_PAGE_SIZE 32768 +#endif + + +#ifndef ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty +#else +# include +# define ENTT_IS_EMPTY(Type) std::false_type +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) +# define ENTT_PRETTY_FUNCTION_CONSTEXPR +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif 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_CONSTEXPR +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + + +#ifndef ENTT_STANDALONE +# define ENTT_FAST_PATH(...) false +#else +# define ENTT_FAST_PATH(Cond) Cond +#endif + + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + + +// #include "../config/config.h" + + + +namespace entt { + + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + + +} + + +#endif + + + +namespace entt { + + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_t = Type; + + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_v = Value; + + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + + +/** + * @brief Alias template to ease the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + + +/** + * @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 A class to use to push around lists of types, nothing more. */ +template +struct type_list {}; + + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_size; + + +/** + * @brief Compile-time number of elements in a type list. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_size> + : std::integral_constant +{}; + + +/** + * @brief Helper variable template. + * @tparam List Type list. + */ +template +inline constexpr auto type_list_size_v = type_list_size::value; + + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + std::disjunction_v...>, + typename type_list_unique>::type, + type_list_cat_t, typename type_list_unique>::type> + >; +}; + + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = type_list<>; +}; + + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type Potentially equality comparable type. + */ +template> +struct is_equality_comparable: std::false_type {}; + + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable() == std::declval())>> + : std::true_type +{}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially equality comparable type. + */ +template +inline constexpr auto is_equality_comparable_v = is_equality_comparable::value; + + +/** + * @brief Provides the member constant `value` to true if a given type is empty + * and the empty type optimization is enabled, false otherwise. + * @tparam Type Potential empty type. + */ +template +struct is_eto_eligible + : ENTT_IS_EMPTY(Type) +{}; + + +/** + * @brief Helper variable template. + * @tparam Type Potential empty type. + */ +template +inline constexpr auto is_eto_eligible_v = is_eto_eligible::value; + + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class * clazz(Ret(Class:: *)(Args...)); + + template + static Class * clazz(Ret(Class:: *)(Args...) const); + + template + static Class * clazz(Type Class:: *); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + + +} + + +#endif + + + +namespace entt { + + +/** + * @brief Base class for processes. + * + * This class stays true to the CRTP idiom. Derived classes must specify what's + * the intended type for elapsed times.
+ * A process should expose publicly the following member functions whether + * required: + * + * * @code{.cpp} + * void update(Delta, void *); + * @endcode + * + * It's invoked once per tick until a process is explicitly aborted or it + * terminates either with or without errors. Even though it's not mandatory to + * declare this member function, as a rule of thumb each process should at + * least define it to work properly. The `void *` parameter is an opaque + * pointer to user data (if any) forwarded directly to the process during an + * update. + * + * * @code{.cpp} + * void init(); + * @endcode + * + * It's invoked when the process joins the running queue of a scheduler. This + * happens as soon as it's attached to the scheduler if the process is a top + * level one, otherwise when it replaces its parent if the process is a + * continuation. + * + * * @code{.cpp} + * void succeeded(); + * @endcode + * + * It's invoked in case of success, immediately after an update and during the + * same tick. + * + * * @code{.cpp} + * void failed(); + * @endcode + * + * It's invoked in case of errors, immediately after an update and during the + * same tick. + * + * * @code{.cpp} + * void aborted(); + * @endcode + * + * It's invoked only if a process is explicitly aborted. There is no guarantee + * that it executes in the same tick, this depends solely on whether the + * process is aborted immediately or not. + * + * Derived classes can change the internal state of a process by invoking the + * `succeed` and `fail` protected member functions and even pause or unpause the + * process itself. + * + * @sa scheduler + * + * @tparam Derived Actual type of process that extends the class template. + * @tparam Delta Type to use to provide elapsed time. + */ +template +class process { + enum class state: unsigned int { + UNINITIALIZED = 0, + RUNNING, + PAUSED, + SUCCEEDED, + FAILED, + ABORTED, + FINISHED + }; + + template + auto next(integral_constant) + -> decltype(std::declval().init(), void()) { + static_cast(this)->init(); + } + + template + auto next(integral_constant, Delta delta, void *data) + -> decltype(std::declval().update(delta, data), void()) { + static_cast(this)->update(delta, data); + } + + template + auto next(integral_constant) + -> decltype(std::declval().succeeded(), void()) { + static_cast(this)->succeeded(); + } + + template + auto next(integral_constant) + -> decltype(std::declval().failed(), void()) { + static_cast(this)->failed(); + } + + template + auto next(integral_constant) + -> decltype(std::declval().aborted(), void()) { + static_cast(this)->aborted(); + } + + void next(...) const ENTT_NOEXCEPT {} + +protected: + /** + * @brief Terminates a process with success if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + */ + void succeed() ENTT_NOEXCEPT { + if(alive()) { + current = state::SUCCEEDED; + } + } + + /** + * @brief Terminates a process with errors if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + */ + void fail() ENTT_NOEXCEPT { + if(alive()) { + current = state::FAILED; + } + } + + /** + * @brief Stops a process if it's in a running state. + * + * The function is idempotent and it does nothing if the process isn't + * running. + */ + void pause() ENTT_NOEXCEPT { + if(current == state::RUNNING) { + current = state::PAUSED; + } + } + + /** + * @brief Restarts a process if it's paused. + * + * The function is idempotent and it does nothing if the process isn't + * paused. + */ + void unpause() ENTT_NOEXCEPT { + if(current == state::PAUSED) { + current = state::RUNNING; + } + } + +public: + /*! @brief Type used to provide elapsed time. */ + using delta_type = Delta; + + /*! @brief Default destructor. */ + virtual ~process() { + static_assert(std::is_base_of_v, "Incorrect use of the class template"); + } + + /** + * @brief Aborts a process if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + * + * @param immediately Requests an immediate operation. + */ + void abort(const bool immediately = false) { + if(alive()) { + current = state::ABORTED; + + if(immediately) { + tick({}); + } + } + } + + /** + * @brief Returns true if a process is either running or paused. + * @return True if the process is still alive, false otherwise. + */ + [[nodiscard]] bool alive() const ENTT_NOEXCEPT { + return current == state::RUNNING || current == state::PAUSED; + } + + /** + * @brief Returns true if a process is already terminated. + * @return True if the process is terminated, false otherwise. + */ + [[nodiscard]] bool dead() const ENTT_NOEXCEPT { + return current == state::FINISHED; + } + + /** + * @brief Returns true if a process is currently paused. + * @return True if the process is paused, false otherwise. + */ + [[nodiscard]] bool paused() const ENTT_NOEXCEPT { + return current == state::PAUSED; + } + + /** + * @brief Returns true if a process terminated with errors. + * @return True if the process terminated with errors, false otherwise. + */ + [[nodiscard]] bool rejected() const ENTT_NOEXCEPT { + return stopped; + } + + /** + * @brief Updates a process and its internal state if required. + * @param delta Elapsed time. + * @param data Optional data. + */ + void tick(const Delta delta, void *data = nullptr) { + switch (current) { + case state::UNINITIALIZED: + next(integral_constant{}); + current = state::RUNNING; + break; + case state::RUNNING: + next(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(integral_constant{}); + current = state::FINISHED; + break; + case state::FAILED: + next(integral_constant{}); + current = state::FINISHED; + stopped = true; + break; + case state::ABORTED: + next(integral_constant{}); + current = state::FINISHED; + stopped = true; + break; + default: + // suppress warnings + break; + } + } + +private: + state current{state::UNINITIALIZED}; + bool stopped{false}; +}; + + +/** + * @brief Adaptor for lambdas and functors to turn them into processes. + * + * Lambdas and functors can't be used directly with a scheduler for they are not + * properly defined processes with managed life cycles.
+ * This class helps in filling the gap and turning lambdas and functors into + * full featured processes usable by a scheduler. + * + * The signature of the function call operator should be equivalent to the + * following: + * + * @code{.cpp} + * void(Delta delta, void *data, auto succeed, auto fail); + * @endcode + * + * Where: + * + * * `delta` is the elapsed time. + * * `data` is an opaque pointer to user data if any, `nullptr` otherwise. + * * `succeed` is a function to call when a process terminates with success. + * * `fail` is a function to call when a process terminates with errors. + * + * The signature of the function call operator of both `succeed` and `fail` + * is equivalent to the following: + * + * @code{.cpp} + * void(); + * @endcode + * + * Usually users shouldn't worry about creating adaptors. A scheduler will + * create them internally each and avery time a lambda or a functor is used as + * a process. + * + * @sa process + * @sa scheduler + * + * @tparam Func Actual type of process. + * @tparam Delta Type to use to provide elapsed time. + */ +template +struct process_adaptor: process, Delta>, private Func { + /** + * @brief Constructs a process adaptor from a lambda or a functor. + * @tparam Args Types of arguments to use to initialize the actual process. + * @param args Parameters to use to initialize the actual process. + */ + template + process_adaptor(Args &&... args) + : Func{std::forward(args)...} + {} + + /** + * @brief Updates a process and its internal state if required. + * @param delta Elapsed time. + * @param data Optional data. + */ + void update(const Delta delta, void *data) { + Func::operator()(delta, data, [this]() { this->succeed(); }, [this]() { this->fail(); }); + } +}; + + +} + + +#endif + +// #include "process/scheduler.hpp" +#ifndef ENTT_PROCESS_SCHEDULER_HPP +#define ENTT_PROCESS_SCHEDULER_HPP + + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "process.hpp" + + + +namespace entt { + + +/** + * @brief Cooperative scheduler for processes. + * + * A cooperative scheduler runs processes and helps managing their life cycles. + * + * Each process is invoked once per tick. If a process terminates, it's + * removed automatically from the scheduler and it's never invoked again.
+ * A process can also have a child. In this case, the process is replaced with + * its child when it terminates if it returns with success. In case of errors, + * both the process and its child are discarded. + * + * Example of use (pseudocode): + * + * @code{.cpp} + * scheduler.attach([](auto delta, void *, auto succeed, auto fail) { + * // code + * }).then(arguments...); + * @endcode + * + * In order to invoke all scheduled processes, call the `update` member function + * passing it the elapsed time to forward to the tasks. + * + * @sa process + * + * @tparam Delta Type to use to provide elapsed time. + */ +template +class scheduler { + struct process_handler { + using instance_type = std::unique_ptr; + using update_fn_type = bool(process_handler &, Delta, void *); + using abort_fn_type = void(process_handler &, bool); + using next_type = std::unique_ptr; + + instance_type instance; + update_fn_type *update; + abort_fn_type *abort; + next_type next; + }; + + struct continuation { + continuation(process_handler *ref) + : handler{ref} + { + ENTT_ASSERT(handler); + } + + template + continuation then(Args &&... args) { + static_assert(std::is_base_of_v, Proc>, "Invalid process type"); + auto proc = typename process_handler::instance_type{new Proc{std::forward(args)...}, &scheduler::deleter}; + handler->next.reset(new process_handler{std::move(proc), &scheduler::update, &scheduler::abort, nullptr}); + handler = handler->next.get(); + return *this; + } + + template + continuation then(Func &&func) { + return then, Delta>>(std::forward(func)); + } + + private: + process_handler *handler; + }; + + template + [[nodiscard]] static bool update(process_handler &handler, const Delta delta, void *data) { + auto *process = static_cast(handler.instance.get()); + process->tick(delta, data); + + auto dead = process->dead(); + + if(dead) { + if(handler.next && !process->rejected()) { + handler = std::move(*handler.next); + // forces the process to exit the uninitialized state + dead = handler.update(handler, {}, nullptr); + } else { + handler.instance.reset(); + } + } + + return dead; + } + + template + static void abort(process_handler &handler, const bool immediately) { + static_cast(handler.instance.get())->abort(immediately); + } + + template + static void deleter(void *proc) { + delete static_cast(proc); + } + +public: + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + + /*! @brief Default constructor. */ + scheduler() = default; + + /*! @brief Default move constructor. */ + scheduler(scheduler &&) = default; + + /*! @brief Default move assignment operator. @return This scheduler. */ + scheduler & operator=(scheduler &&) = default; + + /** + * @brief Number of processes currently scheduled. + * @return Number of processes currently scheduled. + */ + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + return handlers.size(); + } + + /** + * @brief Returns true if at least a process is currently scheduled. + * @return True if there are scheduled processes, false otherwise. + */ + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return handlers.empty(); + } + + /** + * @brief Discards all scheduled processes. + * + * Processes aren't aborted. They are discarded along with their children + * and never executed again. + */ + void clear() { + handlers.clear(); + } + + /** + * @brief Schedules a process for the next tick. + * + * Returned value is an opaque object that can be used to attach a child to + * the given process. The child is automatically scheduled when the process + * terminates and only if the process returns with success. + * + * Example of use (pseudocode): + * + * @code{.cpp} + * // schedules a task in the form of a process class + * scheduler.attach(arguments...) + * // appends a child in the form of a lambda function + * .then([](auto delta, void *, auto succeed, auto fail) { + * // code + * }) + * // appends a child in the form of another process class + * .then(); + * @endcode + * + * @tparam Proc Type of process to schedule. + * @tparam Args Types of arguments to use to initialize the process. + * @param args Parameters to use to initialize the process. + * @return An opaque object to use to concatenate processes. + */ + template + auto attach(Args &&... args) { + static_assert(std::is_base_of_v, Proc>, "Invalid process type"); + auto proc = typename process_handler::instance_type{new Proc{std::forward(args)...}, &scheduler::deleter}; + process_handler handler{std::move(proc), &scheduler::update, &scheduler::abort, nullptr}; + // forces the process to exit the uninitialized state + handler.update(handler, {}, nullptr); + return continuation{&handlers.emplace_back(std::move(handler))}; + } + + /** + * @brief Schedules a process for the next tick. + * + * A process can be either a lambda or a functor. The scheduler wraps both + * of them in a process adaptor internally.
+ * The signature of the function call operator should be equivalent to the + * following: + * + * @code{.cpp} + * void(Delta delta, void *data, auto succeed, auto fail); + * @endcode + * + * Where: + * + * * `delta` is the elapsed time. + * * `data` is an opaque pointer to user data if any, `nullptr` otherwise. + * * `succeed` is a function to call when a process terminates with success. + * * `fail` is a function to call when a process terminates with errors. + * + * The signature of the function call operator of both `succeed` and `fail` + * is equivalent to the following: + * + * @code{.cpp} + * void(); + * @endcode + * + * Returned value is an opaque object that can be used to attach a child to + * the given process. The child is automatically scheduled when the process + * terminates and only if the process returns with success. + * + * Example of use (pseudocode): + * + * @code{.cpp} + * // schedules a task in the form of a lambda function + * scheduler.attach([](auto delta, void *, auto succeed, auto fail) { + * // code + * }) + * // appends a child in the form of another lambda function + * .then([](auto delta, void *, auto succeed, auto fail) { + * // code + * }) + * // appends a child in the form of a process class + * .then(arguments...); + * @endcode + * + * @sa process_adaptor + * + * @tparam Func Type of process to schedule. + * @param func Either a lambda or a functor to use as a process. + * @return An opaque object to use to concatenate processes. + */ + template + auto attach(Func &&func) { + using Proc = process_adaptor, Delta>; + return attach(std::forward(func)); + } + + /** + * @brief Updates all scheduled processes. + * + * All scheduled processes are executed in no specific order.
+ * If a process terminates with success, it's replaced with its child, if + * any. Otherwise, if a process terminates with an error, it's removed along + * with its child. + * + * @param delta Elapsed time. + * @param data Optional data. + */ + void update(const Delta delta, void *data = nullptr) { + bool clean = false; + + for(auto pos = handlers.size(); pos; --pos) { + auto &handler = handlers[pos-1]; + const bool dead = handler.update(handler, delta, data); + clean = clean || dead; + } + + if(clean) { + handlers.erase(std::remove_if(handlers.begin(), handlers.end(), [](auto &handler) { + return !handler.instance; + }), handlers.end()); + } + } + + /** + * @brief Aborts all scheduled processes. + * + * Unless an immediate operation is requested, the abort is scheduled for + * the next tick. Processes won't be executed anymore in any case.
+ * Once a process is fully aborted and thus finished, it's discarded along + * with its child, if any. + * + * @param immediately Requests an immediate operation. + */ + void abort(const bool immediately = false) { + decltype(handlers) exec; + exec.swap(handlers); + + for(auto &&handler: exec) { + handler.abort(handler, immediately); + } + + std::move(handlers.begin(), handlers.end(), std::back_inserter(exec)); + handlers.swap(exec); + } + +private: + std::vector handlers{}; +}; + + +} + + +#endif + +// #include "resource/cache.hpp" +#ifndef ENTT_RESOURCE_CACHE_HPP +#define ENTT_RESOURCE_CACHE_HPP + + +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_HS_SUFFIX +# define ENTT_HS_SUFFIX _hs +#endif + + +#ifndef ENTT_HWS_SUFFIX +# define ENTT_HWS_SUFFIX _hws +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifndef ENTT_PAGE_SIZE +# define ENTT_PAGE_SIZE 32768 +#endif + + +#ifndef ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty +#else +# include +# define ENTT_IS_EMPTY(Type) std::false_type +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) +# define ENTT_PRETTY_FUNCTION_CONSTEXPR +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif 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_CONSTEXPR +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + + +#ifndef ENTT_STANDALONE +# define ENTT_FAST_PATH(...) false +#else +# define ENTT_FAST_PATH(Cond) Cond +#endif + + +#endif + +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + + +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_HS_SUFFIX +# define ENTT_HS_SUFFIX _hs +#endif + + +#ifndef ENTT_HWS_SUFFIX +# define ENTT_HWS_SUFFIX _hws +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifndef ENTT_PAGE_SIZE +# define ENTT_PAGE_SIZE 32768 +#endif + + +#ifndef ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty +#else +# include +# define ENTT_IS_EMPTY(Type) std::false_type +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) +# define ENTT_PRETTY_FUNCTION_CONSTEXPR +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif 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_CONSTEXPR +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + + +#ifndef ENTT_STANDALONE +# define ENTT_FAST_PATH(...) false +#else +# define ENTT_FAST_PATH(Cond) Cond +#endif + + +#endif + + + +namespace entt { + + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + + +} + + +#endif + +// #include "handle.hpp" +#ifndef ENTT_RESOURCE_HANDLE_HPP +#define ENTT_RESOURCE_HANDLE_HPP + + +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" +#ifndef ENTT_RESOURCE_FWD_HPP +#define ENTT_RESOURCE_FWD_HPP + + +namespace entt { + + +template +struct resource_cache; + + +template +class resource_handle; + + +template +class resource_loader; + + +} + + +#endif + + + +namespace entt { + + +/** + * @brief Shared resource handle. + * + * A shared resource handle is a small class that wraps a resource and keeps it + * alive even if it's deleted from the cache. It can be either copied or + * moved. A handle shares a reference to the same resource with all the other + * handles constructed for the same identifier.
+ * As a rule of thumb, resources should never be copied nor moved. Handles are + * the way to go to keep references to them. + * + * @tparam Resource Type of resource managed by a handle. + */ +template +class resource_handle { + /*! @brief Resource handles are friends of their caches. */ + friend struct resource_cache; + + resource_handle(std::shared_ptr res) ENTT_NOEXCEPT + : resource{std::move(res)} + {} + +public: + /*! @brief Default constructor. */ + resource_handle() ENTT_NOEXCEPT = default; + + /** + * @brief Gets a reference to the managed resource. + * + * @warning + * The behavior is undefined if the handle doesn't contain a resource.
+ * An assertion will abort the execution at runtime in debug mode if the + * handle is empty. + * + * @return A reference to the managed resource. + */ + [[nodiscard]] const Resource & get() const ENTT_NOEXCEPT { + ENTT_ASSERT(static_cast(resource)); + return *resource; + } + + /*! @copydoc get */ + [[nodiscard]] Resource & get() ENTT_NOEXCEPT { + return const_cast(std::as_const(*this).get()); + } + + /*! @copydoc get */ + [[nodiscard]] operator const Resource & () const ENTT_NOEXCEPT { + return get(); + } + + /*! @copydoc get */ + [[nodiscard]] operator Resource & () ENTT_NOEXCEPT { + return get(); + } + + /*! @copydoc get */ + [[nodiscard]] const Resource & operator *() const ENTT_NOEXCEPT { + return get(); + } + + /*! @copydoc get */ + [[nodiscard]] Resource & operator *() ENTT_NOEXCEPT { + return get(); + } + + /** + * @brief Gets a pointer to the managed resource. + * + * @warning + * The behavior is undefined if the handle doesn't contain a resource.
+ * An assertion will abort the execution at runtime in debug mode if the + * handle is empty. + * + * @return A pointer to the managed resource or `nullptr` if the handle + * contains no resource at all. + */ + [[nodiscard]] const Resource * operator->() const ENTT_NOEXCEPT { + ENTT_ASSERT(static_cast(resource)); + return resource.get(); + } + + /*! @copydoc operator-> */ + [[nodiscard]] Resource * operator->() ENTT_NOEXCEPT { + return const_cast(std::as_const(*this).operator->()); + } + + /** + * @brief Returns true if a handle contains a resource, false otherwise. + * @return True if the handle contains a resource, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return static_cast(resource); + } + +private: + std::shared_ptr resource; +}; + + +} + + +#endif + +// #include "loader.hpp" +#ifndef ENTT_RESOURCE_LOADER_HPP +#define ENTT_RESOURCE_LOADER_HPP + + +#include +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Base class for resource loaders. + * + * Resource loaders must inherit from this class and stay true to the CRTP + * idiom. Moreover, a resource loader must expose a public, const member + * function named `load` that accepts a variable number of arguments and returns + * a shared pointer to the resource just created.
+ * As an example: + * + * @code{.cpp} + * struct my_resource {}; + * + * struct my_loader: entt::resource_loader { + * std::shared_ptr load(int) const { + * // use the integer value somehow + * return std::make_shared(); + * } + * }; + * @endcode + * + * In general, resource loaders should not have a state or retain data of any + * type. They should let the cache manage their resources instead. + * + * @note + * Base class and CRTP idiom aren't strictly required with the current + * implementation. One could argue that a cache can easily work with loaders of + * any type. However, future changes won't be breaking ones by forcing the use + * of a base class today and that's why the model is already in its place. + * + * @tparam Loader Type of the derived class. + * @tparam Resource Type of resource for which to use the loader. + */ +template +class resource_loader { + /*! @brief Resource loaders are friends of their caches. */ + friend struct resource_cache; + + /** + * @brief Loads the resource and returns it. + * @tparam Args Types of arguments for the loader. + * @param args Arguments for the loader. + * @return The resource just loaded or an empty pointer in case of errors. + */ + template + [[nodiscard]] std::shared_ptr get(Args &&... args) const { + return static_cast(this)->load(std::forward(args)...); + } +}; + + +} + + +#endif + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Simple cache for resources of a given type. + * + * Minimal implementation of a cache for resources of a given type. It doesn't + * offer much functionalities but it's suitable for small or medium sized + * applications and can be freely inherited to add targeted functionalities for + * large sized applications. + * + * @tparam Resource Type of resources managed by a cache. + */ +template +struct resource_cache { + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of resources managed by a cache. */ + using resource_type = Resource; + + /*! @brief Default constructor. */ + resource_cache() = default; + + /*! @brief Default move constructor. */ + resource_cache(resource_cache &&) = default; + + /*! @brief Default move assignment operator. @return This cache. */ + resource_cache & operator=(resource_cache &&) = default; + + /** + * @brief Number of resources managed by a cache. + * @return Number of resources currently stored. + */ + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + return resources.size(); + } + + /** + * @brief Returns true if a cache contains no resources, false otherwise. + * @return True if the cache contains no resources, false otherwise. + */ + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return resources.empty(); + } + + /** + * @brief Clears a cache and discards all its resources. + * + * Handles are not invalidated and the memory used by a resource isn't + * freed as long as at least a handle keeps the resource itself alive. + */ + void clear() ENTT_NOEXCEPT { + resources.clear(); + } + + /** + * @brief Loads the resource that corresponds to a given identifier. + * + * In case an identifier isn't already present in the cache, it loads its + * resource and stores it aside for future uses. Arguments are forwarded + * directly to the loader in order to construct properly the requested + * resource. + * + * @note + * If the identifier is already present in the cache, this function does + * nothing and the arguments are simply discarded. + * + * @warning + * If the resource cannot be loaded correctly, the returned handle will be + * invalid and any use of it will result in undefined behavior. + * + * @tparam Loader Type of loader to use to load the resource if required. + * @tparam Args Types of arguments to use to load the resource if required. + * @param id Unique resource identifier. + * @param args Arguments to use to load the resource if required. + * @return A handle for the given resource. + */ + template + resource_handle load(const id_type id, Args &&... args) { + static_assert(std::is_base_of_v, Loader>, "Invalid loader type"); + resource_handle resource{}; + + if(auto it = resources.find(id); it == resources.cend()) { + if(auto instance = Loader{}.get(std::forward(args)...); instance) { + resources[id] = instance; + resource = std::move(instance); + } + } else { + resource = it->second; + } + + return resource; + } + + /** + * @brief Reloads a resource or loads it for the first time if not present. + * + * Equivalent to the following snippet (pseudocode): + * + * @code{.cpp} + * cache.discard(id); + * cache.load(id, args...); + * @endcode + * + * Arguments are forwarded directly to the loader in order to construct + * properly the requested resource. + * + * @warning + * If the resource cannot be loaded correctly, the returned handle will be + * invalid and any use of it will result in undefined behavior. + * + * @tparam Loader Type of loader to use to load the resource. + * @tparam Args Types of arguments to use to load the resource. + * @param id Unique resource identifier. + * @param args Arguments to use to load the resource. + * @return A handle for the given resource. + */ + template + resource_handle reload(const id_type id, Args &&... args) { + return (discard(id), load(id, std::forward(args)...)); + } + + /** + * @brief Creates a temporary handle for a resource. + * + * Arguments are forwarded directly to the loader in order to construct + * properly the requested resource. The handle isn't stored aside and the + * cache isn't in charge of the lifetime of the resource itself. + * + * @tparam Loader Type of loader to use to load the resource. + * @tparam Args Types of arguments to use to load the resource. + * @param args Arguments to use to load the resource. + * @return A handle for the given resource. + */ + template + [[nodiscard]] resource_handle temp(Args &&... args) const { + return { Loader{}.get(std::forward(args)...) }; + } + + /** + * @brief Creates a handle for a given resource identifier. + * + * A resource handle can be in a either valid or invalid state. In other + * terms, a resource handle is properly initialized with a resource if the + * cache contains the resource itself. Otherwise the returned handle is + * uninitialized and accessing it results in undefined behavior. + * + * @sa resource_handle + * + * @param id Unique resource identifier. + * @return A handle for the given resource. + */ + [[nodiscard]] resource_handle handle(const id_type id) const { + auto it = resources.find(id); + return { it == resources.end() ? nullptr : it->second }; + } + + /** + * @brief Checks if a cache contains a given identifier. + * @param id Unique resource identifier. + * @return True if the cache contains the resource, false otherwise. + */ + [[nodiscard]] bool contains(const id_type id) const { + return (resources.find(id) != resources.cend()); + } + + /** + * @brief Discards the resource that corresponds to a given identifier. + * + * Handles are not invalidated and the memory used by the resource isn't + * freed as long as at least a handle keeps the resource itself alive. + * + * @param id Unique resource identifier. + */ + void discard(const id_type id) { + if(auto it = resources.find(id); it != resources.end()) { + resources.erase(it); + } + } + + /** + * @brief Iterates all resources. + * + * The function object is invoked for each element. It is provided with + * either the resource identifier, the resource handle or both of them.
+ * The signature of the function must be equivalent to one of the following + * forms: + * + * @code{.cpp} + * void(const entt::id_type); + * void(entt::resource_handle); + * void(const entt::id_type, entt::resource_handle); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + auto begin = resources.begin(); + auto end = resources.end(); + + while(begin != end) { + auto curr = begin++; + + if constexpr(std::is_invocable_v) { + func(curr->first); + } else if constexpr(std::is_invocable_v>) { + func(resource_handle{ curr->second }); + } else { + func(curr->first, resource_handle{ curr->second }); + } + } + } + +private: + std::unordered_map> resources; +}; + + +} + + +#endif + +// #include "resource/handle.hpp" + +// #include "resource/loader.hpp" + +// #include "signal/delegate.hpp" +#ifndef ENTT_SIGNAL_DELEGATE_HPP +#define ENTT_SIGNAL_DELEGATE_HPP + + +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_HS_SUFFIX +# define ENTT_HS_SUFFIX _hs +#endif + + +#ifndef ENTT_HWS_SUFFIX +# define ENTT_HWS_SUFFIX _hws +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifndef ENTT_PAGE_SIZE +# define ENTT_PAGE_SIZE 32768 +#endif + + +#ifndef ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty +#else +# include +# define ENTT_IS_EMPTY(Type) std::false_type +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) +# define ENTT_PRETTY_FUNCTION_CONSTEXPR +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif 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_CONSTEXPR +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + + +#ifndef ENTT_STANDALONE +# define ENTT_FAST_PATH(...) false +#else +# define ENTT_FAST_PATH(Cond) Cond +#endif + + +#endif + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +auto function_pointer(Ret(*)(Args...)) -> Ret(*)(Args...); + + +template +auto function_pointer(Ret(*)(Type, Args...), Other &&) -> Ret(*)(Args...); + + +template +auto function_pointer(Ret(Class:: *)(Args...), Other &&...) -> Ret(*)(Args...); + + +template +auto function_pointer(Ret(Class:: *)(Args...) const, Other &&...) -> Ret(*)(Args...); + + +template +auto function_pointer(Type Class:: *, Other &&...) -> Type(*)(); + + +template +using function_pointer_t = decltype(internal::function_pointer(std::declval()...)); + + +template +[[nodiscard]] constexpr auto index_sequence_for(Ret(*)(Args...)) { + return std::index_sequence_for{}; +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/*! @brief Used to wrap a function or a member of a specified type. */ +template +struct connect_arg_t {}; + + +/*! @brief Constant of type connect_arg_t used to disambiguate calls. */ +template +inline constexpr connect_arg_t connect_arg{}; + + +/** + * @brief Basic delegate implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + */ +template +class delegate; + + +/** + * @brief Utility class to use to send around functions and members. + * + * Unmanaged delegate for function pointers and members. Users of this class are + * in charge of disconnecting instances before deleting them. + * + * A delegate can be used as a general purpose invoker without memory overhead + * for free functions possibly with payloads and bound or unbound members. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +class delegate { + template + [[nodiscard]] auto wrap(std::index_sequence) ENTT_NOEXCEPT { + return [](const void *, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + return Ret(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + }; + } + + template + [[nodiscard]] auto wrap(Type &, std::index_sequence) ENTT_NOEXCEPT { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast, const void *, void *>>(payload)); + return Ret(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + }; + } + + template + [[nodiscard]] auto wrap(Type *, std::index_sequence) ENTT_NOEXCEPT { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast, const void *, void *>>(payload)); + return Ret(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + }; + } + +public: + /*! @brief Function type of the contained target. */ + using function_type = Ret(const void *, Args...); + /*! @brief Function type of the delegate. */ + using type = Ret(Args...); + /*! @brief Return type of the delegate. */ + using result_type = Ret; + + /*! @brief Default constructor. */ + delegate() ENTT_NOEXCEPT + : fn{nullptr}, data{nullptr} + {} + + /** + * @brief Constructs a delegate and connects a free function or an unbound + * member. + * @tparam Candidate Function or member to connect to the delegate. + */ + template + delegate(connect_arg_t) ENTT_NOEXCEPT { + connect(); + } + + /** + * @brief Constructs a delegate and connects a free function with payload or + * a bound member. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + delegate(connect_arg_t, Type &&value_or_instance) ENTT_NOEXCEPT { + connect(std::forward(value_or_instance)); + } + + /** + * @brief Constructs a delegate and connects an user defined function with + * optional payload. + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ + delegate(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT { + connect(function, payload); + } + + /** + * @brief Connects a free function or an unbound member to a delegate. + * @tparam Candidate Function or member to connect to the delegate. + */ + template + void connect() ENTT_NOEXCEPT { + data = nullptr; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *, Args... args) -> Ret { + return Ret(std::invoke(Candidate, std::forward(args)...)); + }; + } else if constexpr(std::is_member_pointer_v) { + fn = wrap(internal::index_sequence_for>>(internal::function_pointer_t{})); + } else { + fn = wrap(internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @brief Connects a free function with payload or a bound member to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of the instance overcomes + * the one of the delegate.
+ * When used to connect a free function with payload, its signature must be + * such that the instance is the first argument before the ones used to + * define the delegate itself. + * + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid reference that fits the purpose. + */ + template + void connect(Type &value_or_instance) ENTT_NOEXCEPT { + data = &value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast, const void *, void *>>(payload)); + return Ret(std::invoke(Candidate, *curr, std::forward(args)...)); + }; + } else { + fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @brief Connects a free function with payload or a bound member to a + * delegate. + * + * @sa connect(Type &) + * + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid pointer that fits the purpose. + */ + template + void connect(Type *value_or_instance) ENTT_NOEXCEPT { + data = value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast, const void *, void *>>(payload)); + return Ret(std::invoke(Candidate, curr, std::forward(args)...)); + }; + } else { + fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @brief Connects an user defined function with optional payload to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of an instance overcomes + * the one of the delegate.
+ * The payload is returned as the first argument to the target function in + * all cases. + * + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ + void connect(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT { + fn = function; + data = payload; + } + + /** + * @brief Resets a delegate. + * + * After a reset, a delegate cannot be invoked anymore. + */ + void reset() ENTT_NOEXCEPT { + fn = nullptr; + data = nullptr; + } + + /** + * @brief Returns the instance or the payload linked to a delegate, if any. + * @return An opaque pointer to the underlying data. + */ + [[nodiscard]] const void * instance() const ENTT_NOEXCEPT { + return data; + } + + /** + * @brief Triggers a delegate. + * + * The delegate invokes the underlying function and returns the result. + * + * @warning + * Attempting to trigger an invalid delegate results in undefined + * behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * delegate has not yet been set. + * + * @param args Arguments to use to invoke the underlying function. + * @return The value returned by the underlying function. + */ + Ret operator()(Args... args) const { + ENTT_ASSERT(fn); + return fn(data, std::forward(args)...); + } + + /** + * @brief Checks whether a delegate actually stores a listener. + * @return False if the delegate is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + // no need to test also data + return !(fn == nullptr); + } + + /** + * @brief Compares the contents of two delegates. + * @param other Delegate with which to compare. + * @return False if the two contents differ, true otherwise. + */ + [[nodiscard]] bool operator==(const delegate &other) const ENTT_NOEXCEPT { + return fn == other.fn && data == other.data; + } + +private: + function_type *fn; + const void *data; +}; + + +/** + * @brief Compares the contents of two delegates. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @param lhs A valid delegate object. + * @param rhs A valid delegate object. + * @return True if the two contents differ, false otherwise. + */ +template +[[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + */ +template +delegate(connect_arg_t) ENTT_NOEXCEPT +-> 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 &&) ENTT_NOEXCEPT +-> delegate>>; + + +/*! @brief Deduction guide. */ +template +delegate(Ret(*)(const void *, Args...), const void * = nullptr) ENTT_NOEXCEPT +-> delegate; + + +} + + +#endif + +// #include "signal/dispatcher.hpp" +#ifndef ENTT_SIGNAL_DISPATCHER_HPP +#define ENTT_SIGNAL_DISPATCHER_HPP + + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + + +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_HS_SUFFIX +# define ENTT_HS_SUFFIX _hs +#endif + + +#ifndef ENTT_HWS_SUFFIX +# define ENTT_HWS_SUFFIX _hws +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifndef ENTT_PAGE_SIZE +# define ENTT_PAGE_SIZE 32768 +#endif + + +#ifndef ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty +#else +# include +# define ENTT_IS_EMPTY(Type) std::false_type +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) +# define ENTT_PRETTY_FUNCTION_CONSTEXPR +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif 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_CONSTEXPR +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + + +#ifndef ENTT_STANDALONE +# define ENTT_FAST_PATH(...) false +#else +# define ENTT_FAST_PATH(Cond) Cond +#endif + + +#endif + + + +namespace entt { + + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + + +} + + +#endif + +// #include "../core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + + +#include +// #include "../config/config.h" + +// #include "../core/attribute.h" +#ifndef ENTT_CORE_ATTRIBUTE_H +#define ENTT_CORE_ATTRIBUTE_H + + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + + +#endif + +// #include "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + + +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +struct fnv1a_traits; + + +template<> +struct fnv1a_traits { + using type = std::uint32_t; + static constexpr std::uint32_t offset = 2166136261; + static constexpr std::uint32_t prime = 16777619; +}; + + +template<> +struct fnv1a_traits { + using type = std::uint64_t; + static constexpr std::uint64_t offset = 14695981039346656037ull; + static constexpr std::uint64_t prime = 1099511628211ull; +}; + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifers in the codebase while using their numeric + * counterparts at runtime.
+ * Because of that, a hashed string can also be used in constant expressions if + * required. + * + * @tparam Char Character type. + */ +template +class basic_hashed_string { + using traits_type = internal::fnv1a_traits; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const Char *curr) ENTT_NOEXCEPT: str{curr} {} + const Char *str; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr id_type helper(const Char *curr) ENTT_NOEXCEPT { + auto value = traits_type::offset; + + while(*curr != 0) { + value = (value ^ static_cast(*(curr++))) * traits_type::prime; + } + + return value; + } + +public: + /*! @brief Character type. */ + using value_type = Char; + /*! @brief Unsigned integer type. */ + using hash_type = id_type; + + /** + * @brief Returns directly the numeric representation of a string. + * + * Forcing template resolution avoids implicit conversions. An + * human-readable identifier can be anything but a plain, old bunch of + * characters.
+ * Example of use: + * @code{.cpp} + * const auto value = basic_hashed_string::to_value("my.png"); + * @endcode + * + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifer. + * @return The numeric representation of the string. + */ + template + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { + return helper(str); + } + + /** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ + [[nodiscard]] static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { + return helper(wrapper.str); + } + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifer. + * @param size Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static hash_type value(const value_type *str, std::size_t size) ENTT_NOEXCEPT { + id_type partial{traits_type::offset}; + while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } + return partial; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() ENTT_NOEXCEPT + : str{nullptr}, hash{} + {} + + /** + * @brief Constructs a hashed string from an array of const characters. + * + * Forcing template resolution avoids implicit conversions. An + * human-readable identifier can be anything but a plain, old bunch of + * characters.
+ * Example of use: + * @code{.cpp} + * basic_hashed_string hs{"my.png"}; + * @endcode + * + * @tparam N Number of characters of the identifier. + * @param curr Human-readable identifer. + */ + template + constexpr basic_hashed_string(const value_type (&curr)[N]) ENTT_NOEXCEPT + : str{curr}, hash{helper(curr)} + {} + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT + : str{wrapper.str}, hash{helper(wrapper.str)} + {} + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the instance. + */ + [[nodiscard]] constexpr const value_type * data() const ENTT_NOEXCEPT { + return str; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the instance. + */ + [[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { + return hash; + } + + /*! @copydoc data */ + [[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { return data(); } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the instance. + */ + [[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { return value(); } + + /** + * @brief Compares two hashed strings. + * @param other Hashed string with which to compare. + * @return True if the two hashed strings are identical, false otherwise. + */ + [[nodiscard]] constexpr bool operator==(const basic_hashed_string &other) const ENTT_NOEXCEPT { + return hash == other.hash; + } + +private: + const value_type *str; + hash_type hash; +}; + + +/** + * @brief Deduction guide. + * + * It allows to deduce the character type of the hashed string directly from a + * human-readable identifer provided to the constructor. + * + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifer. + */ +template +basic_hashed_string(const Char (&str)[N]) ENTT_NOEXCEPT +-> basic_hashed_string; + + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + + +} + + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr entt::hashed_string operator"" ENTT_HS_SUFFIX(const char *str, std::size_t) ENTT_NOEXCEPT { + return entt::hashed_string{str}; +} + + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] constexpr entt::hashed_wstring operator"" ENTT_HWS_SUFFIX(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { + return entt::hashed_wstring{str}; +} + + +#endif + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +struct ENTT_API type_index { + [[nodiscard]] static id_type next() ENTT_NOEXCEPT { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + + +template +[[nodiscard]] constexpr auto type_name() ENTT_NOEXCEPT { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX)+1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{}; +#endif +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Type index. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() ENTT_NOEXCEPT { + static const id_type value = internal::type_index::next(); + return value; + } +}; + + +/** + * @brief Provides the member constant `value` to true if a given type is + * indexable, false otherwise. + * @tparam Type Potentially indexable type. + */ +template +struct has_type_index: std::false_type {}; + + +/*! @brief has_type_index */ +template +struct has_type_index::value())>>: std::true_type {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially indexable type. + */ +template +inline constexpr bool has_type_index_v = has_type_index::value; + + +/** + * @brief Type info. + * @tparam Type Type for which to generate information. + */ +template +struct type_info { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION_CONSTEXPR + [[nodiscard]] static constexpr id_type id() ENTT_NOEXCEPT { + constexpr auto value = hashed_string::value(ENTT_PRETTY_FUNCTION); + return value; + } +#elif defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static id_type id() ENTT_NOEXCEPT { + static const auto value = hashed_string::value(ENTT_PRETTY_FUNCTION); + return value; + } +#else + [[nodiscard]] static id_type id() ENTT_NOEXCEPT { + return type_index::value(); + } +#endif + + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION_CONSTEXPR + [[nodiscard]] static constexpr std::string_view name() ENTT_NOEXCEPT { + constexpr auto value = internal::type_name(); + return value; + } +#elif defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static std::string_view name() ENTT_NOEXCEPT { + static const auto value = internal::type_name(); + return value; + } +#else + [[nodiscard]] static constexpr std::string_view name() ENTT_NOEXCEPT { + return internal::type_name(); + } +#endif +}; + + +} + + +#endif + +// #include "sigh.hpp" +#ifndef ENTT_SIGNAL_SIGH_HPP +#define ENTT_SIGNAL_SIGH_HPP + + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "delegate.hpp" + +// #include "fwd.hpp" +#ifndef ENTT_SIGNAL_FWD_HPP +#define ENTT_SIGNAL_FWD_HPP + + +namespace entt { + + +template +class delegate; + + +class dispatcher; + + +template +class emitter; + + +class connection; + + +struct scoped_connection; + + +template +class sink; + + +template +class sigh; + + +} + + +#endif + + + +namespace entt { + + +/** + * @brief Sink class. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Function A valid function type. + */ +template +class sink; + + +/** + * @brief Unmanaged signal handler. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Function A valid function type. + */ +template +class sigh; + + +/** + * @brief Unmanaged signal handler. + * + * It works directly with references to classes and pointers to member functions + * as well as pointers to free functions. Users of this class are in charge of + * disconnecting instances before deleting them. + * + * This class serves mainly two purposes: + * + * * Creating signals to use later to notify a bunch of listeners. + * * Collecting results from a set of functions like in a voting system. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +class sigh { + /*! @brief A sink is allowed to modify a signal. */ + friend class sink; + +public: + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Sink type. */ + using sink_type = sink; + + /** + * @brief Instance type when it comes to connecting member functions. + * @tparam Class Type of class to which the member function belongs. + */ + template + using instance_type = Class *; + + /** + * @brief Number of listeners connected to the signal. + * @return Number of listeners currently connected. + */ + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + return calls.size(); + } + + /** + * @brief Returns false if at least a listener is connected to the signal. + * @return True if the signal has no listeners connected, false otherwise. + */ + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return calls.empty(); + } + + /** + * @brief Triggers a signal. + * + * All the listeners are notified. Order isn't guaranteed. + * + * @param args Arguments to use to invoke listeners. + */ + void publish(Args... args) const { + for(auto &&call: std::as_const(calls)) { + call(args...); + } + } + + /** + * @brief Collects return values from the listeners. + * + * The collector must expose a call operator with the following properties: + * + * * The return type is either `void` or such that it's convertible to + * `bool`. In the second case, a true value will stop the iteration. + * * The list of parameters is empty if `Ret` is `void`, otherwise it + * contains a single element such that `Ret` is convertible to it. + * + * @tparam Func Type of collector to use, if any. + * @param func A valid function object. + * @param args Arguments to use to invoke listeners. + */ + template + void collect(Func func, Args... args) const { + for(auto &&call: calls) { + if constexpr(std::is_void_v) { + if constexpr(std::is_invocable_r_v) { + call(args...); + if(func()) { break; } + } else { + call(args...); + func(); + } + } else { + if constexpr(std::is_invocable_r_v) { + if(func(call(args...))) { break; } + } else { + func(call(args...)); + } + } + } + } + +private: + std::vector> calls; +}; + + +/** + * @brief Connection class. + * + * Opaque object the aim of which is to allow users to release an already + * estabilished connection without having to keep a reference to the signal or + * the sink that generated it. + */ +class connection { + /*! @brief A sink is allowed to create connection objects. */ + template + friend class sink; + + connection(delegate fn, void *ref) + : disconnect{fn}, signal{ref} + {} + +public: + /*! @brief Default constructor. */ + connection() = default; + + /** + * @brief Checks whether a connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return static_cast(disconnect); + } + + /*! @brief Breaks the connection. */ + void release() { + if(disconnect) { + disconnect(signal); + disconnect.reset(); + } + } + +private: + delegate disconnect; + void *signal{}; +}; + + +/** + * @brief Scoped connection class. + * + * Opaque object the aim of which is to allow users to release an already + * estabilished connection without having to keep a reference to the signal or + * the sink that generated it.
+ * A scoped connection automatically breaks the link between the two objects + * when it goes out of scope. + */ +struct scoped_connection { + /*! @brief Default constructor. */ + scoped_connection() = default; + + /** + * @brief Constructs a scoped connection from a basic connection. + * @param other A valid connection object. + */ + scoped_connection(const connection &other) + : conn{other} + {} + + /*! @brief Default copy constructor, deleted on purpose. */ + scoped_connection(const scoped_connection &) = delete; + + /*! @brief Automatically breaks the link on destruction. */ + ~scoped_connection() { + conn.release(); + } + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This scoped connection. + */ + scoped_connection & operator=(const scoped_connection &) = delete; + + /** + * @brief Acquires a connection. + * @param other The connection object to acquire. + * @return This scoped connection. + */ + scoped_connection & operator=(connection other) { + conn = std::move(other); + return *this; + } + + /** + * @brief Checks whether a scoped connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return static_cast(conn); + } + + /*! @brief Breaks the connection. */ + void release() { + conn.release(); + } + +private: + connection conn; +}; + + +/** + * @brief Sink class. + * + * A sink is used to connect listeners to signals and to disconnect them.
+ * The function type for a listener is the one of the signal to which it + * belongs. + * + * The clear separation between a signal and a sink permits to store the former + * as private data member without exposing the publish functionality to the + * users of the class. + * + * @warning + * Lifetime of a sink must not overcome that of the signal to which it refers. + * In any other case, attempting to use a sink results in undefined behavior. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +class sink { + using signal_type = sigh; + using difference_type = typename std::iterator_traits::difference_type; + + template + static void release(Type value_or_instance, void *signal) { + sink{*static_cast(signal)}.disconnect(value_or_instance); + } + + template + static void release(void *signal) { + sink{*static_cast(signal)}.disconnect(); + } + +public: + /** + * @brief Constructs a sink that is allowed to modify a given signal. + * @param ref A valid reference to a signal object. + */ + sink(sigh &ref) ENTT_NOEXCEPT + : offset{}, + signal{&ref} + {} + + /** + * @brief Returns false if at least a listener is connected to the sink. + * @return True if the sink has no listeners connected, false otherwise. + */ + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return signal->calls.empty(); + } + + /** + * @brief Returns a sink that connects before a given free function or an + * unbound member. + * @tparam Function A valid free function pointer. + * @return A properly initialized sink object. + */ + template + [[nodiscard]] sink before() { + delegate call{}; + call.template connect(); + + const auto &calls = signal->calls; + const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); + + sink other{*this}; + other.offset = std::distance(it, calls.cend()); + return other; + } + + /** + * @brief Returns a sink that connects before a free function with payload + * or a bound member. + * @tparam Candidate Member or free function to look for. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized sink object. + */ + template + [[nodiscard]] sink before(Type &&value_or_instance) { + delegate call{}; + call.template connect(value_or_instance); + + const auto &calls = signal->calls; + const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); + + sink other{*this}; + other.offset = std::distance(it, calls.cend()); + return other; + } + + /** + * @brief Returns a sink that connects before a given instance or specific + * payload. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized sink object. + */ + template + [[nodiscard]] sink before(Type &value_or_instance) { + return before(&value_or_instance); + } + + /** + * @brief Returns a sink that connects before a given instance or specific + * payload. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid pointer that fits the purpose. + * @return A properly initialized sink object. + */ + template + [[nodiscard]] sink before(Type *value_or_instance) { + sink other{*this}; + + if(value_or_instance) { + const auto &calls = signal->calls; + const auto it = std::find_if(calls.cbegin(), calls.cend(), [value_or_instance](const auto &delegate) { + return delegate.instance() == value_or_instance; + }); + + other.offset = std::distance(it, calls.cend()); + } + + return other; + } + + /** + * @brief Returns a sink that connects before anything else. + * @return A properly initialized sink object. + */ + [[nodiscard]] sink before() { + sink other{*this}; + other.offset = signal->calls.size(); + return other; + } + + /** + * @brief Connects a free function or an unbound member to a signal. + * + * The signal handler performs checks to avoid multiple connections for the + * same function. + * + * @tparam Candidate Function or member to connect to the signal. + * @return A properly initialized connection object. + */ + template + connection connect() { + disconnect(); + + delegate call{}; + call.template connect(); + signal->calls.insert(signal->calls.end() - offset, std::move(call)); + + delegate conn{}; + conn.template connect<&release>(); + return { std::move(conn), signal }; + } + + /** + * @brief Connects a free function with payload or a bound member to a + * signal. + * + * The signal isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of the instance overcomes + * the one of the signal. On the other side, the signal handler performs + * checks to avoid multiple connections for the same function.
+ * When used to connect a free function with payload, its signature must be + * such that the instance is the first argument before the ones used to + * define the signal itself. + * + * @tparam Candidate Function or member to connect to the signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized connection object. + */ + template + connection connect(Type &&value_or_instance) { + disconnect(value_or_instance); + + delegate call{}; + call.template connect(value_or_instance); + signal->calls.insert(signal->calls.end() - offset, std::move(call)); + + delegate conn{}; + conn.template connect<&release>(value_or_instance); + return { std::move(conn), signal }; + } + + /** + * @brief Disconnects a free function or an unbound member from a signal. + * @tparam Candidate Function or member to disconnect from the signal. + */ + template + void disconnect() { + auto &calls = signal->calls; + delegate call{}; + call.template connect(); + calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); + } + + /** + * @brief Disconnects a free function with payload or a bound member from a + * signal. + * @tparam Candidate Function or member to disconnect from the signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type &&value_or_instance) { + auto &calls = signal->calls; + delegate call{}; + call.template connect(value_or_instance); + calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); + } + + /** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type &value_or_instance) { + disconnect(&value_or_instance); + } + + /** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type *value_or_instance) { + if(value_or_instance) { + auto &calls = signal->calls; + calls.erase(std::remove_if(calls.begin(), calls.end(), [value_or_instance](const auto &delegate) { + return delegate.instance() == value_or_instance; + }), calls.end()); + } + } + + /*! @brief Disconnects all the listeners from a signal. */ + void disconnect() { + signal->calls.clear(); + } + +private: + difference_type offset; + signal_type *signal; +}; + + +/** + * @brief Deduction guide. + * + * It allows to deduce the function type of a sink directly from the signal it + * refers to. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +sink(sigh &) ENTT_NOEXCEPT -> sink; + + +} + + +#endif + + + +namespace entt { + + +/** + * @brief Basic dispatcher implementation. + * + * A dispatcher can be used either to trigger an immediate event or to enqueue + * events to be published all together once per tick.
+ * Listeners are provided in the form of member functions. For each event of + * type `Event`, listeners are such that they can be invoked with an argument of + * type `Event &`, no matter what the return type is. + * + * The dispatcher creates instances of the `sigh` class internally. Refer to the + * documentation of the latter for more details. + */ +class dispatcher { + struct basic_pool { + virtual ~basic_pool() = default; + virtual void publish() = 0; + virtual void disconnect(void *) = 0; + virtual void clear() ENTT_NOEXCEPT = 0; + [[nodiscard]] virtual id_type type_id() const ENTT_NOEXCEPT = 0; + }; + + template + struct pool_handler final: basic_pool { + using signal_type = sigh; + using sink_type = typename signal_type::sink_type; + + 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 { + sink().disconnect(instance); + } + + void clear() ENTT_NOEXCEPT override { + events.clear(); + } + + [[nodiscard]] sink_type sink() ENTT_NOEXCEPT { + return entt::sink{signal}; + } + + template + void trigger(Args &&... args) { + Event instance{std::forward(args)...}; + signal.publish(instance); + } + + template + void enqueue(Args &&... args) { + if constexpr(std::is_aggregate_v) { + events.push_back(Event{std::forward(args)...}); + } else { + events.emplace_back(std::forward(args)...); + } + } + + [[nodiscard]] id_type type_id() const ENTT_NOEXCEPT override { + return type_info::id(); + } + + private: + signal_type signal{}; + std::vector events; + }; + + template + [[nodiscard]] pool_handler & assure() { + static_assert(std::is_same_v>, "Invalid event type"); + + if constexpr(ENTT_FAST_PATH(has_type_index_v)) { + const auto index = type_index::value(); + + if(!(index < pools.size())) { + pools.resize(index+1u); + } + + if(!pools[index]) { + pools[index].reset(new pool_handler{}); + } + + return static_cast &>(*pools[index]); + } else { + auto it = std::find_if(pools.begin(), pools.end(), [id = type_info::id()](const auto &cpool) { return id == cpool->type_id(); }); + return static_cast &>(it == pools.cend() ? *pools.emplace_back(new pool_handler{}) : **it); + } + } + +public: + /** + * @brief Returns a sink object for the given event. + * + * A sink is an opaque object used to connect listeners to events. + * + * The function type for a listener is _compatible_ with: + * @code{.cpp} + * void(Event &); + * @endcode + * + * The order of invocation of the listeners isn't guaranteed. + * + * @sa sink + * + * @tparam Event Type of event of which to get the sink. + * @return A temporary sink object. + */ + template + [[nodiscard]] auto sink() { + return assure().sink(); + } + + /** + * @brief Triggers an immediate event of the given type. + * + * All the listeners registered for the given type are immediately notified. + * The event is discarded after the execution. + * + * @tparam Event Type of event to trigger. + * @tparam Args Types of arguments to use to construct the event. + * @param args Arguments to use to construct the event. + */ + template + void trigger(Args &&... args) { + assure().trigger(std::forward(args)...); + } + + /** + * @brief Triggers an immediate event of the given type. + * + * All the listeners registered for the given type are immediately notified. + * The event is discarded after the execution. + * + * @tparam Event Type of event to trigger. + * @param event An instance of the given type of event. + */ + template + void trigger(Event &&event) { + assure>().trigger(std::forward(event)); + } + + /** + * @brief Enqueues an event of the given type. + * + * An event of the given type is queued. No listener is invoked. Use the + * `update` member function to notify listeners when ready. + * + * @tparam Event Type of event to enqueue. + * @tparam Args Types of arguments to use to construct the event. + * @param args Arguments to use to construct the event. + */ + template + void enqueue(Args &&... args) { + assure().enqueue(std::forward(args)...); + } + + /** + * @brief Enqueues an event of the given type. + * + * An event of the given type is queued. No listener is invoked. Use the + * `update` member function to notify listeners when ready. + * + * @tparam Event Type of event to enqueue. + * @param event An instance of the given type of event. + */ + template + void enqueue(Event &&event) { + assure>().enqueue(std::forward(event)); + } + + /** + * @brief Utility function to disconnect everything related to a given value + * or instance from a dispatcher. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type &value_or_instance) { + disconnect(&value_or_instance); + } + + /** + * @brief Utility function to disconnect everything related to a given value + * or instance from a dispatcher. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type *value_or_instance) { + for(auto &&cpool: pools) { + if(cpool) { + cpool->disconnect(value_or_instance); + } + } + } + + /** + * @brief Discards all the events queued so far. + * + * If no types are provided, the dispatcher will clear all the existing + * pools. + * + * @tparam Event Type of events to discard. + */ + template + void clear() { + if constexpr(sizeof...(Event) == 0) { + for(auto &&cpool: pools) { + if(cpool) { + cpool->clear(); + } + } + } else { + (assure().clear(), ...); + } + } + + /** + * @brief Delivers all the pending events of the given type. + * + * This method is blocking and it doesn't return until all the events are + * delivered to the registered listeners. It's responsibility of the users + * to reduce at a minimum the time spent in the bodies of the listeners. + * + * @tparam Event Type of events to send. + */ + template + void update() { + assure().publish(); + } + + /** + * @brief Delivers all the pending events. + * + * This method is blocking and it doesn't return until all the events are + * delivered to the registered listeners. It's responsibility of the users + * to reduce at a minimum the time spent in the bodies of the listeners. + */ + void update() const { + for(auto pos = pools.size(); pos; --pos) { + if(auto &&cpool = pools[pos-1]; cpool) { + cpool->publish(); + } + } + } + +private: + std::vector> pools; +}; + + +} + + +#endif + +// #include "signal/emitter.hpp" +#ifndef ENTT_SIGNAL_EMITTER_HPP +#define ENTT_SIGNAL_EMITTER_HPP + + +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/fwd.hpp" + +// #include "../core/type_info.hpp" + + + +namespace entt { + + +/** + * @brief General purpose event emitter. + * + * The emitter class template follows the CRTP idiom. To create a custom emitter + * type, derived classes must inherit directly from the base class as: + * + * @code{.cpp} + * struct my_emitter: emitter { + * // ... + * } + * @endcode + * + * Pools for the type of events are created internally on the fly. It's not + * required to specify in advance the full list of accepted types.
+ * Moreover, whenever an event is published, an emitter provides the listeners + * with a reference to itself along with a reference to the event. Therefore + * listeners have an handy way to work with it without incurring in the need of + * capturing a reference to the emitter. + * + * @tparam Derived Actual type of emitter that extends the class template. + */ +template +class emitter { + struct basic_pool { + virtual ~basic_pool() = default; + virtual bool empty() const ENTT_NOEXCEPT = 0; + virtual void clear() ENTT_NOEXCEPT = 0; + virtual id_type type_id() const ENTT_NOEXCEPT = 0; + }; + + template + struct pool_handler final: basic_pool { + using listener_type = std::function; + using element_type = std::pair; + using container_type = std::list; + using connection_type = typename container_type::iterator; + + [[nodiscard]] bool empty() const ENTT_NOEXCEPT override { + auto pred = [](auto &&element) { return element.first; }; + + return std::all_of(once_list.cbegin(), once_list.cend(), pred) && + std::all_of(on_list.cbegin(), on_list.cend(), pred); + } + + void clear() ENTT_NOEXCEPT override { + if(publishing) { + for(auto &&element: once_list) { + element.first = true; + } + + for(auto &&element: on_list) { + element.first = true; + } + } else { + once_list.clear(); + on_list.clear(); + } + } + + connection_type once(listener_type listener) { + return once_list.emplace(once_list.cend(), false, std::move(listener)); + } + + connection_type on(listener_type listener) { + return on_list.emplace(on_list.cend(), false, std::move(listener)); + } + + void erase(connection_type conn) { + conn->first = true; + + if(!publishing) { + auto pred = [](auto &&element) { return element.first; }; + once_list.remove_if(pred); + on_list.remove_if(pred); + } + } + + void publish(Event &event, Derived &ref) { + container_type swap_list; + once_list.swap(swap_list); + + publishing = true; + + for(auto &&element: on_list) { + element.first ? void() : element.second(event, ref); + } + + for(auto &&element: swap_list) { + element.first ? void() : element.second(event, ref); + } + + publishing = false; + + on_list.remove_if([](auto &&element) { return element.first; }); + } + + [[nodiscard]] id_type type_id() const ENTT_NOEXCEPT override { + return type_info::id(); + } + + private: + bool publishing{false}; + container_type once_list{}; + container_type on_list{}; + }; + + template + [[nodiscard]] const pool_handler & assure() const { + static_assert(std::is_same_v>, "Invalid event type"); + + if constexpr(ENTT_FAST_PATH(has_type_index_v)) { + const auto index = type_index::value(); + + if(!(index < pools.size())) { + pools.resize(index+1u); + } + + if(!pools[index]) { + pools[index].reset(new pool_handler{}); + } + + return static_cast &>(*pools[index]); + } else { + auto it = std::find_if(pools.begin(), pools.end(), [id = type_info::id()](const auto &cpool) { return id == cpool->type_id(); }); + return static_cast &>(it == pools.cend() ? *pools.emplace_back(new pool_handler{}) : **it); + } + } + + template + [[nodiscard]] pool_handler & assure() { + return const_cast &>(std::as_const(*this).template assure()); + } + +public: + /** @brief Type of listeners accepted for the given event. */ + template + using listener = typename pool_handler::listener_type; + + /** + * @brief Generic connection type for events. + * + * Type of the connection object returned by the event emitter whenever a + * listener for the given type is registered.
+ * It can be used to break connections still in use. + * + * @tparam Event Type of event for which the connection is created. + */ + template + struct connection: private pool_handler::connection_type { + /** @brief Event emitters are friend classes of connections. */ + friend class emitter; + + /*! @brief Default constructor. */ + connection() = default; + + /** + * @brief Creates a connection that wraps its underlying instance. + * @param conn A connection object to wrap. + */ + connection(typename pool_handler::connection_type conn) + : pool_handler::connection_type{std::move(conn)} + {} + }; + + /*! @brief Default constructor. */ + emitter() = default; + + /*! @brief Default destructor. */ + virtual ~emitter() { + static_assert(std::is_base_of_v, Derived>, "Incorrect use of the class template"); + } + + /*! @brief Default move constructor. */ + emitter(emitter &&) = default; + + /*! @brief Default move assignment operator. @return This emitter. */ + emitter & operator=(emitter &&) = default; + + /** + * @brief Emits the given event. + * + * All the listeners registered for the specific event type are invoked with + * the given event. The event type must either have a proper constructor for + * the arguments provided or be an aggregate type. + * + * @tparam Event Type of event to publish. + * @tparam Args Types of arguments to use to construct the event. + * @param args Parameters to use to initialize the event. + */ + template + void publish(Args &&... args) { + Event instance{std::forward(args)...}; + assure().publish(instance, *static_cast(this)); + } + + /** + * @brief Registers a long-lived listener with the event emitter. + * + * This method can be used to register a listener designed to be invoked + * more than once for the given event type.
+ * The connection returned by the method can be freely discarded. It's meant + * to be used later to disconnect the listener if required. + * + * The listener is as a callable object that can be moved and the type of + * which is _compatible_ with `void(Event &, Derived &)`. + * + * @note + * Whenever an event is emitted, the emitter provides the listener with a + * reference to the derived class. Listeners don't have to capture those + * instances for later uses. + * + * @tparam Event Type of event to which to connect the listener. + * @param instance The listener to register. + * @return Connection object that can be used to disconnect the listener. + */ + template + connection on(listener instance) { + return assure().on(std::move(instance)); + } + + /** + * @brief Registers a short-lived listener with the event emitter. + * + * This method can be used to register a listener designed to be invoked + * only once for the given event type.
+ * The connection returned by the method can be freely discarded. It's meant + * to be used later to disconnect the listener if required. + * + * The listener is as a callable object that can be moved and the type of + * which is _compatible_ with `void(Event &, Derived &)`. + * + * @note + * Whenever an event is emitted, the emitter provides the listener with a + * reference to the derived class. Listeners don't have to capture those + * instances for later uses. + * + * @tparam Event Type of event to which to connect the listener. + * @param instance The listener to register. + * @return Connection object that can be used to disconnect the listener. + */ + template + connection once(listener instance) { + return assure().once(std::move(instance)); + } + + /** + * @brief Disconnects a listener from the event emitter. + * + * Do not use twice the same connection to disconnect a listener, it results + * in undefined behavior. Once used, discard the connection object. + * + * @tparam Event Type of event of the connection. + * @param conn A valid connection. + */ + template + void erase(connection conn) { + assure().erase(std::move(conn)); + } + + /** + * @brief Disconnects all the listeners for the given event type. + * + * All the connections previously returned for the given event are + * invalidated. Using them results in undefined behavior. + * + * @tparam Event Type of event to reset. + */ + template + void clear() { + assure().clear(); + } + + /** + * @brief Disconnects all the listeners. + * + * All the connections previously returned are invalidated. Using them + * results in undefined behavior. + */ + void clear() ENTT_NOEXCEPT { + for(auto &&cpool: pools) { + if(cpool) { + cpool->clear(); + } + } + } + + /** + * @brief Checks if there are listeners registered for the specific event. + * @tparam Event Type of event to test. + * @return True if there are no listeners registered, false otherwise. + */ + template + [[nodiscard]] bool empty() const { + return assure().empty(); + } + + /** + * @brief Checks if there are listeners registered with the event emitter. + * @return True if there are no listeners registered, false otherwise. + */ + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return std::all_of(pools.cbegin(), pools.cend(), [](auto &&cpool) { + return !cpool || cpool->empty(); + }); + } + +private: + mutable std::vector> pools{}; +}; + + +} + + +#endif + +// #include "signal/sigh.hpp" + diff --git a/src/runtime/pixwerx.cpp b/src/runtime/pixwerx.cpp index f4297e7..504785c 100644 --- a/src/runtime/pixwerx.cpp +++ b/src/runtime/pixwerx.cpp @@ -23,7 +23,7 @@ int main(int argc,const char** argv) { argagg::parser argparser {{ { "help", {"-h", "--help"}, - "shows this help message", 0}, + "shows this help message", 0}, { "file", {"-f", "--file"}, "load file to run", 1} } @@ -52,12 +52,12 @@ int main(int argc,const char** argv) { std::ostringstream sout; std::copy(std::istreambuf_iterator(input), - std::istreambuf_iterator(), - std::ostreambuf_iterator(sout)); + std::istreambuf_iterator(), + std::ostreambuf_iterator(sout)); input.close(); - pw::script::initialize(); + pw::script::initialize(); pw::script s; diff --git a/src/scene/CMakeLists.txt b/src/scene/CMakeLists.txt index 797376f..5e32a8c 100644 --- a/src/scene/CMakeLists.txt +++ b/src/scene/CMakeLists.txt @@ -1,6 +1,7 @@ -set(hdrs - include/pw/scene/camera.hpp +set(hdrs + include/pw/scene/entity.hpp + include/pw/scene/projection.hpp include/pw/scene/component.hpp include/pw/scene/node.hpp include/pw/scene/scene.hpp @@ -9,8 +10,9 @@ set(hdrs ) set(srcs + src/entity.cpp src/node.cpp - src/camera.cpp + src/projection.cpp src/component.cpp src/scene.cpp src/transform.cpp @@ -25,6 +27,7 @@ add_library(pwscene target_include_directories( pwscene + PUBLIC include ) @@ -32,6 +35,7 @@ target_include_directories( target_include_directories( pwscene PUBLIC + ${CMAKE_SOURCE_DIR}/src/deps/entt ) target_link_libraries(pwscene pwcore) diff --git a/src/scene/include/pw/scene/component.hpp b/src/scene/include/pw/scene/component.hpp index 4edb95f..59713c7 100644 --- a/src/scene/include/pw/scene/component.hpp +++ b/src/scene/include/pw/scene/component.hpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999-2017 Hartmut Seichter + * Copyright (C) 1999-2020 Hartmut Seichter * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/src/scene/include/pw/scene/entity.hpp b/src/scene/include/pw/scene/entity.hpp new file mode 100644 index 0000000..8e9d16a --- /dev/null +++ b/src/scene/include/pw/scene/entity.hpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 1999-2020 Hartmut Seichter + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 AUTHOR 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 AUTHOR 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. + */ +#ifndef PW_SCENE_ENTITY_HPP +#define PW_SCENE_ENTITY_HPP + +#include +#include + +#include +#include +#include + +#include + +namespace pw { + + +class entity { +public: + entity() = delete; + +protected: + entity(entt::entity e, scene *s); + +private: + + entt::entity _entity { entt::null }; + scene *_scene; + + friend class scene; + +}; + +} +#endif diff --git a/src/scene/include/pw/scene/camera.hpp b/src/scene/include/pw/scene/projection.hpp similarity index 75% rename from src/scene/include/pw/scene/camera.hpp rename to src/scene/include/pw/scene/projection.hpp index 713aaad..a47b9f5 100644 --- a/src/scene/include/pw/scene/camera.hpp +++ b/src/scene/include/pw/scene/projection.hpp @@ -22,8 +22,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef PW_SCENE_CAMERA_HPP -#define PW_SCENE_CAMERA_HPP +#ifndef PW_SCENE_PROJECTION_HPP +#define PW_SCENE_PROJECTION_HPP #include @@ -31,40 +31,24 @@ namespace pw { -class camera : public component { +class projection : public component { public: - enum clearflags { - color, - depth, - none - }; + using component::component; - enum projection { - orthographic, - perspective - }; + projection(node &host); - using component::component; + void set_matrix(const matrix4x4 &projection); + const matrix4x4& matrix() const; - camera(node &host); - - void set_projection(const matrix4x4 &projection); - const matrix4x4& projection() const; - -// void set_field_of_view(float) - -protected: - - real_t _fov; - real_t _near_plane; - real_t _far_plane; - - real_t _ortho_size = 2; + void set_frustum(real_t left, + real_t right, + real_t top, + real_t bottom); private: - matrix4x4 _projection; + matrix4x4 _m; }; diff --git a/src/scene/include/pw/scene/scene.hpp b/src/scene/include/pw/scene/scene.hpp index 1081a43..547657b 100644 --- a/src/scene/include/pw/scene/scene.hpp +++ b/src/scene/include/pw/scene/scene.hpp @@ -26,22 +26,22 @@ #define PW_SCENE_SCENE_HPP #include -#include +#include namespace pw { +class entity; + class scene { public: - //! set root node - void set_root(node::ref root); + scene(); - // request the root node - inline node::ref root() const { return _root; } + entity create_entity(); protected: - node::ref _root; //!< root node + entt::registry _registry; }; diff --git a/src/scene/src/camera.cpp b/src/scene/src/camera.cpp deleted file mode 100644 index 1797cd9..0000000 --- a/src/scene/src/camera.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "pw/scene/camera.hpp" -#include "pw/core/matrix_transform.hpp" - -namespace pw { - -camera::camera(node &host) - : component (host) - , _fov(60.0) - , _near_plane(0.2f) - , _far_plane(1000) -{ - set_projection(matrix_transform::perspective_projection(_fov,1,_near_plane,_far_plane)); -} - -void camera::set_projection(const matrix4x4 &projection) -{ - this->_projection = projection; - - // recompute the simplified parameters - - auto near = _projection(3,4); - auto far = _projection(3,4); - -// right = (1-m14) / m11 - -// near = (1+m34)/m33; -// far = -(1-m34)/m33; -// bottom = (1-m24)/m22; -// top = -(1+m24)/m22; -// left = -(1+m14)/m11; -// right = (1-m14)/m11; - -// real_t fov_raw = float( atan (top / near_)) * * 2.0f; -} - -const matrix4x4 &camera::projection() const -{ - return _projection; -} - - -} diff --git a/src/scene/src/entity.cpp b/src/scene/src/entity.cpp new file mode 100644 index 0000000..65f258e --- /dev/null +++ b/src/scene/src/entity.cpp @@ -0,0 +1,12 @@ +#include "pw/scene/entity.hpp" + +namespace pw { + +entity::entity(entt::entity e, scene *s) + : _entity(e) + , _scene(s) +{ +} + +} + diff --git a/src/scene/src/projection.cpp b/src/scene/src/projection.cpp new file mode 100644 index 0000000..a71cf1a --- /dev/null +++ b/src/scene/src/projection.cpp @@ -0,0 +1,45 @@ +#include "pw/scene/projection.hpp" +#include "pw/core/matrix_transform.hpp" + +namespace pw { + +projection::projection(node &host) + : component (host) +{ + _m.set_identity(); + // set_matrix(matrix_transform::perspective_projection(_fov,1,_near_plane,_far_plane)); +} + +void projection::set_matrix(const matrix4x4 &projection) +{ + this->_m = projection; + + // recompute the simplified parameters + +// auto near = _projection(3,4); +// auto far = _projection(3,4); + +// right = (1-m14) / m11 + +// near = (1+m34)/m33; +// far = -(1-m34)/m33; +// bottom = (1-m24)/m22; +// top = -(1+m24)/m22; +// left = -(1+m14)/m11; +// right = (1-m14)/m11; + +// real_t fov_raw = float( atan (top / near_)) * * 2.0f; +} + +const matrix4x4 &projection::matrix() const +{ + return _m; +} + +void projection::set_frustum(real_t left, real_t right, real_t top, real_t bottom) +{ + +} + + +} diff --git a/src/scene/src/scene.cpp b/src/scene/src/scene.cpp index 5b39396..b1070ff 100644 --- a/src/scene/src/scene.cpp +++ b/src/scene/src/scene.cpp @@ -1,11 +1,24 @@ #include "pw/scene/scene.hpp" +#include "pw/scene/entity.hpp" namespace pw { -void scene::set_root(node::ref root) +struct component { + int data; +}; + +scene::scene() { - this->_root = root; } +entity +scene::create_entity() { + return entity{_registry.create(),this}; +} + + + + + } diff --git a/src/scene/tests/CMakeLists.txt b/src/scene/tests/CMakeLists.txt index 4e66de9..9c2f6e5 100644 --- a/src/scene/tests/CMakeLists.txt +++ b/src/scene/tests/CMakeLists.txt @@ -12,3 +12,11 @@ add_executable(pwscene_test_traverser target_link_libraries(pwscene_test_traverser pwcore pwscene) + +add_executable(pwscene_test_scene + pwscene_test_scene.cpp + ) + +target_link_libraries(pwscene_test_scene + pwcore pwscene) + diff --git a/src/scene/tests/pwscene_test_scene.cpp b/src/scene/tests/pwscene_test_scene.cpp new file mode 100644 index 0000000..37ddecc --- /dev/null +++ b/src/scene/tests/pwscene_test_scene.cpp @@ -0,0 +1,24 @@ +#include +#include +#include +#include +#include +#include + + +#include + +#include + +int main(int argc,char **argv) { + + using pw::scene; + + auto s = std::make_unique(); + + auto e = s->create_entity(); + + +// e.add_component(); + +} diff --git a/src/scripts/demos/simple_000.lua b/src/scripts/demos/simple_000.lua index 39ec9f5..3a54ff4 100644 --- a/src/scripts/demos/simple_000.lua +++ b/src/scripts/demos/simple_000.lua @@ -14,10 +14,10 @@ w.visible = false w.title = "pixwerx 0.1" -- set size ---w.size = pw.size.new(640,480) +w.size = pw.size.new(640,480) -- move window ---w.position = pw.point.new(100,100) +w.position = pw.point.new(100,100) print("client size after resize: ",w.client_size.width,w.client_size.height) @@ -32,7 +32,6 @@ end w.on_update = function(self) pl:draw() -- print("test on update",w.position.x,w.position.y,pw.time.now) - end -- show all displays @@ -51,6 +50,11 @@ while w:update() do w.fullscreen = not w.fullscreen end + -- keycode for quit + if (pw.input.get().input_string == 'q') then + break; + end + -- just to check if (pw.input:get().mouse_button == 1) then print("elapsed",t.elapsed) diff --git a/src/system/src/window.cpp b/src/system/src/window.cpp index 635dae7..002735a 100644 --- a/src/system/src/window.cpp +++ b/src/system/src/window.cpp @@ -207,16 +207,15 @@ struct window::impl { if (_window && !glfwWindowShouldClose(_window)) { - // // reset input - // input::get().reset(); + input::get().reset(); + + // get new events + glfwPollEvents(); _parent._on_update(_parent); glfwSwapBuffers(_window); - // // get new events - glfwPollEvents(); - return true; } diff --git a/src/visual/src/shader/unlit_vs.glsl b/src/visual/src/shader/unlit_vs.glsl index d67c8bb..a464bf3 100644 --- a/src/visual/src/shader/unlit_vs.glsl +++ b/src/visual/src/shader/unlit_vs.glsl @@ -1,5 +1,8 @@ #version 400 in vec3 vp; + +in mat4 mvp; + void main() { - gl_Position = vec4(vp, 1.0); + gl_Position = mvp * vec4(vp, 1.0); }