diff options
Diffstat (limited to 'external/entt/include')
78 files changed, 24084 insertions, 0 deletions
diff --git a/external/entt/include/entt/config/config.h b/external/entt/include/entt/config/config.h new file mode 100644 index 0000000..80a3305 --- /dev/null +++ b/external/entt/include/entt/config/config.h @@ -0,0 +1,95 @@ +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +#include "version.h" + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#else +# include <cstdint> // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif diff --git a/external/entt/include/entt/config/macro.h b/external/entt/include/entt/config/macro.h new file mode 100644 index 0000000..b7b2323 --- /dev/null +++ b/external/entt/include/entt/config/macro.h @@ -0,0 +1,11 @@ +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif diff --git a/external/entt/include/entt/config/version.h b/external/entt/include/entt/config/version.h new file mode 100644 index 0000000..843443f --- /dev/null +++ b/external/entt/include/entt/config/version.h @@ -0,0 +1,18 @@ +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +#include "macro.h" + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 14 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif diff --git a/external/entt/include/entt/container/dense_map.hpp b/external/entt/include/entt/container/dense_map.hpp new file mode 100644 index 0000000..6bf7238 --- /dev/null +++ b/external/entt/include/entt/container/dense_map.hpp @@ -0,0 +1,1043 @@ +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include <cmath> +#include <cstddef> +#include <functional> +#include <iterator> +#include <limits> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> +#include <vector> +#include "../config/config.h" +#include "../core/compressed_pair.hpp" +#include "../core/iterator.hpp" +#include "../core/memory.hpp" +#include "../core/bit.hpp" +#include "../core/type_traits.hpp" +#include "fwd.hpp" + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template<typename Key, typename Type> +struct dense_map_node final { + using value_type = std::pair<Key, Type>; + + template<typename... Args> + dense_map_node(const std::size_t pos, Args &&...args) + : next{pos}, + element{std::forward<Args>(args)...} {} + + template<typename Allocator, typename... Args> + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) + : next{pos}, + element{entt::make_obj_using_allocator<value_type>(allocator, std::forward<Args>(args)...)} {} + + template<typename Allocator> + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) + : next{other.next}, + element{entt::make_obj_using_allocator<value_type>(allocator, other.element)} {} + + template<typename Allocator> + dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) + : next{other.next}, + element{entt::make_obj_using_allocator<value_type>(allocator, std::move(other.element))} {} + + std::size_t next; + value_type element; +}; + +template<typename It> +class dense_map_iterator final { + template<typename> + friend class dense_map_iterator; + + using first_type = decltype(std::as_const(std::declval<It>()->element.first)); + using second_type = decltype((std::declval<It>()->element.second)); + +public: + using value_type = std::pair<first_type, second_type>; + using pointer = input_iterator_pointer<value_type>; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr dense_map_iterator() noexcept + : it{} {} + + constexpr dense_map_iterator(const It iter) noexcept + : it{iter} {} + + template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>> + constexpr dense_map_iterator(const dense_map_iterator<Other> &other) noexcept + : it{other.it} {} + + constexpr dense_map_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_map_iterator operator++(int) noexcept { + dense_map_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_map_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_map_iterator operator--(int) noexcept { + dense_map_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { + dense_map_iterator copy = *this; + return (copy += value); + } + + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + template<typename Lhs, typename Rhs> + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator<Lhs> &, const dense_map_iterator<Rhs> &) noexcept; + + template<typename Lhs, typename Rhs> + friend constexpr bool operator==(const dense_map_iterator<Lhs> &, const dense_map_iterator<Rhs> &) noexcept; + + template<typename Lhs, typename Rhs> + friend constexpr bool operator<(const dense_map_iterator<Lhs> &, const dense_map_iterator<Rhs> &) noexcept; + +private: + It it; +}; + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept { + return lhs.it - rhs.it; +} + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr bool operator==(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept { + return lhs.it == rhs.it; +} + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept { + return !(lhs == rhs); +} + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr bool operator<(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept { + return lhs.it < rhs.it; +} + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr bool operator>(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept { + return rhs < lhs; +} + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept { + return !(lhs > rhs); +} + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept { + return !(lhs < rhs); +} + +template<typename It> +class dense_map_local_iterator final { + template<typename> + friend class dense_map_local_iterator; + + using first_type = decltype(std::as_const(std::declval<It>()->element.first)); + using second_type = decltype((std::declval<It>()->element.second)); + +public: + using value_type = std::pair<first_type, second_type>; + using pointer = input_iterator_pointer<value_type>; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr dense_map_local_iterator() noexcept + : it{}, + offset{} {} + + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>> + constexpr dense_map_local_iterator(const dense_map_local_iterator<Other> &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_map_local_iterator &operator++() noexcept { + return offset = it[offset].next, *this; + } + + constexpr dense_map_local_iterator operator++(int) noexcept { + dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it[offset].element.first, it[offset].element.second}; + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator<Lhs> &lhs, const dense_map_local_iterator<Rhs> &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator<Lhs> &lhs, const dense_map_local_iterator<Rhs> &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Associative container for key-value pairs with unique keys. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on the hash of its key. Keys with the same hash + * code appear in the same bucket. + * + * @tparam Key Key type of the associative container. + * @tparam Type Mapped type of the associative container. + * @tparam Hash Type of function to use to hash the keys. + * @tparam KeyEqual Type of function to use to compare the keys for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Key, typename Type, typename Hash, typename KeyEqual, typename Allocator> +class dense_map { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + + using node_type = internal::dense_map_node<Key, Type>; + using alloc_traits = std::allocator_traits<Allocator>; + static_assert(std::is_same_v<typename alloc_traits::value_type, std::pair<const Key, Type>>, "Invalid value type"); + using sparse_container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>; + using packed_container_type = std::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>; + + template<typename Other> + [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { + return fast_mod(static_cast<size_type>(sparse.second()(key)), bucket_count()); + } + + template<typename Other> + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { + for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return begin() + static_cast<typename iterator::difference_type>(it.index()); + } + } + + return end(); + } + + template<typename Other> + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { + for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return cbegin() + static_cast<typename iterator::difference_type>(it.index()); + } + } + + return cend(); + } + + template<typename Other, typename... Args> + [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward<Other>(key)), std::forward_as_tuple(std::forward<Args>(args)...)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + template<typename Other, typename Arg> + [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + it->second = std::forward<Arg>(value); + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward<Other>(key), std::forward<Arg>(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = &sparse.first()[key_to_bucket(packed.first().back().element.first)]; + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].next) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(size() > (bucket_count() * max_load_factor())) { + rehash(bucket_count() * 2u); + } + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Key type of the container. */ + using key_type = Key; + /*! @brief Mapped type of the container. */ + using mapped_type = Type; + /*! @brief Key-value type of the container. */ + using value_type = std::pair<const Key, Type>; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of function to use to hash the keys. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the keys for equality. */ + using key_equal = KeyEqual; + /*! @brief Input iterator type. */ + using iterator = internal::dense_map_iterator<typename packed_container_type::iterator>; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::dense_map_iterator<typename packed_container_type::const_iterator>; + /*! @brief Input iterator type. */ + using local_iterator = internal::dense_map_local_iterator<typename packed_container_type::iterator>; + /*! @brief Constant input iterator type. */ + using const_local_iterator = internal::dense_map_local_iterator<typename packed_container_type::const_iterator>; + + /*! @brief Default constructor. */ + dense_map() + : dense_map{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_map(const allocator_type &allocator) + : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_map(const dense_map &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_map(const dense_map &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v<compressed_pair<sparse_container_type, hasher>> && std::is_nothrow_move_constructible_v<compressed_pair<packed_container_type, key_equal>>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_map(dense_map &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /*! @brief Default destructor. */ + ~dense_map() noexcept = default; + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_map &operator=(const dense_map &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v<compressed_pair<sparse_container_type, hasher>> && std::is_nothrow_move_assignable_v<compressed_pair<packed_container_type, key_equal>>) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair<iterator, bool> insert(const value_type &value) { + return insert_or_do_nothing(value.first, value.second); + } + + /*! @copydoc insert */ + std::pair<iterator, bool> insert(value_type &&value) { + return insert_or_do_nothing(std::move(value.first), std::move(value.second)); + } + + /** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ + template<typename Arg> + std::enable_if_t<std::is_constructible_v<value_type, Arg &&>, std::pair<iterator, bool>> + insert(Arg &&value) { + return insert_or_do_nothing(std::forward<Arg>(value).first, std::forward<Arg>(value).second); + } + + /** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template<typename It> + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ + template<typename Arg> + std::pair<iterator, bool> insert_or_assign(const key_type &key, Arg &&value) { + return insert_or_overwrite(key, std::forward<Arg>(value)); + } + + /*! @copydoc insert_or_assign */ + template<typename Arg> + std::pair<iterator, bool> insert_or_assign(key_type &&key, Arg &&value) { + return insert_or_overwrite(std::move(key), std::forward<Arg>(value)); + } + + /** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template<typename... Args> + std::pair<iterator, bool> emplace([[maybe_unused]] Args &&...args) { + if constexpr(sizeof...(Args) == 0u) { + return insert_or_do_nothing(key_type{}); + } else if constexpr(sizeof...(Args) == 1u) { + return insert_or_do_nothing(std::forward<Args>(args).first..., std::forward<Args>(args).second...); + } else if constexpr(sizeof...(Args) == 2u) { + return insert_or_do_nothing(std::forward<Args>(args)...); + } else { + auto &node = packed.first().emplace_back(packed.first().size(), std::forward<Args>(args)...); + const auto index = key_to_bucket(node.element.first); + + if(auto it = constrained_find(node.element.first, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.next, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template<typename... Args> + std::pair<iterator, bool> try_emplace(const key_type &key, Args &&...args) { + return insert_or_do_nothing(key, std::forward<Args>(args)...); + } + + /*! @copydoc try_emplace */ + template<typename... Args> + std::pair<iterator, bool> try_emplace(key_type &&key, Args &&...args) { + return insert_or_do_nothing(std::move(key), std::forward<Args>(args)...); + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(pos->first); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[from - 1u].element.first); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const key_type &key) { + for(size_type *curr = &sparse.first()[key_to_bucket(key)]; *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].next) { + if(packed.second()(packed.first()[*curr].element.first, key)) { + const auto index = *curr; + *curr = packed.first()[*curr].next; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_map &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &at(const key_type &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + [[nodiscard]] const mapped_type &at(const key_type &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](const key_type &key) { + return insert_or_do_nothing(key).first->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](key_type &&key) { + return insert_or_do_nothing(std::move(key)).first->second; + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template<typename Other> + [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, size_type>> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const key_type &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const key_type &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Finds an element with a key that compares _equivalent_ to a given + * key. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + template<typename Other> + [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, iterator>> + find(const Other &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + template<typename Other> + [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, const_iterator>> + find(const Other &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair<iterator, iterator> equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair<const_iterator, const_iterator> equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template<typename Other> + [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, std::pair<iterator, iterator>>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template<typename Other> + [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, std::pair<const_iterator, const_iterator>>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const key_type &key) const { + return (find(key) != cend()); + } + + /** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template<typename Other> + [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, bool>> + contains(const Other &key) const { + return (find(key) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {packed.first().begin(), (std::numeric_limits<size_type>::max)()}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {packed.first().begin(), (std::numeric_limits<size_type>::max)()}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast<size_type>(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ + [[nodiscard]] size_type bucket(const key_type &key) const { + return key_to_bucket(key); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return size() / static_cast<float>(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast<size_type>(size() / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = (std::numeric_limits<size_type>::max)(); + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = key_to_bucket(packed.first()[pos].element.first); + packed.first()[pos].next = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast<size_type>(std::ceil(cnt / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair<sparse_container_type, hasher> sparse; + compressed_pair<packed_container_type, key_equal> packed; + float threshold{default_threshold}; +}; + +} // namespace entt + +/*! @cond TURN_OFF_DOXYGEN */ +namespace std { + +template<typename Key, typename Value, typename Allocator> +struct uses_allocator<entt::internal::dense_map_node<Key, Value>, Allocator> + : std::true_type {}; + +} // namespace std +/*! @endcond */ + +#endif diff --git a/external/entt/include/entt/container/dense_set.hpp b/external/entt/include/entt/container/dense_set.hpp new file mode 100644 index 0000000..1ad5057 --- /dev/null +++ b/external/entt/include/entt/container/dense_set.hpp @@ -0,0 +1,927 @@ +#ifndef ENTT_CONTAINER_DENSE_SET_HPP +#define ENTT_CONTAINER_DENSE_SET_HPP + +#include <cmath> +#include <cstddef> +#include <functional> +#include <iterator> +#include <limits> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> +#include <vector> +#include "../config/config.h" +#include "../core/bit.hpp" +#include "../core/compressed_pair.hpp" +#include "../core/type_traits.hpp" +#include "fwd.hpp" + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template<typename It> +class dense_set_iterator final { + template<typename> + friend class dense_set_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::random_access_iterator_tag; + + constexpr dense_set_iterator() noexcept + : it{} {} + + constexpr dense_set_iterator(const It iter) noexcept + : it{iter} {} + + template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>> + constexpr dense_set_iterator(const dense_set_iterator<Other> &other) noexcept + : it{other.it} {} + + constexpr dense_set_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_set_iterator operator++(int) noexcept { + dense_set_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_set_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_set_iterator operator--(int) noexcept { + dense_set_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_set_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_set_iterator operator+(const difference_type value) const noexcept { + dense_set_iterator copy = *this; + return (copy += value); + } + + constexpr dense_set_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_set_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return it[value].second; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(operator[](0)); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + template<typename Lhs, typename Rhs> + friend constexpr std::ptrdiff_t operator-(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept; + + template<typename Lhs, typename Rhs> + friend constexpr bool operator==(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept; + + template<typename Lhs, typename Rhs> + friend constexpr bool operator<(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept; + +private: + It it; +}; + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept { + return lhs.it - rhs.it; +} + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr bool operator==(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept { + return lhs.it == rhs.it; +} + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr bool operator!=(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept { + return !(lhs == rhs); +} + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr bool operator<(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept { + return lhs.it < rhs.it; +} + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr bool operator>(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept { + return rhs < lhs; +} + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr bool operator<=(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept { + return !(lhs > rhs); +} + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr bool operator>=(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept { + return !(lhs < rhs); +} + +template<typename It> +class dense_set_local_iterator final { + template<typename> + friend class dense_set_local_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + constexpr dense_set_local_iterator() noexcept + : it{}, + offset{} {} + + constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>> + constexpr dense_set_local_iterator(const dense_set_local_iterator<Other> &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_set_local_iterator &operator++() noexcept { + return offset = it[offset].first, *this; + } + + constexpr dense_set_local_iterator operator++(int) noexcept { + dense_set_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(it[offset].second); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator<Lhs> &lhs, const dense_set_local_iterator<Rhs> &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator<Lhs> &lhs, const dense_set_local_iterator<Rhs> &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Associative container for unique objects of a given type. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on its hash. Elements with the same hash code + * appear in the same bucket. + * + * @tparam Type Value type of the associative container. + * @tparam Hash Type of function to use to hash the values. + * @tparam KeyEqual Type of function to use to compare the values for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Type, typename Hash, typename KeyEqual, typename Allocator> +class dense_set { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + + using node_type = std::pair<std::size_t, Type>; + using alloc_traits = std::allocator_traits<Allocator>; + static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type"); + using sparse_container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>; + using packed_container_type = std::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>; + + template<typename Other> + [[nodiscard]] std::size_t value_to_bucket(const Other &value) const noexcept { + return fast_mod(static_cast<size_type>(sparse.second()(value)), bucket_count()); + } + + template<typename Other> + [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) { + for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { + if(packed.second()(*it, value)) { + return begin() + static_cast<typename iterator::difference_type>(it.index()); + } + } + + return end(); + } + + template<typename Other> + [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) const { + for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { + if(packed.second()(*it, value)) { + return cbegin() + static_cast<typename iterator::difference_type>(it.index()); + } + } + + return cend(); + } + + template<typename Other> + [[nodiscard]] auto insert_or_do_nothing(Other &&value) { + const auto index = value_to_bucket(value); + + if(auto it = constrained_find(value, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward<Other>(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = &sparse.first()[value_to_bucket(packed.first().back().second)]; + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].first) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(size() > (bucket_count() * max_load_factor())) { + rehash(bucket_count() * 2u); + } + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Key type of the container. */ + using key_type = Type; + /*! @brief Value type of the container. */ + using value_type = Type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of function to use to hash the elements. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the elements for equality. */ + using key_equal = KeyEqual; + /*! @brief Random access iterator type. */ + using iterator = internal::dense_set_iterator<typename packed_container_type::iterator>; + /*! @brief Constant random access iterator type. */ + using const_iterator = internal::dense_set_iterator<typename packed_container_type::const_iterator>; + /*! @brief Reverse iterator type. */ + using reverse_iterator = std::reverse_iterator<iterator>; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = std::reverse_iterator<const_iterator>; + /*! @brief Forward iterator type. */ + using local_iterator = internal::dense_set_local_iterator<typename packed_container_type::iterator>; + /*! @brief Constant forward iterator type. */ + using const_local_iterator = internal::dense_set_local_iterator<typename packed_container_type::const_iterator>; + + /*! @brief Default constructor. */ + dense_set() + : dense_set{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_set(const allocator_type &allocator) + : dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const allocator_type &allocator) + : dense_set{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_set{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_set(const dense_set &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_set(const dense_set &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_set(dense_set &&) noexcept(std::is_nothrow_move_constructible_v<compressed_pair<sparse_container_type, hasher>> && std::is_nothrow_move_constructible_v<compressed_pair<packed_container_type, key_equal>>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_set(dense_set &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /*! @brief Default destructor. */ + ~dense_set() noexcept = default; + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_set &operator=(const dense_set &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_set &operator=(dense_set &&) noexcept(std::is_nothrow_move_assignable_v<compressed_pair<sparse_container_type, hasher>> && std::is_nothrow_move_assignable_v<compressed_pair<packed_container_type, key_equal>>) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first instance of the reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { + return std::make_reverse_iterator(cend()); + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() noexcept { + return std::make_reverse_iterator(end()); + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last instance of the + * reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return std::make_reverse_iterator(cbegin()); + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const noexcept { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() noexcept { + return std::make_reverse_iterator(begin()); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if it does not exist. + * @param value An element to insert into the container. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair<iterator, bool> insert(const value_type &value) { + return insert_or_do_nothing(value); + } + + /*! @copydoc insert */ + std::pair<iterator, bool> insert(value_type &&value) { + return insert_or_do_nothing(std::move(value)); + } + + /** + * @brief Inserts elements into the container, if they do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template<typename It> + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Constructs an element in-place, if it does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template<typename... Args> + std::pair<iterator, bool> emplace(Args &&...args) { + if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v<std::decay_t<Args>, value_type>)) { + return insert_or_do_nothing(std::forward<Args>(args)...); + } else { + auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward<Args>(args)...)); + const auto index = value_to_bucket(node.second); + + if(auto it = constrained_find(node.second, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.first, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(*pos); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[from - 1u].second); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given value. + * @param value Value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const value_type &value) { + for(size_type *curr = &sparse.first()[value_to_bucket(value)]; *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].first) { + if(packed.second()(packed.first()[*curr].second, value)) { + const auto index = *curr; + *curr = packed.first()[*curr].first; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_set &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Returns the number of elements matching a value (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const value_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template<typename Other> + [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, size_type>> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given value. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const value_type &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const value_type &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Finds an element that compares _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + template<typename Other> + [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, iterator>> + find(const Other &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + template<typename Other> + [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, const_iterator>> + find(const Other &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Returns a range containing all elements with a given value. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair<iterator, iterator> equal_range(const value_type &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair<const_iterator, const_iterator> equal_range(const value_type &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template<typename Other> + [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, std::pair<iterator, iterator>>> + equal_range(const Other &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template<typename Other> + [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, std::pair<const_iterator, const_iterator>>> + equal_range(const Other &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given value. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const value_type &value) const { + return (find(value) != cend()); + } + + /** + * @brief Checks if the container contains an element that compares + * _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template<typename Other> + [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, bool>> + contains(const Other &value) const { + return (find(value) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {packed.first().begin(), (std::numeric_limits<size_type>::max)()}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {packed.first().begin(), (std::numeric_limits<size_type>::max)()}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast<size_type>(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given element. + * @param value The value of the element to examine. + * @return The bucket for the given element. + */ + [[nodiscard]] size_type bucket(const value_type &value) const { + return value_to_bucket(value); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return size() / static_cast<float>(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast<size_type>(size() / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = (std::numeric_limits<size_type>::max)(); + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = value_to_bucket(packed.first()[pos].second); + packed.first()[pos].first = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast<size_type>(std::ceil(cnt / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the elements. + * @return The function used to hash the elements. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare elements for equality. + * @return The function used to compare elements for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair<sparse_container_type, hasher> sparse; + compressed_pair<packed_container_type, key_equal> packed; + float threshold{default_threshold}; +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/container/fwd.hpp b/external/entt/include/entt/container/fwd.hpp new file mode 100644 index 0000000..4622263 --- /dev/null +++ b/external/entt/include/entt/container/fwd.hpp @@ -0,0 +1,38 @@ +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include <functional> +#include <memory> +#include <utility> +#include <vector> + +namespace entt { + +template< + typename Key, + typename Type, + typename = std::hash<Key>, + typename = std::equal_to<>, + typename = std::allocator<std::pair<const Key, Type>>> +class dense_map; + +template< + typename Type, + typename = std::hash<Type>, + typename = std::equal_to<>, + typename = std::allocator<Type>> +class dense_set; + +template<typename...> +class basic_table; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Type Element types. + */ +template<typename... Type> +using table = basic_table<std::vector<Type>...>; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/container/table.hpp b/external/entt/include/entt/container/table.hpp new file mode 100644 index 0000000..f69e221 --- /dev/null +++ b/external/entt/include/entt/container/table.hpp @@ -0,0 +1,460 @@ +#ifndef ENTT_CONTAINER_TABLE_HPP +#define ENTT_CONTAINER_TABLE_HPP + +#include <cstddef> +#include <iterator> +#include <tuple> +#include <type_traits> +#include <utility> +#include "../config/config.h" +#include "../core/iterator.hpp" +#include "fwd.hpp" + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template<typename... It> +class table_iterator { + template<typename...> + friend class table_iterator; + +public: + using value_type = decltype(std::forward_as_tuple(*std::declval<It>()...)); + using pointer = input_iterator_pointer<value_type>; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr table_iterator() noexcept + : it{} {} + + constexpr table_iterator(It... from) noexcept + : it{from...} {} + + template<typename... Other, typename = std::enable_if_t<(std::is_constructible_v<It, Other> && ...)>> + constexpr table_iterator(const table_iterator<Other...> &other) noexcept + : table_iterator{std::get<Other>(other.it)...} {} + + constexpr table_iterator &operator++() noexcept { + return (++std::get<It>(it), ...), *this; + } + + constexpr table_iterator operator++(int) noexcept { + table_iterator orig = *this; + return ++(*this), orig; + } + + constexpr table_iterator &operator--() noexcept { + return (--std::get<It>(it), ...), *this; + } + + constexpr table_iterator operator--(int) noexcept { + table_iterator orig = *this; + return operator--(), orig; + } + + constexpr table_iterator &operator+=(const difference_type value) noexcept { + return ((std::get<It>(it) += value), ...), *this; + } + + constexpr table_iterator operator+(const difference_type value) const noexcept { + table_iterator copy = *this; + return (copy += value); + } + + constexpr table_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr table_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return std::forward_as_tuple(std::get<It>(it)[value]...); + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return {operator[](0)}; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + template<typename... Lhs, typename... Rhs> + friend constexpr std::ptrdiff_t operator-(const table_iterator<Lhs...> &, const table_iterator<Rhs...> &) noexcept; + + template<typename... Lhs, typename... Rhs> + friend constexpr bool operator==(const table_iterator<Lhs...> &, const table_iterator<Rhs...> &) noexcept; + + template<typename... Lhs, typename... Rhs> + friend constexpr bool operator<(const table_iterator<Lhs...> &, const table_iterator<Rhs...> &) noexcept; + +private: + std::tuple<It...> it; +}; + +template<typename... Lhs, typename... Rhs> +[[nodiscard]] constexpr std::ptrdiff_t operator-(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept { + return std::get<0>(lhs.it) - std::get<0>(rhs.it); +} + +template<typename... Lhs, typename... Rhs> +[[nodiscard]] constexpr bool operator==(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept { + return std::get<0>(lhs.it) == std::get<0>(rhs.it); +} + +template<typename... Lhs, typename... Rhs> +[[nodiscard]] constexpr bool operator!=(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept { + return !(lhs == rhs); +} + +template<typename... Lhs, typename... Rhs> +[[nodiscard]] constexpr bool operator<(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept { + return std::get<0>(lhs.it) < std::get<0>(rhs.it); +} + +template<typename... Lhs, typename... Rhs> +[[nodiscard]] constexpr bool operator>(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept { + return rhs < lhs; +} + +template<typename... Lhs, typename... Rhs> +[[nodiscard]] constexpr bool operator<=(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept { + return !(lhs > rhs); +} + +template<typename... Lhs, typename... Rhs> +[[nodiscard]] constexpr bool operator>=(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept { + return !(lhs < rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic table implementation. + * + * Internal data structures arrange elements to maximize performance. There are + * no guarantees that objects are returned in the insertion order when iterate + * a table. Do not make assumption on the order in any case. + * + * @tparam Container Sequence container row types. + */ +template<typename... Container> +class basic_table { + using container_type = std::tuple<Container...>; + +public: + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Input iterator type. */ + using iterator = internal::table_iterator<typename Container::iterator...>; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::table_iterator<typename Container::const_iterator...>; + /*! @brief Reverse iterator type. */ + using reverse_iterator = internal::table_iterator<typename Container::reverse_iterator...>; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = internal::table_iterator<typename Container::const_reverse_iterator...>; + + /*! @brief Default constructor. */ + basic_table() + : payload{} { + } + + /** + * @brief Copy constructs the underlying containers. + * @param container The containers to copy from. + */ + explicit basic_table(const Container &...container) noexcept + : payload{container...} { + ENTT_ASSERT((((std::get<Container>(payload).size() * sizeof...(Container)) == (std::get<Container>(payload).size() + ...)) && ...), "Unexpected container size"); + } + + /** + * @brief Move constructs the underlying containers. + * @param container The containers to move from. + */ + explicit basic_table(Container &&...container) noexcept + : payload{std::move(container)...} { + ENTT_ASSERT((((std::get<Container>(payload).size() * sizeof...(Container)) == (std::get<Container>(payload).size() + ...)) && ...), "Unexpected container size"); + } + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_table(const basic_table &) = delete; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_table(basic_table &&other) noexcept + : payload{std::move(other.payload)} {} + + /** + * @brief Constructs the underlying containers using a given allocator. + * @tparam Allocator Type of allocator. + * @param allocator A valid allocator. + */ + template<typename Allocator> + explicit basic_table(const Allocator &allocator) + : payload{Container{allocator}...} {} + + /** + * @brief Copy constructs the underlying containers using a given allocator. + * @tparam Allocator Type of allocator. + * @param container The containers to copy from. + * @param allocator A valid allocator. + */ + template<class Allocator> + basic_table(const Container &...container, const Allocator &allocator) noexcept + : payload{Container{container, allocator}...} { + ENTT_ASSERT((((std::get<Container>(payload).size() * sizeof...(Container)) == (std::get<Container>(payload).size() + ...)) && ...), "Unexpected container size"); + } + + /** + * @brief Move constructs the underlying containers using a given allocator. + * @tparam Allocator Type of allocator. + * @param container The containers to move from. + * @param allocator A valid allocator. + */ + template<class Allocator> + basic_table(Container &&...container, const Allocator &allocator) noexcept + : payload{Container{std::move(container), allocator}...} { + ENTT_ASSERT((((std::get<Container>(payload).size() * sizeof...(Container)) == (std::get<Container>(payload).size() + ...)) && ...), "Unexpected container size"); + } + + /** + * @brief Allocator-extended move constructor. + * @tparam Allocator Type of allocator. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + template<class Allocator> + basic_table(basic_table &&other, const Allocator &allocator) + : payload{Container{std::move(std::get<Container>(other.payload)), allocator}...} {} + + /*! @brief Default destructor. */ + ~basic_table() noexcept = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This container. + */ + basic_table &operator=(const basic_table &) = delete; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This container. + */ + basic_table &operator=(basic_table &&other) noexcept { + payload = std::move(other.payload); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given table. + * @param other Table to exchange the content with. + */ + void swap(basic_table &other) { + using std::swap; + swap(payload, other.payload); + } + + /** + * @brief Increases the capacity of a table. + * + * 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) { + (std::get<Container>(payload).reserve(cap), ...); + } + + /** + * @brief Returns the number of rows that a table has currently allocated + * space for. + * @return Capacity of the table. + */ + [[nodiscard]] size_type capacity() const noexcept { + return std::get<0>(payload).capacity(); + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() { + (std::get<Container>(payload).shrink_to_fit(), ...); + } + + /** + * @brief Returns the number of rows in a table. + * @return Number of rows. + */ + [[nodiscard]] size_type size() const noexcept { + return std::get<0>(payload).size(); + } + + /** + * @brief Checks whether a table is empty. + * @return True if the table is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return std::get<0>(payload).empty(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the table is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first row of the table. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return {std::get<Container>(payload).cbegin()...}; + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return {std::get<Container>(payload).begin()...}; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last row of the table. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return {std::get<Container>(payload).cend()...}; + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return {std::get<Container>(payload).end()...}; + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * If the table is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first row of the reversed table. + */ + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { + return {std::get<Container>(payload).crbegin()...}; + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() noexcept { + return {std::get<Container>(payload).rbegin()...}; + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last row of the reversed + * table. + */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return {std::get<Container>(payload).crend()...}; + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const noexcept { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() noexcept { + return {std::get<Container>(payload).rend()...}; + } + + /** + * @brief Appends a row to the end of a table. + * @tparam Args Types of arguments to use to construct the row data. + * @param args Parameters to use to construct the row data. + * @return A reference to the newly created row data. + */ + template<typename... Args> + std::tuple<typename Container::value_type &...> emplace(Args &&...args) { + if constexpr(sizeof...(Args) == 0u) { + return std::forward_as_tuple(std::get<Container>(payload).emplace_back()...); + } else { + return std::forward_as_tuple(std::get<Container>(payload).emplace_back(std::forward<Args>(args))...); + } + } + + /** + * @brief Removes a row from a table. + * @param pos An iterator to the row to remove. + * @return An iterator following the removed row. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - begin(); + return {std::get<Container>(payload).erase(std::get<Container>(payload).begin() + diff)...}; + } + + /** + * @brief Removes a row from a table. + * @param pos Index of the row to remove. + */ + void erase(const size_type pos) { + ENTT_ASSERT(pos < size(), "Index out of bounds"); + erase(begin() + static_cast<typename const_iterator::difference_type>(pos)); + } + + /** + * @brief Returns the row data at specified location. + * @param pos The row for which to return the data. + * @return The row data at specified location. + */ + [[nodiscard]] std::tuple<const typename Container::value_type &...> operator[](const size_type pos) const { + ENTT_ASSERT(pos < size(), "Index out of bounds"); + return std::forward_as_tuple(std::get<Container>(payload)[pos]...); + } + + /*! @copydoc operator[] */ + [[nodiscard]] std::tuple<typename Container::value_type &...> operator[](const size_type pos) { + ENTT_ASSERT(pos < size(), "Index out of bounds"); + return std::forward_as_tuple(std::get<Container>(payload)[pos]...); + } + + /*! @brief Clears a table. */ + void clear() { + (std::get<Container>(payload).clear(), ...); + } + +private: + container_type payload; +}; + +} // namespace entt + +/*! @cond TURN_OFF_DOXYGEN */ +namespace std { + +template<typename... Container, typename Allocator> +struct uses_allocator<entt::basic_table<Container...>, Allocator> + : std::bool_constant<(std::uses_allocator_v<Container, Allocator> && ...)> {}; + +} // namespace std +/*! @endcond */ + +#endif diff --git a/external/entt/include/entt/core/algorithm.hpp b/external/entt/include/entt/core/algorithm.hpp new file mode 100644 index 0000000..c3c6354 --- /dev/null +++ b/external/entt/include/entt/core/algorithm.hpp @@ -0,0 +1,143 @@ +#ifndef ENTT_CORE_ALGORITHM_HPP +#define ENTT_CORE_ALGORITHM_HPP + +#include <algorithm> +#include <functional> +#include <iterator> +#include <utility> +#include <vector> +#include "utility.hpp" + +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.<br/> + * 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 It, typename Compare = std::less<>, typename... Args> + void operator()(It first, It last, Compare compare = Compare{}, Args &&...args) const { + std::sort(std::forward<Args>(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<typename It, typename Compare = std::less<>> + 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; + + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) + for(; pre > first && compare(value, *(pre - 1)); --pre) { + *pre = std::move(*(pre - 1)); + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + + *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<std::size_t Bit, std::size_t N> +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<typename It, typename Getter = identity> + void operator()(It first, It last, Getter getter = Getter{}) const { + if(first < last) { + constexpr auto passes = N / Bit; + + using value_type = typename std::iterator_traits<It>::value_type; + std::vector<value_type> aux(std::distance(first, last)); + + auto part = [getter = std::move(getter)](auto from, auto to, auto out, auto start) { + constexpr auto mask = (1 << Bit) - 1; + constexpr auto buckets = 1 << Bit; + + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) + std::size_t count[buckets]{}; + + for(auto it = from; it != to; ++it) { + ++count[(getter(*it) >> start) & mask]; + } + + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) + std::size_t index[buckets]{}; + + for(std::size_t pos{}, end = buckets - 1u; pos < end; ++pos) { + index[pos + 1u] = index[pos] + count[pos]; + } + + for(auto it = from; it != to; ++it) { + out[index[(getter(*it) >> start) & mask]++] = std::move(*it); + } + }; + + for(std::size_t pass = 0; pass < (passes & ~1); pass += 2) { + part(first, last, aux.begin(), pass * Bit); + part(aux.begin(), aux.end(), first, (pass + 1) * Bit); + } + + if constexpr(passes & 1) { + part(first, last, aux.begin(), (passes - 1) * Bit); + std::move(aux.begin(), aux.end(), first); + } + } + } +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/core/any.hpp b/external/entt/include/entt/core/any.hpp new file mode 100644 index 0000000..65d8736 --- /dev/null +++ b/external/entt/include/entt/core/any.hpp @@ -0,0 +1,513 @@ +#ifndef ENTT_CORE_ANY_HPP +#define ENTT_CORE_ANY_HPP + +#include <cstddef> +#include <memory> +#include <type_traits> +#include <utility> +#include "../config/config.h" +#include "../core/utility.hpp" +#include "fwd.hpp" +#include "type_info.hpp" +#include "type_traits.hpp" + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +enum class any_operation : std::uint8_t { + copy, + move, + transfer, + assign, + destroy, + compare, + get +}; + +} // namespace internal +/*! @endcond */ + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, the object owns the contained element. */ + owner, + /*! @brief Aliasing mode, the object _points_ to a non-const element. */ + ref, + /*! @brief Const aliasing mode, the object _points_ to a const element. */ + cref +}; + +/** + * @brief A SBO friendly, type-safe container for single values of any type. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + */ +template<std::size_t Len, std::size_t Align> +class basic_any { + using operation = internal::any_operation; + using vtable_type = const void *(const operation, const basic_any &, const void *); + + struct storage_type { + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) + alignas(Align) std::byte data[Len + !Len]; + }; + + template<typename Type> + static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v<Type>; + + template<typename Type> + static const void *basic_vtable(const operation op, const basic_any &value, const void *other) { + static_assert(!std::is_void_v<Type> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type"); + const Type *elem = nullptr; + + if constexpr(in_situ<Type>) { + elem = (value.mode == any_policy::owner) ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance); + } else { + elem = static_cast<const Type *>(value.instance); + } + + switch(op) { + case operation::copy: + if constexpr(std::is_copy_constructible_v<Type>) { + static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*elem); + } + break; + case operation::move: + if constexpr(in_situ<Type>) { + if(value.mode == any_policy::owner) { + return ::new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(elem))}; + } + } + + return (static_cast<basic_any *>(const_cast<void *>(other))->instance = std::exchange(const_cast<basic_any &>(value).instance, nullptr)); + case operation::transfer: + if constexpr(std::is_move_assignable_v<Type>) { + *const_cast<Type *>(elem) = std::move(*static_cast<Type *>(const_cast<void *>(other))); + return other; + } + [[fallthrough]]; + case operation::assign: + if constexpr(std::is_copy_assignable_v<Type>) { + *const_cast<Type *>(elem) = *static_cast<const Type *>(other); + return other; + } + break; + case operation::destroy: + if constexpr(in_situ<Type>) { + elem->~Type(); + } else if constexpr(std::is_array_v<Type>) { + delete[] elem; + } else { + delete elem; + } + break; + case operation::compare: + if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) { + return *elem == *static_cast<const Type *>(other) ? other : nullptr; + } else { + return (elem == other) ? other : nullptr; + } + case operation::get: + return elem; + } + + return nullptr; + } + + template<typename Type, typename... Args> + void initialize([[maybe_unused]] Args &&...args) { + using plain_type = std::remove_cv_t<std::remove_reference_t<Type>>; + info = &type_id<plain_type>(); + + if constexpr(!std::is_void_v<Type>) { + vtable = basic_vtable<plain_type>; + + if constexpr(std::is_lvalue_reference_v<Type>) { + static_assert((std::is_lvalue_reference_v<Args> && ...) && (sizeof...(Args) == 1u), "Invalid arguments"); + mode = std::is_const_v<std::remove_reference_t<Type>> ? any_policy::cref : any_policy::ref; + instance = (std::addressof(args), ...); + } else if constexpr(in_situ<plain_type>) { + if constexpr(std::is_aggregate_v<plain_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<plain_type>)) { + ::new(&storage) plain_type{std::forward<Args>(args)...}; + } else { + ::new(&storage) plain_type(std::forward<Args>(args)...); + } + } else { + if constexpr(std::is_aggregate_v<plain_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<plain_type>)) { + instance = new plain_type{std::forward<Args>(args)...}; + } else if constexpr(std::is_array_v<plain_type>) { + static_assert(sizeof...(Args) == 0u, "Invalid arguments"); + instance = new plain_type[std::extent_v<plain_type>](); + } else { + instance = new plain_type(std::forward<Args>(args)...); + } + } + } + } + + basic_any(const basic_any &other, const any_policy pol) noexcept + : instance{other.data()}, + info{other.info}, + vtable{other.vtable}, + mode{pol} {} + +public: + /*! @brief Size of the internal storage. */ + static constexpr auto length = Len; + /*! @brief Alignment requirement. */ + static constexpr auto alignment = Align; + + /*! @brief Default constructor. */ + constexpr basic_any() noexcept + : basic_any{std::in_place_type<void>} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template<typename Type, typename... Args> + explicit basic_any(std::in_place_type_t<Type>, Args &&...args) + : instance{}, + info{}, + vtable{}, + mode{any_policy::owner} { + initialize<Type>(std::forward<Args>(args)...); + } + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>> + basic_any(Type &&value) + : basic_any{std::in_place_type<std::decay_t<Type>>, std::forward<Type>(value)} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + basic_any(const basic_any &other) + : basic_any{} { + if(other.vtable) { + other.vtable(operation::copy, other, this); + } + } + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_any(basic_any &&other) noexcept + : instance{}, + info{other.info}, + vtable{other.vtable}, + mode{other.mode} { + if(other.vtable) { + other.vtable(operation::move, other, this); + } + } + + /*! @brief Frees the internal storage, whatever it means. */ + ~basic_any() noexcept { + if(vtable && (mode == any_policy::owner)) { + vtable(operation::destroy, *this, nullptr); + } + } + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This any object. + */ + basic_any &operator=(const basic_any &other) { + if(this != &other) { + reset(); + + if(other.vtable) { + other.vtable(operation::copy, other, this); + } + } + + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This any object. + */ + basic_any &operator=(basic_any &&other) noexcept { + ENTT_ASSERT(this != &other, "Self move assignment"); + + reset(); + + if(other.vtable) { + other.vtable(operation::move, other, this); + info = other.info; + vtable = other.vtable; + mode = other.mode; + } + + return *this; + } + + /** + * @brief Value assignment operator. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + * @return This any object. + */ + template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>> + basic_any &operator=(Type &&value) { + emplace<std::decay_t<Type>>(std::forward<Type>(value)); + return *this; + } + + /** + * @brief Returns the object type if any, `type_id<void>()` otherwise. + * @return The object type if any, `type_id<void>()` otherwise. + */ + [[nodiscard]] const type_info &type() const noexcept { + return *info; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data() const noexcept { + return vtable ? vtable(operation::get, *this, nullptr) : nullptr; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data(const type_info &req) const noexcept { + return *info == req ? data() : nullptr; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] void *data() noexcept { + return mode == any_policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data()); + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] void *data(const type_info &req) noexcept { + return mode == any_policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data(req)); + } + + /** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template<typename Type, typename... Args> + void emplace(Args &&...args) { + reset(); + initialize<Type>(std::forward<Args>(args)...); + } + + /** + * @brief Assigns a value to the contained object without replacing it. + * @param other The value to assign to the contained object. + * @return True in case of success, false otherwise. + */ + bool assign(const basic_any &other) { + if(vtable && mode != any_policy::cref && *info == *other.info) { + return (vtable(operation::assign, *this, other.data()) != nullptr); + } + + return false; + } + + /*! @copydoc assign */ + bool assign(basic_any &&other) { + if(vtable && mode != any_policy::cref && *info == *other.info) { + if(auto *val = other.data(); val) { + return (vtable(operation::transfer, *this, val) != nullptr); + } else { + return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr); + } + } + + return false; + } + + /*! @brief Destroys contained object */ + void reset() { + if(vtable && (mode == any_policy::owner)) { + vtable(operation::destroy, *this, nullptr); + } + + // unnecessary but it helps to detect nasty bugs + ENTT_ASSERT((instance = nullptr) == nullptr, ""); + info = &type_id<void>(); + vtable = nullptr; + mode = any_policy::owner; + } + + /** + * @brief Returns false if a wrapper is empty, true otherwise. + * @return False if the wrapper is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return vtable != nullptr; + } + + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return False if the two objects differ in their content, true otherwise. + */ + [[nodiscard]] bool operator==(const basic_any &other) const noexcept { + if(vtable && *info == *other.info) { + return (vtable(operation::compare, *this, other.data()) != nullptr); + } + + return (!vtable && !other.vtable); + } + + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return True if the two objects differ in their content, false otherwise. + */ + [[nodiscard]] bool operator!=(const basic_any &other) const noexcept { + return !(*this == other); + } + + /** + * @brief Aliasing constructor. + * @return A wrapper that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_any as_ref() noexcept { + return basic_any{*this, (mode == any_policy::cref ? any_policy::cref : any_policy::ref)}; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_any as_ref() const noexcept { + return basic_any{*this, any_policy::cref}; + } + + /** + * @brief Returns the current mode of an any object. + * @return The current mode of the any object. + */ + [[nodiscard]] any_policy policy() const noexcept { + return mode; + } + +private: + union { + const void *instance; + storage_type storage; + }; + const type_info *info; + vtable_type *vtable; + any_policy mode; +}; + +/** + * @brief Performs type-safe access to the contained object. + * @tparam Type Type to which conversion is required. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param data Target any object. + * @return The element converted to the requested type. + */ +template<typename Type, std::size_t Len, std::size_t Align> +[[nodiscard]] Type any_cast(const basic_any<Len, Align> &data) noexcept { + const auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast<Type>(*instance); +} + +/*! @copydoc any_cast */ +template<typename Type, std::size_t Len, std::size_t Align> +[[nodiscard]] Type any_cast(basic_any<Len, Align> &data) noexcept { + // forces const on non-reference types to make them work also with wrappers for const references + auto *const instance = any_cast<std::remove_reference_t<const Type>>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast<Type>(*instance); +} + +/*! @copydoc any_cast */ +template<typename Type, std::size_t Len, std::size_t Align> +[[nodiscard]] Type any_cast(basic_any<Len, Align> &&data) noexcept { + if constexpr(std::is_copy_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>) { + if(auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); instance) { + return static_cast<Type>(std::move(*instance)); + } else { + return any_cast<Type>(data); + } + } else { + auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast<Type>(std::move(*instance)); + } +} + +/*! @copydoc any_cast */ +template<typename Type, std::size_t Len, std::size_t Align> +[[nodiscard]] const Type *any_cast(const basic_any<Len, Align> *data) noexcept { + const auto &info = type_id<std::remove_cv_t<Type>>(); + return static_cast<const Type *>(data->data(info)); +} + +/*! @copydoc any_cast */ +template<typename Type, std::size_t Len, std::size_t Align> +[[nodiscard]] Type *any_cast(basic_any<Len, Align> *data) noexcept { + if constexpr(std::is_const_v<Type>) { + // last attempt to make wrappers for const references return their values + return any_cast<Type>(&std::as_const(*data)); + } else { + const auto &info = type_id<std::remove_cv_t<Type>>(); + return static_cast<Type *>(data->data(info)); + } +} + +/** + * @brief Constructs a wrapper from a given type, passing it all arguments. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + * @return A properly initialized wrapper for an object of the given type. + */ +template<typename Type, std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename... Args> +[[nodiscard]] basic_any<Len, Align> make_any(Args &&...args) { + return basic_any<Len, Align>{std::in_place_type<Type>, std::forward<Args>(args)...}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @return A properly initialized and not necessarily owning wrapper. + */ +template<std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename Type> +[[nodiscard]] basic_any<Len, Align> forward_as_any(Type &&value) { + return basic_any<Len, Align>{std::in_place_type<Type &&>, std::forward<Type>(value)}; +} + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/core/attribute.h b/external/entt/include/entt/core/attribute.h new file mode 100644 index 0000000..b1d0503 --- /dev/null +++ b/external/entt/include/entt/core/attribute.h @@ -0,0 +1,30 @@ +#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 diff --git a/external/entt/include/entt/core/bit.hpp b/external/entt/include/entt/core/bit.hpp new file mode 100644 index 0000000..6622bd6 --- /dev/null +++ b/external/entt/include/entt/core/bit.hpp @@ -0,0 +1,69 @@ +#ifndef ENTT_CORE_BIT_HPP +#define ENTT_CORE_BIT_HPP + +#include <cstddef> +#include <limits> +#include <type_traits> +#include "../config/config.h" + +namespace entt { + +/** + * @brief Returns the number of set bits in a value (waiting for C++20 and + * `std::popcount`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return The number of set bits in the value. + */ +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, int> popcount(const Type value) noexcept { + return value ? (int(value & 1) + popcount(static_cast<Type>(value >> 1))) : 0; +} + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return True if the value is a power of two, false otherwise. + */ +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, bool> has_single_bit(const Type value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return The smallest power of two greater than or equal to the given value. + */ +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, Type> next_power_of_two(const Type value) noexcept { + ENTT_ASSERT_CONSTEXPR(value < (Type{1u} << (std::numeric_limits<Type>::digits - 1)), "Numeric limits exceeded"); + Type curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits<Type>::digits; next = next * 2) { + curr |= (curr >> next); + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, Type> fast_mod(const Type value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(has_single_bit(mod), "Value must be a power of two"); + return value & (mod - 1u); +} + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/core/compressed_pair.hpp b/external/entt/include/entt/core/compressed_pair.hpp new file mode 100644 index 0000000..f1bea5a --- /dev/null +++ b/external/entt/include/entt/core/compressed_pair.hpp @@ -0,0 +1,272 @@ +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include <cstddef> +#include <tuple> +#include <type_traits> +#include <utility> +#include "fwd.hpp" +#include "type_traits.hpp" + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template<typename Type, std::size_t, typename = void> +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template<typename Dummy = Type, typename = std::enable_if_t<std::is_default_constructible_v<Dummy>>> + // NOLINTNEXTLINE(modernize-use-equals-default) + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<Type>) {} + + template<typename Arg, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Arg>>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v<Type, Arg>) + : value{std::forward<Arg>(arg)} {} + + template<typename... Args, std::size_t... Index> + constexpr compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) noexcept(std::is_nothrow_constructible_v<Type, Args...>) + : value{std::forward<Args>(std::get<Index>(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value{}; +}; + +template<typename Type, std::size_t Tag> +struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Type>>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template<typename Dummy = Type, typename = std::enable_if_t<std::is_default_constructible_v<Dummy>>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<base_type>) + : base_type{} {} + + template<typename Arg, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Arg>>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v<base_type, Arg>) + : base_type{std::forward<Arg>(arg)} {} + + template<typename... Args, std::size_t... Index> + constexpr compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) noexcept(std::is_nothrow_constructible_v<base_type, Args...>) + : base_type{std::forward<Args>(std::get<Index>(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<typename First, typename Second> +class compressed_pair final + : internal::compressed_pair_element<First, 0u>, + internal::compressed_pair_element<Second, 1u> { + using first_base = internal::compressed_pair_element<First, 0u>; + using second_base = internal::compressed_pair_element<Second, 1u>; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v<first_base> && std::is_nothrow_default_constructible_v<second_base>) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v<first_base> && std::is_nothrow_copy_constructible_v<second_base>) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v<first_base> && std::is_nothrow_move_constructible_v<second_base>) = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template<typename Arg, typename Other> + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v<first_base, Arg> && std::is_nothrow_constructible_v<second_base, Other>) + : first_base{std::forward<Arg>(arg)}, + second_base{std::forward<Other>(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template<typename... Args, typename... Other> + constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other) noexcept(std::is_nothrow_constructible_v<first_base, Args...> && std::is_nothrow_constructible_v<second_base, Other...>) + : first_base{std::move(args), std::index_sequence_for<Args...>{}}, + second_base{std::move(other), std::index_sequence_for<Other...>{}} {} + + /*! @brief Default destructor. */ + ~compressed_pair() noexcept(std::is_nothrow_destructible_v<First> && std::is_nothrow_destructible_v<Second>) = default; + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v<first_base> && std::is_nothrow_copy_assignable_v<second_base>) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v<first_base> && std::is_nothrow_move_assignable_v<second_base>) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast<first_base &>(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast<const first_base &>(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast<second_base &>(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast<const second_base &>(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v<first_type> && std::is_nothrow_swappable_v<second_type>) { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template<std::size_t Index> + [[nodiscard]] constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template<std::size_t Index> + [[nodiscard]] constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template<typename Type, typename Other> +compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::decay_t<Other>>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template<typename First, typename Second> +inline constexpr void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) { + lhs.swap(rhs); +} + +} // namespace entt + +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<typename First, typename Second> +struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_t, 2u> {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<size_t Index, typename First, typename Second> +struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<Index == 0u, First, Second> { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std + +#endif diff --git a/external/entt/include/entt/core/enum.hpp b/external/entt/include/entt/core/enum.hpp new file mode 100644 index 0000000..5922c2f --- /dev/null +++ b/external/entt/include/entt/core/enum.hpp @@ -0,0 +1,97 @@ +#ifndef ENTT_CORE_ENUM_HPP +#define ENTT_CORE_ENUM_HPP + +#include <type_traits> + +namespace entt { + +/** + * @brief Enable bitmask support for enum classes. + * @tparam Type The enum type for which to enable bitmask support. + */ +template<typename Type, typename = void> +struct enum_as_bitmask: std::false_type {}; + +/*! @copydoc enum_as_bitmask */ +template<typename Type> +struct enum_as_bitmask<Type, std::void_t<decltype(Type::_entt_enum_as_bitmask)>>: std::is_enum<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type The enum class type for which to enable bitmask support. + */ +template<typename Type> +inline constexpr bool enum_as_bitmask_v = enum_as_bitmask<Type>::value; + +} // namespace entt + +/** + * @brief Operator available for enums for which bitmask support is enabled. + * @tparam Type Enum class type. + * @param lhs The first value to use. + * @param rhs The second value to use. + * @return The result of invoking the operator on the underlying types of the + * two values provided. + */ +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type> +operator|(const Type lhs, const Type rhs) noexcept { + return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) | static_cast<std::underlying_type_t<Type>>(rhs)); +} + +/*! @copydoc operator| */ +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type> +operator&(const Type lhs, const Type rhs) noexcept { + return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) & static_cast<std::underlying_type_t<Type>>(rhs)); +} + +/*! @copydoc operator| */ +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type> +operator^(const Type lhs, const Type rhs) noexcept { + return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) ^ static_cast<std::underlying_type_t<Type>>(rhs)); +} + +/** + * @brief Operator available for enums for which bitmask support is enabled. + * @tparam Type Enum class type. + * @param value The value to use. + * @return The result of invoking the operator on the underlying types of the + * value provided. + */ +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type> +operator~(const Type value) noexcept { + return static_cast<Type>(~static_cast<std::underlying_type_t<Type>>(value)); +} + +/*! @copydoc operator~ */ +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, bool> +operator!(const Type value) noexcept { + return !static_cast<std::underlying_type_t<Type>>(value); +} + +/*! @copydoc operator| */ +template<typename Type> +constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &> +operator|=(Type &lhs, const Type rhs) noexcept { + return (lhs = (lhs | rhs)); +} + +/*! @copydoc operator| */ +template<typename Type> +constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &> +operator&=(Type &lhs, const Type rhs) noexcept { + return (lhs = (lhs & rhs)); +} + +/*! @copydoc operator| */ +template<typename Type> +constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &> +operator^=(Type &lhs, const Type rhs) noexcept { + return (lhs = (lhs ^ rhs)); +} + +#endif diff --git a/external/entt/include/entt/core/family.hpp b/external/entt/include/entt/core/family.hpp new file mode 100644 index 0000000..3a83b88 --- /dev/null +++ b/external/entt/include/entt/core/family.hpp @@ -0,0 +1,33 @@ +#ifndef ENTT_CORE_FAMILY_HPP +#define ENTT_CORE_FAMILY_HPP + +#include "../config/config.h" +#include "fwd.hpp" + +namespace entt { + +/** + * @brief Dynamic identifier generator. + * + * Utility class template that can be used to assign unique identifiers to types + * at runtime. Use different specializations to create separate sets of + * identifiers. + */ +template<typename...> +class family { + // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) + inline static ENTT_MAYBE_ATOMIC(id_type) identifier{}; + +public: + /*! @brief Unsigned integer type. */ + using value_type = id_type; + + /*! @brief Statically generated unique identifier for the given type. */ + template<typename... Type> + // at the time I'm writing, clang crashes during compilation if auto is used instead of family_type + inline static const value_type value = identifier++; +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/core/fwd.hpp b/external/entt/include/entt/core/fwd.hpp new file mode 100644 index 0000000..bdd0b23 --- /dev/null +++ b/external/entt/include/entt/core/fwd.hpp @@ -0,0 +1,36 @@ +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include <cstddef> +#include "../config/config.h" + +namespace entt { + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(double[2])> +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +template<typename, typename> +class compressed_pair; + +template<typename> +class basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string<char>; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string<wchar_t>; + +// NOLINTNEXTLINE(bugprone-forward-declaration-namespace) +struct type_info; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/core/hashed_string.hpp b/external/entt/include/entt/core/hashed_string.hpp new file mode 100644 index 0000000..df3e964 --- /dev/null +++ b/external/entt/include/entt/core/hashed_string.hpp @@ -0,0 +1,311 @@ +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + +#include <cstddef> +#include <cstdint> +#include <string_view> +#include "fwd.hpp" + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template<typename = id_type> +struct fnv_1a_params; + +template<> +struct fnv_1a_params<std::uint32_t> { + static constexpr auto offset = 2166136261; + static constexpr auto prime = 16777619; +}; + +template<> +struct fnv_1a_params<std::uint64_t> { + static constexpr auto offset = 14695981039346656037ull; + static constexpr auto prime = 1099511628211ull; +}; + +template<typename Char> +struct basic_hashed_string { + using value_type = Char; + using size_type = std::size_t; + using hash_type = id_type; + + const value_type *repr; + size_type length; + hash_type hash; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifiers in the codebase while using their numeric + * counterparts at runtime.<br/> + * Because of that, a hashed string can also be used in constant expressions if + * required. + * + * @warning + * This class doesn't take ownership of user-supplied strings nor does it make a + * copy of them. + * + * @tparam Char Character type. + */ +template<typename Char> +class basic_hashed_string: internal::basic_hashed_string<Char> { + using base_type = internal::basic_hashed_string<Char>; + using params = internal::fnv_1a_params<>; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const Char *str) noexcept + : repr{str} {} + + const Char *repr; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const std::basic_string_view<Char> view) noexcept { + base_type base{view.data(), view.size(), params::offset}; + + for(auto &&curr: view) { + base.hash = (base.hash ^ static_cast<id_type>(curr)) * params::prime; + } + + return base; + } + +public: + /*! @brief Character type. */ + using value_type = typename base_type::value_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Unsigned integer type. */ + using hash_type = typename base_type::hash_type; + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { + return basic_hashed_string{str, len}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + * @return The numeric representation of the string. + */ + template<std::size_t N> + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept { + return basic_hashed_string{str}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept { + return basic_hashed_string{wrapper}; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() noexcept + : basic_hashed_string{nullptr, 0u} {} + + /** + * @brief Constructs a hashed string from a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ + constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept + : base_type{helper({str, len})} {} + + /** + * @brief Constructs a hashed string from an array of const characters. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ + template<std::size_t N> + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) + constexpr basic_hashed_string(const value_type (&str)[N]) noexcept + : base_type{helper({static_cast<const value_type *>(str)})} {} + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * + * @warning + * The lifetime of the string is not extended nor is it copied. + * + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept + : base_type{helper({wrapper.repr})} {} + + /** + * @brief Returns the size a hashed string. + * @return The size of the hashed string. + */ + [[nodiscard]] constexpr size_type size() const noexcept { + return base_type::length; + } + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the hashed string. + */ + [[nodiscard]] constexpr const value_type *data() const noexcept { + return base_type::repr; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr hash_type value() const noexcept { + return base_type::hash; + } + + /*! @copydoc data */ + [[nodiscard]] constexpr operator const value_type *() const noexcept { + return data(); + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr operator hash_type() const noexcept { + return value(); + } +}; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +template<typename Char> +basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string<Char>; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template<typename Char, std::size_t N> +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string<Char>; + +/** + * @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<typename Char> +[[nodiscard]] constexpr bool operator==(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept { + return lhs.value() == rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings differ, false otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than the second, false otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator<(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept { + return lhs.value() < rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than the second, false + * otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator>(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept { + return !(lhs < rhs); +} + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept { + return hashed_string{str}; +} + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept { + return hashed_wstring{str}; +} + +} // namespace literals + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/core/ident.hpp b/external/entt/include/entt/core/ident.hpp new file mode 100644 index 0000000..2ff6026 --- /dev/null +++ b/external/entt/include/entt/core/ident.hpp @@ -0,0 +1,35 @@ +#ifndef ENTT_CORE_IDENT_HPP +#define ENTT_CORE_IDENT_HPP + +#include <cstddef> +#include <type_traits> +#include <utility> +#include "fwd.hpp" +#include "type_traits.hpp" + +namespace entt { + +/** + * @brief Type integral identifiers. + * @tparam Type List of types for which to generate identifiers. + */ +template<typename... Type> +class ident { + template<typename Curr, std::size_t... Index> + [[nodiscard]] static constexpr id_type get(std::index_sequence<Index...>) noexcept { + static_assert((std::is_same_v<Curr, Type> || ...), "Invalid type"); + return (0 + ... + (std::is_same_v<Curr, type_list_element_t<Index, type_list<std::decay_t<Type>...>>> ? id_type{Index} : id_type{})); + } + +public: + /*! @brief Unsigned integer type. */ + using value_type = id_type; + + /*! @brief Statically generated unique identifier for the given type. */ + template<typename Curr> + static constexpr value_type value = get<std::decay_t<Curr>>(std::index_sequence_for<Type...>{}); +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/core/iterator.hpp b/external/entt/include/entt/core/iterator.hpp new file mode 100644 index 0000000..42db97e --- /dev/null +++ b/external/entt/include/entt/core/iterator.hpp @@ -0,0 +1,197 @@ +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include <iterator> +#include <memory> +#include <type_traits> +#include <utility> + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template<typename Type> +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v<value_type>) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template<typename Type> +class iota_iterator final { + static_assert(std::is_integral_v<Type>, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template<typename Type> +[[nodiscard]] constexpr bool operator==(const iota_iterator<Type> &lhs, const iota_iterator<Type> &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template<typename Type> +[[nodiscard]] constexpr bool operator!=(const iota_iterator<Type> &lhs, const iota_iterator<Type> &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template<typename It, typename Sentinel = It> +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits<It>::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v<iterator> &&std::is_nothrow_default_constructible_v<sentinel>) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v<iterator> &&std::is_nothrow_move_constructible_v<sentinel>) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/core/memory.hpp b/external/entt/include/entt/core/memory.hpp new file mode 100644 index 0000000..8f6f34b --- /dev/null +++ b/external/entt/include/entt/core/memory.hpp @@ -0,0 +1,244 @@ +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include <cstddef> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> +#include "../config/config.h" + +namespace entt { + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template<typename Type> +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v<std::decay_t<Type>>) { + return ptr; + } else { + return to_address(std::forward<Type>(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template<typename Allocator> +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template<typename Allocator> +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template<typename Allocator> +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Allocator> +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits<Allocator>::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v<allocator_type>) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v<typename allocator_type::value_type>) { + using alloc_traits = std::allocator_traits<Allocator>; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template<typename Type, typename Allocator, typename... Args> +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v<Type>, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<Type>; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward<Args>(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template<typename Type> +struct uses_allocator_construction { + template<typename Allocator, typename... Params> + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v<Type, Allocator> && std::is_constructible_v<Type, Params...>) { + return std::forward_as_tuple(std::forward<Params>(params)...); + } else { + static_assert(std::uses_allocator_v<Type, Allocator>, "Ill-formed request"); + + if constexpr(std::is_constructible_v<Type, std::allocator_arg_t, const Allocator &, Params...>) { + return std::tuple<std::allocator_arg_t, const Allocator &, Params &&...>{std::allocator_arg, allocator, std::forward<Params>(params)...}; + } else { + static_assert(std::is_constructible_v<Type, Params..., const Allocator &>, "Ill-formed request"); + return std::forward_as_tuple(std::forward<Params>(params)..., allocator); + } + } + } +}; + +template<typename Type, typename Other> +struct uses_allocator_construction<std::pair<Type, Other>> { + using type = std::pair<Type, Other>; + + template<typename Allocator, typename First, typename Second> + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Type>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<First>(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Other>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<Second>(second))); + } + + template<typename Allocator> + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template<typename Allocator, typename First, typename Second> + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward<First>(first)), std::forward_as_tuple(std::forward<Second>(second))); + } + + template<typename Allocator, typename First, typename Second> + static constexpr auto args(const Allocator &allocator, const std::pair<First, Second> &value) noexcept { + return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template<typename Allocator, typename First, typename Second> + static constexpr auto args(const Allocator &allocator, std::pair<First, Second> &&value) noexcept { + return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template<typename Type, typename Allocator, typename... Args> +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template<typename Type, typename Allocator, typename... Args> +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template<typename Type, typename Allocator, typename... Args> +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return ::new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...)); +} + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/core/monostate.hpp b/external/entt/include/entt/core/monostate.hpp new file mode 100644 index 0000000..4116bda --- /dev/null +++ b/external/entt/include/entt/core/monostate.hpp @@ -0,0 +1,60 @@ +#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.<br/> + * 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<id_type> +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. + * @return This monostate object. + */ + template<typename Type> + monostate &operator=(Type val) noexcept { + value<Type> = val; + return *this; + } + + /** + * @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<typename Type> + operator Type() const noexcept { + return value<Type>; + } + +private: + template<typename Type> + // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) + inline static ENTT_MAYBE_ATOMIC(Type) value{}; +}; + +/** + * @brief Helper variable template. + * @tparam Value Value used to differentiate between different variables. + */ +template<id_type Value> +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +inline monostate<Value> monostate_v{}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/core/ranges.hpp b/external/entt/include/entt/core/ranges.hpp new file mode 100644 index 0000000..c1d062c --- /dev/null +++ b/external/entt/include/entt/core/ranges.hpp @@ -0,0 +1,20 @@ +#ifndef ENTT_CORE_RANGES_HPP +#define ENTT_CORE_RANGES_HPP + +#if __has_include(<version>) +# include <version> +# +# if defined(__cpp_lib_ranges) +# include <ranges> +# include "iterator.hpp" + +template<class... Args> +inline constexpr bool std::ranges::enable_borrowed_range<entt::iterable_adaptor<Args...>>{true}; + +template<class... Args> +inline constexpr bool std::ranges::enable_view<entt::iterable_adaptor<Args...>>{true}; + +# endif +#endif + +#endif
\ No newline at end of file diff --git a/external/entt/include/entt/core/tuple.hpp b/external/entt/include/entt/core/tuple.hpp new file mode 100644 index 0000000..1a2dc4e --- /dev/null +++ b/external/entt/include/entt/core/tuple.hpp @@ -0,0 +1,95 @@ +#ifndef ENTT_CORE_TUPLE_HPP +#define ENTT_CORE_TUPLE_HPP + +#include <tuple> +#include <type_traits> +#include <utility> + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template<typename> +struct is_tuple_impl: std::false_type {}; + +template<typename... Args> +struct is_tuple_impl<std::tuple<Args...>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is a + * tuple, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type> +struct is_tuple: internal::is_tuple_impl<std::remove_cv_t<Type>> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_tuple_v = is_tuple<Type>::value; + +/** + * @brief Utility function to unwrap tuples of a single element. + * @tparam Type Tuple type of any sizes. + * @param value A tuple object of the given type. + * @return The tuple itself if it contains more than one element, the first + * element otherwise. + */ +template<typename Type> +constexpr decltype(auto) unwrap_tuple(Type &&value) noexcept { + if constexpr(std::tuple_size_v<std::remove_reference_t<Type>> == 1u) { + return std::get<0>(std::forward<Type>(value)); + } else { + return std::forward<Type>(value); + } +} + +/** + * @brief Utility class to forward-and-apply tuple objects. + * @tparam Func Type of underlying invocable object. + */ +template<typename Func> +struct forward_apply: private Func { + /** + * @brief Constructs a forward-and-apply object. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template<typename... Args> + constexpr forward_apply(Args &&...args) noexcept(std::is_nothrow_constructible_v<Func, Args...>) + : Func{std::forward<Args>(args)...} {} + + /** + * @brief Forwards and applies the arguments with the underlying function. + * @tparam Type Tuple-like type to forward to the underlying function. + * @param args Parameters to forward to the underlying function. + * @return Return value of the underlying function, if any. + */ + template<typename Type> + constexpr decltype(auto) operator()(Type &&args) noexcept(noexcept(std::apply(std::declval<Func &>(), args))) { + return std::apply(static_cast<Func &>(*this), std::forward<Type>(args)); + } + + /*! @copydoc operator()() */ + template<typename Type> + constexpr decltype(auto) operator()(Type &&args) const noexcept(noexcept(std::apply(std::declval<const Func &>(), args))) { + return std::apply(static_cast<const Func &>(*this), std::forward<Type>(args)); + } +}; + +/** + * @brief Deduction guide. + * @tparam Func Type of underlying invocable object. + */ +template<typename Func> +forward_apply(Func) -> forward_apply<std::remove_reference_t<std::remove_cv_t<Func>>>; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/core/type_info.hpp b/external/entt/include/entt/core/type_info.hpp new file mode 100644 index 0000000..1937506 --- /dev/null +++ b/external/entt/include/entt/core/type_info.hpp @@ -0,0 +1,268 @@ +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include <string_view> +#include <type_traits> +#include <utility> +#include "../config/config.h" +#include "../core/attribute.h" +#include "fwd.hpp" +#include "hashed_string.hpp" + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template<typename Type> +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{static_cast<const char *>(ENTT_PRETTY_FUNCTION)}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif +} + +template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')> +[[nodiscard]] constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name<Type>(); + return value; +} + +template<typename Type> +[[nodiscard]] std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name<Type>(); + return value; +} + +template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')> +[[nodiscard]] constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name<Type>(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template<typename Type> +[[nodiscard]] id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name<Type>()); + return value; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template<typename Type, typename = void> +struct ENTT_API type_index final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template<typename Type, typename = void> +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() noexcept { + return internal::type_hash<Type>(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index<Type>::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template<typename Type, typename = void> +struct type_name final { + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ + [[nodiscard]] static constexpr std::string_view value() noexcept { + return internal::type_name<Type>(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { + /** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ + template<typename Type> + // NOLINTBEGIN(modernize-use-transparent-functors) + constexpr type_info(std::in_place_type_t<Type>) noexcept + : seq{type_index<std::remove_cv_t<std::remove_reference_t<Type>>>::value()}, + identifier{type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()}, + alias{type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()} {} + // NOLINTEND(modernize-use-transparent-functors) + + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] inline constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] inline constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] inline constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.<br/> + * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template<typename Type> +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>) { + static type_info instance{std::in_place_type<Type>}; + return instance; + } else { + return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>(); + } +} + +/*! @copydoc type_id */ +template<typename Type> +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>(); +} + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/core/type_traits.hpp b/external/entt/include/entt/core/type_traits.hpp new file mode 100644 index 0000000..f6a9ea9 --- /dev/null +++ b/external/entt/include/entt/core/type_traits.hpp @@ -0,0 +1,919 @@ +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include <cstddef> +#include <iterator> +#include <tuple> +#include <type_traits> +#include <utility> +#include "../config/config.h" +#include "fwd.hpp" + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template<std::size_t N> +struct choice_t + // unfortunately, doxygen cannot parse such a construct + : /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template<std::size_t N> +inline constexpr choice_t<N> choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template<typename Type> +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template<typename Type> +using type_identity_t = typename type_identity<Type>::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template<typename Type, typename = void> +struct size_of: std::integral_constant<std::size_t, 0u> {}; + +/*! @copydoc size_of */ +template<typename Type> +struct size_of<Type, std::void_t<decltype(sizeof(Type))>> + : std::integral_constant<std::size_t, sizeof(Type)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template<typename Type> +inline constexpr std::size_t size_of_v = size_of<Type>::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template<typename Type, typename> +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template<auto Value, typename> +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template<auto Value> +using integral_constant = std::integral_constant<decltype(Value), Value>; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template<id_type Value> +using tag = integral_constant<Value>; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<std::size_t Index, typename First, typename... Other> +struct type_list_element<Index, type_list<First, Other...>> + : type_list_element<Index - 1u, type_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<typename First, typename... Other> +struct type_list_element<0u, type_list<First, Other...>> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template<std::size_t Index, typename List> +using type_list_element_t = typename type_list_element<Index, List>::type; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename, typename> +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<typename Type, typename First, typename... Other> +struct type_list_index<Type, type_list<First, Other...>> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index<Type, type_list<Other...>>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template<typename Type, typename... Other> +struct type_list_index<Type, type_list<Type, Other...>> { + static_assert(type_list_index<Type, type_list<Other...>>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template<typename Type> +struct type_list_index<Type, type_list<>> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template<typename Type, typename List> +inline constexpr std::size_t type_list_index_v = type_list_index<Type, List>::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template<typename... Type, typename... Other> +constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +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<typename... Type, typename... Other, typename... List> +struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list_cat<type_list<Type...>> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<Type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template<typename... List> +using type_list_cat_t = typename type_list_cat<List...>::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template<typename...> +struct type_list_unique; + +template<typename First, typename... Other, typename... Type> +struct type_list_unique<type_list<First, Other...>, Type...> + : std::conditional_t<(std::is_same_v<First, Type> || ...), type_list_unique<type_list<Other...>, Type...>, type_list_unique<type_list<Other...>, Type..., First>> {}; + +template<typename... Type> +struct type_list_unique<type_list<>, Type...> { + using type = type_list<Type...>; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template<typename List> +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique<List>::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template<typename List> +using type_list_unique_t = typename type_list_unique<List>::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template<typename... Type, typename Other> +struct type_list_contains<type_list<Type...>, Other> + : std::bool_constant<(std::is_same_v<Type, Other> || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template<typename... Type, typename... Other> +struct type_list_diff<type_list<Type...>, type_list<Other...>> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template<typename... List> +using type_list_diff_t = typename type_list_diff<List...>::type; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename, template<typename...> class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template<typename... Type, template<typename...> class Op> +struct type_list_transform<type_list<Type...>, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list<typename Op<Type>::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template<typename List, template<typename...> class Op> +using type_list_transform_t = typename type_list_transform<List, Op>::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<std::size_t Index, auto Value, auto... Other> +struct value_list_element<Index, value_list<Value, Other...>> + : value_list_element<Index - 1u, value_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<auto Value, auto... Other> +struct value_list_element<0u, value_list<Value, Other...>> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template<std::size_t Index, typename List> +using value_list_element_t = typename value_list_element<Index, List>::type; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template<std::size_t Index, typename List> +inline constexpr auto value_list_element_v = value_list_element<Index, List>::value; + +/*! @brief Primary template isn't defined on purpose. */ +template<auto, typename> +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<auto Value, auto First, auto... Other> +struct value_list_index<Value, value_list<First, Other...>> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index<Value, value_list<Other...>>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template<auto Value, auto... Other> +struct value_list_index<Value, value_list<Value, Other...>> { + static_assert(value_list_index<Value, value_list<Other...>>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template<auto Value> +struct value_list_index<Value, value_list<>> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template<auto Value, typename List> +inline constexpr std::size_t value_list_index_v = value_list_index<Value, List>::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template<auto... Value, auto... Other> +constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template<auto... Value, auto... Other, typename... List> +struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list_cat<value_list<Value...>> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<Value...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template<typename... List> +using value_list_cat_t = typename value_list_cat<List...>::type; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename> +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template<auto Value, auto... Other> +struct value_list_unique<value_list<Value, Other...>> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique<value_list<Other...>>::type, + value_list_cat_t<value_list<Value>, typename value_list_unique<value_list<Other...>>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique<value_list<>> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template<typename Type> +using value_list_unique_t = typename value_list_unique<Type>::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template<typename List, auto Value> +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template<auto... Value, auto Other> +struct value_list_contains<value_list<Value...>, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template<typename List, auto Value> +inline constexpr bool value_list_contains_v = value_list_contains<List, Value>::value; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template<auto... Value, auto... Other> +struct value_list_diff<value_list<Value...>, value_list<Other...>> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t<std::conditional_t<value_list_contains_v<value_list<Other...>, Value>, value_list<>, value_list<Value>>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template<typename... List> +using value_list_diff_t = typename value_list_diff<List...>::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template<typename, typename> +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, typename Args> +inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template<typename, typename, typename> +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename... Args> +struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename Args> +inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template<typename Type> +struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_complete_v = is_complete<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template<typename, typename = void> +struct has_iterator_category: std::false_type {}; + +template<typename Type> +struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template<typename Type> +struct is_iterator<Type, std::enable_if_t<!std::is_void_v<std::remove_cv_t<std::remove_pointer_t<Type>>>>> + : internal::has_iterator_category<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_iterator_v = is_iterator<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template<typename Type> +struct is_ebco_eligible + : std::bool_constant<std::is_empty_v<Type> && !std::is_final_v<Type>> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template<typename Type> +struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_transparent_v = is_transparent<Type>::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template<typename, typename = void> +struct has_tuple_size_value: std::false_type {}; + +template<typename Type> +struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {}; + +template<typename, typename = void> +struct has_value_type: std::false_type {}; + +template<typename Type> +struct has_value_type<Type, std::void_t<typename Type::value_type>>: std::true_type {}; + +template<typename> +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template<typename Type, std::size_t... Index> +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) { + return (dispatch_is_equality_comparable<std::tuple_element_t<Index, Type>>() && ...); +} + +template<typename> +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template<typename Type> +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval<Type>() == std::declval<Type>()) { + return true; +} + +template<typename Type> +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v<Type>) { + return false; + } else if constexpr(!is_iterator_v<Type> && has_value_type<Type>::value) { + if constexpr(std::is_same_v<typename Type::value_type, Type> || dispatch_is_equality_comparable<typename Type::value_type>()) { + return maybe_equality_comparable<Type>(0); + } else { + return false; + } + } else if constexpr(is_complete_v<std::tuple_size<std::remove_cv_t<Type>>>) { + if constexpr(has_tuple_size_value<Type>::value) { + return maybe_equality_comparable<Type>(0) && unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{}); + } else { + return maybe_equality_comparable<Type>(0); + } + } else { + return maybe_equality_comparable<Type>(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type> +struct is_equality_comparable: std::bool_constant<internal::dispatch_is_equality_comparable<Type>()> {}; + +/*! @copydoc is_equality_comparable */ +template<typename Type> +struct is_equality_comparable<const Type>: is_equality_comparable<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t<To>; +}; + +/*! @copydoc constness_as */ +template<typename To, typename From> +struct constness_as<To, const From> { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +using constness_as_t = typename constness_as<To, From>::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +class member_class { + static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function"); + + template<typename Class, typename Ret, typename... Args> + static Class *clazz(Ret (Class::*)(Args...)); + + template<typename Class, typename Ret, typename... Args> + static Class *clazz(Ret (Class::*)(Args...) const); + + template<typename Class, typename Type> + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +using member_class_t = typename member_class<Member>::type; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template<std::size_t Index, typename Candidate> +class nth_argument { + template<typename Ret, typename... Args> + static constexpr type_list<Args...> pick_up(Ret (*)(Args...)); + + template<typename Ret, typename Class, typename... Args> + static constexpr type_list<Args...> pick_up(Ret (Class ::*)(Args...)); + + template<typename Ret, typename Class, typename... Args> + static constexpr type_list<Args...> pick_up(Ret (Class ::*)(Args...) const); + + template<typename Type, typename Class> + static constexpr type_list<Type> pick_up(Type Class ::*); + + template<typename Type> + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t<Index, decltype(pick_up(std::declval<Candidate>()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template<std::size_t Index, typename Candidate> +using nth_argument_t = typename nth_argument<Index, Candidate>::type; + +} // namespace entt + +template<typename... Type> +struct std::tuple_size<entt::type_list<Type...>>: std::integral_constant<std::size_t, entt::type_list<Type...>::size> {}; + +template<std::size_t Index, typename... Type> +struct std::tuple_element<Index, entt::type_list<Type...>>: entt::type_list_element<Index, entt::type_list<Type...>> {}; + +template<auto... Value> +struct std::tuple_size<entt::value_list<Value...>>: std::integral_constant<std::size_t, entt::value_list<Value...>::size> {}; + +template<std::size_t Index, auto... Value> +struct std::tuple_element<Index, entt::value_list<Value...>>: entt::value_list_element<Index, entt::value_list<Value...>> {}; + +#endif diff --git a/external/entt/include/entt/core/utility.hpp b/external/entt/include/entt/core/utility.hpp new file mode 100644 index 0000000..af8b5bd --- /dev/null +++ b/external/entt/include/entt/core/utility.hpp @@ -0,0 +1,101 @@ +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include <type_traits> +#include <utility> + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template<typename Type> + [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { + return std::forward<Type>(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<typename Type, typename Class> +[[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { + return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template<typename Func> +[[nodiscard]] constexpr auto overload(Func *func) noexcept { + return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template<typename... Func> +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template<typename... Func> +overloaded(Func...) -> overloaded<Func...>; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template<typename Func> +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v<Func>) + : 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<typename... Args> + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v<Func, const y_combinator &, Args...>) { + return func(*this, std::forward<Args>(args)...); + } + + /*! @copydoc operator()() */ + template<typename... Args> + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v<Func, y_combinator &, Args...>) { + return func(*this, std::forward<Args>(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/entity/component.hpp b/external/entt/include/entt/entity/component.hpp new file mode 100644 index 0000000..2e8a02b --- /dev/null +++ b/external/entt/include/entt/entity/component.hpp @@ -0,0 +1,56 @@ +#ifndef ENTT_ENTITY_COMPONENT_HPP +#define ENTT_ENTITY_COMPONENT_HPP + +#include <cstddef> +#include <type_traits> +#include "../config/config.h" +#include "fwd.hpp" + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template<typename Type, typename = void> +struct in_place_delete: std::bool_constant<!(std::is_move_constructible_v<Type> && std::is_move_assignable_v<Type>)> {}; + +template<> +struct in_place_delete<void>: std::false_type {}; + +template<typename Type> +struct in_place_delete<Type, std::enable_if_t<Type::in_place_delete>> + : std::true_type {}; + +template<typename Type, typename = void> +struct page_size: std::integral_constant<std::size_t, !std::is_empty_v<ENTT_ETO_TYPE(Type)> * ENTT_PACKED_PAGE> {}; + +template<> +struct page_size<void>: std::integral_constant<std::size_t, 0u> {}; + +template<typename Type> +struct page_size<Type, std::void_t<decltype(Type::page_size)>> + : std::integral_constant<std::size_t, Type::page_size> {}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Common way to access various properties of components. + * @tparam Type Type of component. + */ +template<typename Type, typename = void> +struct component_traits { + static_assert(std::is_same_v<std::decay_t<Type>, Type>, "Unsupported type"); + + /*! @brief Component type. */ + using type = Type; + + /*! @brief Pointer stability, default is `false`. */ + static constexpr bool in_place_delete = internal::in_place_delete<Type>::value; + /*! @brief Page size, default is `ENTT_PACKED_PAGE` for non-empty types. */ + static constexpr std::size_t page_size = internal::page_size<Type>::value; +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/entity/entity.hpp b/external/entt/include/entt/entity/entity.hpp new file mode 100644 index 0000000..3c3dae6 --- /dev/null +++ b/external/entt/include/entt/entity/entity.hpp @@ -0,0 +1,388 @@ +#ifndef ENTT_ENTITY_ENTITY_HPP +#define ENTT_ENTITY_ENTITY_HPP + +#include <cstddef> +#include <cstdint> +#include <type_traits> +#include "../config/config.h" +#include "../core/bit.hpp" +#include "fwd.hpp" + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template<typename, typename = void> +struct entt_traits; + +template<typename Type> +struct entt_traits<Type, std::enable_if_t<std::is_enum_v<Type>>> + : entt_traits<std::underlying_type_t<Type>> { + using value_type = Type; +}; + +template<typename Type> +struct entt_traits<Type, std::enable_if_t<std::is_class_v<Type>>> + : entt_traits<typename Type::entity_type> { + using value_type = Type; +}; + +template<> +struct entt_traits<std::uint32_t> { + using value_type = std::uint32_t; + + using entity_type = std::uint32_t; + using version_type = std::uint16_t; + + static constexpr entity_type entity_mask = 0xFFFFF; + static constexpr entity_type version_mask = 0xFFF; +}; + +template<> +struct entt_traits<std::uint64_t> { + using value_type = std::uint64_t; + + using entity_type = std::uint64_t; + using version_type = std::uint32_t; + + static constexpr entity_type entity_mask = 0xFFFFFFFF; + static constexpr entity_type version_mask = 0xFFFFFFFF; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Common basic entity traits implementation. + * @tparam Traits Actual entity traits to use. + */ +template<typename Traits> +class basic_entt_traits { + static constexpr auto length = popcount(Traits::entity_mask); + + static_assert(Traits::entity_mask && ((Traits::entity_mask & (Traits::entity_mask + 1)) == 0), "Invalid entity mask"); + static_assert((Traits::version_mask & (Traits::version_mask + 1)) == 0, "Invalid version mask"); + +public: + /*! @brief Value type. */ + using value_type = typename Traits::value_type; + /*! @brief Underlying entity type. */ + using entity_type = typename Traits::entity_type; + /*! @brief Underlying version type. */ + using version_type = typename Traits::version_type; + + /*! @brief Entity mask size. */ + static constexpr entity_type entity_mask = Traits::entity_mask; + /*! @brief Version mask size */ + static constexpr entity_type version_mask = Traits::version_mask; + + /** + * @brief Converts an entity to its underlying type. + * @param value The value to convert. + * @return The integral representation of the given value. + */ + [[nodiscard]] static constexpr entity_type to_integral(const value_type value) noexcept { + return static_cast<entity_type>(value); + } + + /** + * @brief Returns the entity part once converted to the underlying type. + * @param value The value to convert. + * @return The integral representation of the entity part. + */ + [[nodiscard]] static constexpr entity_type to_entity(const value_type value) noexcept { + return (to_integral(value) & entity_mask); + } + + /** + * @brief Returns the version part once converted to the underlying type. + * @param value The value to convert. + * @return The integral representation of the version part. + */ + [[nodiscard]] static constexpr version_type to_version(const value_type value) noexcept { + if constexpr(Traits::version_mask == 0u) { + return version_type{}; + } else { + return (static_cast<version_type>(to_integral(value) >> length) & version_mask); + } + } + + /** + * @brief Returns the successor of a given identifier. + * @param value The identifier of which to return the successor. + * @return The successor of the given identifier. + */ + [[nodiscard]] static constexpr value_type next(const value_type value) noexcept { + const auto vers = to_version(value) + 1; + return construct(to_integral(value), static_cast<version_type>(vers + (vers == version_mask))); + } + + /** + * @brief Constructs an identifier from its parts. + * + * If the version part is not provided, a tombstone is returned.<br/> + * If the entity part is not provided, a null identifier is returned. + * + * @param entity The entity part of the identifier. + * @param version The version part of the identifier. + * @return A properly constructed identifier. + */ + [[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) noexcept { + if constexpr(Traits::version_mask == 0u) { + return value_type{entity & entity_mask}; + } else { + return value_type{(entity & entity_mask) | (static_cast<entity_type>(version & version_mask) << length)}; + } + } + + /** + * @brief Combines two identifiers in a single one. + * + * The returned identifier is a copy of the first element except for its + * version, which is taken from the second element. + * + * @param lhs The identifier from which to take the entity part. + * @param rhs The identifier from which to take the version part. + * @return A properly constructed identifier. + */ + [[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept { + if constexpr(Traits::version_mask == 0u) { + return value_type{lhs & entity_mask}; + } else { + return value_type{(lhs & entity_mask) | (rhs & (version_mask << length))}; + } + } +}; + +/** + * @brief Entity traits. + * @tparam Type Type of identifier. + */ +template<typename Type> +struct entt_traits: basic_entt_traits<internal::entt_traits<Type>> { + /*! @brief Base type. */ + using base_type = basic_entt_traits<internal::entt_traits<Type>>; + /*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */ + static constexpr std::size_t page_size = ENTT_SPARSE_PAGE; +}; + +/** + * @brief Converts an entity to its underlying type. + * @tparam Entity The value type. + * @param value The value to convert. + * @return The integral representation of the given value. + */ +template<typename Entity> +[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_integral(const Entity value) noexcept { + return entt_traits<Entity>::to_integral(value); +} + +/** + * @brief Returns the entity part once converted to the underlying type. + * @tparam Entity The value type. + * @param value The value to convert. + * @return The integral representation of the entity part. + */ +template<typename Entity> +[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_entity(const Entity value) noexcept { + return entt_traits<Entity>::to_entity(value); +} + +/** + * @brief Returns the version part once converted to the underlying type. + * @tparam Entity The value type. + * @param value The value to convert. + * @return The integral representation of the version part. + */ +template<typename Entity> +[[nodiscard]] constexpr typename entt_traits<Entity>::version_type to_version(const Entity value) noexcept { + return entt_traits<Entity>::to_version(value); +} + +/*! @brief Null object for all identifiers. */ +struct null_t { + /** + * @brief Converts the null object to identifiers of any type. + * @tparam Entity Type of identifier. + * @return The null representation for the given type. + */ + template<typename Entity> + [[nodiscard]] constexpr operator Entity() const noexcept { + using traits_type = entt_traits<Entity>; + constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask); + return value; + } + + /** + * @brief Compares two null objects. + * @param other A null object. + * @return True in all cases. + */ + [[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const noexcept { + return true; + } + + /** + * @brief Compares two null objects. + * @param other A null object. + * @return False in all cases. + */ + [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const noexcept { + return false; + } + + /** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return False if the two elements differ, true otherwise. + */ + template<typename Entity> + [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept { + using traits_type = entt_traits<Entity>; + return traits_type::to_entity(entity) == traits_type::to_entity(*this); + } + + /** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return True if the two elements differ, false otherwise. + */ + template<typename Entity> + [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept { + return !(entity == *this); + } +}; + +/** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param lhs Identifier with which to compare. + * @param rhs A null object yet to be converted. + * @return False if the two elements differ, true otherwise. + */ +template<typename Entity> +[[nodiscard]] constexpr bool operator==(const Entity lhs, const null_t rhs) noexcept { + return rhs.operator==(lhs); +} + +/** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param lhs Identifier with which to compare. + * @param rhs A null object yet to be converted. + * @return True if the two elements differ, false otherwise. + */ +template<typename Entity> +[[nodiscard]] constexpr bool operator!=(const Entity lhs, const null_t rhs) noexcept { + return !(rhs == lhs); +} + +/*! @brief Tombstone object for all identifiers. */ +struct tombstone_t { + /** + * @brief Converts the tombstone object to identifiers of any type. + * @tparam Entity Type of identifier. + * @return The tombstone representation for the given type. + */ + template<typename Entity> + [[nodiscard]] constexpr operator Entity() const noexcept { + using traits_type = entt_traits<Entity>; + constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask); + return value; + } + + /** + * @brief Compares two tombstone objects. + * @param other A tombstone object. + * @return True in all cases. + */ + [[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const noexcept { + return true; + } + + /** + * @brief Compares two tombstone objects. + * @param other A tombstone object. + * @return False in all cases. + */ + [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const noexcept { + return false; + } + + /** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return False if the two elements differ, true otherwise. + */ + template<typename Entity> + [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept { + using traits_type = entt_traits<Entity>; + + if constexpr(traits_type::version_mask == 0u) { + return false; + } else { + return (traits_type::to_version(entity) == traits_type::to_version(*this)); + } + } + + /** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return True if the two elements differ, false otherwise. + */ + template<typename Entity> + [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept { + return !(entity == *this); + } +}; + +/** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param lhs Identifier with which to compare. + * @param rhs A tombstone object yet to be converted. + * @return False if the two elements differ, true otherwise. + */ +template<typename Entity> +[[nodiscard]] constexpr bool operator==(const Entity lhs, const tombstone_t rhs) noexcept { + return rhs.operator==(lhs); +} + +/** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param lhs Identifier with which to compare. + * @param rhs A tombstone object yet to be converted. + * @return True if the two elements differ, false otherwise. + */ +template<typename Entity> +[[nodiscard]] constexpr bool operator!=(const Entity lhs, const tombstone_t rhs) noexcept { + return !(rhs == lhs); +} + +/** + * @brief Compile-time constant for null entities. + * + * There exist implicit conversions from this variable to identifiers of any + * allowed type. Similarly, there exist comparison operators between the null + * entity and any other identifier. + */ +inline constexpr null_t null{}; + +/** + * @brief Compile-time constant for tombstone entities. + * + * There exist implicit conversions from this variable to identifiers of any + * allowed type. Similarly, there exist comparison operators between the + * tombstone entity and any other identifier. + */ +inline constexpr tombstone_t tombstone{}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/entity/fwd.hpp b/external/entt/include/entt/entity/fwd.hpp new file mode 100644 index 0000000..17d23f6 --- /dev/null +++ b/external/entt/include/entt/entity/fwd.hpp @@ -0,0 +1,267 @@ +#ifndef ENTT_ENTITY_FWD_HPP +#define ENTT_ENTITY_FWD_HPP + +#include <cstdint> +#include <memory> +#include <type_traits> +#include "../config/config.h" +#include "../core/fwd.hpp" +#include "../core/type_traits.hpp" + +namespace entt { + +/*! @brief Default entity identifier. */ +enum class entity : id_type {}; + +/*! @brief Storage deletion policy. */ +enum class deletion_policy : std::uint8_t { + /*! @brief Swap-and-pop deletion policy. */ + swap_and_pop = 0u, + /*! @brief In-place deletion policy. */ + in_place = 1u, + /*! @brief Swap-only deletion policy. */ + swap_only = 2u +}; + +template<typename Entity = entity, typename = std::allocator<Entity>> +class basic_sparse_set; + +template<typename Type, typename = entity, typename = std::allocator<Type>, typename = void> +class basic_storage; + +template<typename, typename> +class basic_sigh_mixin; + +template<typename Entity = entity, typename = std::allocator<Entity>> +class basic_registry; + +template<typename, typename, typename = void> +class basic_view; + +template<typename Type, typename = std::allocator<Type *>> +class basic_runtime_view; + +template<typename, typename, typename> +class basic_group; + +template<typename, typename = std::allocator<void>> +class basic_observer; + +template<typename> +class basic_organizer; + +template<typename, typename...> +class basic_handle; + +template<typename> +class basic_snapshot; + +template<typename> +class basic_snapshot_loader; + +template<typename> +class basic_continuous_loader; + +/*! @brief Alias declaration for the most common use case. */ +using sparse_set = basic_sparse_set<>; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Type Element type. + */ +template<typename Type> +using storage = basic_storage<Type>; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Type Underlying storage type. + */ +template<typename Type> +using sigh_mixin = basic_sigh_mixin<Type, basic_registry<typename Type::entity_type, typename Type::base_type::allocator_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<registry>; + +/*! @brief Alias declaration for the most common use case. */ +using organizer = basic_organizer<registry>; + +/*! @brief Alias declaration for the most common use case. */ +using handle = basic_handle<registry>; + +/*! @brief Alias declaration for the most common use case. */ +using const_handle = basic_handle<const registry>; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Args Other template parameters. + */ +template<typename... Args> +using handle_view = basic_handle<registry, Args...>; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Args Other template parameters. + */ +template<typename... Args> +using const_handle_view = basic_handle<const registry, Args...>; + +/*! @brief Alias declaration for the most common use case. */ +using snapshot = basic_snapshot<registry>; + +/*! @brief Alias declaration for the most common use case. */ +using snapshot_loader = basic_snapshot_loader<registry>; + +/*! @brief Alias declaration for the most common use case. */ +using continuous_loader = basic_continuous_loader<registry>; + +/*! @brief Alias declaration for the most common use case. */ +using runtime_view = basic_runtime_view<sparse_set>; + +/*! @brief Alias declaration for the most common use case. */ +using const_runtime_view = basic_runtime_view<const sparse_set>; + +/** + * @brief Alias for exclusion lists. + * @tparam Type List of types. + */ +template<typename... Type> +struct exclude_t final: type_list<Type...> { + /*! @brief Default constructor. */ + explicit constexpr exclude_t() = default; +}; + +/** + * @brief Variable template for exclusion lists. + * @tparam Type List of types. + */ +template<typename... Type> +inline constexpr exclude_t<Type...> exclude{}; + +/** + * @brief Alias for lists of observed elements. + * @tparam Type List of types. + */ +template<typename... Type> +struct get_t final: type_list<Type...> { + /*! @brief Default constructor. */ + explicit constexpr get_t() = default; +}; + +/** + * @brief Variable template for lists of observed elements. + * @tparam Type List of types. + */ +template<typename... Type> +inline constexpr get_t<Type...> get{}; + +/** + * @brief Alias for lists of owned elements. + * @tparam Type List of types. + */ +template<typename... Type> +struct owned_t final: type_list<Type...> { + /*! @brief Default constructor. */ + explicit constexpr owned_t() = default; +}; + +/** + * @brief Variable template for lists of owned elements. + * @tparam Type List of types. + */ +template<typename... Type> +inline constexpr owned_t<Type...> owned{}; + +/** + * @brief Applies a given _function_ to a get list and generate a new list. + * @tparam Type Types provided by the get list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template<typename... Type, template<typename...> class Op> +struct type_list_transform<get_t<Type...>, Op> { + /*! @brief Resulting get list after applying the transform function. */ + using type = get_t<typename Op<Type>::type...>; +}; + +/** + * @brief Applies a given _function_ to an exclude list and generate a new list. + * @tparam Type Types provided by the exclude list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template<typename... Type, template<typename...> class Op> +struct type_list_transform<exclude_t<Type...>, Op> { + /*! @brief Resulting exclude list after applying the transform function. */ + using type = exclude_t<typename Op<Type>::type...>; +}; + +/** + * @brief Applies a given _function_ to an owned list and generate a new list. + * @tparam Type Types provided by the owned list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template<typename... Type, template<typename...> class Op> +struct type_list_transform<owned_t<Type...>, Op> { + /*! @brief Resulting owned list after applying the transform function. */ + using type = owned_t<typename Op<Type>::type...>; +}; + +/** + * @brief Provides a common way to define storage types. + * @tparam Type Storage value type. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Type, typename Entity = entity, typename Allocator = std::allocator<Type>, typename = void> +struct storage_type { + /*! @brief Type-to-storage conversion result. */ + using type = ENTT_STORAGE(sigh_mixin, basic_storage<Type, Entity, Allocator>); +}; + +/** + * @brief Helper type. + * @tparam Args Arguments to forward. + */ +template<typename... Args> +using storage_type_t = typename storage_type<Args...>::type; + +/** + * Type-to-storage conversion utility that preserves constness. + * @tparam Type Storage value type, eventually const. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Type, typename Entity = entity, typename Allocator = std::allocator<std::remove_const_t<Type>>> +struct storage_for { + /*! @brief Type-to-storage conversion result. */ + using type = constness_as_t<storage_type_t<std::remove_const_t<Type>, Entity, Allocator>, Type>; +}; + +/** + * @brief Helper type. + * @tparam Args Arguments to forward. + */ +template<typename... Args> +using storage_for_t = typename storage_for<Args...>::type; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Get Types of storage iterated by the view. + * @tparam Exclude Types of storage used to filter the view. + */ +template<typename Get, typename Exclude = exclude_t<>> +using view = basic_view<type_list_transform_t<Get, storage_for>, type_list_transform_t<Exclude, storage_for>>; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Owned Types of storage _owned_ by the group. + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. + */ +template<typename Owned, typename Get, typename Exclude> +using group = basic_group<type_list_transform_t<Owned, storage_for>, type_list_transform_t<Get, storage_for>, type_list_transform_t<Exclude, storage_for>>; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/entity/group.hpp b/external/entt/include/entt/entity/group.hpp new file mode 100644 index 0000000..32abb3a --- /dev/null +++ b/external/entt/include/entt/entity/group.hpp @@ -0,0 +1,1057 @@ +#ifndef ENTT_ENTITY_GROUP_HPP +#define ENTT_ENTITY_GROUP_HPP + +#include <array> +#include <cstddef> +#include <iterator> +#include <tuple> +#include <type_traits> +#include <utility> +#include "../config/config.h" +#include "../core/algorithm.hpp" +#include "../core/fwd.hpp" +#include "../core/iterator.hpp" +#include "../core/type_info.hpp" +#include "../core/type_traits.hpp" +#include "entity.hpp" +#include "fwd.hpp" + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template<typename, typename, typename> +class extended_group_iterator; + +template<typename It, typename... Owned, typename... Get> +class extended_group_iterator<It, owned_t<Owned...>, get_t<Get...>> { + template<typename Type> + [[nodiscard]] auto index_to_element([[maybe_unused]] Type &cpool) const { + if constexpr(std::is_void_v<typename Type::value_type>) { + return std::make_tuple(); + } else { + return std::forward_as_tuple(cpool.rbegin()[it.index()]); + } + } + +public: + using iterator_type = It; + using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::declval<Owned>().get_as_tuple({})..., std::declval<Get>().get_as_tuple({})...)); + using pointer = input_iterator_pointer<value_type>; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr extended_group_iterator() + : it{}, + pools{} {} + + extended_group_iterator(iterator_type from, std::tuple<Owned *..., Get *...> cpools) + : it{from}, + pools{std::move(cpools)} {} + + extended_group_iterator &operator++() noexcept { + return ++it, *this; + } + + extended_group_iterator operator++(int) noexcept { + extended_group_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const noexcept { + return std::tuple_cat(std::make_tuple(*it), index_to_element(*std::get<Owned *>(pools))..., std::get<Get *>(pools)->get_as_tuple(*it)...); + } + + [[nodiscard]] pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr iterator_type base() const noexcept { + return it; + } + + template<typename... Lhs, typename... Rhs> + friend constexpr bool operator==(const extended_group_iterator<Lhs...> &, const extended_group_iterator<Rhs...> &) noexcept; + +private: + It it; + std::tuple<Owned *..., Get *...> pools; +}; + +template<typename... Lhs, typename... Rhs> +[[nodiscard]] constexpr bool operator==(const extended_group_iterator<Lhs...> &lhs, const extended_group_iterator<Rhs...> &rhs) noexcept { + return lhs.it == rhs.it; +} + +template<typename... Lhs, typename... Rhs> +[[nodiscard]] constexpr bool operator!=(const extended_group_iterator<Lhs...> &lhs, const extended_group_iterator<Rhs...> &rhs) noexcept { + return !(lhs == rhs); +} + +struct group_descriptor { + using size_type = std::size_t; + virtual ~group_descriptor() noexcept = default; + [[nodiscard]] virtual bool owned(const id_type) const noexcept { + return false; + } +}; + +template<typename Type, std::size_t Owned, std::size_t Get, std::size_t Exclude> +class group_handler final: public group_descriptor { + using entity_type = typename Type::entity_type; + + void swap_elements(const std::size_t pos, const entity_type entt) { + for(size_type next{}; next < Owned; ++next) { + pools[next]->swap_elements((*pools[next])[pos], entt); + } + } + + void push_on_construct(const entity_type entt) { + if(std::apply([entt, pos = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < pos) && (other->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) { + swap_elements(len++, entt); + } + } + + void push_on_destroy(const entity_type entt) { + if(std::apply([entt, pos = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < pos) && (other->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) { + swap_elements(len++, entt); + } + } + + void remove_if(const entity_type entt) { + if(pools[0u]->contains(entt) && (pools[0u]->index(entt) < len)) { + swap_elements(--len, entt); + } + } + + void common_setup() { + // we cannot iterate backwards because we want to leave behind valid entities in case of owned types + for(auto first = pools[0u]->rbegin(), last = first + pools[0u]->size(); first != last; ++first) { + push_on_construct(*first); + } + } + +public: + using common_type = Type; + using size_type = typename Type::size_type; + + template<typename... OGType, typename... EType> + group_handler(std::tuple<OGType &...> ogpool, std::tuple<EType &...> epool) + : pools{std::apply([](auto &&...cpool) { return std::array<common_type *, (Owned + Get)>{&cpool...}; }, ogpool)}, + filter{std::apply([](auto &&...cpool) { return std::array<common_type *, Exclude>{&cpool...}; }, epool)} { + std::apply([this](auto &...cpool) { ((cpool.on_construct().template connect<&group_handler::push_on_construct>(*this), cpool.on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, ogpool); + std::apply([this](auto &...cpool) { ((cpool.on_construct().template connect<&group_handler::remove_if>(*this), cpool.on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, epool); + common_setup(); + } + + [[nodiscard]] bool owned(const id_type hash) const noexcept override { + for(size_type pos{}; pos < Owned; ++pos) { + if(pools[pos]->type().hash() == hash) { + return true; + } + } + + return false; + } + + [[nodiscard]] size_type length() const noexcept { + return len; + } + + template<std::size_t Index> + [[nodiscard]] common_type *storage() const noexcept { + if constexpr(Index < (Owned + Get)) { + return pools[Index]; + } else { + return filter[Index - (Owned + Get)]; + } + } + +private: + std::array<common_type *, (Owned + Get)> pools; + std::array<common_type *, Exclude> filter; + std::size_t len{}; +}; + +template<typename Type, std::size_t Get, std::size_t Exclude> +class group_handler<Type, 0u, Get, Exclude> final: public group_descriptor { + using entity_type = typename Type::entity_type; + + void push_on_construct(const entity_type entt) { + if(!elem.contains(entt) + && std::apply([entt](auto *...cpool) { return (cpool->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) { + elem.push(entt); + } + } + + void push_on_destroy(const entity_type entt) { + if(!elem.contains(entt) + && std::apply([entt](auto *...cpool) { return (cpool->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) { + elem.push(entt); + } + } + + void remove_if(const entity_type entt) { + elem.remove(entt); + } + + void common_setup() { + for(const auto entity: *pools[0u]) { + push_on_construct(entity); + } + } + +public: + using common_type = Type; + + template<typename Allocator, typename... GType, typename... EType> + group_handler(const Allocator &allocator, std::tuple<GType &...> gpool, std::tuple<EType &...> epool) + : pools{std::apply([](auto &&...cpool) { return std::array<common_type *, Get>{&cpool...}; }, gpool)}, + filter{std::apply([](auto &&...cpool) { return std::array<common_type *, Exclude>{&cpool...}; }, epool)}, + elem{allocator} { + std::apply([this](auto &...cpool) { ((cpool.on_construct().template connect<&group_handler::push_on_construct>(*this), cpool.on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, gpool); + std::apply([this](auto &...cpool) { ((cpool.on_construct().template connect<&group_handler::remove_if>(*this), cpool.on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, epool); + common_setup(); + } + + [[nodiscard]] common_type &handle() noexcept { + return elem; + } + + [[nodiscard]] const common_type &handle() const noexcept { + return elem; + } + + template<std::size_t Index> + [[nodiscard]] common_type *storage() const noexcept { + if constexpr(Index < Get) { + return pools[Index]; + } else { + return filter[Index - Get]; + } + } + +private: + std::array<common_type *, Get> pools; + std::array<common_type *, Exclude> filter; + common_type elem; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Group. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + */ +template<typename, typename, typename> +class basic_group; + +/** + * @brief Non-owning group. + * + * A non-owning group returns all entities and only the entities that are at + * least in the given storage. Moreover, it's guaranteed that the entity list is + * tightly packed in memory for fast iterations. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New elements are added to the storage. + * * The entity currently pointed is modified (for example, elements are added + * or removed from it). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators. + * + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. + */ +template<typename... Get, typename... Exclude> +class basic_group<owned_t<>, get_t<Get...>, exclude_t<Exclude...>> { + using base_type = std::common_type_t<typename Get::base_type..., typename Exclude::base_type...>; + using underlying_type = typename base_type::entity_type; + + template<typename Type> + static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Get::element_type..., typename Exclude::element_type...>>; + + template<std::size_t... Index> + [[nodiscard]] auto pools_for(std::index_sequence<Index...>) const noexcept { + using return_type = std::tuple<Get *...>; + return descriptor ? return_type{static_cast<Get *>(descriptor->template storage<Index>())...} : return_type{}; + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = underlying_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Common type among all storage types. */ + using common_type = base_type; + /*! @brief Random access iterator type. */ + using iterator = typename common_type::iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = typename common_type::reverse_iterator; + /*! @brief Iterable group type. */ + using iterable = iterable_adaptor<internal::extended_group_iterator<iterator, owned_t<>, get_t<Get...>>>; + /*! @brief Group handler type. */ + using handler = internal::group_handler<common_type, 0u, sizeof...(Get), sizeof...(Exclude)>; + + /** + * @brief Group opaque identifier. + * @return Group opaque identifier. + */ + static id_type group_id() noexcept { + return type_hash<basic_group<owned_t<>, get_t<std::remove_const_t<Get>...>, exclude_t<std::remove_const_t<Exclude>...>>>::value(); + } + + /*! @brief Default constructor to use to create empty, invalid groups. */ + basic_group() noexcept + : descriptor{} {} + + /** + * @brief Constructs a group from a set of storage classes. + * @param ref A reference to a group handler. + */ + basic_group(handler &ref) noexcept + : descriptor{&ref} {} + + /** + * @brief Returns the leading storage of a group. + * @return The leading storage of the group. + */ + [[nodiscard]] const common_type &handle() const noexcept { + return descriptor->handle(); + } + + /** + * @brief Returns the storage for a given element type, if any. + * @tparam Type Type of element of which to return the storage. + * @return The storage for the given element type. + */ + template<typename Type> + [[nodiscard]] auto *storage() const noexcept { + return storage<index_of<Type>>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template<std::size_t Index> + [[nodiscard]] auto *storage() const noexcept { + using type = type_list_element_t<Index, type_list<Get..., Exclude...>>; + return *this ? static_cast<type *>(descriptor->template storage<Index>()) : nullptr; + } + + /** + * @brief Returns the number of entities that are part of the group. + * @return Number of entities that are part of the group. + */ + [[nodiscard]] size_type size() const noexcept { + return *this ? handle().size() : size_type{}; + } + + /** + * @brief Returns the number of elements that a group has currently + * allocated space for. + * @return Capacity of the group. + */ + [[nodiscard]] size_type capacity() const noexcept { + return *this ? handle().capacity() : size_type{}; + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() { + if(*this) { + descriptor->handle().shrink_to_fit(); + } + } + + /** + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return !*this || handle().empty(); + } + + /** + * @brief Returns an iterator to the first entity of the group. + * + * If the group is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the group. + */ + [[nodiscard]] iterator begin() const noexcept { + return *this ? handle().begin() : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the group. + * @return An iterator to the entity following the last entity of the + * group. + */ + [[nodiscard]] iterator end() const noexcept { + return *this ? handle().end() : iterator{}; + } + + /** + * @brief Returns an iterator to the first entity of the reversed group. + * + * If the group is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed group. + */ + [[nodiscard]] reverse_iterator rbegin() const noexcept { + return *this ? handle().rbegin() : reverse_iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * group. + * @return An iterator to the entity following the last entity of the + * reversed group. + */ + [[nodiscard]] reverse_iterator rend() const noexcept { + return *this ? handle().rend() : reverse_iterator{}; + } + + /** + * @brief Returns the first entity of the group, if any. + * @return The first entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const noexcept { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity of the group, if any. + * @return The last entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + const auto it = rbegin(); + return it != rend() ? *it : null; + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + return *this ? handle().find(entt) : iterator{}; + } + + /** + * @brief Returns the identifier that occupies the given position. + * @param pos Position of the element to return. + * @return The identifier that occupies the given position. + */ + [[nodiscard]] entity_type operator[](const size_type pos) const { + return begin()[pos]; + } + + /** + * @brief Checks if a group is properly initialized. + * @return True if the group is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return descriptor != nullptr; + } + + /** + * @brief Checks if a group contains an entity. + * @param entt A valid identifier. + * @return True if the group contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + return *this && handle().contains(entt); + } + + /** + * @brief Returns the elements assigned to the given entity. + * @tparam Type Type of the element to get. + * @tparam Other Other types of elements to get. + * @param entt A valid identifier. + * @return The elements assigned to the entity. + */ + template<typename Type, typename... Other> + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + return get<index_of<Type>, index_of<Other>...>(entt); + } + + /** + * @brief Returns the elements assigned to the given entity. + * @tparam Index Indexes of the elements to get. + * @param entt A valid identifier. + * @return The elements assigned to the entity. + */ + template<std::size_t... Index> + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + const auto cpools = pools_for(std::index_sequence_for<Get...>{}); + + if constexpr(sizeof...(Index) == 0) { + return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools); + } else if constexpr(sizeof...(Index) == 1) { + return (std::get<Index>(cpools)->get(entt), ...); + } else { + return std::tuple_cat(std::get<Index>(cpools)->get_as_tuple(entt)...); + } + } + + /** + * @brief Iterates entities and elements 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 elements. The + * _constness_ of the elements is as requested.<br/> + * 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<typename Func> + void each(Func func) const { + for(const auto entt: *this) { + if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_group>().get({})))>) { + std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt))); + } else { + std::apply(func, get(entt)); + } + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a group. + * + * The iterable object returns tuples that contain the current entity and a + * set of references to its non-empty elements. The _constness_ of the + * elements is as requested. + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @return An iterable object to use to _visit_ the group. + */ + [[nodiscard]] iterable each() const noexcept { + const auto cpools = pools_for(std::index_sequence_for<Get...>{}); + return iterable{{begin(), cpools}, {end(), cpools}}; + } + + /** + * @brief Sort a group according to the given comparison function. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(std::tuple<Type &...>, std::tuple<Type &...>); + * bool(const Type &..., const Type &...); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Type` are such that they are iterated by the group.<br/> + * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function object must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * @tparam Type Optional type of element to compare. + * @tparam Other Other optional types of elements 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<typename Type, typename... Other, typename Compare, typename Sort = std_sort, typename... Args> + void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { + sort<index_of<Type>, index_of<Other>...>(std::move(compare), std::move(algo), std::forward<Args>(args)...); + } + + /** + * @brief Sort a group according to the given comparison function. + * + * @sa sort + * + * @tparam Index Optional indexes of elements 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<std::size_t... Index, typename Compare, typename Sort = std_sort, typename... Args> + void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { + if(*this) { + if constexpr(sizeof...(Index) == 0) { + static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function"); + descriptor->handle().sort(std::move(compare), std::move(algo), std::forward<Args>(args)...); + } else { + auto comp = [&compare, cpools = pools_for(std::index_sequence_for<Get...>{})](const entity_type lhs, const entity_type rhs) { + if constexpr(sizeof...(Index) == 1) { + return compare((std::get<Index>(cpools)->get(lhs), ...), (std::get<Index>(cpools)->get(rhs), ...)); + } else { + return compare(std::forward_as_tuple(std::get<Index>(cpools)->get(lhs)...), std::forward_as_tuple(std::get<Index>(cpools)->get(rhs)...)); + } + }; + + descriptor->handle().sort(std::move(comp), std::move(algo), std::forward<Args>(args)...); + } + } + } + + /** + * @brief Sort entities according to their order in a range. + * + * The shared pool of entities and thus its order is affected by the changes + * to each and every pool that it tracks. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + template<typename It> + void sort_as(It first, It last) const { + if(*this) { + descriptor->handle().sort_as(first, last); + } + } + +private: + handler *descriptor; +}; + +/** + * @brief Owning group. + * + * Owning groups returns all entities and only the entities that are at + * least in the given storage. Moreover: + * + * * It's guaranteed that the entity list is tightly packed in memory for fast + * iterations. + * * It's guaranteed that all elements in the owned storage are tightly packed + * in memory for even faster iterations and to allow direct access. + * * They stay true to the order of the owned storage and all instances have the + * same order in memory. + * + * The more types of storage are owned, the faster it is to iterate a group. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New elements are added to the storage. + * * The entity currently pointed is modified (for example, elements are added + * or removed from it). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators. + * + * @tparam Owned Types of storage _owned_ by the group. + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. + */ +template<typename... Owned, typename... Get, typename... Exclude> +class basic_group<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> { + static_assert(((Owned::storage_policy != deletion_policy::in_place) && ...), "Groups do not support in-place delete"); + + using base_type = std::common_type_t<typename Owned::base_type..., typename Get::base_type..., typename Exclude::base_type...>; + using underlying_type = typename base_type::entity_type; + + template<typename Type> + static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Owned::element_type..., typename Get::element_type..., typename Exclude::element_type...>>; + + template<std::size_t... Index, std::size_t... Other> + [[nodiscard]] auto pools_for(std::index_sequence<Index...>, std::index_sequence<Other...>) const noexcept { + using return_type = std::tuple<Owned *..., Get *...>; + return descriptor ? return_type{static_cast<Owned *>(descriptor->template storage<Index>())..., static_cast<Get *>(descriptor->template storage<sizeof...(Owned) + Other>())...} : return_type{}; + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = underlying_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Common type among all storage types. */ + using common_type = base_type; + /*! @brief Random access iterator type. */ + using iterator = typename common_type::iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = typename common_type::reverse_iterator; + /*! @brief Iterable group type. */ + using iterable = iterable_adaptor<internal::extended_group_iterator<iterator, owned_t<Owned...>, get_t<Get...>>>; + /*! @brief Group handler type. */ + using handler = internal::group_handler<common_type, sizeof...(Owned), sizeof...(Get), sizeof...(Exclude)>; + + /** + * @brief Group opaque identifier. + * @return Group opaque identifier. + */ + static id_type group_id() noexcept { + return type_hash<basic_group<owned_t<std::remove_const_t<Owned>...>, get_t<std::remove_const_t<Get>...>, exclude_t<std::remove_const_t<Exclude>...>>>::value(); + } + + /*! @brief Default constructor to use to create empty, invalid groups. */ + basic_group() noexcept + : descriptor{} {} + + /** + * @brief Constructs a group from a set of storage classes. + * @param ref A reference to a group handler. + */ + basic_group(handler &ref) noexcept + : descriptor{&ref} {} + + /** + * @brief Returns the leading storage of a group. + * @return The leading storage of the group. + */ + [[nodiscard]] const common_type &handle() const noexcept { + return *storage<0>(); + } + + /** + * @brief Returns the storage for a given element type, if any. + * @tparam Type Type of element of which to return the storage. + * @return The storage for the given element type. + */ + template<typename Type> + [[nodiscard]] auto *storage() const noexcept { + return storage<index_of<Type>>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template<std::size_t Index> + [[nodiscard]] auto *storage() const noexcept { + using type = type_list_element_t<Index, type_list<Owned..., Get..., Exclude...>>; + return *this ? static_cast<type *>(descriptor->template storage<Index>()) : nullptr; + } + + /** + * @brief Returns the number of entities that that are part of the group. + * @return Number of entities that that are part of the group. + */ + [[nodiscard]] size_type size() const noexcept { + return *this ? descriptor->length() : size_type{}; + } + + /** + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return !*this || !descriptor->length(); + } + + /** + * @brief Returns an iterator to the first entity of the group. + * + * If the group is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the group. + */ + [[nodiscard]] iterator begin() const noexcept { + return *this ? (handle().end() - descriptor->length()) : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the group. + * @return An iterator to the entity following the last entity of the + * group. + */ + [[nodiscard]] iterator end() const noexcept { + return *this ? handle().end() : iterator{}; + } + + /** + * @brief Returns an iterator to the first entity of the reversed group. + * + * If the group is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed group. + */ + [[nodiscard]] reverse_iterator rbegin() const noexcept { + return *this ? handle().rbegin() : reverse_iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * group. + * @return An iterator to the entity following the last entity of the + * reversed group. + */ + [[nodiscard]] reverse_iterator rend() const noexcept { + return *this ? (handle().rbegin() + descriptor->length()) : reverse_iterator{}; + } + + /** + * @brief Returns the first entity of the group, if any. + * @return The first entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const noexcept { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity of the group, if any. + * @return The last entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + const auto it = rbegin(); + return it != rend() ? *it : null; + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + const auto it = *this ? handle().find(entt) : iterator{}; + return it >= begin() ? it : iterator{}; + } + + /** + * @brief Returns the identifier that occupies the given position. + * @param pos Position of the element to return. + * @return The identifier that occupies the given position. + */ + [[nodiscard]] entity_type operator[](const size_type pos) const { + return begin()[pos]; + } + + /** + * @brief Checks if a group is properly initialized. + * @return True if the group is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return descriptor != nullptr; + } + + /** + * @brief Checks if a group contains an entity. + * @param entt A valid identifier. + * @return True if the group contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + return *this && handle().contains(entt) && (handle().index(entt) < (descriptor->length())); + } + + /** + * @brief Returns the elements assigned to the given entity. + * @tparam Type Type of the element to get. + * @tparam Other Other types of elements to get. + * @param entt A valid identifier. + * @return The elements assigned to the entity. + */ + template<typename Type, typename... Other> + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + return get<index_of<Type>, index_of<Other>...>(entt); + } + + /** + * @brief Returns the elements assigned to the given entity. + * @tparam Index Indexes of the elements to get. + * @param entt A valid identifier. + * @return The elements assigned to the entity. + */ + template<std::size_t... Index> + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + const auto cpools = pools_for(std::index_sequence_for<Owned...>{}, std::index_sequence_for<Get...>{}); + + if constexpr(sizeof...(Index) == 0) { + return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools); + } else if constexpr(sizeof...(Index) == 1) { + return (std::get<Index>(cpools)->get(entt), ...); + } else { + return std::tuple_cat(std::get<Index>(cpools)->get_as_tuple(entt)...); + } + } + + /** + * @brief Iterates entities and elements 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 elements. The + * _constness_ of the elements is as requested.<br/> + * 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<typename Func> + void each(Func func) const { + for(auto args: each()) { + if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_group>().get({})))>) { + std::apply(func, args); + } else { + std::apply([&func](auto, auto &&...less) { func(std::forward<decltype(less)>(less)...); }, args); + } + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a group. + * + * The iterable object returns tuples that contain the current entity and a + * set of references to its non-empty elements. The _constness_ of the + * elements is as requested. + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @return An iterable object to use to _visit_ the group. + */ + [[nodiscard]] iterable each() const noexcept { + const auto cpools = pools_for(std::index_sequence_for<Owned...>{}, std::index_sequence_for<Get...>{}); + return iterable{{begin(), cpools}, {end(), cpools}}; + } + + /** + * @brief Sort a group according to the given comparison function. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(std::tuple<Type &...>, std::tuple<Type &...>); + * bool(const Type &, const Type &); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Type` are either owned types or not but still such that they are + * iterated by the group.<br/> + * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function object must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * @tparam Type Optional type of element to compare. + * @tparam Other Other optional types of elements 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<typename Type, typename... Other, typename Compare, typename Sort = std_sort, typename... Args> + void sort(Compare compare, Sort algo = Sort{}, Args &&...args) const { + sort<index_of<Type>, index_of<Other>...>(std::move(compare), std::move(algo), std::forward<Args>(args)...); + } + + /** + * @brief Sort a group according to the given comparison function. + * + * @sa sort + * + * @tparam Index Optional indexes of elements 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<std::size_t... Index, typename Compare, typename Sort = std_sort, typename... Args> + void sort(Compare compare, Sort algo = Sort{}, Args &&...args) const { + const auto cpools = pools_for(std::index_sequence_for<Owned...>{}, std::index_sequence_for<Get...>{}); + + if constexpr(sizeof...(Index) == 0) { + static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function"); + storage<0>()->sort_n(descriptor->length(), std::move(compare), std::move(algo), std::forward<Args>(args)...); + } else { + auto comp = [&compare, &cpools](const entity_type lhs, const entity_type rhs) { + if constexpr(sizeof...(Index) == 1) { + return compare((std::get<Index>(cpools)->get(lhs), ...), (std::get<Index>(cpools)->get(rhs), ...)); + } else { + return compare(std::forward_as_tuple(std::get<Index>(cpools)->get(lhs)...), std::forward_as_tuple(std::get<Index>(cpools)->get(rhs)...)); + } + }; + + storage<0>()->sort_n(descriptor->length(), std::move(comp), std::move(algo), std::forward<Args>(args)...); + } + + auto cb = [this](auto *head, auto *...other) { + for(auto next = descriptor->length(); next; --next) { + const auto pos = next - 1; + [[maybe_unused]] const auto entt = head->data()[pos]; + (other->swap_elements(other->data()[pos], entt), ...); + } + }; + + std::apply(cb, cpools); + } + +private: + handler *descriptor; +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/entity/handle.hpp b/external/entt/include/entt/entity/handle.hpp new file mode 100644 index 0000000..15e82fd --- /dev/null +++ b/external/entt/include/entt/entity/handle.hpp @@ -0,0 +1,431 @@ +#ifndef ENTT_ENTITY_HANDLE_HPP +#define ENTT_ENTITY_HANDLE_HPP + +#include <iterator> +#include <tuple> +#include <type_traits> +#include <utility> +#include "../config/config.h" +#include "../core/iterator.hpp" +#include "../core/type_traits.hpp" +#include "entity.hpp" +#include "fwd.hpp" + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template<typename It> +class handle_storage_iterator final { + template<typename Other> + friend class handle_storage_iterator; + + using underlying_type = std::remove_reference_t<typename It::value_type::second_type>; + using entity_type = typename underlying_type::entity_type; + +public: + using value_type = typename std::iterator_traits<It>::value_type; + using pointer = input_iterator_pointer<value_type>; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr handle_storage_iterator() noexcept + : entt{null}, + it{}, + last{} {} + + constexpr handle_storage_iterator(entity_type value, It from, It to) noexcept + : entt{value}, + it{from}, + last{to} { + while(it != last && !it->second.contains(entt)) { + ++it; + } + } + + constexpr handle_storage_iterator &operator++() noexcept { + while(++it != last && !it->second.contains(entt)) {} + return *this; + } + + constexpr handle_storage_iterator operator++(int) noexcept { + handle_storage_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *it; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + template<typename ILhs, typename IRhs> + friend constexpr bool operator==(const handle_storage_iterator<ILhs> &, const handle_storage_iterator<IRhs> &) noexcept; + +private: + entity_type entt; + It it; + It last; +}; + +template<typename ILhs, typename IRhs> +[[nodiscard]] constexpr bool operator==(const handle_storage_iterator<ILhs> &lhs, const handle_storage_iterator<IRhs> &rhs) noexcept { + return lhs.it == rhs.it; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] constexpr bool operator!=(const handle_storage_iterator<ILhs> &lhs, const handle_storage_iterator<IRhs> &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Non-owning handle to an entity. + * + * Tiny wrapper around a registry and an entity. + * + * @tparam Registry Basic registry type. + * @tparam Scope Types to which to restrict the scope of a handle. + */ +template<typename Registry, typename... Scope> +class basic_handle { + using traits_type = entt_traits<typename Registry::entity_type>; + + [[nodiscard]] auto &owner_or_assert() const noexcept { + ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); + return static_cast<Registry &>(*owner); + } + +public: + /*! @brief Type of registry accepted by the handle. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename traits_type::value_type; + /*! @brief Underlying version type. */ + using version_type = typename traits_type::version_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Iterable handle type. */ + using iterable = iterable_adaptor<internal::handle_storage_iterator<typename decltype(std::declval<registry_type>().storage())::iterator>>; + + /*! @brief Constructs an invalid handle. */ + basic_handle() noexcept + : owner{}, + entt{null} {} + + /** + * @brief Constructs a handle from a given registry and entity. + * @param ref An instance of the registry class. + * @param value A valid identifier. + */ + basic_handle(registry_type &ref, entity_type value) noexcept + : owner{&ref}, + entt{value} {} + + /** + * @brief Returns an iterable object to use to _visit_ a handle. + * + * The iterable object returns a pair that contains the name and a reference + * to the current storage.<br/> + * Returned storage are those that contain the entity associated with the + * handle. + * + * @return An iterable object to use to _visit_ the handle. + */ + [[nodiscard]] iterable storage() const noexcept { + auto underlying = owner_or_assert().storage(); + return iterable{{entt, underlying.begin(), underlying.end()}, {entt, underlying.end(), underlying.end()}}; + } + + /*! @copydoc valid */ + [[nodiscard]] explicit operator bool() const noexcept { + return owner && owner->valid(entt); + } + + /** + * @brief Checks if a handle refers to non-null registry pointer and entity. + * @return True if the handle refers to non-null registry and entity, false + * otherwise. + */ + [[nodiscard]] bool valid() const { + return static_cast<bool>(*this); + } + + /** + * @brief Returns a pointer to the underlying registry, if any. + * @return A pointer to the underlying registry, if any. + */ + [[nodiscard]] registry_type *registry() const noexcept { + return owner; + } + + /** + * @brief Returns the entity associated with a handle. + * @return The entity associated with the handle. + */ + [[nodiscard]] entity_type entity() const noexcept { + return entt; + } + + /*! @copydoc entity */ + [[nodiscard]] operator entity_type() const noexcept { + return entity(); + } + + /*! @brief Destroys the entity associated with a handle. */ + void destroy() { + owner_or_assert().destroy(std::exchange(entt, null)); + } + + /** + * @brief Destroys the entity associated with a handle. + * @param version A desired version upon destruction. + */ + void destroy(const version_type version) { + owner_or_assert().destroy(std::exchange(entt, null), version); + } + + /** + * @brief Assigns the given element to a handle. + * @tparam Type Type of element to create. + * @tparam Args Types of arguments to use to construct the element. + * @param args Parameters to use to initialize the element. + * @return A reference to the newly created element. + */ + template<typename Type, typename... Args> + // NOLINTNEXTLINE(modernize-use-nodiscard) + decltype(auto) emplace(Args &&...args) const { + static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type"); + return owner_or_assert().template emplace<Type>(entt, std::forward<Args>(args)...); + } + + /** + * @brief Assigns or replaces the given element for a handle. + * @tparam Type Type of element to assign or replace. + * @tparam Args Types of arguments to use to construct the element. + * @param args Parameters to use to initialize the element. + * @return A reference to the newly created element. + */ + template<typename Type, typename... Args> + decltype(auto) emplace_or_replace(Args &&...args) const { + static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type"); + return owner_or_assert().template emplace_or_replace<Type>(entt, std::forward<Args>(args)...); + } + + /** + * @brief Patches the given element for a handle. + * @tparam Type Type of element to patch. + * @tparam Func Types of the function objects to invoke. + * @param func Valid function objects. + * @return A reference to the patched element. + */ + template<typename Type, typename... Func> + decltype(auto) patch(Func &&...func) const { + static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type"); + return owner_or_assert().template patch<Type>(entt, std::forward<Func>(func)...); + } + + /** + * @brief Replaces the given element for a handle. + * @tparam Type Type of element to replace. + * @tparam Args Types of arguments to use to construct the element. + * @param args Parameters to use to initialize the element. + * @return A reference to the element being replaced. + */ + template<typename Type, typename... Args> + decltype(auto) replace(Args &&...args) const { + static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type"); + return owner_or_assert().template replace<Type>(entt, std::forward<Args>(args)...); + } + + /** + * @brief Removes the given elements from a handle. + * @tparam Type Types of elements to remove. + * @return The number of elements actually removed. + */ + template<typename... Type> + // NOLINTNEXTLINE(modernize-use-nodiscard) + size_type remove() const { + static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Type> && ...), "Invalid type"); + return owner_or_assert().template remove<Type...>(entt); + } + + /** + * @brief Erases the given elements from a handle. + * @tparam Type Types of elements to erase. + */ + template<typename... Type> + void erase() const { + static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Type> && ...), "Invalid type"); + owner_or_assert().template erase<Type...>(entt); + } + + /** + * @brief Checks if a handle has all the given elements. + * @tparam Type Elements for which to perform the check. + * @return True if the handle has all the elements, false otherwise. + */ + template<typename... Type> + [[nodiscard]] decltype(auto) all_of() const { + return owner_or_assert().template all_of<Type...>(entt); + } + + /** + * @brief Checks if a handle has at least one of the given elements. + * @tparam Type Elements for which to perform the check. + * @return True if the handle has at least one of the given elements, + * false otherwise. + */ + template<typename... Type> + [[nodiscard]] decltype(auto) any_of() const { + return owner_or_assert().template any_of<Type...>(entt); + } + + /** + * @brief Returns references to the given elements for a handle. + * @tparam Type Types of elements to get. + * @return References to the elements owned by the handle. + */ + template<typename... Type> + [[nodiscard]] decltype(auto) get() const { + static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Type> && ...), "Invalid type"); + return owner_or_assert().template get<Type...>(entt); + } + + /** + * @brief Returns a reference to the given element for a handle. + * @tparam Type Type of element to get. + * @tparam Args Types of arguments to use to construct the element. + * @param args Parameters to use to initialize the element. + * @return Reference to the element owned by the handle. + */ + template<typename Type, typename... Args> + [[nodiscard]] decltype(auto) get_or_emplace(Args &&...args) const { + static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type"); + return owner_or_assert().template get_or_emplace<Type>(entt, std::forward<Args>(args)...); + } + + /** + * @brief Returns pointers to the given elements for a handle. + * @tparam Type Types of elements to get. + * @return Pointers to the elements owned by the handle. + */ + template<typename... Type> + [[nodiscard]] auto try_get() const { + static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Type> && ...), "Invalid type"); + return owner_or_assert().template try_get<Type...>(entt); + } + + /** + * @brief Checks if a handle has elements assigned. + * @return True if the handle has no elements assigned, false otherwise. + */ + [[nodiscard]] bool orphan() const { + return owner_or_assert().orphan(entt); + } + + /** + * @brief Returns a const handle from a non-const one. + * @tparam Other A valid entity type. + * @tparam Args Scope of the handle to construct. + * @return A const handle referring to the same registry and the same + * entity. + */ + template<typename Other, typename... Args> + operator basic_handle<Other, Args...>() const noexcept { + static_assert(std::is_same_v<Other, Registry> || std::is_same_v<std::remove_const_t<Other>, Registry>, "Invalid conversion between different handles"); + static_assert((sizeof...(Scope) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Scope)) && ... && (type_list_contains_v<type_list<Scope...>, Args>))), "Invalid conversion between different handles"); + return owner ? basic_handle<Other, Args...>{*owner, entt} : basic_handle<Other, Args...>{}; + } + +private: + registry_type *owner; + entity_type entt; +}; + +/** + * @brief Compares two handles. + * @tparam Args Scope of the first handle. + * @tparam Other Scope of the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if both handles refer to the same registry and the same + * entity, false otherwise. + */ +template<typename... Args, typename... Other> +[[nodiscard]] bool operator==(const basic_handle<Args...> &lhs, const basic_handle<Other...> &rhs) noexcept { + return lhs.registry() == rhs.registry() && lhs.entity() == rhs.entity(); +} + +/** + * @brief Compares two handles. + * @tparam Args Scope of the first handle. + * @tparam Other Scope of the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return False if both handles refer to the same registry and the same + * entity, true otherwise. + */ +template<typename... Args, typename... Other> +[[nodiscard]] bool operator!=(const basic_handle<Args...> &lhs, const basic_handle<Other...> &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares a handle with the null object. + * @tparam Args Scope of the handle. + * @param lhs A valid handle. + * @param rhs A null object yet to be converted. + * @return False if the two elements differ, true otherwise. + */ +template<typename... Args> +[[nodiscard]] constexpr bool operator==(const basic_handle<Args...> &lhs, const null_t rhs) noexcept { + return (lhs.entity() == rhs); +} + +/** + * @brief Compares a handle with the null object. + * @tparam Args Scope of the handle. + * @param lhs A null object yet to be converted. + * @param rhs A valid handle. + * @return False if the two elements differ, true otherwise. + */ +template<typename... Args> +[[nodiscard]] constexpr bool operator==(const null_t lhs, const basic_handle<Args...> &rhs) noexcept { + return (rhs == lhs); +} + +/** + * @brief Compares a handle with the null object. + * @tparam Args Scope of the handle. + * @param lhs A valid handle. + * @param rhs A null object yet to be converted. + * @return True if the two elements differ, false otherwise. + */ +template<typename... Args> +[[nodiscard]] constexpr bool operator!=(const basic_handle<Args...> &lhs, const null_t rhs) noexcept { + return (lhs.entity() != rhs); +} + +/** + * @brief Compares a handle with the null object. + * @tparam Args Scope of the handle. + * @param lhs A null object yet to be converted. + * @param rhs A valid handle. + * @return True if the two elements differ, false otherwise. + */ +template<typename... Args> +[[nodiscard]] constexpr bool operator!=(const null_t lhs, const basic_handle<Args...> &rhs) noexcept { + return (rhs != lhs); +} + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/entity/helper.hpp b/external/entt/include/entt/entity/helper.hpp new file mode 100644 index 0000000..4ad2e27 --- /dev/null +++ b/external/entt/include/entt/entity/helper.hpp @@ -0,0 +1,255 @@ +#ifndef ENTT_ENTITY_HELPER_HPP +#define ENTT_ENTITY_HELPER_HPP + +#include <memory> +#include <type_traits> +#include <utility> +#include "../core/fwd.hpp" +#include "../core/type_traits.hpp" +#include "component.hpp" +#include "fwd.hpp" +#include "group.hpp" +#include "storage.hpp" +#include "view.hpp" + +namespace entt { + +/** + * @brief Converts a registry to a view. + * @tparam Registry Basic registry type. + */ +template<typename Registry> +class as_view { + template<typename... Get, typename... Exclude> + [[nodiscard]] auto dispatch(get_t<Get...>, exclude_t<Exclude...>) const { + return reg.template view<constness_as_t<typename Get::element_type, Get>...>(exclude_t<constness_as_t<typename Exclude::element_type, Exclude>...>{}); + } + +public: + /*! @brief Type of registry to convert. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + + /** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ + as_view(registry_type &source) noexcept + : reg{source} {} + + /** + * @brief Conversion function from a registry to a view. + * @tparam Get Type of storage used to construct the view. + * @tparam Exclude Types of storage used to filter the view. + * @return A newly created view. + */ + template<typename Get, typename Exclude> + operator basic_view<Get, Exclude>() const { + return dispatch(Get{}, Exclude{}); + } + +private: + registry_type ® +}; + +/** + * @brief Converts a registry to a group. + * @tparam Registry Basic registry type. + */ +template<typename Registry> +class as_group { + template<typename... Owned, typename... Get, typename... Exclude> + [[nodiscard]] auto dispatch(owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>) const { + if constexpr(std::is_const_v<registry_type>) { + return reg.template group_if_exists<typename Owned::element_type...>(get_t<typename Get::element_type...>{}, exclude_t<typename Exclude::element_type...>{}); + } else { + return reg.template group<constness_as_t<typename Owned::element_type, Owned>...>(get_t<constness_as_t<typename Get::element_type, Get>...>{}, exclude_t<constness_as_t<typename Exclude::element_type, Exclude>...>{}); + } + } + +public: + /*! @brief Type of registry to convert. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + + /** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ + as_group(registry_type &source) noexcept + : reg{source} {} + + /** + * @brief Conversion function from a registry to a group. + * @tparam Owned Types of _owned_ by the group. + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. + * @return A newly created group. + */ + template<typename Owned, typename Get, typename Exclude> + operator basic_group<Owned, Get, Exclude>() const { + return dispatch(Owned{}, Get{}, Exclude{}); + } + +private: + registry_type ® +}; + +/** + * @brief Helper to create a listener that directly invokes a member function. + * @tparam Member Member function to invoke on an element of the given type. + * @tparam Registry Basic registry type. + * @param reg A registry that contains the given entity and its elements. + * @param entt Entity from which to get the element. + */ +template<auto Member, typename Registry = std::decay_t<nth_argument_t<0u, decltype(Member)>>> +void invoke(Registry ®, const typename Registry::entity_type entt) { + static_assert(std::is_member_function_pointer_v<decltype(Member)>, "Invalid pointer to non-static member function"); + (reg.template get<member_class_t<decltype(Member)>>(entt).*Member)(reg, entt); +} + +/** + * @brief Returns the entity associated with a given element. + * + * @warning + * Currently, this function only works correctly with the default storage as it + * makes assumptions about how the elements are laid out. + * + * @tparam Args Storage type template parameters. + * @param storage A storage that contains the given element. + * @param instance A valid element instance. + * @return The entity associated with the given element. + */ +template<typename... Args> +typename basic_storage<Args...>::entity_type to_entity(const basic_storage<Args...> &storage, const typename basic_storage<Args...>::value_type &instance) { + using traits_type = component_traits<typename basic_storage<Args...>::value_type>; + static_assert(traits_type::page_size != 0u, "Unexpected page size"); + const typename basic_storage<Args...>::base_type &base = storage; + const auto *addr = std::addressof(instance); + + for(auto it = base.rbegin(), last = base.rend(); it < last; it += traits_type::page_size) { + if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < static_cast<decltype(dist)>(traits_type::page_size)) { + return *(it + dist); + } + } + + return null; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct sigh_helper; + +/** + * @brief Signal connection helper for registries. + * @tparam Registry Basic registry type. + */ +template<typename Registry> +struct sigh_helper<Registry> { + /*! @brief Registry type. */ + using registry_type = Registry; + + /** + * @brief Constructs a helper for a given registry. + * @param ref A valid reference to a registry. + */ + sigh_helper(registry_type &ref) + : bucket{&ref} {} + + /** + * @brief Binds a properly initialized helper to a given signal type. + * @tparam Type Type of signal to bind the helper to. + * @param id Optional name for the underlying storage to use. + * @return A helper for a given registry and signal type. + */ + template<typename Type> + auto with(const id_type id = type_hash<Type>::value()) noexcept { + return sigh_helper<registry_type, Type>{*bucket, id}; + } + + /** + * @brief Returns a reference to the underlying registry. + * @return A reference to the underlying registry. + */ + [[nodiscard]] registry_type ®istry() noexcept { + return *bucket; + } + +private: + registry_type *bucket; +}; + +/** + * @brief Signal connection helper for registries. + * @tparam Registry Basic registry type. + * @tparam Type Type of signal to connect listeners to. + */ +template<typename Registry, typename Type> +struct sigh_helper<Registry, Type> final: sigh_helper<Registry> { + /*! @brief Registry type. */ + using registry_type = Registry; + + /** + * @brief Constructs a helper for a given registry. + * @param ref A valid reference to a registry. + * @param id Optional name for the underlying storage to use. + */ + sigh_helper(registry_type &ref, const id_type id = type_hash<Type>::value()) + : sigh_helper<Registry>{ref}, + name{id} {} + + /** + * @brief Forwards the call to `on_construct` on the underlying storage. + * @tparam Candidate Function or member to connect. + * @tparam Args Type of class or type of payload, if any. + * @param args A valid object that fits the purpose, if any. + * @return This helper. + */ + template<auto Candidate, typename... Args> + auto on_construct(Args &&...args) { + this->registry().template on_construct<Type>(name).template connect<Candidate>(std::forward<Args>(args)...); + return *this; + } + + /** + * @brief Forwards the call to `on_update` on the underlying storage. + * @tparam Candidate Function or member to connect. + * @tparam Args Type of class or type of payload, if any. + * @param args A valid object that fits the purpose, if any. + * @return This helper. + */ + template<auto Candidate, typename... Args> + auto on_update(Args &&...args) { + this->registry().template on_update<Type>(name).template connect<Candidate>(std::forward<Args>(args)...); + return *this; + } + + /** + * @brief Forwards the call to `on_destroy` on the underlying storage. + * @tparam Candidate Function or member to connect. + * @tparam Args Type of class or type of payload, if any. + * @param args A valid object that fits the purpose, if any. + * @return This helper. + */ + template<auto Candidate, typename... Args> + auto on_destroy(Args &&...args) { + this->registry().template on_destroy<Type>(name).template connect<Candidate>(std::forward<Args>(args)...); + return *this; + } + +private: + id_type name; +}; + +/** + * @brief Deduction guide. + * @tparam Registry Basic registry type. + */ +template<typename Registry> +sigh_helper(Registry &) -> sigh_helper<Registry>; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/entity/mixin.hpp b/external/entt/include/entt/entity/mixin.hpp new file mode 100644 index 0000000..c8ebd57 --- /dev/null +++ b/external/entt/include/entt/entity/mixin.hpp @@ -0,0 +1,318 @@ +#ifndef ENTT_ENTITY_MIXIN_HPP +#define ENTT_ENTITY_MIXIN_HPP + +#include <type_traits> +#include <utility> +#include "../config/config.h" +#include "../core/any.hpp" +#include "../signal/sigh.hpp" +#include "entity.hpp" +#include "fwd.hpp" + +namespace entt { + +/** + * @brief Mixin type used to add signal support to storage types. + * + * The function type of a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry<entity_type> &, entity_type); + * @endcode + * + * This applies to all signals made available. + * + * @tparam Type Underlying storage type. + * @tparam Registry Basic registry type. + */ +template<typename Type, typename Registry> +class basic_sigh_mixin final: public Type { + using underlying_type = Type; + using owner_type = Registry; + + using basic_registry_type = basic_registry<typename underlying_type::entity_type, typename underlying_type::base_type::allocator_type>; + using sigh_type = sigh<void(owner_type &, const typename underlying_type::entity_type), typename underlying_type::allocator_type>; + using underlying_iterator = typename underlying_type::base_type::basic_iterator; + + static_assert(std::is_base_of_v<basic_registry_type, owner_type>, "Invalid registry type"); + + [[nodiscard]] auto &owner_or_assert() const noexcept { + ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); + return static_cast<owner_type &>(*owner); + } + +private: + void pop(underlying_iterator first, underlying_iterator last) final { + if(auto ® = owner_or_assert(); destruction.empty()) { + underlying_type::pop(first, last); + } else { + for(; first != last; ++first) { + const auto entt = *first; + destruction.publish(reg, entt); + const auto it = underlying_type::find(entt); + underlying_type::pop(it, it + 1u); + } + } + } + + void pop_all() final { + if(auto ® = owner_or_assert(); !destruction.empty()) { + if constexpr(std::is_same_v<typename underlying_type::element_type, typename underlying_type::entity_type>) { + for(typename underlying_type::size_type pos{}, last = underlying_type::free_list(); pos < last; ++pos) { + destruction.publish(reg, underlying_type::base_type::operator[](pos)); + } + } else { + for(auto entt: static_cast<typename underlying_type::base_type &>(*this)) { + if constexpr(underlying_type::storage_policy == deletion_policy::in_place) { + if(entt != tombstone) { + destruction.publish(reg, entt); + } + } else { + destruction.publish(reg, entt); + } + } + } + } + + underlying_type::pop_all(); + } + + underlying_iterator try_emplace(const typename underlying_type::entity_type entt, const bool force_back, const void *value) final { + const auto it = underlying_type::try_emplace(entt, force_back, value); + + if(auto ® = owner_or_assert(); it != underlying_type::base_type::end()) { + construction.publish(reg, *it); + } + + return it; + } + +public: + /*! @brief Allocator type. */ + using allocator_type = typename underlying_type::allocator_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename underlying_type::entity_type; + /*! @brief Expected registry type. */ + using registry_type = owner_type; + + /*! @brief Default constructor. */ + basic_sigh_mixin() + : basic_sigh_mixin{allocator_type{}} {} + + /** + * @brief Constructs an empty storage with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_sigh_mixin(const allocator_type &allocator) + : underlying_type{allocator}, + owner{}, + construction{allocator}, + destruction{allocator}, + update{allocator} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_sigh_mixin(const basic_sigh_mixin &) = delete; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_sigh_mixin(basic_sigh_mixin &&other) noexcept + : underlying_type{std::move(other)}, + owner{other.owner}, + construction{std::move(other.construction)}, + destruction{std::move(other.destruction)}, + update{std::move(other.update)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_sigh_mixin(basic_sigh_mixin &&other, const allocator_type &allocator) + : underlying_type{std::move(other), allocator}, + owner{other.owner}, + construction{std::move(other.construction), allocator}, + destruction{std::move(other.destruction), allocator}, + update{std::move(other.update), allocator} {} + + /*! @brief Default destructor. */ + ~basic_sigh_mixin() noexcept override = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This mixin. + */ + basic_sigh_mixin &operator=(const basic_sigh_mixin &) = delete; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This mixin. + */ + basic_sigh_mixin &operator=(basic_sigh_mixin &&other) noexcept { + owner = other.owner; + construction = std::move(other.construction); + destruction = std::move(other.destruction); + update = std::move(other.update); + underlying_type::operator=(std::move(other)); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given storage. + * @param other Storage to exchange the content with. + */ + void swap(basic_sigh_mixin &other) { + using std::swap; + swap(owner, other.owner); + swap(construction, other.construction); + swap(destruction, other.destruction); + swap(update, other.update); + underlying_type::swap(other); + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever a new instance is created and assigned to an entity.<br/> + * Listeners are invoked after the object has been assigned to the entity. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_construct() noexcept { + return sink{construction}; + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is explicitly updated.<br/> + * Listeners are invoked after the object has been updated. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_update() noexcept { + return sink{update}; + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is removed from an entity and thus destroyed.<br/> + * Listeners are invoked before the object has been removed from the entity. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_destroy() noexcept { + return sink{destruction}; + } + + /** + * @brief Emplace elements into a storage. + * + * The behavior of this operation depends on the underlying storage type + * (for example, components vs entities).<br/> + * Refer to the specific documentation for more details. + * + * @return A return value as returned by the underlying storage. + */ + auto emplace() { + const auto entt = underlying_type::emplace(); + construction.publish(owner_or_assert(), entt); + return entt; + } + + /** + * @brief Emplace elements into a storage. + * + * The behavior of this operation depends on the underlying storage type + * (for example, components vs entities).<br/> + * Refer to the specific documentation for more details. + * + * @tparam Args Types of arguments to forward to the underlying storage. + * @param hint A valid identifier. + * @param args Parameters to forward to the underlying storage. + * @return A return value as returned by the underlying storage. + */ + template<typename... Args> + decltype(auto) emplace(const entity_type hint, Args &&...args) { + if constexpr(std::is_same_v<typename underlying_type::element_type, typename underlying_type::entity_type>) { + const auto entt = underlying_type::emplace(hint, std::forward<Args>(args)...); + construction.publish(owner_or_assert(), entt); + return entt; + } else { + underlying_type::emplace(hint, std::forward<Args>(args)...); + construction.publish(owner_or_assert(), hint); + return this->get(hint); + } + } + + /** + * @brief Patches the given instance for an entity. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + * @return A reference to the patched instance. + */ + template<typename... Func> + decltype(auto) patch(const entity_type entt, Func &&...func) { + underlying_type::patch(entt, std::forward<Func>(func)...); + update.publish(owner_or_assert(), entt); + return this->get(entt); + } + + /** + * @brief Emplace elements into a storage. + * + * The behavior of this operation depends on the underlying storage type + * (for example, components vs entities).<br/> + * Refer to the specific documentation for more details. + * + * @tparam It Iterator type (as required by the underlying storage type). + * @tparam Args Types of arguments to forward to the underlying storage. + * @param first An iterator to the first element of the range. + * @param last An iterator past the last element of the range. + * @param args Parameters to use to forward to the underlying storage. + */ + template<typename It, typename... Args> + void insert(It first, It last, Args &&...args) { + auto from = underlying_type::size(); + underlying_type::insert(first, last, std::forward<Args>(args)...); + + if(auto ® = owner_or_assert(); !construction.empty()) { + for(const auto to = underlying_type::size(); from != to; ++from) { + construction.publish(reg, underlying_type::operator[](from)); + } + } + } + + /** + * @brief Forwards variables to derived classes, if any. + * @param value A variable wrapped in an opaque container. + */ + void bind(any value) noexcept final { + auto *reg = any_cast<basic_registry_type>(&value); + owner = reg ? reg : owner; + underlying_type::bind(std::move(value)); + } + +private: + basic_registry_type *owner; + sigh_type construction; + sigh_type destruction; + sigh_type update; +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/entity/observer.hpp b/external/entt/include/entt/entity/observer.hpp new file mode 100644 index 0000000..c280e4d --- /dev/null +++ b/external/entt/include/entt/entity/observer.hpp @@ -0,0 +1,438 @@ +#ifndef ENTT_ENTITY_OBSERVER_HPP +#define ENTT_ENTITY_OBSERVER_HPP + +#include <cstddef> +#include <cstdint> +#include <limits> +#include <type_traits> +#include <utility> +#include "../core/type_traits.hpp" +#include "fwd.hpp" +#include "storage.hpp" + +namespace entt { + +/*! @brief Grouping matcher. */ +template<typename...> +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<typename...> +struct basic_collector; + +/** + * @brief Collector. + * + * A collector contains a set of rules (literally, matchers) to use to track + * entities.<br/> + * 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 elements tracked by the matcher. + * @tparam NoneOf Types of elements used to filter out entities. + * @return The updated collector. + */ + template<typename... AllOf, typename... NoneOf> + static constexpr auto group(exclude_t<NoneOf...> = exclude_t{}) noexcept { + return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>>{}; + } + + /** + * @brief Adds an observing matcher to the collector. + * @tparam AnyOf Type of element for which changes should be detected. + * @return The updated collector. + */ + template<typename AnyOf> + static constexpr auto update() noexcept { + return basic_collector<matcher<type_list<>, 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<typename... Reject, typename... Require, typename... Rule, typename... Other> +struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule...>, Other...> { + /*! @brief Current matcher. */ + using current_type = matcher<type_list<Reject...>, type_list<Require...>, Rule...>; + + /** + * @brief Adds a grouping matcher to the collector. + * @tparam AllOf Types of elements tracked by the matcher. + * @tparam NoneOf Types of elements used to filter out entities. + * @return The updated collector. + */ + template<typename... AllOf, typename... NoneOf> + static constexpr auto group(exclude_t<NoneOf...> = exclude_t{}) noexcept { + return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>, current_type, Other...>{}; + } + + /** + * @brief Adds an observing matcher to the collector. + * @tparam AnyOf Type of element for which changes should be detected. + * @return The updated collector. + */ + template<typename AnyOf> + static constexpr auto update() noexcept { + return basic_collector<matcher<type_list<>, type_list<>, AnyOf>, current_type, Other...>{}; + } + + /** + * @brief Updates the filter of the last added matcher. + * @tparam AllOf Types of elements required by the matcher. + * @tparam NoneOf Types of elements used to filter out entities. + * @return The updated collector. + */ + template<typename... AllOf, typename... NoneOf> + static constexpr auto where(exclude_t<NoneOf...> = exclude_t{}) noexcept { + using extended_type = matcher<type_list<Reject..., NoneOf...>, type_list<Require..., AllOf...>, Rule...>; + return basic_collector<extended_type, Other...>{}; + } +}; + +/*! @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.<br/> + * In general, observers don't stay true to the order of any set of elements. + * + * 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 elements 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.<br/> + * 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 elements are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given elements 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 elements in any way + * invalidates all the iterators. + * + * @warning + * Lifetime of an observer doesn't necessarily have to overcome that of the + * registry to which it is connected. However, the observer must be disconnected + * from the registry before being destroyed to avoid crashes due to dangling + * pointers. + * + * @tparam Registry Basic registry type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Registry, typename Allocator> +class basic_observer { + using mask_type = std::uint64_t; + using storage_type = basic_storage<mask_type, typename Registry::entity_type, typename std::allocator_traits<Allocator>::template rebind_alloc<mask_type>>; + + template<std::size_t Index> + static void discard_if(storage_type &storage, Registry &, const typename Registry::entity_type entt) { + if(storage.contains(entt) && !(storage.get(entt) &= (~(1 << Index)))) { + storage.erase(entt); + } + } + + template<typename> + struct matcher_handler; + + template<typename... Reject, typename... Require, typename AnyOf> + struct matcher_handler<matcher<type_list<Reject...>, type_list<Require...>, AnyOf>> { + template<std::size_t Index> + static void maybe_valid_if(storage_type &storage, Registry &parent, const typename Registry::entity_type entt) { + if(parent.template all_of<Require...>(entt) && !parent.template any_of<Reject...>(entt)) { + if(!storage.contains(entt)) { + storage.emplace(entt); + } + + storage.get(entt) |= (1 << Index); + } + } + + template<std::size_t Index> + static void connect(storage_type &storage, Registry &parent) { + (parent.template on_destroy<Require>().template connect<&discard_if<Index>>(storage), ...); + (parent.template on_construct<Reject>().template connect<&discard_if<Index>>(storage), ...); + parent.template on_update<AnyOf>().template connect<&maybe_valid_if<Index>>(storage); + parent.template on_destroy<AnyOf>().template connect<&discard_if<Index>>(storage); + } + + static void disconnect(storage_type &storage, Registry &parent) { + (parent.template on_destroy<Require>().disconnect(&storage), ...); + (parent.template on_construct<Reject>().disconnect(&storage), ...); + parent.template on_update<AnyOf>().disconnect(&storage); + parent.template on_destroy<AnyOf>().disconnect(&storage); + } + }; + + template<typename... Reject, typename... Require, typename... NoneOf, typename... AllOf> + struct matcher_handler<matcher<type_list<Reject...>, type_list<Require...>, type_list<NoneOf...>, AllOf...>> { + template<std::size_t Index, typename... Ignore> + static void maybe_valid_if(storage_type &storage, Registry &parent, const typename Registry::entity_type entt) { + bool guard{}; + + if constexpr(sizeof...(Ignore) == 0) { + guard = parent.template all_of<AllOf..., Require...>(entt) && !parent.template any_of<NoneOf..., Reject...>(entt); + } else { + guard = parent.template all_of<AllOf..., Require...>(entt) && ((std::is_same_v<Ignore..., NoneOf> || !parent.template any_of<NoneOf>(entt)) && ...) && !parent.template any_of<Reject...>(entt); + } + + if(guard) { + if(!storage.contains(entt)) { + storage.emplace(entt); + } + + storage.get(entt) |= (1 << Index); + } + } + + template<std::size_t Index> + static void connect(storage_type &storage, Registry &parent) { + (parent.template on_destroy<Require>().template connect<&discard_if<Index>>(storage), ...); + (parent.template on_construct<Reject>().template connect<&discard_if<Index>>(storage), ...); + (parent.template on_construct<AllOf>().template connect<&maybe_valid_if<Index>>(storage), ...); + (parent.template on_destroy<NoneOf>().template connect<&maybe_valid_if<Index, NoneOf>>(storage), ...); + (parent.template on_destroy<AllOf>().template connect<&discard_if<Index>>(storage), ...); + (parent.template on_construct<NoneOf>().template connect<&discard_if<Index>>(storage), ...); + } + + static void disconnect(storage_type &storage, Registry &parent) { + (parent.template on_destroy<Require>().disconnect(&storage), ...); + (parent.template on_construct<Reject>().disconnect(&storage), ...); + (parent.template on_construct<AllOf>().disconnect(&storage), ...); + (parent.template on_destroy<NoneOf>().disconnect(&storage), ...); + (parent.template on_destroy<AllOf>().disconnect(&storage), ...); + (parent.template on_construct<NoneOf>().disconnect(&storage), ...); + } + }; + + template<typename... Matcher> + static void disconnect(Registry &parent, storage_type &storage) { + (matcher_handler<Matcher>::disconnect(storage, parent), ...); + } + + template<typename... Matcher, std::size_t... Index> + static void connect(Registry &parent, storage_type &storage, std::index_sequence<Index...>) { + static_assert(sizeof...(Matcher) < std::numeric_limits<mask_type>::digits, "Too many matchers"); + (matcher_handler<Matcher>::template connect<Index>(storage, parent), ...); + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! Basic registry type. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Random access iterator type. */ + using iterator = typename registry_type::common_type::iterator; + + /*! @brief Default constructor. */ + basic_observer() + : basic_observer{allocator_type{}} {} + + /** + * @brief Constructs an empty storage with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_observer(const allocator_type &allocator) + : release{}, + parent{}, + storage{allocator} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_observer(const basic_observer &) = delete; + + /*! @brief Default move constructor, deleted on purpose. */ + basic_observer(basic_observer &&) = delete; + + /** + * @brief Creates an observer and connects it to a given registry. + * @tparam Matcher Types of matchers to use to initialize the observer. + * @param reg A valid reference to a registry. + * @param allocator The allocator to use. + */ + template<typename... Matcher> + basic_observer(registry_type ®, basic_collector<Matcher...>, const allocator_type &allocator = allocator_type{}) + : release{&basic_observer::disconnect<Matcher...>}, + parent{®}, + storage{allocator} { + connect<Matcher...>(reg, storage, std::index_sequence_for<Matcher...>{}); + } + + /*! @brief Default destructor. */ + ~basic_observer() noexcept = 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<typename... Matcher> + void connect(registry_type ®, basic_collector<Matcher...>) { + disconnect(); + storage.clear(); + + parent = ® + release = &basic_observer::disconnect<Matcher...>; + connect<Matcher...>(reg, storage, std::index_sequence_for<Matcher...>{}); + } + + /*! @brief Disconnects an observer from the registry it keeps track of. */ + void disconnect() { + if(release) { + release(*parent, storage); + release = nullptr; + } + } + + /** + * @brief Returns the number of elements in an observer. + * @return Number of elements. + */ + [[nodiscard]] size_type size() const noexcept { + return storage.size(); + } + + /** + * @brief Checks whether an observer is empty. + * @return True if the observer is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return storage.empty(); + } + + /** + * @brief Direct access to the list of entities of the observer. + * + * The returned pointer is such that range `[data(), data() + size())` is + * always a valid range, even if the container is empty. + * + * @note + * Entities are in the reverse order as returned by the `begin`/`end` + * iterators. + * + * @return A pointer to the array of entities. + */ + [[nodiscard]] const entity_type *data() const noexcept { + return storage.data(); + } + + /** + * @brief Returns an iterator to the first entity of the observer. + * + * If the observer is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the observer. + */ + [[nodiscard]] iterator begin() const noexcept { + return storage.storage_type::base_type::begin(); + } + + /** + * @brief Returns an iterator that is past the last entity of the observer. + * @return An iterator to the entity following the last entity of the + * observer. + */ + [[nodiscard]] iterator end() const noexcept { + return storage.storage_type::base_type::end(); + } + + /*! @brief Clears the underlying container. */ + void clear() noexcept { + storage.clear(); + } + + /** + * @brief Iterates entities and applies the given function object to them. + * + * The function object is invoked for each entity.<br/> + * 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<typename Func> + 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<typename Func> + void each(Func func) { + std::as_const(*this).each(std::move(func)); + clear(); + } + +private: + void (*release)(registry_type &, storage_type &); + registry_type *parent; + storage_type storage; +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/entity/organizer.hpp b/external/entt/include/entt/entity/organizer.hpp new file mode 100644 index 0000000..a73fea4 --- /dev/null +++ b/external/entt/include/entt/entity/organizer.hpp @@ -0,0 +1,424 @@ +#ifndef ENTT_ENTITY_ORGANIZER_HPP +#define ENTT_ENTITY_ORGANIZER_HPP + +#include <cstddef> +#include <type_traits> +#include <utility> +#include <vector> +#include "../core/type_info.hpp" +#include "../core/type_traits.hpp" +#include "../core/utility.hpp" +#include "../graph/adjacency_matrix.hpp" +#include "../graph/flow.hpp" +#include "fwd.hpp" +#include "helper.hpp" + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template<typename> +struct is_view: std::false_type {}; + +template<typename... Args> +struct is_view<basic_view<Args...>>: std::true_type {}; + +template<typename Type> +inline constexpr bool is_view_v = is_view<Type>::value; + +template<typename Type, typename Override> +struct unpack_type { + using ro = std::conditional_t< + type_list_contains_v<Override, const Type> || (std::is_const_v<Type> && !type_list_contains_v<Override, std::remove_const_t<Type>>), + type_list<std::remove_const_t<Type>>, + type_list<>>; + + using rw = std::conditional_t< + type_list_contains_v<Override, std::remove_const_t<Type>> || (!std::is_const_v<Type> && !type_list_contains_v<Override, const Type>), + type_list<Type>, + type_list<>>; +}; + +template<typename... Args, typename... Override> +struct unpack_type<basic_registry<Args...>, type_list<Override...>> { + using ro = type_list<>; + using rw = type_list<>; +}; + +template<typename... Args, typename... Override> +struct unpack_type<const basic_registry<Args...>, type_list<Override...>> + : unpack_type<basic_registry<Args...>, type_list<Override...>> {}; + +template<typename... Get, typename... Exclude, typename... Override> +struct unpack_type<basic_view<get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>> { + using ro = type_list_cat_t<type_list<typename Exclude::element_type...>, typename unpack_type<constness_as_t<typename Get::element_type, Get>, type_list<Override...>>::ro...>; + using rw = type_list_cat_t<typename unpack_type<constness_as_t<typename Get::element_type, Get>, type_list<Override...>>::rw...>; +}; + +template<typename... Get, typename... Exclude, typename... Override> +struct unpack_type<const basic_view<get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>> + : unpack_type<basic_view<get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>> {}; + +template<typename, typename> +struct resource_traits; + +template<typename... Args, typename... Req> +struct resource_traits<type_list<Args...>, type_list<Req...>> { + using args = type_list<std::remove_const_t<Args>...>; + using ro = type_list_cat_t<typename unpack_type<Args, type_list<Req...>>::ro..., typename unpack_type<Req, type_list<>>::ro...>; + using rw = type_list_cat_t<typename unpack_type<Args, type_list<Req...>>::rw..., typename unpack_type<Req, type_list<>>::rw...>; +}; + +template<typename... Req, typename Ret, typename... Args> +resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> free_function_to_resource_traits(Ret (*)(Args...)); + +template<typename... Req, typename Ret, typename Type, typename... Args> +resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (*)(Type &, Args...)); + +template<typename... Req, typename Ret, typename Class, typename... Args> +resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...)); + +template<typename... Req, typename Ret, typename Class, typename... Args> +resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...) const); + +} // namespace internal +/*! @endcond */ + +/** + * @brief Utility class for creating a static task graph. + * + * This class offers minimal support (but sufficient in many cases) for creating + * an execution graph from functions and their requirements on resources.<br/> + * Note that the resulting tasks aren't executed in any case. This isn't the + * goal of the tool. Instead, they are returned to the user in the form of a + * graph that allows for safe execution. + * + * @tparam Registry Basic registry type. + */ +template<typename Registry> +class basic_organizer final { + using callback_type = void(const void *, Registry &); + using prepare_type = void(Registry &); + using dependency_type = std::size_t(const bool, const type_info **, const std::size_t); + + struct vertex_data final { + std::size_t ro_count{}; + std::size_t rw_count{}; + const char *name{}; + const void *payload{}; + callback_type *callback{}; + dependency_type *dependency{}; + prepare_type *prepare{}; + const type_info *info{}; + }; + + template<typename Type> + [[nodiscard]] static decltype(auto) extract(Registry ®) { + if constexpr(std::is_same_v<Type, Registry>) { + return reg; + } else if constexpr(internal::is_view_v<Type>) { + return static_cast<Type>(as_view{reg}); + } else { + return reg.ctx().template emplace<std::remove_reference_t<Type>>(); + } + } + + template<typename... Args> + [[nodiscard]] static auto to_args(Registry ®, type_list<Args...>) { + return std::tuple<decltype(extract<Args>(reg))...>(extract<Args>(reg)...); + } + + template<typename... Type> + [[nodiscard]] static std::size_t fill_dependencies(type_list<Type...>, [[maybe_unused]] const type_info **buffer, [[maybe_unused]] const std::size_t count) { + if constexpr(sizeof...(Type) == 0u) { + return {}; + } else { + const type_info *info[]{&type_id<Type>()...}; + const auto length = count < sizeof...(Type) ? count : sizeof...(Type); + + for(std::size_t pos{}; pos < length; ++pos) { + buffer[pos] = info[pos]; + } + + return length; + } + } + + template<typename... RO, typename... RW> + void track_dependencies(std::size_t index, const bool requires_registry, type_list<RO...>, type_list<RW...>) { + builder.bind(static_cast<id_type>(index)); + builder.set(type_hash<Registry>::value(), requires_registry || (sizeof...(RO) + sizeof...(RW) == 0u)); + (builder.ro(type_hash<RO>::value()), ...); + (builder.rw(type_hash<RW>::value()), ...); + } + +public: + /*! Basic registry type. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Raw task function type. */ + using function_type = callback_type; + + /*! @brief Vertex type of a task graph defined as an adjacency list. */ + struct vertex { + /** + * @brief Constructs a vertex of the task graph. + * @param data The data associated with the vertex. + * @param from List of in-edges of the vertex. + * @param to List of out-edges of the vertex. + */ + vertex(vertex_data data, std::vector<std::size_t> from, std::vector<std::size_t> to) + : node{std::move(data)}, + in{std::move(from)}, + out{std::move(to)} {} + + /** + * @brief Fills a buffer with the type info objects for the writable + * resources of a vertex. + * @param buffer A buffer pre-allocated by the user. + * @param length The length of the user-supplied buffer. + * @return The number of type info objects written to the buffer. + */ + [[nodiscard]] size_type ro_dependency(const type_info **buffer, const std::size_t length) const noexcept { + return node.dependency(false, buffer, length); + } + + /** + * @brief Fills a buffer with the type info objects for the read-only + * resources of a vertex. + * @param buffer A buffer pre-allocated by the user. + * @param length The length of the user-supplied buffer. + * @return The number of type info objects written to the buffer. + */ + [[nodiscard]] size_type rw_dependency(const type_info **buffer, const std::size_t length) const noexcept { + return node.dependency(true, buffer, length); + } + + /** + * @brief Returns the number of read-only resources of a vertex. + * @return The number of read-only resources of the vertex. + */ + [[nodiscard]] size_type ro_count() const noexcept { + return node.ro_count; + } + + /** + * @brief Returns the number of writable resources of a vertex. + * @return The number of writable resources of the vertex. + */ + [[nodiscard]] size_type rw_count() const noexcept { + return node.rw_count; + } + + /** + * @brief Checks if a vertex is also a top-level one. + * @return True if the vertex is a top-level one, false otherwise. + */ + [[nodiscard]] bool top_level() const noexcept { + return in.empty(); + } + + /** + * @brief Returns a type info object associated with a vertex. + * @return A properly initialized type info object. + */ + [[nodiscard]] const type_info &info() const noexcept { + return *node.info; + } + + /** + * @brief Returns a user defined name associated with a vertex, if any. + * @return The user defined name associated with the vertex, if any. + */ + [[nodiscard]] const char *name() const noexcept { + return node.name; + } + + /** + * @brief Returns the function associated with a vertex. + * @return The function associated with the vertex. + */ + [[nodiscard]] function_type *callback() const noexcept { + return node.callback; + } + + /** + * @brief Returns the payload associated with a vertex, if any. + * @return The payload associated with the vertex, if any. + */ + [[nodiscard]] const void *data() const noexcept { + return node.payload; + } + + /** + * @brief Returns the list of in-edges of a vertex. + * @return The list of in-edges of a vertex. + */ + [[nodiscard]] const std::vector<std::size_t> &in_edges() const noexcept { + return in; + } + + /** + * @brief Returns the list of out-edges of a vertex. + * @return The list of out-edges of a vertex. + */ + [[nodiscard]] const std::vector<std::size_t> &out_edges() const noexcept { + return out; + } + + /** + * @brief Returns the list of nodes reachable from a given vertex. + * @return The list of nodes reachable from the vertex. + */ + [[deprecated("use ::out_edges")]] [[nodiscard]] const std::vector<std::size_t> &children() const noexcept { + return out_edges(); + } + + /** + * @brief Prepares a registry and assures that all required resources + * are properly instantiated before using them. + * @param reg A valid registry. + */ + void prepare(registry_type ®) const { + node.prepare ? node.prepare(reg) : void(); + } + + private: + vertex_data node; + std::vector<std::size_t> in; + std::vector<std::size_t> out; + }; + + /** + * @brief Adds a free function to the task list. + * @tparam Candidate Function to add to the task list. + * @tparam Req Additional requirements and/or override resource access mode. + * @param name Optional name to associate with the task. + */ + template<auto Candidate, typename... Req> + void emplace(const char *name = nullptr) { + using resource_type = decltype(internal::free_function_to_resource_traits<Req...>(Candidate)); + constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, registry_type>; + + callback_type *callback = +[](const void *, registry_type ®) { + std::apply(Candidate, to_args(reg, typename resource_type::args{})); + }; + + vertex_data vdata{ + resource_type::ro::size, + resource_type::rw::size, + name, + nullptr, + callback, + +[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); }, + +[](registry_type ®) { void(to_args(reg, typename resource_type::args{})); }, + &type_id<std::integral_constant<decltype(Candidate), Candidate>>()}; + + track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{}); + vertices.push_back(std::move(vdata)); + } + + /** + * @brief Adds a free function with payload or a member function with an + * instance to the task list. + * @tparam Candidate Function or member to add to the task list. + * @tparam Req Additional requirements and/or override resource access mode. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @param name Optional name to associate with the task. + */ + template<auto Candidate, typename... Req, typename Type> + void emplace(Type &value_or_instance, const char *name = nullptr) { + using resource_type = decltype(internal::constrained_function_to_resource_traits<Req...>(Candidate)); + constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, registry_type>; + + callback_type *callback = +[](const void *payload, registry_type ®) { + Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload)); + std::apply(Candidate, std::tuple_cat(std::forward_as_tuple(*curr), to_args(reg, typename resource_type::args{}))); + }; + + vertex_data vdata{ + resource_type::ro::size, + resource_type::rw::size, + name, + &value_or_instance, + callback, + +[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); }, + +[](registry_type ®) { void(to_args(reg, typename resource_type::args{})); }, + &type_id<std::integral_constant<decltype(Candidate), Candidate>>()}; + + track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{}); + vertices.push_back(std::move(vdata)); + } + + /** + * @brief Adds an user defined function with optional payload to the task + * list. + * @tparam Req Additional requirements and/or override resource access mode. + * @param func Function to add to the task list. + * @param payload User defined arbitrary data. + * @param name Optional name to associate with the task. + */ + template<typename... Req> + void emplace(function_type *func, const void *payload = nullptr, const char *name = nullptr) { + using resource_type = internal::resource_traits<type_list<>, type_list<Req...>>; + track_dependencies(vertices.size(), true, typename resource_type::ro{}, typename resource_type::rw{}); + + vertex_data vdata{ + resource_type::ro::size, + resource_type::rw::size, + name, + payload, + func, + +[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); }, + nullptr, + &type_id<void>()}; + + vertices.push_back(std::move(vdata)); + } + + /** + * @brief Generates a task graph for the current content. + * @return The adjacency list of the task graph. + */ + [[nodiscard]] std::vector<vertex> graph() { + std::vector<vertex> adjacency_list{}; + adjacency_list.reserve(vertices.size()); + auto adjacency_matrix = builder.graph(); + + for(auto curr: adjacency_matrix.vertices()) { + std::vector<std::size_t> in{}; + std::vector<std::size_t> out{}; + + for(auto &&edge: adjacency_matrix.in_edges(curr)) { + in.push_back(edge.first); + } + + for(auto &&edge: adjacency_matrix.out_edges(curr)) { + out.push_back(edge.second); + } + + adjacency_list.emplace_back(vertices[curr], std::move(in), std::move(out)); + } + + return adjacency_list; + } + + /*! @brief Erases all elements from a container. */ + void clear() { + builder.clear(); + vertices.clear(); + } + +private: + std::vector<vertex_data> vertices; + flow builder; +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/entity/ranges.hpp b/external/entt/include/entt/entity/ranges.hpp new file mode 100644 index 0000000..a80c84e --- /dev/null +++ b/external/entt/include/entt/entity/ranges.hpp @@ -0,0 +1,26 @@ +#ifndef ENTT_ENTITY_RANGES_HPP +#define ENTT_ENTITY_RANGES_HPP + +#if __has_include(<version>) +# include <version> +# +# if defined(__cpp_lib_ranges) +# include <ranges> +# include "fwd.hpp" + +template<class... Args> +inline constexpr bool std::ranges::enable_borrowed_range<entt::basic_view<Args...>>{true}; + +template<class... Args> +inline constexpr bool std::ranges::enable_borrowed_range<entt::basic_group<Args...>>{true}; + +template<class... Args> +inline constexpr bool std::ranges::enable_view<entt::basic_view<Args...>>{true}; + +template<class... Args> +inline constexpr bool std::ranges::enable_view<entt::basic_group<Args...>>{true}; + +# endif +#endif + +#endif
\ No newline at end of file diff --git a/external/entt/include/entt/entity/registry.hpp b/external/entt/include/entt/entity/registry.hpp new file mode 100644 index 0000000..c4c0098 --- /dev/null +++ b/external/entt/include/entt/entity/registry.hpp @@ -0,0 +1,1222 @@ +#ifndef ENTT_ENTITY_REGISTRY_HPP +#define ENTT_ENTITY_REGISTRY_HPP + +#include <algorithm> +#include <array> +#include <cstddef> +#include <functional> +#include <iterator> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> +#include <vector> +#include "../config/config.h" +#include "../container/dense_map.hpp" +#include "../core/algorithm.hpp" +#include "../core/any.hpp" +#include "../core/fwd.hpp" +#include "../core/iterator.hpp" +#include "../core/memory.hpp" +#include "../core/type_info.hpp" +#include "../core/type_traits.hpp" +#include "../core/utility.hpp" +#include "entity.hpp" +#include "fwd.hpp" +#include "group.hpp" +#include "mixin.hpp" +#include "sparse_set.hpp" +#include "storage.hpp" +#include "view.hpp" + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template<typename It> +class registry_storage_iterator final { + template<typename Other> + friend class registry_storage_iterator; + + using mapped_type = std::remove_reference_t<decltype(std::declval<It>()->second)>; + +public: + using value_type = std::pair<id_type, constness_as_t<typename mapped_type::element_type, mapped_type> &>; + using pointer = input_iterator_pointer<value_type>; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr registry_storage_iterator() noexcept + : it{} {} + + constexpr registry_storage_iterator(It iter) noexcept + : it{iter} {} + + template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>> + constexpr registry_storage_iterator(const registry_storage_iterator<Other> &other) noexcept + : registry_storage_iterator{other.it} {} + + constexpr registry_storage_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr registry_storage_iterator operator++(int) noexcept { + registry_storage_iterator orig = *this; + return ++(*this), orig; + } + + constexpr registry_storage_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr registry_storage_iterator operator--(int) noexcept { + registry_storage_iterator orig = *this; + return operator--(), orig; + } + + constexpr registry_storage_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr registry_storage_iterator operator+(const difference_type value) const noexcept { + registry_storage_iterator copy = *this; + return (copy += value); + } + + constexpr registry_storage_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr registry_storage_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].first, *it[value].second}; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + template<typename Lhs, typename Rhs> + friend constexpr std::ptrdiff_t operator-(const registry_storage_iterator<Lhs> &, const registry_storage_iterator<Rhs> &) noexcept; + + template<typename Lhs, typename Rhs> + friend constexpr bool operator==(const registry_storage_iterator<Lhs> &, const registry_storage_iterator<Rhs> &) noexcept; + + template<typename Lhs, typename Rhs> + friend constexpr bool operator<(const registry_storage_iterator<Lhs> &, const registry_storage_iterator<Rhs> &) noexcept; + +private: + It it; +}; + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr std::ptrdiff_t operator-(const registry_storage_iterator<Lhs> &lhs, const registry_storage_iterator<Rhs> &rhs) noexcept { + return lhs.it - rhs.it; +} + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr bool operator==(const registry_storage_iterator<Lhs> &lhs, const registry_storage_iterator<Rhs> &rhs) noexcept { + return lhs.it == rhs.it; +} + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr bool operator!=(const registry_storage_iterator<Lhs> &lhs, const registry_storage_iterator<Rhs> &rhs) noexcept { + return !(lhs == rhs); +} + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr bool operator<(const registry_storage_iterator<Lhs> &lhs, const registry_storage_iterator<Rhs> &rhs) noexcept { + return lhs.it < rhs.it; +} + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr bool operator>(const registry_storage_iterator<Lhs> &lhs, const registry_storage_iterator<Rhs> &rhs) noexcept { + return rhs < lhs; +} + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr bool operator<=(const registry_storage_iterator<Lhs> &lhs, const registry_storage_iterator<Rhs> &rhs) noexcept { + return !(lhs > rhs); +} + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr bool operator>=(const registry_storage_iterator<Lhs> &lhs, const registry_storage_iterator<Rhs> &rhs) noexcept { + return !(lhs < rhs); +} + +template<typename Allocator> +class registry_context { + using alloc_traits = std::allocator_traits<Allocator>; + using allocator_type = typename alloc_traits::template rebind_alloc<std::pair<const id_type, basic_any<0u>>>; + +public: + explicit registry_context(const allocator_type &allocator) + : ctx{allocator} {} + + template<typename Type, typename... Args> + Type &emplace_as(const id_type id, Args &&...args) { + return any_cast<Type &>(ctx.try_emplace(id, std::in_place_type<Type>, std::forward<Args>(args)...).first->second); + } + + template<typename Type, typename... Args> + Type &emplace(Args &&...args) { + return emplace_as<Type>(type_id<Type>().hash(), std::forward<Args>(args)...); + } + + template<typename Type> + Type &insert_or_assign(const id_type id, Type &&value) { + return any_cast<std::remove_cv_t<std::remove_reference_t<Type>> &>(ctx.insert_or_assign(id, std::forward<Type>(value)).first->second); + } + + template<typename Type> + Type &insert_or_assign(Type &&value) { + return insert_or_assign(type_id<Type>().hash(), std::forward<Type>(value)); + } + + template<typename Type> + bool erase(const id_type id = type_id<Type>().hash()) { + const auto it = ctx.find(id); + return it != ctx.end() && it->second.type() == type_id<Type>() ? (ctx.erase(it), true) : false; + } + + template<typename Type> + [[nodiscard]] const Type &get(const id_type id = type_id<Type>().hash()) const { + return any_cast<const Type &>(ctx.at(id)); + } + + template<typename Type> + [[nodiscard]] Type &get(const id_type id = type_id<Type>().hash()) { + return any_cast<Type &>(ctx.at(id)); + } + + template<typename Type> + [[nodiscard]] const Type *find(const id_type id = type_id<Type>().hash()) const { + const auto it = ctx.find(id); + return it != ctx.cend() ? any_cast<const Type>(&it->second) : nullptr; + } + + template<typename Type> + [[nodiscard]] Type *find(const id_type id = type_id<Type>().hash()) { + const auto it = ctx.find(id); + return it != ctx.end() ? any_cast<Type>(&it->second) : nullptr; + } + + template<typename Type> + [[nodiscard]] bool contains(const id_type id = type_id<Type>().hash()) const { + const auto it = ctx.find(id); + return it != ctx.cend() && it->second.type() == type_id<Type>(); + } + +private: + dense_map<id_type, basic_any<0u>, identity, std::equal_to<>, allocator_type> ctx; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Fast and reliable entity-component system. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Entity, typename Allocator> +class basic_registry { + using base_type = basic_sparse_set<Entity, Allocator>; + using alloc_traits = std::allocator_traits<Allocator>; + static_assert(std::is_same_v<typename alloc_traits::value_type, Entity>, "Invalid value type"); + // std::shared_ptr because of its type erased allocator which is useful here + using pool_container_type = dense_map<id_type, std::shared_ptr<base_type>, identity, std::equal_to<>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, std::shared_ptr<base_type>>>>; + using group_container_type = dense_map<id_type, std::shared_ptr<internal::group_descriptor>, identity, std::equal_to<>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, std::shared_ptr<internal::group_descriptor>>>>; + using traits_type = entt_traits<Entity>; + + template<typename Type> + [[nodiscard]] auto &assure([[maybe_unused]] const id_type id = type_hash<Type>::value()) { + static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Non-decayed types not allowed"); + + if constexpr(std::is_same_v<Type, entity_type>) { + ENTT_ASSERT(id == type_hash<Type>::value(), "User entity storage not allowed"); + return entities; + } else { + using storage_type = storage_for_type<Type>; + + if(auto it = pools.find(id); it == pools.cend()) { + using alloc_type = typename storage_type::allocator_type; + typename pool_container_type::mapped_type cpool{}; + + if constexpr(std::is_void_v<Type> && !std::is_constructible_v<alloc_type, allocator_type>) { + // std::allocator<void> has no cross constructors (waiting for C++20) + cpool = std::allocate_shared<storage_type>(get_allocator(), alloc_type{}); + } else { + cpool = std::allocate_shared<storage_type>(get_allocator(), get_allocator()); + } + + pools.emplace(id, cpool); + cpool->bind(forward_as_any(*this)); + + return static_cast<storage_type &>(*cpool); + } else { + ENTT_ASSERT(it->second->type() == type_id<Type>(), "Unexpected type"); + return static_cast<storage_type &>(*it->second); + } + } + } + + template<typename Type> + [[nodiscard]] const auto *assure([[maybe_unused]] const id_type id = type_hash<Type>::value()) const { + static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Non-decayed types not allowed"); + + if constexpr(std::is_same_v<Type, entity_type>) { + ENTT_ASSERT(id == type_hash<Type>::value(), "User entity storage not allowed"); + return &entities; + } else { + if(const auto it = pools.find(id); it != pools.cend()) { + ENTT_ASSERT(it->second->type() == type_id<Type>(), "Unexpected type"); + return static_cast<const storage_for_type<Type> *>(it->second.get()); + } + + return static_cast<const storage_for_type<Type> *>(nullptr); + } + } + + void rebind() { + entities.bind(forward_as_any(*this)); + + for(auto &&curr: pools) { + curr.second->bind(forward_as_any(*this)); + } + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Underlying entity identifier. */ + using entity_type = typename traits_type::value_type; + /*! @brief Underlying version type. */ + using version_type = typename traits_type::version_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Common type among all storage types. */ + using common_type = base_type; + /*! @brief Context type. */ + using context = internal::registry_context<allocator_type>; + /*! @brief Iterable registry type. */ + using iterable = iterable_adaptor<internal::registry_storage_iterator<typename pool_container_type::iterator>>; + /*! @brief Constant iterable registry type. */ + using const_iterable = iterable_adaptor<internal::registry_storage_iterator<typename pool_container_type::const_iterator>>; + + /** + * @copybrief storage_for + * @tparam Type Storage value type, eventually const. + */ + template<typename Type> + using storage_for_type = typename storage_for<Type, Entity, typename alloc_traits::template rebind_alloc<std::remove_const_t<Type>>>::type; + + /*! @brief Default constructor. */ + basic_registry() + : basic_registry{allocator_type{}} {} + + /** + * @brief Constructs an empty registry with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_registry(const allocator_type &allocator) + : basic_registry{0u, allocator} {} + + /** + * @brief Allocates enough memory upon construction to store `count` pools. + * @param count The number of pools to allocate memory for. + * @param allocator The allocator to use. + */ + basic_registry(const size_type count, const allocator_type &allocator = allocator_type{}) + : vars{allocator}, + pools{allocator}, + groups{allocator}, + entities{allocator} { + pools.reserve(count); + rebind(); + } + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_registry(const basic_registry &) = delete; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_registry(basic_registry &&other) noexcept + : vars{std::move(other.vars)}, + pools{std::move(other.pools)}, + groups{std::move(other.groups)}, + entities{std::move(other.entities)} { + rebind(); + } + + /*! @brief Default destructor. */ + ~basic_registry() noexcept = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This mixin. + */ + basic_registry &operator=(const basic_registry &) = delete; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This registry. + */ + basic_registry &operator=(basic_registry &&other) noexcept { + vars = std::move(other.vars); + pools = std::move(other.pools); + groups = std::move(other.groups); + entities = std::move(other.entities); + + rebind(); + + return *this; + } + + /** + * @brief Exchanges the contents with those of a given registry. + * @param other Registry to exchange the content with. + */ + void swap(basic_registry &other) { + using std::swap; + + swap(vars, other.vars); + swap(pools, other.pools); + swap(groups, other.groups); + swap(entities, other.entities); + + rebind(); + other.rebind(); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return entities.get_allocator(); + } + + /** + * @brief Returns an iterable object to use to _visit_ a registry. + * + * The iterable object returns a pair that contains the name and a reference + * to the current storage. + * + * @return An iterable object to use to _visit_ the registry. + */ + [[nodiscard]] iterable storage() noexcept { + return iterable{pools.begin(), pools.end()}; + } + + /*! @copydoc storage */ + [[nodiscard]] const_iterable storage() const noexcept { + return const_iterable{pools.cbegin(), pools.cend()}; + } + + /** + * @brief Finds the storage associated with a given name, if any. + * @param id Name used to map the storage within the registry. + * @return A pointer to the storage if it exists, a null pointer otherwise. + */ + [[nodiscard]] common_type *storage(const id_type id) { + return const_cast<common_type *>(std::as_const(*this).storage(id)); + } + + /** + * @brief Finds the storage associated with a given name, if any. + * @param id Name used to map the storage within the registry. + * @return A pointer to the storage if it exists, a null pointer otherwise. + */ + [[nodiscard]] const common_type *storage(const id_type id) const { + const auto it = pools.find(id); + return it == pools.cend() ? nullptr : it->second.get(); + } + + /** + * @brief Returns the storage for a given element type. + * @tparam Type Type of element of which to return the storage. + * @param id Optional name used to map the storage within the registry. + * @return The storage for the given element type. + */ + template<typename Type> + storage_for_type<Type> &storage(const id_type id = type_hash<Type>::value()) { + return assure<Type>(id); + } + + /** + * @brief Returns the storage for a given element type, if any. + * @tparam Type Type of element of which to return the storage. + * @param id Optional name used to map the storage within the registry. + * @return The storage for the given element type. + */ + template<typename Type> + [[nodiscard]] const storage_for_type<Type> *storage(const id_type id = type_hash<Type>::value()) const { + return assure<Type>(id); + } + + /** + * @brief Checks if an identifier refers to a valid entity. + * @param entt An identifier, either valid or not. + * @return True if the identifier is valid, false otherwise. + */ + [[nodiscard]] bool valid(const entity_type entt) const { + return static_cast<size_type>(entities.find(entt).index()) < entities.free_list(); + } + + /** + * @brief Returns the actual version for an identifier. + * @param entt A valid identifier. + * @return The version for the given identifier if valid, the tombstone + * version otherwise. + */ + [[nodiscard]] version_type current(const entity_type entt) const { + return entities.current(entt); + } + + /** + * @brief Creates a new entity or recycles a destroyed one. + * @return A valid identifier. + */ + [[nodiscard]] entity_type create() { + return entities.emplace(); + } + + /** + * @copybrief create + * + * If the requested entity isn't in use, the suggested identifier is used. + * Otherwise, a new identifier is generated. + * + * @param hint Required identifier. + * @return A valid identifier. + */ + [[nodiscard]] entity_type create(const entity_type hint) { + return entities.emplace(hint); + } + + /** + * @brief Assigns each element in a range an identifier. + * + * @sa create + * + * @tparam It Type of forward iterator. + * @param first An iterator to the first element of the range to generate. + * @param last An iterator past the last element of the range to generate. + */ + template<typename It> + void create(It first, It last) { + entities.insert(std::move(first), std::move(last)); + } + + /** + * @brief Destroys an entity and releases its identifier. + * + * @warning + * Adding or removing elements to an entity that is being destroyed can + * result in undefined behavior. + * + * @param entt A valid identifier. + * @return The version of the recycled entity. + */ + version_type destroy(const entity_type entt) { + for(size_type pos = pools.size(); pos; --pos) { + pools.begin()[pos - 1u].second->remove(entt); + } + + entities.erase(entt); + return entities.current(entt); + } + + /** + * @brief Destroys an entity and releases its identifier. + * + * The suggested version or the valid version closest to the suggested one + * is used instead of the implicitly generated version. + * + * @sa destroy + * + * @param entt A valid identifier. + * @param version A desired version upon destruction. + * @return The version actually assigned to the entity. + */ + version_type destroy(const entity_type entt, const version_type version) { + destroy(entt); + const auto elem = traits_type::construct(traits_type::to_entity(entt), version); + return entities.bump((elem == tombstone) ? traits_type::next(elem) : elem); + } + + /** + * @brief Destroys all entities in a range and releases their identifiers. + * + * @sa destroy + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + template<typename It> + void destroy(It first, It last) { + const auto to = entities.sort_as(first, last); + const auto from = entities.cend() - entities.free_list(); + + for(auto &&curr: pools) { + curr.second->remove(from, to); + } + + entities.erase(from, to); + } + + /** + * @brief Assigns the given element to an entity. + * + * The element must have a proper constructor or be of aggregate type. + * + * @warning + * Attempting to assign an element to an entity that already owns it results + * in undefined behavior. + * + * @tparam Type Type of element to create. + * @tparam Args Types of arguments to use to construct the element. + * @param entt A valid identifier. + * @param args Parameters to use to initialize the element. + * @return A reference to the newly created element. + */ + template<typename Type, typename... Args> + decltype(auto) emplace(const entity_type entt, Args &&...args) { + ENTT_ASSERT(valid(entt), "Invalid entity"); + return assure<Type>().emplace(entt, std::forward<Args>(args)...); + } + + /** + * @brief Assigns each entity in a range the given element. + * + * @sa emplace + * + * @tparam Type Type of element 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 element to assign. + */ + template<typename Type, typename It> + void insert(It first, It last, const Type &value = {}) { + ENTT_ASSERT(std::all_of(first, last, [this](const auto entt) { return valid(entt); }), "Invalid entity"); + assure<Type>().insert(std::move(first), std::move(last), value); + } + + /** + * @brief Assigns each entity in a range the given elements. + * + * @sa emplace + * + * @tparam Type Type of element 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 elements. + */ + template<typename Type, typename EIt, typename CIt, typename = std::enable_if_t<std::is_same_v<typename std::iterator_traits<CIt>::value_type, Type>>> + void insert(EIt first, EIt last, CIt from) { + ENTT_ASSERT(std::all_of(first, last, [this](const auto entt) { return valid(entt); }), "Invalid entity"); + assure<Type>().insert(first, last, from); + } + + /** + * @brief Assigns or replaces the given element for an entity. + * + * @sa emplace + * @sa replace + * + * @tparam Type Type of element to assign or replace. + * @tparam Args Types of arguments to use to construct the element. + * @param entt A valid identifier. + * @param args Parameters to use to initialize the element. + * @return A reference to the newly created element. + */ + template<typename Type, typename... Args> + decltype(auto) emplace_or_replace(const entity_type entt, Args &&...args) { + if(auto &cpool = assure<Type>(); cpool.contains(entt)) { + return cpool.patch(entt, [&args...](auto &...curr) { ((curr = Type{std::forward<Args>(args)...}), ...); }); + } else { + ENTT_ASSERT(valid(entt), "Invalid entity"); + return cpool.emplace(entt, std::forward<Args>(args)...); + } + } + + /** + * @brief Patches the given element for an entity. + * + * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(Type &); + * @endcode + * + * @warning + * Attempting to patch an element of an entity that doesn't own it results + * in undefined behavior. + * + * @tparam Type Type of element to patch. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + * @return A reference to the patched element. + */ + template<typename Type, typename... Func> + decltype(auto) patch(const entity_type entt, Func &&...func) { + return assure<Type>().patch(entt, std::forward<Func>(func)...); + } + + /** + * @brief Replaces the given element for an entity. + * + * The element must have a proper constructor or be of aggregate type. + * + * @warning + * Attempting to replace an element of an entity that doesn't own it results + * in undefined behavior. + * + * @tparam Type Type of element to replace. + * @tparam Args Types of arguments to use to construct the element. + * @param entt A valid identifier. + * @param args Parameters to use to initialize the element. + * @return A reference to the element being replaced. + */ + template<typename Type, typename... Args> + decltype(auto) replace(const entity_type entt, Args &&...args) { + return patch<Type>(entt, [&args...](auto &...curr) { ((curr = Type{std::forward<Args>(args)...}), ...); }); + } + + /** + * @brief Removes the given elements from an entity. + * @tparam Type Type of element to remove. + * @tparam Other Other types of elements to remove. + * @param entt A valid identifier. + * @return The number of elements actually removed. + */ + template<typename Type, typename... Other> + size_type remove(const entity_type entt) { + return (assure<Type>().remove(entt) + ... + assure<Other>().remove(entt)); + } + + /** + * @brief Removes the given elements from all the entities in a range. + * + * @sa remove + * + * @tparam Type Type of element to remove. + * @tparam Other Other types of elements to remove. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @return The number of elements actually removed. + */ + template<typename Type, typename... Other, typename It> + size_type remove(It first, It last) { + size_type count{}; + + if constexpr(std::is_same_v<It, typename common_type::iterator>) { + std::array cpools{static_cast<common_type *>(&assure<Type>()), static_cast<common_type *>(&assure<Other>())...}; + + for(auto from = cpools.begin(), to = cpools.end(); from != to; ++from) { + if constexpr(sizeof...(Other) != 0u) { + if((*from)->data() == first.data()) { + std::swap((*from), cpools.back()); + } + } + + count += (*from)->remove(first, last); + } + + } else { + for(auto cpools = std::forward_as_tuple(assure<Type>(), assure<Other>()...); first != last; ++first) { + count += std::apply([entt = *first](auto &...curr) { return (curr.remove(entt) + ... + 0u); }, cpools); + } + } + + return count; + } + + /** + * @brief Erases the given elements from an entity. + * + * @warning + * Attempting to erase an element from an entity that doesn't own it results + * in undefined behavior. + * + * @tparam Type Types of elements to erase. + * @tparam Other Other types of elements to erase. + * @param entt A valid identifier. + */ + template<typename Type, typename... Other> + void erase(const entity_type entt) { + (assure<Type>().erase(entt), (assure<Other>().erase(entt), ...)); + } + + /** + * @brief Erases the given elements from all the entities in a range. + * + * @sa erase + * + * @tparam Type Types of elements to erase. + * @tparam Other Other types of elements to erase. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + template<typename Type, typename... Other, typename It> + void erase(It first, It last) { + if constexpr(std::is_same_v<It, typename common_type::iterator>) { + std::array cpools{static_cast<common_type *>(&assure<Type>()), static_cast<common_type *>(&assure<Other>())...}; + + for(auto from = cpools.begin(), to = cpools.end(); from != to; ++from) { + if constexpr(sizeof...(Other) != 0u) { + if((*from)->data() == first.data()) { + std::swap(*from, cpools.back()); + } + } + + (*from)->erase(first, last); + } + } else { + for(auto cpools = std::forward_as_tuple(assure<Type>(), assure<Other>()...); first != last; ++first) { + std::apply([entt = *first](auto &...curr) { (curr.erase(entt), ...); }, cpools); + } + } + } + + /** + * @brief Erases elements satisfying specific criteria from an entity. + * + * The function type is equivalent to: + * + * @code{.cpp} + * void(const id_type, typename basic_registry<Entity>::common_type &); + * @endcode + * + * Only storage where the entity exists are passed to the function. + * + * @tparam Func Type of the function object to invoke. + * @param entt A valid identifier. + * @param func A valid function object. + */ + template<typename Func> + void erase_if(const entity_type entt, Func func) { + for(auto [id, cpool]: storage()) { + if(cpool.contains(entt) && func(id, std::as_const(cpool))) { + cpool.erase(entt); + } + } + } + + /** + * @brief Removes all tombstones from a registry or only the pools for the + * given elements. + * @tparam Type Types of elements for which to clear all tombstones. + */ + template<typename... Type> + void compact() { + if constexpr(sizeof...(Type) == 0u) { + for(auto &&curr: pools) { + curr.second->compact(); + } + } else { + (assure<Type>().compact(), ...); + } + } + + /** + * @brief Check if an entity is part of all the given storage. + * @tparam Type Type of storage to check for. + * @param entt A valid identifier. + * @return True if the entity is part of all the storage, false otherwise. + */ + template<typename... Type> + [[nodiscard]] bool all_of([[maybe_unused]] const entity_type entt) const { + if constexpr(sizeof...(Type) == 1u) { + auto *cpool = assure<std::remove_const_t<Type>...>(); + return cpool && cpool->contains(entt); + } else { + return (all_of<Type>(entt) && ...); + } + } + + /** + * @brief Check if an entity is part of at least one given storage. + * @tparam Type Type of storage to check for. + * @param entt A valid identifier. + * @return True if the entity is part of at least one storage, false + * otherwise. + */ + template<typename... Type> + [[nodiscard]] bool any_of([[maybe_unused]] const entity_type entt) const { + return (all_of<Type>(entt) || ...); + } + + /** + * @brief Returns references to the given elements for an entity. + * + * @warning + * Attempting to get an element from an entity that doesn't own it results + * in undefined behavior. + * + * @tparam Type Types of elements to get. + * @param entt A valid identifier. + * @return References to the elements owned by the entity. + */ + template<typename... Type> + [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entt) const { + if constexpr(sizeof...(Type) == 1u) { + return (assure<std::remove_const_t<Type>>()->get(entt), ...); + } else { + return std::forward_as_tuple(get<Type>(entt)...); + } + } + + /*! @copydoc get */ + template<typename... Type> + [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entt) { + if constexpr(sizeof...(Type) == 1u) { + return (static_cast<storage_for_type<Type> &>(assure<std::remove_const_t<Type>>()).get(entt), ...); + } else { + return std::forward_as_tuple(get<Type>(entt)...); + } + } + + /** + * @brief Returns a reference to the given element for an entity. + * + * In case the entity doesn't own the element, the parameters provided are + * used to construct it. + * + * @sa get + * @sa emplace + * + * @tparam Type Type of element to get. + * @tparam Args Types of arguments to use to construct the element. + * @param entt A valid identifier. + * @param args Parameters to use to initialize the element. + * @return Reference to the element owned by the entity. + */ + template<typename Type, typename... Args> + [[nodiscard]] decltype(auto) get_or_emplace(const entity_type entt, Args &&...args) { + if(auto &cpool = assure<Type>(); cpool.contains(entt)) { + return cpool.get(entt); + } else { + ENTT_ASSERT(valid(entt), "Invalid entity"); + return cpool.emplace(entt, std::forward<Args>(args)...); + } + } + + /** + * @brief Returns pointers to the given elements for an entity. + * + * @note + * The registry retains ownership of the pointed-to elements. + * + * @tparam Type Types of elements to get. + * @param entt A valid identifier. + * @return Pointers to the elements owned by the entity. + */ + template<typename... Type> + [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entt) const { + if constexpr(sizeof...(Type) == 1u) { + const auto *cpool = assure<std::remove_const_t<Type>...>(); + return (cpool && cpool->contains(entt)) ? std::addressof(cpool->get(entt)) : nullptr; + } else { + return std::make_tuple(try_get<Type>(entt)...); + } + } + + /*! @copydoc try_get */ + template<typename... Type> + [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entt) { + if constexpr(sizeof...(Type) == 1u) { + return (const_cast<Type *>(std::as_const(*this).template try_get<Type>(entt)), ...); + } else { + return std::make_tuple(try_get<Type>(entt)...); + } + } + + /** + * @brief Clears a whole registry or the pools for the given elements. + * @tparam Type Types of elements to remove from their entities. + */ + template<typename... Type> + void clear() { + if constexpr(sizeof...(Type) == 0u) { + for(size_type pos = pools.size(); pos; --pos) { + pools.begin()[pos - 1u].second->clear(); + } + + const auto elem = entities.each(); + entities.erase(elem.begin().base(), elem.end().base()); + } else { + (assure<Type>().clear(), ...); + } + } + + /** + * @brief Checks if an entity has elements assigned. + * @param entt A valid identifier. + * @return True if the entity has no elements assigned, false otherwise. + */ + [[nodiscard]] bool orphan(const entity_type entt) const { + return std::none_of(pools.cbegin(), pools.cend(), [entt](auto &&curr) { return curr.second->contains(entt); }); + } + + /** + * @brief Returns a sink object for the given element. + * + * Use this function to receive notifications whenever a new instance of the + * given element is created and assigned to an entity.<br/> + * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry<Entity> &, Entity); + * @endcode + * + * Listeners are invoked **after** assigning the element to the entity. + * + * @sa sink + * + * @tparam Type Type of element of which to get the sink. + * @param id Optional name used to map the storage within the registry. + * @return A temporary sink object. + */ + template<typename Type> + [[nodiscard]] auto on_construct(const id_type id = type_hash<Type>::value()) { + return assure<Type>(id).on_construct(); + } + + /** + * @brief Returns a sink object for the given element. + * + * Use this function to receive notifications whenever an instance of the + * given element is explicitly updated.<br/> + * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry<Entity> &, Entity); + * @endcode + * + * Listeners are invoked **after** updating the element. + * + * @sa sink + * + * @tparam Type Type of element of which to get the sink. + * @param id Optional name used to map the storage within the registry. + * @return A temporary sink object. + */ + template<typename Type> + [[nodiscard]] auto on_update(const id_type id = type_hash<Type>::value()) { + return assure<Type>(id).on_update(); + } + + /** + * @brief Returns a sink object for the given element. + * + * Use this function to receive notifications whenever an instance of the + * given element is removed from an entity and thus destroyed.<br/> + * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry<Entity> &, Entity); + * @endcode + * + * Listeners are invoked **before** removing the element from the entity. + * + * @sa sink + * + * @tparam Type Type of element of which to get the sink. + * @param id Optional name used to map the storage within the registry. + * @return A temporary sink object. + */ + template<typename Type> + [[nodiscard]] auto on_destroy(const id_type id = type_hash<Type>::value()) { + return assure<Type>(id).on_destroy(); + } + + /** + * @brief Returns a view for the given elements. + * @tparam Type Type of element used to construct the view. + * @tparam Other Other types of elements used to construct the view. + * @tparam Exclude Types of elements used to filter the view. + * @return A newly created view. + */ + template<typename Type, typename... Other, typename... Exclude> + [[nodiscard]] basic_view<get_t<storage_for_type<const Type>, storage_for_type<const Other>...>, exclude_t<storage_for_type<const Exclude>...>> + view(exclude_t<Exclude...> = exclude_t{}) const { + const auto cpools = std::make_tuple(assure<std::remove_const_t<Type>>(), assure<std::remove_const_t<Other>>()..., assure<std::remove_const_t<Exclude>>()...); + basic_view<get_t<storage_for_type<const Type>, storage_for_type<const Other>...>, exclude_t<storage_for_type<const Exclude>...>> elem{}; + std::apply([&elem](const auto *...curr) { ((curr ? elem.storage(*curr) : void()), ...); }, cpools); + return elem; + } + + /*! @copydoc view */ + template<typename Type, typename... Other, typename... Exclude> + [[nodiscard]] basic_view<get_t<storage_for_type<Type>, storage_for_type<Other>...>, exclude_t<storage_for_type<Exclude>...>> + view(exclude_t<Exclude...> = exclude_t{}) { + return {assure<std::remove_const_t<Type>>(), assure<std::remove_const_t<Other>>()..., assure<std::remove_const_t<Exclude>>()...}; + } + + /** + * @brief Returns a group for the given elements. + * @tparam Owned Types of storage _owned_ by the group. + * @tparam Get Types of storage _observed_ by the group, if any. + * @tparam Exclude Types of storage used to filter the group, if any. + * @return A newly created group. + */ + template<typename... Owned, typename... Get, typename... Exclude> + basic_group<owned_t<storage_for_type<Owned>...>, get_t<storage_for_type<Get>...>, exclude_t<storage_for_type<Exclude>...>> + group(get_t<Get...> = get_t{}, exclude_t<Exclude...> = exclude_t{}) { + using group_type = basic_group<owned_t<storage_for_type<Owned>...>, get_t<storage_for_type<Get>...>, exclude_t<storage_for_type<Exclude>...>>; + using handler_type = typename group_type::handler; + + if(auto it = groups.find(group_type::group_id()); it != groups.cend()) { + return {*std::static_pointer_cast<handler_type>(it->second)}; + } + + std::shared_ptr<handler_type> handler{}; + + if constexpr(sizeof...(Owned) == 0u) { + handler = std::allocate_shared<handler_type>(get_allocator(), get_allocator(), std::forward_as_tuple(assure<std::remove_const_t<Get>>()...), std::forward_as_tuple(assure<std::remove_const_t<Exclude>>()...)); + } else { + handler = std::allocate_shared<handler_type>(get_allocator(), std::forward_as_tuple(assure<std::remove_const_t<Owned>>()..., assure<std::remove_const_t<Get>>()...), std::forward_as_tuple(assure<std::remove_const_t<Exclude>>()...)); + ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), [](const auto &data) { return !(data.second->owned(type_id<Owned>().hash()) || ...); }), "Conflicting groups"); + } + + groups.emplace(group_type::group_id(), handler); + return {*handler}; + } + + /*! @copydoc group */ + template<typename... Owned, typename... Get, typename... Exclude> + [[nodiscard]] basic_group<owned_t<storage_for_type<const Owned>...>, get_t<storage_for_type<const Get>...>, exclude_t<storage_for_type<const Exclude>...>> + group_if_exists(get_t<Get...> = get_t{}, exclude_t<Exclude...> = exclude_t{}) const { + using group_type = basic_group<owned_t<storage_for_type<const Owned>...>, get_t<storage_for_type<const Get>...>, exclude_t<storage_for_type<const Exclude>...>>; + using handler_type = typename group_type::handler; + + if(auto it = groups.find(group_type::group_id()); it != groups.cend()) { + return {*std::static_pointer_cast<handler_type>(it->second)}; + } + + return {}; + } + + /** + * @brief Checks whether the given elements belong to any group. + * @tparam Type Types of elements in which one is interested. + * @return True if the pools of the given elements are _free_, false + * otherwise. + */ + template<typename... Type> + [[nodiscard]] bool owned() const { + return std::any_of(groups.cbegin(), groups.cend(), [](auto &&data) { return (data.second->owned(type_id<Type>().hash()) || ...); }); + } + + /** + * @brief Sorts the elements of a given element. + * + * The comparison function object returns `true` if the first element is + * _less_ than the second one, `false` otherwise. Its signature is also + * equivalent to one of the following: + * + * @code{.cpp} + * bool(const Entity, const Entity); + * bool(const Type &, const Type &); + * @endcode + * + * Moreover, it shall induce a _strict weak ordering_ on the values.<br/> + * The sort function object offers an `operator()` that accepts: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function object to use to compare the elements. + * + * The comparison function object hasn't necessarily the type of the one + * passed along with the other parameters to this member function. + * + * @warning + * Pools of elements owned by a group cannot be sorted. + * + * @tparam Type Type of elements 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<typename Type, typename Compare, typename Sort = std_sort, typename... Args> + void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { + ENTT_ASSERT(!owned<Type>(), "Cannot sort owned storage"); + auto &cpool = assure<Type>(); + + if constexpr(std::is_invocable_v<Compare, decltype(cpool.get({})), decltype(cpool.get({}))>) { + auto comp = [&cpool, compare = std::move(compare)](const auto lhs, const auto rhs) { return compare(std::as_const(cpool.get(lhs)), std::as_const(cpool.get(rhs))); }; + cpool.sort(std::move(comp), std::move(algo), std::forward<Args>(args)...); + } else { + cpool.sort(std::move(compare), std::move(algo), std::forward<Args>(args)...); + } + } + + /** + * @brief Sorts two pools of elements in the same way. + * + * Entities and elements in `To` which are part of both storage are sorted + * internally with the order they have in `From`. The others follow in no + * particular order. + * + * @warning + * Pools of elements owned by a group cannot be sorted. + * + * @tparam To Type of elements to sort. + * @tparam From Type of elements to use to sort. + */ + template<typename To, typename From> + void sort() { + ENTT_ASSERT(!owned<To>(), "Cannot sort owned storage"); + const base_type &cpool = assure<From>(); + assure<To>().sort_as(cpool.begin(), cpool.end()); + } + + /** + * @brief Returns the context object, that is, a general purpose container. + * @return The context object, that is, a general purpose container. + */ + [[nodiscard]] context &ctx() noexcept { + return vars; + } + + /*! @copydoc ctx */ + [[nodiscard]] const context &ctx() const noexcept { + return vars; + } + +private: + context vars; + pool_container_type pools; + group_container_type groups; + storage_for_type<entity_type> entities; +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/entity/runtime_view.hpp b/external/entt/include/entt/entity/runtime_view.hpp new file mode 100644 index 0000000..c0dfa01 --- /dev/null +++ b/external/entt/include/entt/entity/runtime_view.hpp @@ -0,0 +1,318 @@ +#ifndef ENTT_ENTITY_RUNTIME_VIEW_HPP +#define ENTT_ENTITY_RUNTIME_VIEW_HPP + +#include <algorithm> +#include <cstddef> +#include <iterator> +#include <utility> +#include <vector> +#include "entity.hpp" +#include "fwd.hpp" + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template<typename Set> +class runtime_view_iterator final { + using iterator_type = typename Set::iterator; + using iterator_traits = std::iterator_traits<iterator_type>; + + [[nodiscard]] bool valid() const { + return (!tombstone_check || *it != tombstone) + && std::all_of(++pools->begin(), pools->end(), [entt = *it](const auto *curr) { return curr->contains(entt); }) + && std::none_of(filter->cbegin(), filter->cend(), [entt = *it](const auto *curr) { return curr && curr->contains(entt); }); + } + +public: + using value_type = typename iterator_traits::value_type; + using pointer = typename iterator_traits::pointer; + using reference = typename iterator_traits::reference; + using difference_type = typename iterator_traits::difference_type; + using iterator_category = std::bidirectional_iterator_tag; + + constexpr runtime_view_iterator() noexcept + : pools{}, + filter{}, + it{}, + tombstone_check{} {} + + // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) + runtime_view_iterator(const std::vector<Set *> &cpools, const std::vector<Set *> &ignore, iterator_type curr) noexcept + : pools{&cpools}, + filter{&ignore}, + it{curr}, + tombstone_check{pools->size() == 1u && (*pools)[0u]->policy() == deletion_policy::in_place} { + if(it != (*pools)[0]->end() && !valid()) { + ++(*this); + } + } + + runtime_view_iterator &operator++() { + ++it; + for(const auto last = (*pools)[0]->end(); it != last && !valid(); ++it) {} + return *this; + } + + runtime_view_iterator operator++(int) { + runtime_view_iterator orig = *this; + return ++(*this), orig; + } + + runtime_view_iterator &operator--() { + --it; + for(const auto first = (*pools)[0]->begin(); it != first && !valid(); --it) {} + return *this; + } + + runtime_view_iterator operator--(int) { + runtime_view_iterator orig = *this; + return operator--(), orig; + } + + [[nodiscard]] pointer operator->() const noexcept { + return it.operator->(); + } + + [[nodiscard]] reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr bool operator==(const runtime_view_iterator &other) const noexcept { + return it == other.it; + } + + [[nodiscard]] constexpr bool operator!=(const runtime_view_iterator &other) const noexcept { + return !(*this == other); + } + +private: + const std::vector<Set *> *pools; + const std::vector<Set *> *filter; + iterator_type it; + bool tombstone_check; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Generic runtime view. + * + * Runtime views iterate over those entities that are at least in the given + * storage. During initialization, a runtime view looks at the number of + * entities available for each element and uses the smallest set in order to get + * a performance boost when iterating. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New elements are added to the storage. + * * The entity currently pointed is modified (for example, elements are added + * or removed from it). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the storage iterated by the view in any way + * invalidates all the iterators. + * + * @tparam Type Common base type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Type, typename Allocator> +class basic_runtime_view { + using alloc_traits = std::allocator_traits<Allocator>; + static_assert(std::is_same_v<typename alloc_traits::value_type, Type *>, "Invalid value type"); + using container_type = std::vector<Type *, Allocator>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Underlying entity identifier. */ + using entity_type = typename Type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Common type among all storage types. */ + using common_type = Type; + /*! @brief Bidirectional iterator type. */ + using iterator = internal::runtime_view_iterator<common_type>; + + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_runtime_view() noexcept + : basic_runtime_view{allocator_type{}} {} + + /** + * @brief Constructs an empty, invalid view with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_runtime_view(const allocator_type &allocator) + : pools{allocator}, + filter{allocator} {} + + /*! @brief Default copy constructor. */ + basic_runtime_view(const basic_runtime_view &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + basic_runtime_view(const basic_runtime_view &other, const allocator_type &allocator) + : pools{other.pools, allocator}, + filter{other.filter, allocator} {} + + /*! @brief Default move constructor. */ + basic_runtime_view(basic_runtime_view &&) noexcept(std::is_nothrow_move_constructible_v<container_type>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_runtime_view(basic_runtime_view &&other, const allocator_type &allocator) + : pools{std::move(other.pools), allocator}, + filter{std::move(other.filter), allocator} {} + + /*! @brief Default destructor. */ + ~basic_runtime_view() noexcept = default; + + /** + * @brief Default copy assignment operator. + * @return This runtime view. + */ + basic_runtime_view &operator=(const basic_runtime_view &) = default; + + /** + * @brief Default move assignment operator. + * @return This runtime view. + */ + basic_runtime_view &operator=(basic_runtime_view &&) noexcept(std::is_nothrow_move_assignable_v<container_type>) = default; + + /** + * @brief Exchanges the contents with those of a given view. + * @param other View to exchange the content with. + */ + void swap(basic_runtime_view &other) { + using std::swap; + swap(pools, other.pools); + swap(filter, other.filter); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return pools.get_allocator(); + } + + /*! @brief Clears the view. */ + void clear() { + pools.clear(); + filter.clear(); + } + + /** + * @brief Appends an opaque storage object to a runtime view. + * @param base An opaque reference to a storage object. + * @return This runtime view. + */ + basic_runtime_view &iterate(common_type &base) { + if(pools.empty() || !(base.size() < pools[0u]->size())) { + pools.push_back(&base); + } else { + pools.push_back(std::exchange(pools[0u], &base)); + } + + return *this; + } + + /** + * @brief Adds an opaque storage object as a filter of a runtime view. + * @param base An opaque reference to a storage object. + * @return This runtime view. + */ + basic_runtime_view &exclude(common_type &base) { + filter.push_back(&base); + return *this; + } + + /** + * @brief Estimates the number of entities iterated by the view. + * @return Estimated number of entities iterated by the view. + */ + [[nodiscard]] size_type size_hint() const { + return pools.empty() ? size_type{} : pools.front()->size(); + } + + /** + * @brief Returns an iterator to the first entity that has the given + * elements. + * + * If the view is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity that has the given elements. + */ + [[nodiscard]] iterator begin() const { + return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->begin()}; + } + + /** + * @brief Returns an iterator that is past the last entity that has the + * given elements. + * @return An iterator to the entity following the last entity that has the + * given elements. + */ + [[nodiscard]] iterator end() const { + return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->end()}; + } + + /** + * @brief Checks whether a view is initialized or not. + * @return True if the view is initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return !(pools.empty() && filter.empty()); + } + + /** + * @brief Checks if a view contains an entity. + * @param entt A valid identifier. + * @return True if the view contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const { + return !pools.empty() + && std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *curr) { return curr->contains(entt); }) + && std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); }); + } + + /** + * @brief Iterates entities and applies the given function object to them. + * + * The function object is invoked for each entity. It is provided only with + * the entity itself.<br/> + * 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<typename Func> + void each(Func func) const { + for(const auto entity: *this) { + func(entity); + } + } + +private: + container_type pools; + container_type filter; +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/entity/snapshot.hpp b/external/entt/include/entt/entity/snapshot.hpp new file mode 100644 index 0000000..970efba --- /dev/null +++ b/external/entt/include/entt/entity/snapshot.hpp @@ -0,0 +1,509 @@ +#ifndef ENTT_ENTITY_SNAPSHOT_HPP +#define ENTT_ENTITY_SNAPSHOT_HPP + +#include <cstddef> +#include <iterator> +#include <tuple> +#include <type_traits> +#include <utility> +#include <vector> +#include "../config/config.h" +#include "../container/dense_map.hpp" +#include "../core/type_traits.hpp" +#include "entity.hpp" +#include "fwd.hpp" +#include "view.hpp" + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template<typename Registry> +void orphans(Registry ®istry) { + auto &storage = registry.template storage<typename Registry::entity_type>(); + + for(auto entt: storage) { + if(registry.orphan(entt)) { + storage.erase(entt); + } + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Utility class to create snapshots from a registry. + * + * A _snapshot_ can be either a dump of the entire registry or a narrower + * selection of elements of interest.<br/> + * This type can be used in both cases if provided with a correctly configured + * output archive. + * + * @tparam Registry Basic registry type. + */ +template<typename Registry> +class basic_snapshot { + static_assert(!std::is_const_v<Registry>, "Non-const registry type required"); + using traits_type = entt_traits<typename Registry::entity_type>; + +public: + /*! Basic registry type. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + + /** + * @brief Constructs an instance that is bound to a given registry. + * @param source A valid reference to a registry. + */ + basic_snapshot(const registry_type &source) noexcept + : reg{&source} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_snapshot(const basic_snapshot &) = delete; + + /*! @brief Default move constructor. */ + basic_snapshot(basic_snapshot &&) noexcept = default; + + /*! @brief Default destructor. */ + ~basic_snapshot() noexcept = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This snapshot. + */ + basic_snapshot &operator=(const basic_snapshot &) = delete; + + /** + * @brief Default move assignment operator. + * @return This snapshot. + */ + basic_snapshot &operator=(basic_snapshot &&) noexcept = default; + + /** + * @brief Serializes all elements of a type with associated identifiers. + * @tparam Type Type of elements to serialize. + * @tparam Archive Type of output archive. + * @param archive A valid reference to an output archive. + * @param id Optional name used to map the storage within the registry. + * @return An object of this type to continue creating the snapshot. + */ + template<typename Type, typename Archive> + const basic_snapshot &get(Archive &archive, const id_type id = type_hash<Type>::value()) const { + if(const auto *storage = reg->template storage<Type>(id); storage) { + const typename registry_type::common_type &base = *storage; + + archive(static_cast<typename traits_type::entity_type>(storage->size())); + + if constexpr(std::is_same_v<Type, entity_type>) { + archive(static_cast<typename traits_type::entity_type>(storage->free_list())); + + for(auto first = base.rbegin(), last = base.rend(); first != last; ++first) { + archive(*first); + } + } else if constexpr(registry_type::template storage_for_type<Type>::storage_policy == deletion_policy::in_place) { + for(auto it = base.rbegin(), last = base.rend(); it != last; ++it) { + if(const auto entt = *it; entt == tombstone) { + archive(static_cast<entity_type>(null)); + } else { + archive(entt); + std::apply([&archive](auto &&...args) { (archive(std::forward<decltype(args)>(args)), ...); }, storage->get_as_tuple(entt)); + } + } + } else { + for(auto elem: storage->reach()) { + std::apply([&archive](auto &&...args) { (archive(std::forward<decltype(args)>(args)), ...); }, elem); + } + } + } else { + archive(typename traits_type::entity_type{}); + } + + return *this; + } + + /** + * @brief Serializes all elements of a type with associated identifiers for + * the entities in a range. + * @tparam Type Type of elements to serialize. + * @tparam Archive Type of output archive. + * @tparam It Type of input iterator. + * @param archive A valid reference to an output archive. + * @param first An iterator to the first element of the range to serialize. + * @param last An iterator past the last element of the range to serialize. + * @param id Optional name used to map the storage within the registry. + * @return An object of this type to continue creating the snapshot. + */ + template<typename Type, typename Archive, typename It> + const basic_snapshot &get(Archive &archive, It first, It last, const id_type id = type_hash<Type>::value()) const { + static_assert(!std::is_same_v<Type, entity_type>, "Entity types not supported"); + + if(const auto *storage = reg->template storage<Type>(id); storage && !storage->empty()) { + archive(static_cast<typename traits_type::entity_type>(std::distance(first, last))); + + for(; first != last; ++first) { + if(const auto entt = *first; storage->contains(entt)) { + archive(entt); + std::apply([&archive](auto &&...args) { (archive(std::forward<decltype(args)>(args)), ...); }, storage->get_as_tuple(entt)); + } else { + archive(static_cast<entity_type>(null)); + } + } + } else { + archive(typename traits_type::entity_type{}); + } + + return *this; + } + +private: + const registry_type *reg; +}; + +/** + * @brief Utility class to restore a snapshot as a whole. + * + * A snapshot loader requires that the destination registry be empty and loads + * all the data at once while keeping intact the identifiers that the entities + * originally had.<br/> + * An example of use is the implementation of a save/restore utility. + * + * @tparam Registry Basic registry type. + */ +template<typename Registry> +class basic_snapshot_loader { + static_assert(!std::is_const_v<Registry>, "Non-const registry type required"); + using traits_type = entt_traits<typename Registry::entity_type>; + +public: + /*! Basic registry type. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + + /** + * @brief Constructs an instance that is bound to a given registry. + * @param source A valid reference to a registry. + */ + basic_snapshot_loader(registry_type &source) noexcept + : reg{&source} { + // restoring a snapshot as a whole requires a clean registry + ENTT_ASSERT(reg->template storage<entity_type>().free_list() == 0u, "Registry must be empty"); + } + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_snapshot_loader(const basic_snapshot_loader &) = delete; + + /*! @brief Default move constructor. */ + basic_snapshot_loader(basic_snapshot_loader &&) noexcept = default; + + /*! @brief Default destructor. */ + ~basic_snapshot_loader() noexcept = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This loader. + */ + basic_snapshot_loader &operator=(const basic_snapshot_loader &) = delete; + + /** + * @brief Default move assignment operator. + * @return This loader. + */ + basic_snapshot_loader &operator=(basic_snapshot_loader &&) noexcept = default; + + /** + * @brief Restores all elements of a type with associated identifiers. + * @tparam Type Type of elements to restore. + * @tparam Archive Type of input archive. + * @param archive A valid reference to an input archive. + * @param id Optional name used to map the storage within the registry. + * @return A valid loader to continue restoring data. + */ + template<typename Type, typename Archive> + basic_snapshot_loader &get(Archive &archive, const id_type id = type_hash<Type>::value()) { + auto &storage = reg->template storage<Type>(id); + typename traits_type::entity_type length{}; + + archive(length); + + if constexpr(std::is_same_v<Type, entity_type>) { + typename traits_type::entity_type count{}; + + storage.reserve(length); + archive(count); + + for(entity_type entity = null; length; --length) { + archive(entity); + storage.emplace(entity); + } + + storage.free_list(count); + } else { + auto &other = reg->template storage<entity_type>(); + entity_type entt{null}; + + while(length--) { + if(archive(entt); entt != null) { + const auto entity = other.contains(entt) ? entt : other.emplace(entt); + ENTT_ASSERT(entity == entt, "Entity not available for use"); + + if constexpr(std::tuple_size_v<decltype(storage.get_as_tuple({}))> == 0u) { + storage.emplace(entity); + } else { + Type elem{}; + archive(elem); + storage.emplace(entity, std::move(elem)); + } + } + } + } + + return *this; + } + + /** + * @brief Destroys those entities that have no elements. + * + * In case all the entities were serialized but only part of the elements + * was saved, it could happen that some of the entities have no elements + * once restored.<br/> + * This function helps to identify and destroy those entities. + * + * @return A valid loader to continue restoring data. + */ + basic_snapshot_loader &orphans() { + internal::orphans(*reg); + return *this; + } + +private: + registry_type *reg; +}; + +/** + * @brief Utility class for _continuous loading_. + * + * A _continuous loader_ is designed to load data from a source registry to a + * (possibly) non-empty destination. The loader can accommodate in a registry + * more than one snapshot in a sort of _continuous loading_ that updates the + * destination one step at a time.<br/> + * Identifiers that entities originally had are not transferred to the target. + * Instead, the loader maps remote identifiers to local ones while restoring a + * snapshot.<br/> + * An example of use is the implementation of a client-server application with + * the requirement of transferring somehow parts of the representation side to + * side. + * + * @tparam Registry Basic registry type. + */ +template<typename Registry> +class basic_continuous_loader { + static_assert(!std::is_const_v<Registry>, "Non-const registry type required"); + using traits_type = entt_traits<typename Registry::entity_type>; + + void restore(typename Registry::entity_type entt) { + if(const auto entity = to_entity(entt); remloc.contains(entity) && remloc[entity].first == entt) { + if(!reg->valid(remloc[entity].second)) { + remloc[entity].second = reg->create(); + } + } else { + remloc.insert_or_assign(entity, std::make_pair(entt, reg->create())); + } + } + + template<typename Container> + 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<typename std::decay_t<decltype(pair)>::first_type>; + using second_type = typename std::decay_t<decltype(pair)>::second_type; + + if constexpr(std::is_same_v<first_type, entity_type> && std::is_same_v<second_type, entity_type>) { + other.emplace(map(pair.first), map(pair.second)); + } else if constexpr(std::is_same_v<first_type, entity_type>) { + other.emplace(map(pair.first), std::move(pair.second)); + } else { + static_assert(std::is_same_v<second_type, entity_type>, "Neither the key nor the value are of entity type"); + other.emplace(std::move(pair.first), map(pair.second)); + } + } + + using std::swap; + swap(container, other); + } + + template<typename Container> + auto update(char, Container &container) -> decltype(typename Container::value_type{}, void()) { + // vector like container + static_assert(std::is_same_v<typename Container::value_type, entity_type>, "Invalid value type"); + + for(auto &&entt: container) { + entt = map(entt); + } + } + + template<typename Component, typename Other, typename Member> + void update([[maybe_unused]] Component &instance, [[maybe_unused]] Member Other::*member) { + if constexpr(!std::is_same_v<Component, Other>) { + return; + } else if constexpr(std::is_same_v<Member, entity_type>) { + instance.*member = map(instance.*member); + } else { + // maybe a container? let's try... + update(0, instance.*member); + } + } + +public: + /*! Basic registry type. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + + /** + * @brief Constructs an instance that is bound to a given registry. + * @param source A valid reference to a registry. + */ + basic_continuous_loader(registry_type &source) noexcept + : remloc{source.get_allocator()}, + reg{&source} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_continuous_loader(const basic_continuous_loader &) = delete; + + /*! @brief Default move constructor. */ + basic_continuous_loader(basic_continuous_loader &&) noexcept = default; + + /*! @brief Default destructor. */ + ~basic_continuous_loader() noexcept = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This loader. + */ + basic_continuous_loader &operator=(const basic_continuous_loader &) = delete; + + /** + * @brief Default move assignment operator. + * @return This loader. + */ + basic_continuous_loader &operator=(basic_continuous_loader &&) noexcept = default; + + /** + * @brief Restores all elements of a type with associated identifiers. + * + * It creates local counterparts for remote elements as needed.<br/> + * Members are either data members of type entity_type or containers of + * entities. In both cases, a loader visits them and replaces entities with + * their local counterpart. + * + * @tparam Type Type of elements to restore. + * @tparam Archive Type of input archive. + * @param archive A valid reference to an input archive. + * @param id Optional name used to map the storage within the registry. + * @return A valid loader to continue restoring data. + */ + template<typename Type, typename Archive> + basic_continuous_loader &get(Archive &archive, const id_type id = type_hash<Type>::value()) { + auto &storage = reg->template storage<Type>(id); + typename traits_type::entity_type length{}; + entity_type entt{null}; + + archive(length); + + if constexpr(std::is_same_v<Type, entity_type>) { + typename traits_type::entity_type in_use{}; + + storage.reserve(length); + archive(in_use); + + for(std::size_t pos{}; pos < in_use; ++pos) { + archive(entt); + restore(entt); + } + + for(std::size_t pos = in_use; pos < length; ++pos) { + archive(entt); + + if(const auto entity = to_entity(entt); remloc.contains(entity)) { + if(reg->valid(remloc[entity].second)) { + reg->destroy(remloc[entity].second); + } + + remloc.erase(entity); + } + } + } else { + for(auto &&ref: remloc) { + storage.remove(ref.second.second); + } + + while(length--) { + if(archive(entt); entt != null) { + restore(entt); + + if constexpr(std::tuple_size_v<decltype(storage.get_as_tuple({}))> == 0u) { + storage.emplace(map(entt)); + } else { + Type elem{}; + archive(elem); + storage.emplace(map(entt), std::move(elem)); + } + } + } + } + + return *this; + } + + /** + * @brief Destroys those entities that have no elements. + * + * In case all the entities were serialized but only part of the elements + * was saved, it could happen that some of the entities have no elements + * once restored.<br/> + * This function helps to identify and destroy those entities. + * + * @return A non-const reference to this loader. + */ + basic_continuous_loader &orphans() { + internal::orphans(*reg); + return *this; + } + + /** + * @brief Tests if a loader knows about a given entity. + * @param entt A valid identifier. + * @return True if `entity` is managed by the loader, false otherwise. + */ + [[nodiscard]] bool contains(entity_type entt) const noexcept { + const auto it = remloc.find(to_entity(entt)); + return it != remloc.cend() && it->second.first == entt; + } + + /** + * @brief Returns the identifier to which an entity refers. + * @param entt A valid identifier. + * @return The local identifier if any, the null entity otherwise. + */ + [[nodiscard]] entity_type map(entity_type entt) const noexcept { + if(const auto it = remloc.find(to_entity(entt)); it != remloc.cend() && it->second.first == entt) { + return it->second.second; + } + + return null; + } + +private: + dense_map<typename traits_type::entity_type, std::pair<entity_type, entity_type>> remloc; + registry_type *reg; +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/entity/sparse_set.hpp b/external/entt/include/entt/entity/sparse_set.hpp new file mode 100644 index 0000000..b0c251b --- /dev/null +++ b/external/entt/include/entt/entity/sparse_set.hpp @@ -0,0 +1,1068 @@ +#ifndef ENTT_ENTITY_SPARSE_SET_HPP +#define ENTT_ENTITY_SPARSE_SET_HPP + +#include <cstddef> +#include <iterator> +#include <memory> +#include <type_traits> +#include <utility> +#include <vector> +#include "../config/config.h" +#include "../core/algorithm.hpp" +#include "../core/any.hpp" +#include "../core/bit.hpp" +#include "../core/type_info.hpp" +#include "entity.hpp" +#include "fwd.hpp" + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template<typename Container> +struct sparse_set_iterator final { + using value_type = typename Container::value_type; + using pointer = typename Container::const_pointer; + using reference = typename Container::const_reference; + using difference_type = typename Container::difference_type; + using iterator_category = std::random_access_iterator_tag; + + constexpr sparse_set_iterator() noexcept + : packed{}, + offset{} {} + + constexpr sparse_set_iterator(const Container &ref, const difference_type idx) noexcept + : packed{&ref}, + offset{idx} {} + + constexpr sparse_set_iterator &operator++() noexcept { + return --offset, *this; + } + + constexpr sparse_set_iterator operator++(int) noexcept { + sparse_set_iterator orig = *this; + return ++(*this), orig; + } + + constexpr sparse_set_iterator &operator--() noexcept { + return ++offset, *this; + } + + constexpr sparse_set_iterator operator--(int) noexcept { + sparse_set_iterator orig = *this; + return operator--(), orig; + } + + constexpr sparse_set_iterator &operator+=(const difference_type value) noexcept { + offset -= value; + return *this; + } + + constexpr sparse_set_iterator operator+(const difference_type value) const noexcept { + sparse_set_iterator copy = *this; + return (copy += value); + } + + constexpr sparse_set_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr sparse_set_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return (*packed)[index() - value]; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(operator[](0)); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + [[nodiscard]] constexpr pointer data() const noexcept { + return packed ? packed->data() : nullptr; + } + + [[nodiscard]] constexpr difference_type index() const noexcept { + return offset - 1; + } + +private: + const Container *packed; + difference_type offset; +}; + +template<typename Container> +[[nodiscard]] constexpr std::ptrdiff_t operator-(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept { + return rhs.index() - lhs.index(); +} + +template<typename Container> +[[nodiscard]] constexpr bool operator==(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template<typename Container> +[[nodiscard]] constexpr bool operator!=(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept { + return !(lhs == rhs); +} + +template<typename Container> +[[nodiscard]] constexpr bool operator<(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept { + return lhs.index() > rhs.index(); +} + +template<typename Container> +[[nodiscard]] constexpr bool operator>(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept { + return rhs < lhs; +} + +template<typename Container> +[[nodiscard]] constexpr bool operator<=(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept { + return !(lhs > rhs); +} + +template<typename Container> +[[nodiscard]] constexpr bool operator>=(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept { + return !(lhs < rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Sparse set implementation. + * + * Sparse set or packed array or whatever is the name users give it.<br/> + * 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.<br/> + * This type of data structure is widely documented in the literature and on the + * web. This is nothing more than a customized implementation suitable for the + * purpose of the framework. + * + * @note + * Internal data structures arrange elements to maximize performance. There are + * no guarantees that entities are returned in the insertion order when iterate + * a sparse set. Do not make assumption on the order in any case. + * + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Entity, typename Allocator> +class basic_sparse_set { + using alloc_traits = std::allocator_traits<Allocator>; + static_assert(std::is_same_v<typename alloc_traits::value_type, Entity>, "Invalid value type"); + using sparse_container_type = std::vector<typename alloc_traits::pointer, typename alloc_traits::template rebind_alloc<typename alloc_traits::pointer>>; + using packed_container_type = std::vector<Entity, Allocator>; + using traits_type = entt_traits<Entity>; + + static constexpr auto max_size = static_cast<std::size_t>(traits_type::to_entity(null)); + + [[nodiscard]] auto policy_to_head() const noexcept { + return static_cast<size_type>(max_size * (mode != deletion_policy::swap_only)); + } + + [[nodiscard]] auto sparse_ptr(const Entity entt) const { + const auto pos = static_cast<size_type>(traits_type::to_entity(entt)); + const auto page = pos / traits_type::page_size; + return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, traits_type::page_size)) : nullptr; + } + + [[nodiscard]] auto &sparse_ref(const Entity entt) const { + ENTT_ASSERT(sparse_ptr(entt), "Invalid element"); + const auto pos = static_cast<size_type>(traits_type::to_entity(entt)); + return sparse[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)]; + } + + [[nodiscard]] auto to_iterator(const Entity entt) const { + return --(end() - index(entt)); + } + + [[nodiscard]] auto &assure_at_least(const Entity entt) { + const auto pos = static_cast<size_type>(traits_type::to_entity(entt)); + const auto page = pos / traits_type::page_size; + + if(!(page < sparse.size())) { + sparse.resize(page + 1u, nullptr); + } + + if(!sparse[page]) { + constexpr entity_type init = null; + auto page_allocator{packed.get_allocator()}; + sparse[page] = alloc_traits::allocate(page_allocator, traits_type::page_size); + std::uninitialized_fill(sparse[page], sparse[page] + traits_type::page_size, init); + } + + return sparse[page][fast_mod(pos, traits_type::page_size)]; + } + + void release_sparse_pages() { + auto page_allocator{packed.get_allocator()}; + + for(auto &&page: sparse) { + if(page != nullptr) { + std::destroy(page, page + traits_type::page_size); + alloc_traits::deallocate(page_allocator, page, traits_type::page_size); + page = nullptr; + } + } + } + + void swap_at(const std::size_t lhs, const std::size_t rhs) { + auto &from = packed[lhs]; + auto &to = packed[rhs]; + + sparse_ref(from) = traits_type::combine(static_cast<typename traits_type::entity_type>(rhs), traits_type::to_integral(from)); + sparse_ref(to) = traits_type::combine(static_cast<typename traits_type::entity_type>(lhs), traits_type::to_integral(to)); + + std::swap(from, to); + } + +private: + [[nodiscard]] virtual const void *get_at(const std::size_t) const { + return nullptr; + } + + virtual void swap_or_move([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) { + ENTT_ASSERT((mode != deletion_policy::swap_only) || ((lhs < head) == (rhs < head)), "Cross swapping is not supported"); + } + +protected: + /*! @brief Random access iterator type. */ + using basic_iterator = internal::sparse_set_iterator<packed_container_type>; + + /** + * @brief Erases an entity from a sparse set. + * @param it An iterator to the element to pop. + */ + void swap_only(const basic_iterator it) { + ENTT_ASSERT(mode == deletion_policy::swap_only, "Deletion policy mismatch"); + const auto pos = index(*it); + bump(traits_type::next(*it)); + swap_at(pos, head -= (pos < head)); + } + + /** + * @brief Erases an entity from a sparse set. + * @param it An iterator to the element to pop. + */ + void swap_and_pop(const basic_iterator it) { + ENTT_ASSERT(mode == deletion_policy::swap_and_pop, "Deletion policy mismatch"); + auto &self = sparse_ref(*it); + const auto entt = traits_type::to_entity(self); + sparse_ref(packed.back()) = traits_type::combine(entt, traits_type::to_integral(packed.back())); + packed[static_cast<size_type>(entt)] = packed.back(); + // unnecessary but it helps to detect nasty bugs + ENTT_ASSERT((packed.back() = null, true), ""); + // lazy self-assignment guard + self = null; + packed.pop_back(); + } + + /** + * @brief Erases an entity from a sparse set. + * @param it An iterator to the element to pop. + */ + void in_place_pop(const basic_iterator it) { + ENTT_ASSERT(mode == deletion_policy::in_place, "Deletion policy mismatch"); + const auto pos = static_cast<size_type>(traits_type::to_entity(std::exchange(sparse_ref(*it), null))); + packed[pos] = traits_type::combine(static_cast<typename traits_type::entity_type>(std::exchange(head, pos)), tombstone); + } + +protected: + /** + * @brief Erases entities from a sparse set. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + virtual void pop(basic_iterator first, basic_iterator last) { + switch(mode) { + case deletion_policy::swap_and_pop: + for(; first != last; ++first) { + swap_and_pop(first); + } + break; + case deletion_policy::in_place: + for(; first != last; ++first) { + in_place_pop(first); + } + break; + case deletion_policy::swap_only: + for(; first != last; ++first) { + swap_only(first); + } + break; + } + } + + /*! @brief Erases all entities of a sparse set. */ + virtual void pop_all() { + switch(mode) { + case deletion_policy::in_place: + if(head != max_size) { + for(auto first = begin(); !(first.index() < 0); ++first) { + if(*first != tombstone) { + sparse_ref(*first) = null; + } + } + break; + } + [[fallthrough]]; + case deletion_policy::swap_only: + case deletion_policy::swap_and_pop: + for(auto first = begin(); !(first.index() < 0); ++first) { + sparse_ref(*first) = null; + } + break; + } + + head = policy_to_head(); + packed.clear(); + } + + /** + * @brief Assigns an entity to a sparse set. + * @param entt A valid identifier. + * @param force_back Force back insertion. + * @return Iterator pointing to the emplaced element. + */ + virtual basic_iterator try_emplace(const Entity entt, const bool force_back, const void * = nullptr) { + ENTT_ASSERT(entt != null && entt != tombstone, "Invalid element"); + auto &elem = assure_at_least(entt); + auto pos = size(); + + switch(mode) { + case deletion_policy::in_place: + if(head != max_size && !force_back) { + pos = head; + ENTT_ASSERT(elem == null, "Slot not available"); + elem = traits_type::combine(static_cast<typename traits_type::entity_type>(head), traits_type::to_integral(entt)); + head = static_cast<size_type>(traits_type::to_entity(std::exchange(packed[pos], entt))); + break; + } + [[fallthrough]]; + case deletion_policy::swap_and_pop: + packed.push_back(entt); + ENTT_ASSERT(elem == null, "Slot not available"); + elem = traits_type::combine(static_cast<typename traits_type::entity_type>(packed.size() - 1u), traits_type::to_integral(entt)); + break; + case deletion_policy::swap_only: + if(elem == null) { + packed.push_back(entt); + elem = traits_type::combine(static_cast<typename traits_type::entity_type>(packed.size() - 1u), traits_type::to_integral(entt)); + } else { + ENTT_ASSERT(!(static_cast<size_type>(traits_type::to_entity(elem)) < head), "Slot not available"); + bump(entt); + } + + pos = head++; + swap_at(static_cast<size_type>(traits_type::to_entity(elem)), pos); + break; + } + + return --(end() - static_cast<typename iterator::difference_type>(pos)); + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Underlying entity identifier. */ + using entity_type = typename traits_type::value_type; + /*! @brief Underlying version type. */ + using version_type = typename traits_type::version_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Pointer type to contained entities. */ + using pointer = typename packed_container_type::const_pointer; + /*! @brief Random access iterator type. */ + using iterator = basic_iterator; + /*! @brief Constant random access iterator type. */ + using const_iterator = iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = std::reverse_iterator<iterator>; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = std::reverse_iterator<const_iterator>; + + /*! @brief Default constructor. */ + basic_sparse_set() + : basic_sparse_set{type_id<void>()} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_sparse_set(const allocator_type &allocator) + : basic_sparse_set{deletion_policy::swap_and_pop, allocator} {} + + /** + * @brief Constructs an empty container with the given policy and allocator. + * @param pol Type of deletion policy. + * @param allocator The allocator to use (possibly default-constructed). + */ + explicit basic_sparse_set(deletion_policy pol, const allocator_type &allocator = {}) + : basic_sparse_set{type_id<void>(), pol, allocator} {} + + /** + * @brief Constructs an empty container with the given value type, policy + * and allocator. + * @param elem Returned value type, if any. + * @param pol Type of deletion policy. + * @param allocator The allocator to use (possibly default-constructed). + */ + explicit basic_sparse_set(const type_info &elem, deletion_policy pol = deletion_policy::swap_and_pop, const allocator_type &allocator = {}) + : sparse{allocator}, + packed{allocator}, + info{&elem}, + mode{pol}, + head{policy_to_head()} { + ENTT_ASSERT(traits_type::version_mask || mode != deletion_policy::in_place, "Policy does not support zero-sized versions"); + } + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_sparse_set(const basic_sparse_set &) = delete; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_sparse_set(basic_sparse_set &&other) noexcept + : sparse{std::move(other.sparse)}, + packed{std::move(other.packed)}, + info{other.info}, + mode{other.mode}, + head{std::exchange(other.head, policy_to_head())} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) + : sparse{std::move(other.sparse), allocator}, + packed{std::move(other.packed), allocator}, + info{other.info}, + mode{other.mode}, + head{std::exchange(other.head, policy_to_head())} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a sparse set is not allowed"); + } + + /*! @brief Default destructor. */ + virtual ~basic_sparse_set() noexcept { + release_sparse_pages(); + } + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This sparse set. + */ + basic_sparse_set &operator=(const basic_sparse_set &) = delete; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This sparse set. + */ + basic_sparse_set &operator=(basic_sparse_set &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a sparse set is not allowed"); + + release_sparse_pages(); + sparse = std::move(other.sparse); + packed = std::move(other.packed); + info = other.info; + mode = other.mode; + head = std::exchange(other.head, policy_to_head()); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given sparse set. + * @param other Sparse set to exchange the content with. + */ + void swap(basic_sparse_set &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(info, other.info); + swap(mode, other.mode); + swap(head, other.head); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return packed.get_allocator(); + } + + /** + * @brief Returns the deletion policy of a sparse set. + * @return The deletion policy of the sparse set. + */ + [[nodiscard]] deletion_policy policy() const noexcept { + return mode; + } + + /** + * @brief Returns data on the free list whose meaning depends on the mode. + * @return Free list information that is mode dependent. + */ + [[nodiscard]] size_type free_list() const noexcept { + return head; + } + + /** + * @brief Sets data on the free list whose meaning depends on the mode. + * @param value Free list information that is mode dependent. + */ + void free_list(const size_type value) noexcept { + ENTT_ASSERT((mode == deletion_policy::swap_only) && !(value > packed.size()), "Invalid value"); + head = value; + } + + /** + * @brief Increases the capacity of a sparse set. + * + * If the new capacity is greater than the current capacity, new storage is + * allocated, otherwise the method does nothing. + * + * @param cap Desired capacity. + */ + virtual void reserve(const size_type cap) { + packed.reserve(cap); + } + + /** + * @brief Returns the number of elements that a sparse set has currently + * allocated space for. + * @return Capacity of the sparse set. + */ + [[nodiscard]] virtual size_type capacity() const noexcept { + return packed.capacity(); + } + + /*! @brief Requests the removal of unused capacity. */ + virtual void shrink_to_fit() { + packed.shrink_to_fit(); + } + + /** + * @brief Returns the extent of a sparse set. + * + * The extent of a sparse set is also the size of the internal sparse array. + * There is no guarantee that the internal packed array has the same size. + * Usually the size of the internal sparse array is equal or greater than + * the one of the internal packed array. + * + * @return Extent of the sparse set. + */ + [[nodiscard]] size_type extent() const noexcept { + return sparse.size() * traits_type::page_size; + } + + /** + * @brief Returns the number of elements in a sparse set. + * + * The number of elements is also the size of the internal packed array. + * There is no guarantee that the internal sparse array has the same size. + * Usually the size of the internal sparse array is equal or greater than + * the one of the internal packed array. + * + * @return Number of elements. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.size(); + } + + /** + * @brief Checks whether a sparse set is empty. + * @return True if the sparse set is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.empty(); + } + + /** + * @brief Checks whether a sparse set is fully packed. + * @return True if the sparse set is fully packed, false otherwise. + */ + [[nodiscard]] bool contiguous() const noexcept { + return (mode != deletion_policy::in_place) || (head == max_size); + } + + /** + * @brief Direct access to the internal packed array. + * @return A pointer to the internal packed array. + */ + [[nodiscard]] pointer data() const noexcept { + return packed.data(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the sparse set is empty, the returned iterator will be equal to + * `end()`. + * + * @return An iterator to the first entity of the sparse set. + */ + [[nodiscard]] iterator begin() const noexcept { + const auto pos = static_cast<typename iterator::difference_type>(packed.size()); + return iterator{packed, pos}; + } + + /*! @copydoc begin */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last entity of a sparse + * set. + */ + [[nodiscard]] iterator end() const noexcept { + return iterator{packed, {}}; + } + + /*! @copydoc end */ + [[nodiscard]] const_iterator cend() const noexcept { + return end(); + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * If the sparse set is empty, the returned iterator will be equal to + * `rend()`. + * + * @return An iterator to the first entity of the reversed internal packed + * array. + */ + [[nodiscard]] reverse_iterator rbegin() const noexcept { + return std::make_reverse_iterator(end()); + } + + /*! @copydoc rbegin */ + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { + return rbegin(); + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last entity of the + * reversed sparse set. + */ + [[nodiscard]] reverse_iterator rend() const noexcept { + return std::make_reverse_iterator(begin()); + } + + /*! @copydoc rend */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return rend(); + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] const_iterator find(const entity_type entt) const noexcept { + return contains(entt) ? to_iterator(entt) : end(); + } + + /** + * @brief Checks if a sparse set contains an entity. + * @param entt A valid identifier. + * @return True if the sparse set contains the entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + const auto elem = sparse_ptr(entt); + constexpr auto cap = traits_type::entity_mask; + constexpr auto mask = traits_type::to_integral(null) & ~cap; + // testing versions permits to avoid accessing the packed array + return elem && (((mask & traits_type::to_integral(entt)) ^ traits_type::to_integral(*elem)) < cap); + } + + /** + * @brief Returns the contained version for an identifier. + * @param entt A valid identifier. + * @return The version for the given identifier if present, the tombstone + * version otherwise. + */ + [[nodiscard]] version_type current(const entity_type entt) const noexcept { + const auto elem = sparse_ptr(entt); + constexpr auto fallback = traits_type::to_version(tombstone); + return elem ? traits_type::to_version(*elem) : fallback; + } + + /** + * @brief Returns the position of an entity in a sparse set. + * + * @warning + * Attempting to get the position of an entity that doesn't belong to the + * sparse set results in undefined behavior. + * + * @param entt A valid identifier. + * @return The position of the entity in the sparse set. + */ + [[nodiscard]] size_type index(const entity_type entt) const noexcept { + ENTT_ASSERT(contains(entt), "Set does not contain entity"); + return static_cast<size_type>(traits_type::to_entity(sparse_ref(entt))); + } + + /** + * @brief Returns the entity at specified location. + * @param pos The position for which to return the entity. + * @return The entity at specified location. + */ + [[nodiscard]] entity_type operator[](const size_type pos) const noexcept { + ENTT_ASSERT(pos < packed.size(), "Index out of bounds"); + return packed[pos]; + } + + /** + * @brief Returns the element assigned to an entity, if any. + * + * @warning + * Attempting to use an entity that doesn't belong to the sparse set results + * in undefined behavior. + * + * @param entt A valid identifier. + * @return An opaque pointer to the element assigned to the entity, if any. + */ + [[nodiscard]] const void *value(const entity_type entt) const noexcept { + return get_at(index(entt)); + } + + /*! @copydoc value */ + [[nodiscard]] void *value(const entity_type entt) noexcept { + return const_cast<void *>(std::as_const(*this).value(entt)); + } + + /** + * @brief Assigns an entity to a sparse set. + * + * @warning + * Attempting to assign an entity that already belongs to the sparse set + * results in undefined behavior. + * + * @param entt A valid identifier. + * @param elem Optional opaque element to forward to mixins, if any. + * @return Iterator pointing to the emplaced element in case of success, the + * `end()` iterator otherwise. + */ + iterator push(const entity_type entt, const void *elem = nullptr) { + return try_emplace(entt, false, elem); + } + + /** + * @brief Assigns one or more entities to a sparse set. + * + * @warning + * Attempting to assign an entity that already belongs to the sparse set + * results in undefined behavior. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @return Iterator pointing to the first element inserted in case of + * success, the `end()` iterator otherwise. + */ + template<typename It> + iterator push(It first, It last) { + auto curr = end(); + + for(; first != last; ++first) { + curr = try_emplace(*first, true); + } + + return curr; + } + + /** + * @brief Bump the version number of an entity. + * + * @warning + * Attempting to bump the version of an entity that doesn't belong to the + * sparse set results in undefined behavior. + * + * @param entt A valid identifier. + * @return The version of the given identifier. + */ + version_type bump(const entity_type entt) { + auto &elem = sparse_ref(entt); + ENTT_ASSERT(entt != null && elem != tombstone, "Cannot set the required version"); + elem = traits_type::combine(traits_type::to_integral(elem), traits_type::to_integral(entt)); + packed[static_cast<size_type>(traits_type::to_entity(elem))] = entt; + return traits_type::to_version(entt); + } + + /** + * @brief Erases an entity from a sparse set. + * + * @warning + * Attempting to erase an entity that doesn't belong to the sparse set + * results in undefined behavior. + * + * @param entt A valid identifier. + */ + void erase(const entity_type entt) { + const auto it = to_iterator(entt); + pop(it, it + 1u); + } + + /** + * @brief Erases entities from a set. + * + * @sa erase + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + template<typename It> + void erase(It first, It last) { + if constexpr(std::is_same_v<It, basic_iterator>) { + pop(first, last); + } else { + for(; first != last; ++first) { + erase(*first); + } + } + } + + /** + * @brief Removes an entity from a sparse set if it exists. + * @param entt A valid identifier. + * @return True if the entity is actually removed, false otherwise. + */ + bool remove(const entity_type entt) { + return contains(entt) && (erase(entt), true); + } + + /** + * @brief Removes entities from a sparse set if they exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @return The number of entities actually removed. + */ + template<typename It> + size_type remove(It first, It last) { + size_type count{}; + + if constexpr(std::is_same_v<It, basic_iterator>) { + while(first != last) { + while(first != last && !contains(*first)) { + ++first; + } + + const auto it = first; + + while(first != last && contains(*first)) { + ++first; + } + + count += std::distance(it, first); + erase(it, first); + } + } else { + for(; first != last; ++first) { + count += remove(*first); + } + } + + return count; + } + + /*! @brief Removes all tombstones from a sparse set. */ + void compact() { + if(mode == deletion_policy::in_place) { + size_type from = packed.size(); + size_type pos = std::exchange(head, max_size); + + for(; from && packed[from - 1u] == tombstone; --from) {} + + while(pos != max_size) { + if(const auto to = std::exchange(pos, static_cast<size_type>(traits_type::to_entity(packed[pos]))); to < from) { + --from; + swap_or_move(from, to); + + packed[to] = packed[from]; + const auto elem = static_cast<typename traits_type::entity_type>(to); + sparse_ref(packed[to]) = traits_type::combine(elem, traits_type::to_integral(packed[to])); + + for(; from && packed[from - 1u] == tombstone; --from) {} + } + } + + packed.erase(packed.begin() + from, packed.end()); + } + } + + /** + * @brief Swaps two entities in a sparse set. + * + * For what it's worth, this function affects both the internal sparse array + * and the internal packed array. Users should not care of that anyway. + * + * @warning + * Attempting to swap entities that don't belong to the sparse set results + * in undefined behavior. + * + * @param lhs A valid identifier. + * @param rhs A valid identifier. + */ + void swap_elements(const entity_type lhs, const entity_type rhs) { + const auto from = index(lhs); + const auto to = index(rhs); + + // basic no-leak guarantee if swapping throws + swap_or_move(from, to); + swap_at(from, to); + } + + /** + * @brief Sort the first count elements according to the given comparison + * function. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to the following: + * + * @code{.cpp} + * bool(const Entity, const Entity); + * @endcode + * + * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function object must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param length Number of elements to sort. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template<typename Compare, typename Sort = std_sort, typename... Args> + void sort_n(const size_type length, Compare compare, Sort algo = Sort{}, Args &&...args) { + ENTT_ASSERT((mode != deletion_policy::in_place) || (head == max_size), "Sorting with tombstones not allowed"); + ENTT_ASSERT(!(length > packed.size()), "Length exceeds the number of elements"); + + algo(packed.rend() - length, packed.rend(), std::move(compare), std::forward<Args>(args)...); + + for(size_type pos{}; pos < length; ++pos) { + auto curr = pos; + auto next = index(packed[curr]); + + while(curr != next) { + const auto idx = index(packed[next]); + const auto entt = packed[curr]; + + swap_or_move(next, idx); + const auto elem = static_cast<typename traits_type::entity_type>(curr); + sparse_ref(entt) = traits_type::combine(elem, traits_type::to_integral(packed[curr])); + curr = std::exchange(next, idx); + } + } + } + + /** + * @brief Sort all elements according to the given comparison function. + * + * @sa sort_n + * + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template<typename Compare, typename Sort = std_sort, typename... Args> + void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { + const size_type len = (mode == deletion_policy::swap_only) ? head : packed.size(); + sort_n(len, std::move(compare), std::move(algo), std::forward<Args>(args)...); + } + + /** + * @brief Sort entities according to their order in a range. + * + * Entities that are part of both the sparse set and the range are ordered + * internally according to the order they have in the range.<br/> + * All other entities goes to the end of the sparse set and there are no + * guarantees on their order. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @return An iterator past the last of the elements actually shared. + */ + template<typename It> + iterator sort_as(It first, It last) { + ENTT_ASSERT((mode != deletion_policy::in_place) || (head == max_size), "Sorting with tombstones not allowed"); + const size_type len = (mode == deletion_policy::swap_only) ? head : packed.size(); + auto it = end() - static_cast<typename iterator::difference_type>(len); + + for(const auto other = end(); (it != other) && (first != last); ++first) { + if(const auto curr = *first; contains(curr)) { + if(const auto entt = *it; entt != curr) { + // basic no-leak guarantee (with invalid state) if swapping throws + swap_elements(entt, curr); + } + + ++it; + } + } + + return it; + } + + /*! @brief Clears a sparse set. */ + void clear() { + pop_all(); + // sanity check to avoid subtle issues due to storage classes + ENTT_ASSERT((compact(), size()) == 0u, "Non-empty set"); + head = policy_to_head(); + packed.clear(); + } + + /** + * @brief Returned value type, if any. + * @return Returned value type, if any. + */ + [[nodiscard]] const type_info &type() const noexcept { + return *info; + } + + /*! @brief Forwards variables to derived classes, if any. */ + // NOLINTNEXTLINE(performance-unnecessary-value-param) + virtual void bind(any) noexcept {} + +private: + sparse_container_type sparse; + packed_container_type packed; + const type_info *info; + deletion_policy mode; + size_type head; +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/entity/storage.hpp b/external/entt/include/entt/entity/storage.hpp new file mode 100644 index 0000000..6c1c570 --- /dev/null +++ b/external/entt/include/entt/entity/storage.hpp @@ -0,0 +1,1205 @@ +#ifndef ENTT_ENTITY_STORAGE_HPP +#define ENTT_ENTITY_STORAGE_HPP + +#include <cstddef> +#include <iterator> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> +#include <vector> +#include "../config/config.h" +#include "../core/bit.hpp" +#include "../core/iterator.hpp" +#include "../core/memory.hpp" +#include "../core/type_info.hpp" +#include "component.hpp" +#include "entity.hpp" +#include "fwd.hpp" +#include "sparse_set.hpp" + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template<typename Container> +class storage_iterator final { + friend storage_iterator<const Container>; + + using container_type = std::remove_const_t<Container>; + using alloc_traits = std::allocator_traits<typename container_type::allocator_type>; + + using iterator_traits = std::iterator_traits<std::conditional_t< + std::is_const_v<Container>, + typename alloc_traits::template rebind_traits<typename std::pointer_traits<typename container_type::value_type>::element_type>::const_pointer, + typename alloc_traits::template rebind_traits<typename std::pointer_traits<typename container_type::value_type>::element_type>::pointer>>; + +public: + using value_type = typename iterator_traits::value_type; + using pointer = typename iterator_traits::pointer; + using reference = typename iterator_traits::reference; + using difference_type = typename iterator_traits::difference_type; + using iterator_category = std::random_access_iterator_tag; + + constexpr storage_iterator() noexcept = default; + + constexpr storage_iterator(Container *ref, const difference_type idx) noexcept + : payload{ref}, + offset{idx} {} + + template<bool Const = std::is_const_v<Container>, typename = std::enable_if_t<Const>> + constexpr storage_iterator(const storage_iterator<std::remove_const_t<Container>> &other) noexcept + : storage_iterator{other.payload, other.offset} {} + + constexpr storage_iterator &operator++() noexcept { + return --offset, *this; + } + + constexpr storage_iterator operator++(int) noexcept { + storage_iterator orig = *this; + return ++(*this), orig; + } + + constexpr storage_iterator &operator--() noexcept { + return ++offset, *this; + } + + constexpr storage_iterator operator--(int) noexcept { + storage_iterator orig = *this; + return operator--(), orig; + } + + constexpr storage_iterator &operator+=(const difference_type value) noexcept { + offset -= value; + return *this; + } + + constexpr storage_iterator operator+(const difference_type value) const noexcept { + storage_iterator copy = *this; + return (copy += value); + } + + constexpr storage_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr storage_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + const auto pos = index() - value; + constexpr auto page_size = component_traits<value_type>::page_size; + return (*payload)[pos / page_size][fast_mod(static_cast<std::size_t>(pos), page_size)]; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(operator[](0)); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + [[nodiscard]] constexpr difference_type index() const noexcept { + return offset - 1; + } + +private: + Container *payload; + difference_type offset; +}; + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &rhs) noexcept { + return rhs.index() - lhs.index(); +} + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr bool operator==(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr bool operator!=(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &rhs) noexcept { + return !(lhs == rhs); +} + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr bool operator<(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &rhs) noexcept { + return lhs.index() > rhs.index(); +} + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr bool operator>(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &rhs) noexcept { + return rhs < lhs; +} + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr bool operator<=(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &rhs) noexcept { + return !(lhs > rhs); +} + +template<typename Lhs, typename Rhs> +[[nodiscard]] constexpr bool operator>=(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &rhs) noexcept { + return !(lhs < rhs); +} + +template<typename It, typename... Other> +class extended_storage_iterator final { + template<typename Iter, typename... Args> + friend class extended_storage_iterator; + +public: + using iterator_type = It; + using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::forward_as_tuple(*std::declval<Other>()...))); + using pointer = input_iterator_pointer<value_type>; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr extended_storage_iterator() + : it{} {} + + constexpr extended_storage_iterator(iterator_type base, Other... other) + : it{base, other...} {} + + template<typename... Args, typename = std::enable_if_t<(!std::is_same_v<Other, Args> && ...) && (std::is_constructible_v<Other, Args> && ...)>> + constexpr extended_storage_iterator(const extended_storage_iterator<It, Args...> &other) + : it{other.it} {} + + constexpr extended_storage_iterator &operator++() noexcept { + return ++std::get<It>(it), (++std::get<Other>(it), ...), *this; + } + + constexpr extended_storage_iterator operator++(int) noexcept { + extended_storage_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {*std::get<It>(it), *std::get<Other>(it)...}; + } + + [[nodiscard]] constexpr iterator_type base() const noexcept { + return std::get<It>(it); + } + + template<typename... Lhs, typename... Rhs> + friend constexpr bool operator==(const extended_storage_iterator<Lhs...> &, const extended_storage_iterator<Rhs...> &) noexcept; + +private: + std::tuple<It, Other...> it; +}; + +template<typename... Lhs, typename... Rhs> +[[nodiscard]] constexpr bool operator==(const extended_storage_iterator<Lhs...> &lhs, const extended_storage_iterator<Rhs...> &rhs) noexcept { + return std::get<0>(lhs.it) == std::get<0>(rhs.it); +} + +template<typename... Lhs, typename... Rhs> +[[nodiscard]] constexpr bool operator!=(const extended_storage_iterator<Lhs...> &lhs, const extended_storage_iterator<Rhs...> &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Storage implementation. + * + * Internal data structures arrange elements to maximize performance. There are + * no guarantees that objects are returned in the insertion order when iterate + * a storage. Do not make assumption on the order in any case. + * + * @warning + * Empty types aren't explicitly instantiated. Therefore, many of the functions + * normally available for non-empty types will not be available for empty ones. + * + * @tparam Type Element type. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Type, typename Entity, typename Allocator, typename> +class basic_storage: public basic_sparse_set<Entity, typename std::allocator_traits<Allocator>::template rebind_alloc<Entity>> { + using alloc_traits = std::allocator_traits<Allocator>; + static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type"); + using container_type = std::vector<typename alloc_traits::pointer, typename alloc_traits::template rebind_alloc<typename alloc_traits::pointer>>; + using underlying_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>; + using underlying_iterator = typename underlying_type::basic_iterator; + using traits_type = component_traits<Type>; + + [[nodiscard]] auto &element_at(const std::size_t pos) const { + return payload[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)]; + } + + auto assure_at_least(const std::size_t pos) { + const auto idx = pos / traits_type::page_size; + + if(!(idx < payload.size())) { + auto curr = payload.size(); + allocator_type allocator{get_allocator()}; + payload.resize(idx + 1u, nullptr); + + ENTT_TRY { + for(const auto last = payload.size(); curr < last; ++curr) { + payload[curr] = alloc_traits::allocate(allocator, traits_type::page_size); + } + } + ENTT_CATCH { + payload.resize(curr); + ENTT_THROW; + } + } + + return payload[idx] + fast_mod(pos, traits_type::page_size); + } + + template<typename... Args> + auto emplace_element(const Entity entt, const bool force_back, Args &&...args) { + const auto it = base_type::try_emplace(entt, force_back); + + ENTT_TRY { + auto elem = assure_at_least(static_cast<size_type>(it.index())); + entt::uninitialized_construct_using_allocator(to_address(elem), get_allocator(), std::forward<Args>(args)...); + } + ENTT_CATCH { + base_type::pop(it, it + 1u); + ENTT_THROW; + } + + return it; + } + + void shrink_to_size(const std::size_t sz) { + const auto from = (sz + traits_type::page_size - 1u) / traits_type::page_size; + allocator_type allocator{get_allocator()}; + + for(auto pos = sz, length = base_type::size(); pos < length; ++pos) { + if constexpr(traits_type::in_place_delete) { + if(base_type::data()[pos] != tombstone) { + alloc_traits::destroy(allocator, std::addressof(element_at(pos))); + } + } else { + alloc_traits::destroy(allocator, std::addressof(element_at(pos))); + } + } + + for(auto pos = from, last = payload.size(); pos < last; ++pos) { + alloc_traits::deallocate(allocator, payload[pos], traits_type::page_size); + } + + payload.resize(from); + } + +private: + [[nodiscard]] const void *get_at(const std::size_t pos) const final { + return std::addressof(element_at(pos)); + } + + void swap_or_move([[maybe_unused]] const std::size_t from, [[maybe_unused]] const std::size_t to) override { + static constexpr bool is_pinned_type_v = !(std::is_move_constructible_v<Type> && std::is_move_assignable_v<Type>); + // use a runtime value to avoid compile-time suppression that drives the code coverage tool crazy + ENTT_ASSERT((from + 1u) && !is_pinned_type_v, "Pinned type"); + + if constexpr(!is_pinned_type_v) { + auto &elem = element_at(from); + + if constexpr(traits_type::in_place_delete) { + if(base_type::operator[](to) == tombstone) { + allocator_type allocator{get_allocator()}; + entt::uninitialized_construct_using_allocator(to_address(assure_at_least(to)), allocator, std::move(elem)); + alloc_traits::destroy(allocator, std::addressof(elem)); + return; + } + } + + using std::swap; + swap(elem, element_at(to)); + } + } + +protected: + /** + * @brief Erases entities from a storage. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + void pop(underlying_iterator first, underlying_iterator last) override { + for(allocator_type allocator{get_allocator()}; first != last; ++first) { + // cannot use first.index() because it would break with cross iterators + auto &elem = element_at(base_type::index(*first)); + + if constexpr(traits_type::in_place_delete) { + base_type::in_place_pop(first); + alloc_traits::destroy(allocator, std::addressof(elem)); + } else { + auto &other = element_at(base_type::size() - 1u); + // destroying on exit allows reentrant destructors + [[maybe_unused]] auto unused = std::exchange(elem, std::move(other)); + alloc_traits::destroy(allocator, std::addressof(other)); + base_type::swap_and_pop(first); + } + } + } + + /*! @brief Erases all entities of a storage. */ + void pop_all() override { + allocator_type allocator{get_allocator()}; + + for(auto first = base_type::begin(); !(first.index() < 0); ++first) { + if constexpr(traits_type::in_place_delete) { + if(*first != tombstone) { + base_type::in_place_pop(first); + alloc_traits::destroy(allocator, std::addressof(element_at(static_cast<size_type>(first.index())))); + } + } else { + base_type::swap_and_pop(first); + alloc_traits::destroy(allocator, std::addressof(element_at(static_cast<size_type>(first.index())))); + } + } + } + + /** + * @brief Assigns an entity to a storage. + * @param entt A valid identifier. + * @param value Optional opaque value. + * @param force_back Force back insertion. + * @return Iterator pointing to the emplaced element. + */ + underlying_iterator try_emplace([[maybe_unused]] const Entity entt, [[maybe_unused]] const bool force_back, const void *value) override { + if(value) { + if constexpr(std::is_copy_constructible_v<element_type>) { + return emplace_element(entt, force_back, *static_cast<const element_type *>(value)); + } else { + return base_type::end(); + } + } else { + if constexpr(std::is_default_constructible_v<element_type>) { + return emplace_element(entt, force_back); + } else { + return base_type::end(); + } + } + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Base type. */ + using base_type = underlying_type; + /*! @brief Element type. */ + using element_type = Type; + /*! @brief Type of the objects assigned to entities. */ + using value_type = element_type; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Pointer type to contained elements. */ + using pointer = typename container_type::pointer; + /*! @brief Constant pointer type to contained elements. */ + using const_pointer = typename alloc_traits::template rebind_traits<typename alloc_traits::const_pointer>::const_pointer; + /*! @brief Random access iterator type. */ + using iterator = internal::storage_iterator<container_type>; + /*! @brief Constant random access iterator type. */ + using const_iterator = internal::storage_iterator<const container_type>; + /*! @brief Reverse iterator type. */ + using reverse_iterator = std::reverse_iterator<iterator>; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = std::reverse_iterator<const_iterator>; + /*! @brief Extended iterable storage proxy. */ + using iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::iterator, iterator>>; + /*! @brief Constant extended iterable storage proxy. */ + using const_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_iterator, const_iterator>>; + /*! @brief Extended reverse iterable storage proxy. */ + using reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::reverse_iterator, reverse_iterator>>; + /*! @brief Constant extended reverse iterable storage proxy. */ + using const_reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_reverse_iterator, const_reverse_iterator>>; + /*! @brief Storage deletion policy. */ + static constexpr deletion_policy storage_policy{traits_type::in_place_delete}; + + /*! @brief Default constructor. */ + basic_storage() + : basic_storage{allocator_type{}} {} + + /** + * @brief Constructs an empty storage with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_storage(const allocator_type &allocator) + : base_type{type_id<element_type>(), storage_policy, allocator}, + payload{allocator} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_storage(const basic_storage &) = delete; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_storage(basic_storage &&other) noexcept + : base_type{std::move(other)}, + payload{std::move(other.payload)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_storage(basic_storage &&other, const allocator_type &allocator) + : base_type{std::move(other), allocator}, + payload{std::move(other.payload), allocator} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a storage is not allowed"); + } + + /*! @brief Default destructor. */ + ~basic_storage() noexcept override { + shrink_to_size(0u); + } + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This storage. + */ + basic_storage &operator=(const basic_storage &) = delete; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + basic_storage &operator=(basic_storage &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a storage is not allowed"); + shrink_to_size(0u); + payload = std::move(other.payload); + base_type::operator=(std::move(other)); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given storage. + * @param other Storage to exchange the content with. + */ + void swap(basic_storage &other) { + using std::swap; + swap(payload, other.payload); + base_type::swap(other); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return payload.get_allocator(); + } + + /** + * @brief Increases the capacity of a storage. + * + * If the new capacity is greater than the current capacity, new storage is + * allocated, otherwise the method does nothing. + * + * @param cap Desired capacity. + */ + void reserve(const size_type cap) override { + if(cap != 0u) { + base_type::reserve(cap); + assure_at_least(cap - 1u); + } + } + + /** + * @brief Returns the number of elements that a storage has currently + * allocated space for. + * @return Capacity of the storage. + */ + [[nodiscard]] size_type capacity() const noexcept override { + return payload.size() * traits_type::page_size; + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() override { + base_type::shrink_to_fit(); + shrink_to_size(base_type::size()); + } + + /** + * @brief Direct access to the array of objects. + * @return A pointer to the array of objects. + */ + [[nodiscard]] const_pointer raw() const noexcept { + return payload.data(); + } + + /*! @copydoc raw */ + [[nodiscard]] pointer raw() noexcept { + return payload.data(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the storage is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + const auto pos = static_cast<typename iterator::difference_type>(base_type::size()); + return const_iterator{&payload, pos}; + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + const auto pos = static_cast<typename iterator::difference_type>(base_type::size()); + return iterator{&payload, pos}; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return const_iterator{&payload, {}}; + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return iterator{&payload, {}}; + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * If the storage is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first instance of the reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { + return std::make_reverse_iterator(cend()); + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() noexcept { + return std::make_reverse_iterator(end()); + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last instance of the + * reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return std::make_reverse_iterator(cbegin()); + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const noexcept { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() noexcept { + return std::make_reverse_iterator(begin()); + } + + /** + * @brief Returns the object assigned to an entity. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + * @return The object assigned to the entity. + */ + [[nodiscard]] const value_type &get(const entity_type entt) const noexcept { + return element_at(base_type::index(entt)); + } + + /*! @copydoc get */ + [[nodiscard]] value_type &get(const entity_type entt) noexcept { + return const_cast<value_type &>(std::as_const(*this).get(entt)); + } + + /** + * @brief Returns the object assigned to an entity as a tuple. + * @param entt A valid identifier. + * @return The object assigned to the entity as a tuple. + */ + [[nodiscard]] std::tuple<const value_type &> get_as_tuple(const entity_type entt) const noexcept { + return std::forward_as_tuple(get(entt)); + } + + /*! @copydoc get_as_tuple */ + [[nodiscard]] std::tuple<value_type &> get_as_tuple(const entity_type entt) noexcept { + return std::forward_as_tuple(get(entt)); + } + + /** + * @brief Assigns an entity to a storage and constructs its object. + * + * @warning + * Attempting to use an entity that already belongs to the storage results + * in undefined behavior. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entt A valid identifier. + * @param args Parameters to use to construct an object for the entity. + * @return A reference to the newly created object. + */ + template<typename... Args> + value_type &emplace(const entity_type entt, Args &&...args) { + if constexpr(std::is_aggregate_v<value_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<value_type>)) { + const auto it = emplace_element(entt, false, Type{std::forward<Args>(args)...}); + return element_at(static_cast<size_type>(it.index())); + } else { + const auto it = emplace_element(entt, false, std::forward<Args>(args)...); + return element_at(static_cast<size_type>(it.index())); + } + } + + /** + * @brief Updates the instance assigned to a given entity in-place. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + * @return A reference to the updated instance. + */ + template<typename... Func> + value_type &patch(const entity_type entt, Func &&...func) { + const auto idx = base_type::index(entt); + auto &elem = element_at(idx); + (std::forward<Func>(func)(elem), ...); + return elem; + } + + /** + * @brief Assigns one or more entities to a storage and constructs their + * objects from a given instance. + * + * @warning + * Attempting to assign an entity that already belongs to the storage + * results in undefined behavior. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param value An instance of the object to construct. + * @return Iterator pointing to the first element inserted, if any. + */ + template<typename It> + iterator insert(It first, It last, const value_type &value = {}) { + for(; first != last; ++first) { + emplace_element(*first, true, value); + } + + return begin(); + } + + /** + * @brief Assigns one or more entities to a storage and constructs their + * objects from a given range. + * + * @sa construct + * + * @tparam EIt Type of input iterator. + * @tparam CIt Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param from An iterator to the first element of the range of objects. + * @return Iterator pointing to the first element inserted, if any. + */ + template<typename EIt, typename CIt, typename = std::enable_if_t<std::is_same_v<typename std::iterator_traits<CIt>::value_type, value_type>>> + iterator insert(EIt first, EIt last, CIt from) { + for(; first != last; ++first, ++from) { + emplace_element(*first, true, *from); + } + + return begin(); + } + + /** + * @brief Returns an iterable object to use to _visit_ a storage. + * + * The iterable object returns a tuple that contains the current entity and + * a reference to its element. + * + * @return An iterable object to use to _visit_ the storage. + */ + [[nodiscard]] iterable each() noexcept { + return iterable{{base_type::begin(), begin()}, {base_type::end(), end()}}; + } + + /*! @copydoc each */ + [[nodiscard]] const_iterable each() const noexcept { + return const_iterable{{base_type::cbegin(), cbegin()}, {base_type::cend(), cend()}}; + } + + /** + * @brief Returns a reverse iterable object to use to _visit_ a storage. + * + * @sa each + * + * @return A reverse iterable object to use to _visit_ the storage. + */ + [[nodiscard]] reverse_iterable reach() noexcept { + return reverse_iterable{{base_type::rbegin(), rbegin()}, {base_type::rend(), rend()}}; + } + + /*! @copydoc reach */ + [[nodiscard]] const_reverse_iterable reach() const noexcept { + return const_reverse_iterable{{base_type::crbegin(), crbegin()}, {base_type::crend(), crend()}}; + } + +private: + container_type payload; +}; + +/*! @copydoc basic_storage */ +template<typename Type, typename Entity, typename Allocator> +class basic_storage<Type, Entity, Allocator, std::enable_if_t<component_traits<Type>::page_size == 0u>> + : public basic_sparse_set<Entity, typename std::allocator_traits<Allocator>::template rebind_alloc<Entity>> { + using alloc_traits = std::allocator_traits<Allocator>; + static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type"); + using traits_type = component_traits<Type>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Base type. */ + using base_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>; + /*! @brief Element type. */ + using element_type = Type; + /*! @brief Type of the objects assigned to entities. */ + using value_type = void; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Extended iterable storage proxy. */ + using iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::iterator>>; + /*! @brief Constant extended iterable storage proxy. */ + using const_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_iterator>>; + /*! @brief Extended reverse iterable storage proxy. */ + using reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::reverse_iterator>>; + /*! @brief Constant extended reverse iterable storage proxy. */ + using const_reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_reverse_iterator>>; + /*! @brief Storage deletion policy. */ + static constexpr deletion_policy storage_policy{traits_type::in_place_delete}; + + /*! @brief Default constructor. */ + basic_storage() + : basic_storage{allocator_type{}} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_storage(const allocator_type &allocator) + : base_type{type_id<element_type>(), storage_policy, allocator} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_storage(const basic_storage &) = delete; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_storage(basic_storage &&other) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_storage(basic_storage &&other, const allocator_type &allocator) + : base_type{std::move(other), allocator} {} + + /*! @brief Default destructor. */ + ~basic_storage() noexcept override = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This storage. + */ + basic_storage &operator=(const basic_storage &) = delete; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + basic_storage &operator=(basic_storage &&other) noexcept = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + // std::allocator<void> has no cross constructors (waiting for C++20) + if constexpr(std::is_void_v<element_type> && !std::is_constructible_v<allocator_type, typename base_type::allocator_type>) { + return allocator_type{}; + } else { + return allocator_type{base_type::get_allocator()}; + } + } + + /** + * @brief Returns the object assigned to an entity, that is `void`. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + */ + void get([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::contains(entt), "Invalid entity"); + } + + /** + * @brief Returns an empty tuple. + * @param entt A valid identifier. + * @return Returns an empty tuple. + */ + [[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::contains(entt), "Invalid entity"); + return std::tuple{}; + } + + /** + * @brief Assigns an entity to a storage and constructs its object. + * + * @warning + * Attempting to use an entity that already belongs to the storage results + * in undefined behavior. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entt A valid identifier. + */ + template<typename... Args> + void emplace(const entity_type entt, Args &&...) { + base_type::try_emplace(entt, false); + } + + /** + * @brief Updates the instance assigned to a given entity in-place. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + */ + template<typename... Func> + void patch([[maybe_unused]] const entity_type entt, Func &&...func) { + ENTT_ASSERT(base_type::contains(entt), "Invalid entity"); + (std::forward<Func>(func)(), ...); + } + + /** + * @brief Assigns entities to a storage. + * @tparam It Type of input iterator. + * @tparam Args Types of optional arguments. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + template<typename It, typename... Args> + void insert(It first, It last, Args &&...) { + for(; first != last; ++first) { + base_type::try_emplace(*first, true); + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a storage. + * + * The iterable object returns a tuple that contains the current entity. + * + * @return An iterable object to use to _visit_ the storage. + */ + [[nodiscard]] iterable each() noexcept { + return iterable{base_type::begin(), base_type::end()}; + } + + /*! @copydoc each */ + [[nodiscard]] const_iterable each() const noexcept { + return const_iterable{base_type::cbegin(), base_type::cend()}; + } + + /** + * @brief Returns a reverse iterable object to use to _visit_ a storage. + * + * @sa each + * + * @return A reverse iterable object to use to _visit_ the storage. + */ + [[nodiscard]] reverse_iterable reach() noexcept { + return reverse_iterable{{base_type::rbegin()}, {base_type::rend()}}; + } + + /*! @copydoc reach */ + [[nodiscard]] const_reverse_iterable reach() const noexcept { + return const_reverse_iterable{{base_type::crbegin()}, {base_type::crend()}}; + } +}; + +/** + * @brief Swap-only entity storage specialization. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Entity, typename Allocator> +class basic_storage<Entity, Entity, Allocator> + : public basic_sparse_set<Entity, Allocator> { + using alloc_traits = std::allocator_traits<Allocator>; + static_assert(std::is_same_v<typename alloc_traits::value_type, Entity>, "Invalid value type"); + using underlying_iterator = typename basic_sparse_set<Entity, Allocator>::basic_iterator; + using traits_type = entt_traits<Entity>; + + auto next() noexcept { + entity_type entt = null; + + do { + ENTT_ASSERT(placeholder < traits_type::to_entity(null), "No more entities available"); + entt = traits_type::combine(static_cast<typename traits_type::entity_type>(placeholder++), {}); + } while(base_type::current(entt) != traits_type::to_version(tombstone) && entt != null); + + return entt; + } + +protected: + /*! @brief Erases all entities of a storage. */ + void pop_all() override { + base_type::pop_all(); + placeholder = {}; + } + + /** + * @brief Assigns an entity to a storage. + * @param hint A valid identifier. + * @return Iterator pointing to the emplaced element. + */ + underlying_iterator try_emplace(const Entity hint, const bool, const void *) override { + return base_type::find(emplace(hint)); + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Base type. */ + using base_type = basic_sparse_set<Entity, Allocator>; + /*! @brief Element type. */ + using element_type = Entity; + /*! @brief Type of the objects assigned to entities. */ + using value_type = void; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Extended iterable storage proxy. */ + using iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::iterator>>; + /*! @brief Constant extended iterable storage proxy. */ + using const_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_iterator>>; + /*! @brief Extended reverse iterable storage proxy. */ + using reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::reverse_iterator>>; + /*! @brief Constant extended reverse iterable storage proxy. */ + using const_reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_reverse_iterator>>; + /*! @brief Storage deletion policy. */ + static constexpr deletion_policy storage_policy = deletion_policy::swap_only; + + /*! @brief Default constructor. */ + basic_storage() + : basic_storage{allocator_type{}} { + } + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_storage(const allocator_type &allocator) + : base_type{type_id<void>(), storage_policy, allocator} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_storage(const basic_storage &) = delete; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_storage(basic_storage &&other) noexcept + : base_type{std::move(other)}, + placeholder{other.placeholder} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_storage(basic_storage &&other, const allocator_type &allocator) + : base_type{std::move(other), allocator}, + placeholder{other.placeholder} {} + + /*! @brief Default destructor. */ + ~basic_storage() noexcept override = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This storage. + */ + basic_storage &operator=(const basic_storage &) = delete; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + basic_storage &operator=(basic_storage &&other) noexcept { + placeholder = other.placeholder; + base_type::operator=(std::move(other)); + return *this; + } + + /** + * @brief Returns the object assigned to an entity, that is `void`. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + */ + void get([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::index(entt) < base_type::free_list(), "The requested entity is not a live one"); + } + + /** + * @brief Returns an empty tuple. + * @param entt A valid identifier. + * @return Returns an empty tuple. + */ + [[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::index(entt) < base_type::free_list(), "The requested entity is not a live one"); + return std::tuple{}; + } + + /** + * @brief Creates a new identifier or recycles a destroyed one. + * @return A valid identifier. + */ + entity_type emplace() { + const auto len = base_type::free_list(); + const auto entt = (len == base_type::size()) ? next() : base_type::data()[len]; + return *base_type::try_emplace(entt, true); + } + + /** + * @brief Creates a new identifier or recycles a destroyed one. + * + * If the requested identifier isn't in use, the suggested one is used. + * Otherwise, a new identifier is returned. + * + * @param hint Required identifier. + * @return A valid identifier. + */ + entity_type emplace(const entity_type hint) { + if(hint != null && hint != tombstone) { + if(const auto curr = traits_type::construct(traits_type::to_entity(hint), base_type::current(hint)); curr == tombstone || !(base_type::index(curr) < base_type::free_list())) { + return *base_type::try_emplace(hint, true); + } + } + + return emplace(); + } + + /** + * @brief Updates a given identifier. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + */ + template<typename... Func> + void patch([[maybe_unused]] const entity_type entt, Func &&...func) { + ENTT_ASSERT(base_type::index(entt) < base_type::free_list(), "The requested entity is not a live one"); + (std::forward<Func>(func)(), ...); + } + + /** + * @brief Assigns each element in a range an identifier. + * @tparam It Type of mutable forward iterator. + * @param first An iterator to the first element of the range to generate. + * @param last An iterator past the last element of the range to generate. + */ + template<typename It> + void insert(It first, It last) { + for(const auto sz = base_type::size(); first != last && base_type::free_list() != sz; ++first) { + *first = *base_type::try_emplace(base_type::data()[base_type::free_list()], true); + } + + for(; first != last; ++first) { + *first = *base_type::try_emplace(next(), true); + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a storage. + * + * The iterable object returns a tuple that contains the current entity. + * + * @return An iterable object to use to _visit_ the storage. + */ + [[nodiscard]] iterable each() noexcept { + return std::as_const(*this).each(); + } + + /*! @copydoc each */ + [[nodiscard]] const_iterable each() const noexcept { + const auto it = base_type::cend(); + return const_iterable{it - base_type::free_list(), it}; + } + + /** + * @brief Returns a reverse iterable object to use to _visit_ a storage. + * + * @sa each + * + * @return A reverse iterable object to use to _visit_ the storage. + */ + [[nodiscard]] reverse_iterable reach() noexcept { + return std::as_const(*this).reach(); + } + + /*! @copydoc reach */ + [[nodiscard]] const_reverse_iterable reach() const noexcept { + const auto it = base_type::crbegin(); + return const_reverse_iterable{it, it + base_type::free_list()}; + } + +private: + size_type placeholder{}; +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/entity/storage_mixin.hpp b/external/entt/include/entt/entity/storage_mixin.hpp new file mode 100644 index 0000000..0686e4e --- /dev/null +++ b/external/entt/include/entt/entity/storage_mixin.hpp @@ -0,0 +1,236 @@ +#ifndef ENTT_ENTITY_SIGH_STORAGE_MIXIN_HPP +#define ENTT_ENTITY_SIGH_STORAGE_MIXIN_HPP + +#include <utility> +#include "../config/config.h" +#include "../core/any.hpp" +#include "../signal/sigh.hpp" +#include "fwd.hpp" + +namespace entt { + +/** + * @brief Mixin type used to add signal support to storage types. + * + * The function type of a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry<entity_type> &, entity_type); + * @endcode + * + * This applies to all signals made available. + * + * @tparam Type The type of the underlying storage. + */ +template<typename Type> +class sigh_storage_mixin final: public Type { + using basic_registry_type = basic_registry<typename Type::entity_type, typename Type::base_type::allocator_type>; + using sigh_type = sigh<void(basic_registry_type &, const typename Type::entity_type), typename Type::allocator_type>; + using basic_iterator = typename Type::basic_iterator; + + void pop(basic_iterator first, basic_iterator last) override { + ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); + + for(; first != last; ++first) { + const auto entt = *first; + destruction.publish(*owner, entt); + const auto it = Type::find(entt); + Type::pop(it, it + 1u); + } + } + + basic_iterator try_emplace(const typename basic_registry_type::entity_type entt, const bool force_back, const void *value) final { + ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); + Type::try_emplace(entt, force_back, value); + construction.publish(*owner, entt); + return Type::find(entt); + } + +public: + /*! @brief Allocator type. */ + using allocator_type = typename Type::allocator_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename Type::entity_type; + /*! @brief Expected registry type. */ + using registry_type = basic_registry_type; + + /*! @brief Default constructor. */ + sigh_storage_mixin() + : sigh_storage_mixin{allocator_type{}} {} + + /** + * @brief Constructs an empty storage with a given allocator. + * @param allocator The allocator to use. + */ + explicit sigh_storage_mixin(const allocator_type &allocator) + : Type{allocator}, + owner{}, + construction{allocator}, + destruction{allocator}, + update{allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + sigh_storage_mixin(sigh_storage_mixin &&other) noexcept + : Type{std::move(other)}, + owner{other.owner}, + construction{std::move(other.construction)}, + destruction{std::move(other.destruction)}, + update{std::move(other.update)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + sigh_storage_mixin(sigh_storage_mixin &&other, const allocator_type &allocator) noexcept + : Type{std::move(other), allocator}, + owner{other.owner}, + construction{std::move(other.construction), allocator}, + destruction{std::move(other.destruction), allocator}, + update{std::move(other.update), allocator} {} + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + sigh_storage_mixin &operator=(sigh_storage_mixin &&other) noexcept { + Type::operator=(std::move(other)); + owner = other.owner; + construction = std::move(other.construction); + destruction = std::move(other.destruction); + update = std::move(other.update); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given storage. + * @param other Storage to exchange the content with. + */ + void swap(sigh_storage_mixin &other) { + using std::swap; + Type::swap(other); + swap(owner, other.owner); + swap(construction, other.construction); + swap(destruction, other.destruction); + swap(update, other.update); + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever a new instance is created and assigned to an entity.<br/> + * Listeners are invoked after the object has been assigned to the entity. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_construct() noexcept { + return sink{construction}; + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is explicitly updated.<br/> + * Listeners are invoked after the object has been updated. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_update() noexcept { + return sink{update}; + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is removed from an entity and thus destroyed.<br/> + * Listeners are invoked before the object has been removed from the entity. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_destroy() noexcept { + return sink{destruction}; + } + + /** + * @brief Assigns entities to a storage. + * @tparam Args Types of arguments to use to construct the object. + * @param entt A valid identifier. + * @param args Parameters to use to initialize the object. + * @return A reference to the newly created object. + */ + template<typename... Args> + decltype(auto) emplace(const entity_type entt, Args &&...args) { + ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); + Type::emplace(entt, std::forward<Args>(args)...); + construction.publish(*owner, entt); + return this->get(entt); + } + + /** + * @brief Patches the given instance for an entity. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + * @return A reference to the patched instance. + */ + template<typename... Func> + decltype(auto) patch(const entity_type entt, Func &&...func) { + ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); + Type::patch(entt, std::forward<Func>(func)...); + update.publish(*owner, entt); + return this->get(entt); + } + + /** + * @brief Assigns entities to a storage. + * @tparam It Type of input iterator. + * @tparam Args Types of arguments to use to construct the objects assigned + * to the entities. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param args Parameters to use to initialize the objects assigned to the + * entities. + */ + template<typename It, typename... Args> + void insert(It first, It last, Args &&...args) { + ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); + Type::insert(first, last, std::forward<Args>(args)...); + + for(auto it = construction.empty() ? last : first; it != last; ++it) { + construction.publish(*owner, *it); + } + } + + /** + * @brief Forwards variables to derived classes, if any. + * @param value A variable wrapped in an opaque container. + */ + void bind(any value) noexcept final { + auto *reg = any_cast<basic_registry_type>(&value); + owner = reg ? reg : owner; + Type::bind(std::move(value)); + } + +private: + basic_registry_type *owner; + sigh_type construction; + sigh_type destruction; + sigh_type update; +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/entity/view.hpp b/external/entt/include/entt/entity/view.hpp new file mode 100644 index 0000000..f59bff4 --- /dev/null +++ b/external/entt/include/entt/entity/view.hpp @@ -0,0 +1,1076 @@ +#ifndef ENTT_ENTITY_VIEW_HPP +#define ENTT_ENTITY_VIEW_HPP + +#include <array> +#include <cstddef> +#include <iterator> +#include <tuple> +#include <type_traits> +#include <utility> +#include "../config/config.h" +#include "../core/iterator.hpp" +#include "../core/type_traits.hpp" +#include "entity.hpp" +#include "fwd.hpp" + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template<typename It, typename Entity> +[[nodiscard]] bool all_of(It first, const It last, const Entity entt) noexcept { + for(; (first != last) && (*first)->contains(entt); ++first) {} + return first == last; +} + +template<typename It, typename Entity> +[[nodiscard]] bool none_of(It first, const It last, const Entity entt) noexcept { + for(; (first != last) && !(*first && (*first)->contains(entt)); ++first) {} + return first == last; +} + +template<typename It> +[[nodiscard]] bool fully_initialized(It first, const It last) noexcept { + for(; (first != last) && *first; ++first) {} + return first == last; +} + +template<typename Result, typename View, typename Other, std::size_t... GLhs, std::size_t... ELhs, std::size_t... GRhs, std::size_t... ERhs> +[[nodiscard]] Result view_pack(const View &view, const Other &other, std::index_sequence<GLhs...>, std::index_sequence<ELhs...>, std::index_sequence<GRhs...>, std::index_sequence<ERhs...>) { + Result elem{}; + // friend-initialization, avoid multiple calls to refresh + elem.pools = {view.template storage<GLhs>()..., other.template storage<GRhs>()...}; + elem.filter = {view.template storage<sizeof...(GLhs) + ELhs>()..., other.template storage<sizeof...(GRhs) + ERhs>()...}; + elem.refresh(); + return elem; +} + +template<typename Type, std::size_t Get, std::size_t Exclude> +class view_iterator final { + template<typename, typename...> + friend class extended_view_iterator; + + using iterator_type = typename Type::const_iterator; + using iterator_traits = std::iterator_traits<iterator_type>; + + [[nodiscard]] bool valid(const typename iterator_traits::value_type entt) const noexcept { + return ((Get != 1u) || (entt != tombstone)) + && internal::all_of(pools.begin(), pools.begin() + index, entt) && internal::all_of(pools.begin() + index + 1, pools.end(), entt) + && internal::none_of(filter.begin(), filter.end(), entt); + } + + void seek_next() { + for(constexpr iterator_type sentinel{}; it != sentinel && !valid(*it); ++it) {} + } + +public: + using value_type = typename iterator_traits::value_type; + using pointer = typename iterator_traits::pointer; + using reference = typename iterator_traits::reference; + using difference_type = typename iterator_traits::difference_type; + using iterator_category = std::forward_iterator_tag; + + constexpr view_iterator() noexcept + : it{}, + pools{}, + filter{}, + index{} {} + + view_iterator(iterator_type first, std::array<const Type *, Get> value, std::array<const Type *, Exclude> excl, const std::size_t idx) noexcept + : it{first}, + pools{value}, + filter{excl}, + index{idx} { + seek_next(); + } + + view_iterator &operator++() noexcept { + ++it; + seek_next(); + return *this; + } + + view_iterator operator++(int) noexcept { + view_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] pointer operator->() const noexcept { + return &*it; + } + + [[nodiscard]] reference operator*() const noexcept { + return *operator->(); + } + + template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs> + friend constexpr bool operator==(const view_iterator<LhsType, LhsArgs...> &, const view_iterator<RhsType, RhsArgs...> &) noexcept; + +private: + iterator_type it; + std::array<const Type *, Get> pools; + std::array<const Type *, Exclude> filter; + std::size_t index; +}; + +template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs> +[[nodiscard]] constexpr bool operator==(const view_iterator<LhsType, LhsArgs...> &lhs, const view_iterator<RhsType, RhsArgs...> &rhs) noexcept { + return lhs.it == rhs.it; +} + +template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs> +[[nodiscard]] constexpr bool operator!=(const view_iterator<LhsType, LhsArgs...> &lhs, const view_iterator<RhsType, RhsArgs...> &rhs) noexcept { + return !(lhs == rhs); +} + +template<typename It, typename... Get> +class extended_view_iterator final { + template<std::size_t... Index> + [[nodiscard]] auto dereference(std::index_sequence<Index...>) const noexcept { + return std::tuple_cat(std::make_tuple(*it), static_cast<Get *>(const_cast<constness_as_t<typename Get::base_type, Get> *>(std::get<Index>(it.pools)))->get_as_tuple(*it)...); + } + +public: + using iterator_type = It; + using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::declval<Get>().get_as_tuple({})...)); + using pointer = input_iterator_pointer<value_type>; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr extended_view_iterator() + : it{} {} + + extended_view_iterator(iterator_type from) + : it{from} {} + + extended_view_iterator &operator++() noexcept { + return ++it, *this; + } + + extended_view_iterator operator++(int) noexcept { + extended_view_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const noexcept { + return dereference(std::index_sequence_for<Get...>{}); + } + + [[nodiscard]] pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr iterator_type base() const noexcept { + return it; + } + + template<typename... Lhs, typename... Rhs> + friend bool constexpr operator==(const extended_view_iterator<Lhs...> &, const extended_view_iterator<Rhs...> &) noexcept; + +private: + It it; +}; + +template<typename... Lhs, typename... Rhs> +[[nodiscard]] constexpr bool operator==(const extended_view_iterator<Lhs...> &lhs, const extended_view_iterator<Rhs...> &rhs) noexcept { + return lhs.it == rhs.it; +} + +template<typename... Lhs, typename... Rhs> +[[nodiscard]] constexpr bool operator!=(const extended_view_iterator<Lhs...> &lhs, const extended_view_iterator<Rhs...> &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief View implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + * + * @b Important + * + * View iterators aren't invalidated if: + * + * * New elements are added to the storage iterated by the view. + * * The entity currently returned is modified (for example, elements are added + * or removed from it). + * * The entity currently returned is destroyed. + * + * In all other cases, modifying the storage iterated by a view in any way can + * invalidate all iterators. + */ +template<typename, typename, typename> +class basic_view; + +/** + * @brief Basic storage view implementation. + * @warning For internal use only, backward compatibility not guaranteed. + * @tparam Type Common type among all storage types. + * @tparam Get Number of storage iterated by the view. + * @tparam Exclude Number of storage used to filter the view. + */ +template<typename Type, std::size_t Get, std::size_t Exclude> +class basic_common_view { + template<typename Return, typename View, typename Other, std::size_t... GLhs, std::size_t... ELhs, std::size_t... GRhs, std::size_t... ERhs> + friend Return internal::view_pack(const View &, const Other &, std::index_sequence<GLhs...>, std::index_sequence<ELhs...>, std::index_sequence<GRhs...>, std::index_sequence<ERhs...>); + + [[nodiscard]] auto offset() const noexcept { + ENTT_ASSERT(index != Get, "Invalid view"); + return (pools[index]->policy() == deletion_policy::swap_only) ? pools[index]->free_list() : pools[index]->size(); + } + + void unchecked_refresh() noexcept { + index = 0u; + + if constexpr(Get > 1u) { + for(size_type pos{1u}; pos < Get; ++pos) { + if(pools[pos]->size() < pools[index]->size()) { + index = pos; + } + } + } + } + +protected: + /*! @cond TURN_OFF_DOXYGEN */ + basic_common_view() noexcept = default; + + basic_common_view(std::array<const Type *, Get> value, std::array<const Type *, Exclude> excl) noexcept + : pools{value}, + filter{excl}, + index{Get} { + unchecked_refresh(); + } + + [[nodiscard]] const Type *pool_at(const std::size_t pos) const noexcept { + return pools[pos]; + } + + [[nodiscard]] const Type *storage(const std::size_t pos) const noexcept { + return (pos < Get) ? pools[pos] : filter[pos - Get]; + } + + void storage(const std::size_t pos, const Type *elem) noexcept { + if(pos < Get) { + pools[pos] = elem; + refresh(); + } else { + filter[pos - Get] = elem; + } + } + + [[nodiscard]] bool none_of(const typename Type::entity_type entt) const noexcept { + return internal::none_of(filter.begin(), filter.end(), entt); + } + + void use(const std::size_t pos) noexcept { + index = (index != Get) ? pos : Get; + } + /*! @endcond */ + +public: + /*! @brief Common type among all storage types. */ + using common_type = Type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename Type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Forward iterator type. */ + using iterator = internal::view_iterator<common_type, Get, Exclude>; + + /*! @brief Updates the internal leading view if required. */ + void refresh() noexcept { + size_type pos = (index != Get) * Get; + for(; pos < Get && pools[pos] != nullptr; ++pos) {} + + if(pos == Get) { + unchecked_refresh(); + } + } + + /** + * @brief Returns the leading storage of a view, if any. + * @return The leading storage of the view. + */ + [[nodiscard]] const common_type *handle() const noexcept { + return (index != Get) ? pools[index] : nullptr; + } + + /** + * @brief Estimates the number of entities iterated by the view. + * @return Estimated number of entities iterated by the view. + */ + [[nodiscard]] size_type size_hint() const noexcept { + return (index != Get) ? pools[index]->size() : size_type{}; + } + + /** + * @brief Returns an iterator to the first entity of the view. + * + * If the view is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the view. + */ + [[nodiscard]] iterator begin() const noexcept { + return (index != Get) ? iterator{pools[index]->end() - static_cast<typename iterator::difference_type>(offset()), pools, filter, index} : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the view. + * @return An iterator to the entity following the last entity of the view. + */ + [[nodiscard]] iterator end() const noexcept { + return (index != Get) ? iterator{pools[index]->end(), pools, filter, index} : iterator{}; + } + + /** + * @brief Returns the first entity of the view, if any. + * @return The first entity of the view if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const noexcept { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity of the view, if any. + * @return The last entity of the view if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + if(index != Get) { + auto it = pools[index]->rbegin(); + const auto last = it + static_cast<typename iterator::difference_type>(offset()); + for(; it != last && !contains(*it); ++it) {} + return it == last ? null : *it; + } + + return null; + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + return contains(entt) ? iterator{pools[index]->find(entt), pools, filter, index} : end(); + } + + /** + * @brief Checks if a view is fully initialized. + * @return True if the view is fully initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (index != Get) && internal::fully_initialized(filter.begin(), filter.end()); + } + + /** + * @brief Checks if a view contains an entity. + * @param entt A valid identifier. + * @return True if the view contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + return (index != Get) + && internal::all_of(pools.begin(), pools.end(), entt) + && internal::none_of(filter.begin(), filter.end(), entt) + && pools[index]->index(entt) < offset(); + } + +private: + std::array<const common_type *, Get> pools{}; + std::array<const common_type *, Exclude> filter{}; + size_type index{Get}; +}; + +/** + * @brief General purpose view. + * + * This view visits all entities that are at least in the given storage. During + * initialization, it also looks at the number of elements available for each + * storage and uses the smallest set in order to get a performance boost. + * + * @sa basic_view + * + * @tparam Get Types of storage iterated by the view. + * @tparam Exclude Types of storage used to filter the view. + */ +template<typename... Get, typename... Exclude> +class basic_view<get_t<Get...>, exclude_t<Exclude...>, std::enable_if_t<(sizeof...(Get) + sizeof...(Exclude) > 1)>> + : public basic_common_view<std::common_type_t<typename Get::base_type..., typename Exclude::base_type...>, sizeof...(Get), sizeof...(Exclude)> { + using base_type = basic_common_view<std::common_type_t<typename Get::base_type..., typename Exclude::base_type...>, sizeof...(Get), sizeof...(Exclude)>; + + template<typename Type> + static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Get::element_type..., typename Exclude::element_type...>>; + + template<std::size_t... Index> + [[nodiscard]] auto get(const typename base_type::entity_type entt, std::index_sequence<Index...>) const noexcept { + return std::tuple_cat(storage<Index>()->get_as_tuple(entt)...); + } + + template<std::size_t Curr, std::size_t Other, typename... Args> + [[nodiscard]] auto dispatch_get(const std::tuple<typename base_type::entity_type, Args...> &curr) const { + if constexpr(Curr == Other) { + return std::forward_as_tuple(std::get<Args>(curr)...); + } else { + return storage<Other>()->get_as_tuple(std::get<0>(curr)); + } + } + + template<std::size_t Curr, typename Func, std::size_t... Index> + void each(Func &func, std::index_sequence<Index...>) const { + static constexpr bool tombstone_check_required = ((sizeof...(Get) == 1u) && ... && (Get::storage_policy == deletion_policy::in_place)); + + for(const auto curr: storage<Curr>()->each()) { + if(const auto entt = std::get<0>(curr); (!tombstone_check_required || (entt != tombstone)) && ((Curr == Index || base_type::pool_at(Index)->contains(entt)) && ...) && base_type::none_of(entt)) { + if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_view>().get({})))>) { + std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get<Curr, Index>(curr)...)); + } else { + std::apply(func, std::tuple_cat(dispatch_get<Curr, Index>(curr)...)); + } + } + } + } + + template<typename Func, std::size_t... Index> + void pick_and_each(Func &func, std::index_sequence<Index...> seq) const { + if(const auto *view = base_type::handle(); view != nullptr) { + ((view == base_type::pool_at(Index) ? each<Index>(func, seq) : void()), ...); + } + } + +public: + /*! @brief Common type among all storage types. */ + using common_type = typename base_type::common_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename base_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Forward iterator type. */ + using iterator = typename base_type::iterator; + /*! @brief Iterable view type. */ + using iterable = iterable_adaptor<internal::extended_view_iterator<iterator, Get...>>; + + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_view() noexcept + : base_type{} {} + + /** + * @brief Constructs a view from a set of storage classes. + * @param value The storage for the types to iterate. + * @param excl The storage for the types used to filter the view. + */ + basic_view(Get &...value, Exclude &...excl) noexcept + : base_type{{&value...}, {&excl...}} { + } + + /** + * @brief Constructs a view from a set of storage classes. + * @param value The storage for the types to iterate. + * @param excl The storage for the types used to filter the view. + */ + basic_view(std::tuple<Get &...> value, std::tuple<Exclude &...> excl = {}) noexcept + : basic_view{std::make_from_tuple<basic_view>(std::tuple_cat(value, excl))} {} + + /** + * @brief Forces a view to use a given element to drive iterations + * @tparam Type Type of element to use to drive iterations. + */ + template<typename Type> + void use() noexcept { + use<index_of<Type>>(); + } + + /** + * @brief Forces a view to use a given element to drive iterations + * @tparam Index Index of the element to use to drive iterations. + */ + template<std::size_t Index> + void use() noexcept { + base_type::use(Index); + } + + /** + * @brief Returns the storage for a given element type, if any. + * @tparam Type Type of element of which to return the storage. + * @return The storage for the given element type. + */ + template<typename Type> + [[nodiscard]] auto *storage() const noexcept { + return storage<index_of<Type>>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template<std::size_t Index> + [[nodiscard]] auto *storage() const noexcept { + using type = type_list_element_t<Index, type_list<Get..., Exclude...>>; + return static_cast<type *>(const_cast<constness_as_t<common_type, type> *>(base_type::storage(Index))); + } + + /** + * @brief Assigns a storage to a view. + * @tparam Type Type of storage to assign to the view. + * @param elem A storage to assign to the view. + */ + template<typename Type> + void storage(Type &elem) noexcept { + storage<index_of<typename Type::element_type>>(elem); + } + + /** + * @brief Assigns a storage to a view. + * @tparam Index Index of the storage to assign to the view. + * @tparam Type Type of storage to assign to the view. + * @param elem A storage to assign to the view. + */ + template<std::size_t Index, typename Type> + void storage(Type &elem) noexcept { + static_assert(std::is_convertible_v<Type &, type_list_element_t<Index, type_list<Get..., Exclude...>> &>, "Unexpected type"); + base_type::storage(Index, &elem); + } + + /** + * @brief Returns the elements assigned to the given entity. + * @param entt A valid identifier. + * @return The elements assigned to the given entity. + */ + [[nodiscard]] decltype(auto) operator[](const entity_type entt) const { + return get(entt); + } + + /** + * @brief Returns the elements assigned to the given entity. + * @tparam Type Type of the element to get. + * @tparam Other Other types of elements to get. + * @param entt A valid identifier. + * @return The elements assigned to the entity. + */ + template<typename Type, typename... Other> + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + return get<index_of<Type>, index_of<Other>...>(entt); + } + + /** + * @brief Returns the elements assigned to the given entity. + * @tparam Index Indexes of the elements to get. + * @param entt A valid identifier. + * @return The elements assigned to the entity. + */ + template<std::size_t... Index> + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + if constexpr(sizeof...(Index) == 0) { + return get(entt, std::index_sequence_for<Get...>{}); + } else if constexpr(sizeof...(Index) == 1) { + return (storage<Index>()->get(entt), ...); + } else { + return std::tuple_cat(storage<Index>()->get_as_tuple(entt)...); + } + } + + /** + * @brief Iterates entities and elements and applies the given function + * object to them. + * + * The signature of the function must be equivalent to one of the following + * (non-empty types only, constness as requested): + * + * @code{.cpp} + * void(const entity_type, Type &...); + * void(Type &...); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template<typename Func> + void each(Func func) const { + pick_and_each(func, std::index_sequence_for<Get...>{}); + } + + /** + * @brief Returns an iterable object to use to _visit_ a view. + * + * The iterable object returns a tuple that contains the current entity and + * a set of references to its non-empty elements. The _constness_ of the + * elements is as requested. + * + * @return An iterable object to use to _visit_ the view. + */ + [[nodiscard]] iterable each() const noexcept { + return iterable{base_type::begin(), base_type::end()}; + } + + /** + * @brief Combines two views in a _more specific_ one. + * @tparam OGet Element list of the view to combine with. + * @tparam OExclude Filter list of the view to combine with. + * @param other The view to combine with. + * @return A more specific view. + */ + template<typename... OGet, typename... OExclude> + [[nodiscard]] auto operator|(const basic_view<get_t<OGet...>, exclude_t<OExclude...>> &other) const noexcept { + return internal::view_pack<basic_view<get_t<Get..., OGet...>, exclude_t<Exclude..., OExclude...>>>( + *this, other, std::index_sequence_for<Get...>{}, std::index_sequence_for<Exclude...>{}, std::index_sequence_for<OGet...>{}, std::index_sequence_for<OExclude...>{}); + } +}; + +/** + * @brief Basic storage view implementation. + * @warning For internal use only, backward compatibility not guaranteed. + * @tparam Type Common type among all storage types. + * @tparam Policy Storage policy. + */ +template<typename Type, deletion_policy Policy> +class basic_storage_view { +protected: + /*! @cond TURN_OFF_DOXYGEN */ + basic_storage_view() noexcept = default; + + basic_storage_view(const Type *value) noexcept + : leading{value} { + ENTT_ASSERT(leading->policy() == Policy, "Unexpected storage policy"); + } + /*! @endcond */ + +public: + /*! @brief Common type among all storage types. */ + using common_type = Type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename common_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Random access iterator type. */ + using iterator = std::conditional_t<Policy == deletion_policy::in_place, internal::view_iterator<common_type, 1u, 0u>, typename common_type::iterator>; + /*! @brief Reverse iterator type. */ + using reverse_iterator = std::conditional_t<Policy == deletion_policy::in_place, void, typename common_type::reverse_iterator>; + + /** + * @brief Returns the leading storage of a view, if any. + * @return The leading storage of the view. + */ + [[nodiscard]] const common_type *handle() const noexcept { + return leading; + } + + /** + * @brief Returns the number of entities that have the given element. + * @tparam Pol Dummy template parameter used for sfinae purposes only. + * @return Number of entities that have the given element. + */ + template<typename..., deletion_policy Pol = Policy> + [[nodiscard]] std::enable_if_t<Pol != deletion_policy::in_place, size_type> size() const noexcept { + if constexpr(Policy == deletion_policy::swap_and_pop) { + return leading ? leading->size() : size_type{}; + } else { + static_assert(Policy == deletion_policy::swap_only, "Unexpected storage policy"); + return leading ? leading->free_list() : size_type{}; + } + } + + /** + * @brief Estimates the number of entities iterated by the view. + * @tparam Pol Dummy template parameter used for sfinae purposes only. + * @return Estimated number of entities iterated by the view. + */ + template<typename..., deletion_policy Pol = Policy> + [[nodiscard]] std::enable_if_t<Pol == deletion_policy::in_place, size_type> size_hint() const noexcept { + return leading ? leading->size() : size_type{}; + } + + /** + * @brief Checks whether a view is empty. + * @tparam Pol Dummy template parameter used for sfinae purposes only. + * @return True if the view is empty, false otherwise. + */ + template<typename..., deletion_policy Pol = Policy> + [[nodiscard]] std::enable_if_t<Pol != deletion_policy::in_place, bool> empty() const noexcept { + if constexpr(Policy == deletion_policy::swap_and_pop) { + return !leading || leading->empty(); + } else { + static_assert(Policy == deletion_policy::swap_only, "Unexpected storage policy"); + return !leading || (leading->free_list() == 0u); + } + } + + /** + * @brief Returns an iterator to the first entity of the view. + * + * If the view is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the view. + */ + [[nodiscard]] iterator begin() const noexcept { + if constexpr(Policy == deletion_policy::swap_and_pop) { + return leading ? leading->begin() : iterator{}; + } else if constexpr(Policy == deletion_policy::swap_only) { + return leading ? (leading->end() - leading->free_list()) : iterator{}; + } else { + static_assert(Policy == deletion_policy::in_place, "Unexpected storage policy"); + return leading ? iterator{leading->begin(), {leading}, {}, 0u} : iterator{}; + } + } + + /** + * @brief Returns an iterator that is past the last entity of the view. + * @return An iterator to the entity following the last entity of the view. + */ + [[nodiscard]] iterator end() const noexcept { + if constexpr(Policy == deletion_policy::swap_and_pop || Policy == deletion_policy::swap_only) { + return leading ? leading->end() : iterator{}; + } else { + static_assert(Policy == deletion_policy::in_place, "Unexpected storage policy"); + return leading ? iterator{leading->end(), {leading}, {}, 0u} : iterator{}; + } + } + + /** + * @brief Returns an iterator to the first entity of the reversed view. + * + * If the view is empty, the returned iterator will be equal to `rend()`. + * + * @tparam Pol Dummy template parameter used for sfinae purposes only. + * @return An iterator to the first entity of the reversed view. + */ + template<typename..., deletion_policy Pol = Policy> + [[nodiscard]] std::enable_if_t<Pol != deletion_policy::in_place, reverse_iterator> rbegin() const noexcept { + return leading ? leading->rbegin() : reverse_iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * view. + * @tparam Pol Dummy template parameter used for sfinae purposes only. + * @return An iterator to the entity following the last entity of the + * reversed view. + */ + template<typename..., deletion_policy Pol = Policy> + [[nodiscard]] std::enable_if_t<Pol != deletion_policy::in_place, reverse_iterator> rend() const noexcept { + if constexpr(Policy == deletion_policy::swap_and_pop) { + return leading ? leading->rend() : reverse_iterator{}; + } else { + static_assert(Policy == deletion_policy::swap_only, "Unexpected storage policy"); + return leading ? (leading->rbegin() + leading->free_list()) : reverse_iterator{}; + } + } + + /** + * @brief Returns the first entity of the view, if any. + * @return The first entity of the view if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const noexcept { + if constexpr(Policy == deletion_policy::swap_and_pop) { + return empty() ? null : *leading->begin(); + } else if constexpr(Policy == deletion_policy::swap_only) { + return empty() ? null : *(leading->end() - leading->free_list()); + } else { + static_assert(Policy == deletion_policy::in_place, "Unexpected storage policy"); + const auto it = begin(); + return (it == end()) ? null : *it; + } + } + + /** + * @brief Returns the last entity of the view, if any. + * @return The last entity of the view if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + if constexpr(Policy == deletion_policy::swap_and_pop || Policy == deletion_policy::swap_only) { + return empty() ? null : *leading->rbegin(); + } else { + static_assert(Policy == deletion_policy::in_place, "Unexpected storage policy"); + + if(leading) { + auto it = leading->rbegin(); + const auto last = leading->rend(); + for(; (it != last) && (*it == tombstone); ++it) {} + return it == last ? null : *it; + } + + return null; + } + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + if constexpr(Policy == deletion_policy::swap_and_pop) { + return leading ? leading->find(entt) : iterator{}; + } else if constexpr(Policy == deletion_policy::swap_only) { + const auto it = leading ? leading->find(entt) : iterator{}; + return leading && (static_cast<size_type>(it.index()) < leading->free_list()) ? it : iterator{}; + } else { + const auto it = leading ? leading->find(entt) : typename common_type::iterator{}; + return iterator{it, {leading}, {}, 0u}; + } + } + + /** + * @brief Checks if a view is fully initialized. + * @return True if the view is fully initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (leading != nullptr); + } + + /** + * @brief Checks if a view contains an entity. + * @param entt A valid identifier. + * @return True if the view contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + if constexpr(Policy == deletion_policy::swap_and_pop || Policy == deletion_policy::in_place) { + return leading && leading->contains(entt); + } else { + static_assert(Policy == deletion_policy::swap_only, "Unexpected storage policy"); + return leading && leading->contains(entt) && (leading->index(entt) < leading->free_list()); + } + } + +private: + const common_type *leading{}; +}; + +/** + * @brief Storage view specialization. + * + * This specialization offers a boost in terms of performance. It can access the + * underlying data structure directly and avoid superfluous checks. + * + * @sa basic_view + * + * @tparam Get Type of storage iterated by the view. + */ +template<typename Get> +class basic_view<get_t<Get>, exclude_t<>> + : public basic_storage_view<typename Get::base_type, Get::storage_policy> { + using base_type = basic_storage_view<typename Get::base_type, Get::storage_policy>; + +public: + /*! @brief Common type among all storage types. */ + using common_type = typename base_type::common_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename base_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Random access iterator type. */ + using iterator = typename base_type::iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = typename base_type::reverse_iterator; + /*! @brief Iterable view type. */ + using iterable = std::conditional_t<Get::storage_policy == deletion_policy::in_place, iterable_adaptor<internal::extended_view_iterator<iterator, Get>>, decltype(std::declval<Get>().each())>; + + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_view() noexcept + : base_type{} {} + + /** + * @brief Constructs a view from a storage class. + * @param value The storage for the type to iterate. + */ + basic_view(Get &value) noexcept + : base_type{&value} { + } + + /** + * @brief Constructs a view from a storage class. + * @param value The storage for the type to iterate. + */ + basic_view(std::tuple<Get &> value, std::tuple<> = {}) noexcept + : basic_view{std::get<0>(value)} {} + + /** + * @brief Returns the storage for a given element type, if any. + * @tparam Type Type of element of which to return the storage. + * @return The storage for the given element type. + */ + template<typename Type = typename Get::element_type> + [[nodiscard]] auto *storage() const noexcept { + static_assert(std::is_same_v<std::remove_const_t<Type>, typename Get::element_type>, "Invalid element type"); + return storage<0>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template<std::size_t Index> + [[nodiscard]] auto *storage() const noexcept { + static_assert(Index == 0u, "Index out of bounds"); + return static_cast<Get *>(const_cast<constness_as_t<common_type, Get> *>(base_type::handle())); + } + + /** + * @brief Assigns a storage to a view. + * @param elem A storage to assign to the view. + */ + void storage(Get &elem) noexcept { + storage<0>(elem); + } + + /** + * @brief Assigns a storage to a view. + * @tparam Index Index of the storage to assign to the view. + * @param elem A storage to assign to the view. + */ + template<std::size_t Index> + void storage(Get &elem) noexcept { + static_assert(Index == 0u, "Index out of bounds"); + *this = basic_view{elem}; + } + + /** + * @brief Returns a pointer to the underlying storage. + * @return A pointer to the underlying storage. + */ + [[nodiscard]] Get *operator->() const noexcept { + return storage(); + } + + /** + * @brief Returns the element assigned to the given entity. + * @param entt A valid identifier. + * @return The element assigned to the given entity. + */ + [[nodiscard]] decltype(auto) operator[](const entity_type entt) const { + return storage()->get(entt); + } + + /** + * @brief Returns the element assigned to the given entity. + * @tparam Elem Type of the element to get. + * @param entt A valid identifier. + * @return The element assigned to the entity. + */ + template<typename Elem> + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + static_assert(std::is_same_v<std::remove_const_t<Elem>, typename Get::element_type>, "Invalid element type"); + return get<0>(entt); + } + + /** + * @brief Returns the element assigned to the given entity. + * @tparam Index Index of the element to get. + * @param entt A valid identifier. + * @return The element assigned to the entity. + */ + template<std::size_t... Index> + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + if constexpr(sizeof...(Index) == 0) { + return storage()->get_as_tuple(entt); + } else { + return storage<Index...>()->get(entt); + } + } + + /** + * @brief Iterates entities and elements and applies the given function + * object to them. + * + * The signature of the function must be equivalent to one of the following + * (non-empty types only, constness as requested): + * + * @code{.cpp} + * void(const entity_type, Type &); + * void(typename Type &); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template<typename Func> + void each(Func func) const { + if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_view>().get({})))>) { + for(const auto pack: each()) { + std::apply(func, pack); + } + } else if constexpr(Get::storage_policy == deletion_policy::swap_and_pop || Get::storage_policy == deletion_policy::swap_only) { + if constexpr(std::is_void_v<typename Get::value_type>) { + for(size_type pos = base_type::size(); pos; --pos) { + func(); + } + } else { + if(const auto len = base_type::size(); len != 0u) { + for(auto last = storage()->end(), first = last - len; first != last; ++first) { + func(*first); + } + } + } + } else { + static_assert(Get::storage_policy == deletion_policy::in_place, "Unexpected storage policy"); + + for(const auto pack: each()) { + std::apply([&func](const auto, auto &&...elem) { func(std::forward<decltype(elem)>(elem)...); }, pack); + } + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a view. + * + * The iterable object returns a tuple that contains the current entity and + * a reference to its element if it's a non-empty one. The _constness_ of + * the element is as requested. + * + * @return An iterable object to use to _visit_ the view. + */ + [[nodiscard]] iterable each() const noexcept { + if constexpr(Get::storage_policy == deletion_policy::swap_and_pop || Get::storage_policy == deletion_policy::swap_only) { + return base_type::handle() ? storage()->each() : iterable{}; + } else { + static_assert(Get::storage_policy == deletion_policy::in_place, "Unexpected storage policy"); + return iterable{base_type::begin(), base_type::end()}; + } + } + + /** + * @brief Combines two views in a _more specific_ one. + * @tparam OGet Element list of the view to combine with. + * @tparam OExclude Filter list of the view to combine with. + * @param other The view to combine with. + * @return A more specific view. + */ + template<typename... OGet, typename... OExclude> + [[nodiscard]] auto operator|(const basic_view<get_t<OGet...>, exclude_t<OExclude...>> &other) const noexcept { + return internal::view_pack<basic_view<get_t<Get, OGet...>, exclude_t<OExclude...>>>( + *this, other, std::index_sequence_for<Get>{}, std::index_sequence_for<>{}, std::index_sequence_for<OGet...>{}, std::index_sequence_for<OExclude...>{}); + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of storage classes used to create the view. + * @param storage The storage for the types to iterate. + */ +template<typename... Type> +basic_view(Type &...storage) -> basic_view<get_t<Type...>, exclude_t<>>; + +/** + * @brief Deduction guide. + * @tparam Get Types of elements iterated by the view. + * @tparam Exclude Types of elements used to filter the view. + */ +template<typename... Get, typename... Exclude> +basic_view(std::tuple<Get &...>, std::tuple<Exclude &...> = {}) -> basic_view<get_t<Get...>, exclude_t<Exclude...>>; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/entt.hpp b/external/entt/include/entt/entt.hpp new file mode 100644 index 0000000..a00fed5 --- /dev/null +++ b/external/entt/include/entt/entt.hpp @@ -0,0 +1,67 @@ +// IWYU pragma: begin_exports +#include "config/config.h" +#include "config/macro.h" +#include "config/version.h" +#include "container/dense_map.hpp" +#include "container/dense_set.hpp" +#include "container/table.hpp" +#include "core/algorithm.hpp" +#include "core/any.hpp" +#include "core/attribute.h" +#include "core/bit.hpp" +#include "core/compressed_pair.hpp" +#include "core/enum.hpp" +#include "core/family.hpp" +#include "core/hashed_string.hpp" +#include "core/ident.hpp" +#include "core/iterator.hpp" +#include "core/memory.hpp" +#include "core/monostate.hpp" +#include "core/ranges.hpp" +#include "core/tuple.hpp" +#include "core/type_info.hpp" +#include "core/type_traits.hpp" +#include "core/utility.hpp" +#include "entity/component.hpp" +#include "entity/entity.hpp" +#include "entity/group.hpp" +#include "entity/handle.hpp" +#include "entity/helper.hpp" +#include "entity/mixin.hpp" +#include "entity/observer.hpp" +#include "entity/organizer.hpp" +#include "entity/ranges.hpp" +#include "entity/registry.hpp" +#include "entity/runtime_view.hpp" +#include "entity/snapshot.hpp" +#include "entity/sparse_set.hpp" +#include "entity/storage.hpp" +#include "entity/view.hpp" +#include "graph/adjacency_matrix.hpp" +#include "graph/dot.hpp" +#include "graph/flow.hpp" +#include "locator/locator.hpp" +#include "meta/adl_pointer.hpp" +#include "meta/container.hpp" +#include "meta/context.hpp" +#include "meta/factory.hpp" +#include "meta/meta.hpp" +#include "meta/node.hpp" +#include "meta/pointer.hpp" +#include "meta/policy.hpp" +#include "meta/range.hpp" +#include "meta/resolve.hpp" +#include "meta/template.hpp" +#include "meta/type_traits.hpp" +#include "meta/utility.hpp" +#include "poly/poly.hpp" +#include "process/process.hpp" +#include "process/scheduler.hpp" +#include "resource/cache.hpp" +#include "resource/loader.hpp" +#include "resource/resource.hpp" +#include "signal/delegate.hpp" +#include "signal/dispatcher.hpp" +#include "signal/emitter.hpp" +#include "signal/sigh.hpp" +// IWYU pragma: end_exports diff --git a/external/entt/include/entt/fwd.hpp b/external/entt/include/entt/fwd.hpp new file mode 100644 index 0000000..4b6e60c --- /dev/null +++ b/external/entt/include/entt/fwd.hpp @@ -0,0 +1,11 @@ +// IWYU pragma: begin_exports +#include "container/fwd.hpp" +#include "core/fwd.hpp" +#include "entity/fwd.hpp" +#include "graph/fwd.hpp" +#include "meta/fwd.hpp" +#include "poly/fwd.hpp" +#include "process/fwd.hpp" +#include "resource/fwd.hpp" +#include "signal/fwd.hpp" +// IWYU pragma: end_exports diff --git a/external/entt/include/entt/graph/adjacency_matrix.hpp b/external/entt/include/entt/graph/adjacency_matrix.hpp new file mode 100644 index 0000000..85025be --- /dev/null +++ b/external/entt/include/entt/graph/adjacency_matrix.hpp @@ -0,0 +1,336 @@ +#ifndef ENTT_GRAPH_ADJACENCY_MATRIX_HPP +#define ENTT_GRAPH_ADJACENCY_MATRIX_HPP + +#include <cstddef> +#include <iterator> +#include <memory> +#include <type_traits> +#include <utility> +#include <vector> +#include "../config/config.h" +#include "../core/iterator.hpp" +#include "fwd.hpp" + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template<typename It> +class edge_iterator { + using size_type = std::size_t; + +public: + using value_type = std::pair<size_type, size_type>; + using pointer = input_iterator_pointer<value_type>; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr edge_iterator() noexcept = default; + + // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) + constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept + : it{std::move(base)}, + vert{vertices}, + pos{from}, + last{to}, + offset{step} { + for(; pos != last && !it[pos]; pos += offset) {} + } + + constexpr edge_iterator &operator++() noexcept { + for(pos += offset; pos != last && !it[pos]; pos += offset) {} + return *this; + } + + constexpr edge_iterator operator++(int) noexcept { + edge_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::make_pair<size_type>(pos / vert, pos % vert); + } + + template<typename Type> + friend constexpr bool operator==(const edge_iterator<Type> &, const edge_iterator<Type> &) noexcept; + +private: + It it{}; + size_type vert{}; + size_type pos{}; + size_type last{}; + size_type offset{}; +}; + +template<typename Container> +[[nodiscard]] inline constexpr bool operator==(const edge_iterator<Container> &lhs, const edge_iterator<Container> &rhs) noexcept { + return lhs.pos == rhs.pos; +} + +template<typename Container> +[[nodiscard]] inline constexpr bool operator!=(const edge_iterator<Container> &lhs, const edge_iterator<Container> &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic implementation of a directed adjacency matrix. + * @tparam Category Either a directed or undirected category tag. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Category, typename Allocator> +class adjacency_matrix { + using alloc_traits = std::allocator_traits<Allocator>; + static_assert(std::is_base_of_v<directed_tag, Category>, "Invalid graph category"); + static_assert(std::is_same_v<typename alloc_traits::value_type, std::size_t>, "Invalid value type"); + using container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Vertex type. */ + using vertex_type = size_type; + /*! @brief Edge type. */ + using edge_type = std::pair<vertex_type, vertex_type>; + /*! @brief Vertex iterator type. */ + using vertex_iterator = iota_iterator<vertex_type>; + /*! @brief Edge iterator type. */ + using edge_iterator = internal::edge_iterator<typename container_type::const_iterator>; + /*! @brief Out-edge iterator type. */ + using out_edge_iterator = edge_iterator; + /*! @brief In-edge iterator type. */ + using in_edge_iterator = edge_iterator; + /*! @brief Graph category tag. */ + using graph_category = Category; + + /*! @brief Default constructor. */ + adjacency_matrix() noexcept(noexcept(allocator_type{})) + : adjacency_matrix{0u} { + } + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit adjacency_matrix(const allocator_type &allocator) noexcept + : adjacency_matrix{0u, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied number of vertices. + * @param vertices Number of vertices. + * @param allocator The allocator to use. + */ + adjacency_matrix(const size_type vertices, const allocator_type &allocator = allocator_type{}) + : matrix{vertices * vertices, allocator}, + vert{vertices} {} + + /*! @brief Default copy constructor. */ + adjacency_matrix(const adjacency_matrix &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + adjacency_matrix(const adjacency_matrix &other, const allocator_type &allocator) + : matrix{other.matrix, allocator}, + vert{other.vert} {} + + /*! @brief Default move constructor. */ + adjacency_matrix(adjacency_matrix &&) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator) + : matrix{std::move(other.matrix), allocator}, + vert{other.vert} {} + + /*! @brief Default destructor. */ + ~adjacency_matrix() noexcept = default; + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + adjacency_matrix &operator=(const adjacency_matrix &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + adjacency_matrix &operator=(adjacency_matrix &&) noexcept = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return matrix.get_allocator(); + } + + /*! @brief Clears the adjacency matrix. */ + void clear() noexcept { + matrix.clear(); + vert = {}; + } + + /** + * @brief Exchanges the contents with those of a given adjacency matrix. + * @param other Adjacency matrix to exchange the content with. + */ + void swap(adjacency_matrix &other) { + using std::swap; + swap(matrix, other.matrix); + swap(vert, other.vert); + } + + /** + * @brief Returns true if an adjacency matrix is empty, false otherwise. + * + * @warning + * Potentially expensive, try to avoid it on hot paths. + * + * @return True if the adjacency matrix is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + const auto iterable = edges(); + return (iterable.begin() == iterable.end()); + } + + /** + * @brief Returns the number of vertices. + * @return The number of vertices. + */ + [[nodiscard]] size_type size() const noexcept { + return vert; + } + + /** + * @brief Returns an iterable object to visit all vertices of a matrix. + * @return An iterable object to visit all vertices of a matrix. + */ + [[nodiscard]] iterable_adaptor<vertex_iterator> vertices() const noexcept { + return {0u, vert}; + } + + /** + * @brief Returns an iterable object to visit all edges of a matrix. + * @return An iterable object to visit all edges of a matrix. + */ + [[nodiscard]] iterable_adaptor<edge_iterator> edges() const noexcept { + const auto it = matrix.cbegin(); + const auto sz = matrix.size(); + return {{it, vert, 0u, sz, 1u}, {it, vert, sz, sz, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all out-edges of a vertex. + * @param vertex The vertex of which to return all out-edges. + * @return An iterable object to visit all out-edges of a vertex. + */ + [[nodiscard]] iterable_adaptor<out_edge_iterator> out_edges(const vertex_type vertex) const noexcept { + const auto it = matrix.cbegin(); + const auto from = vertex * vert; + const auto to = from + vert; + return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all in-edges of a vertex. + * @param vertex The vertex of which to return all in-edges. + * @return An iterable object to visit all in-edges of a vertex. + */ + [[nodiscard]] iterable_adaptor<in_edge_iterator> in_edges(const vertex_type vertex) const noexcept { + const auto it = matrix.cbegin(); + const auto from = vertex; + const auto to = vert * vert + from; + return {{it, vert, from, to, vert}, {it, vert, to, to, vert}}; + } + + /** + * @brief Resizes an adjacency matrix. + * @param vertices The new number of vertices. + */ + void resize(const size_type vertices) { + adjacency_matrix other{vertices, get_allocator()}; + + for(auto [lhs, rhs]: edges()) { + other.insert(lhs, rhs); + } + + other.swap(*this); + } + + /** + * @brief Inserts an edge into the adjacency matrix, if it does not exist. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair<edge_iterator, bool> insert(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v<graph_category, undirected_tag>) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 1u; + } + + const auto inserted = !std::exchange(matrix[pos], 1u); + return {edge_iterator{matrix.cbegin(), vert, pos, matrix.size(), 1u}, inserted}; + } + + /** + * @brief Removes the edge associated with a pair of given vertices. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v<graph_category, undirected_tag>) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 0u; + } + + return std::exchange(matrix[pos], 0u); + } + + /** + * @brief Checks if an adjacency matrix contains a given edge. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return True if there is such an edge, false otherwise. + */ + [[nodiscard]] bool contains(const vertex_type lhs, const vertex_type rhs) const { + const auto pos = lhs * vert + rhs; + return pos < matrix.size() && matrix[pos]; + } + +private: + container_type matrix; + size_type vert; +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/graph/dot.hpp b/external/entt/include/entt/graph/dot.hpp new file mode 100644 index 0000000..1da13e8 --- /dev/null +++ b/external/entt/include/entt/graph/dot.hpp @@ -0,0 +1,58 @@ +#ifndef ENTT_GRAPH_DOT_HPP +#define ENTT_GRAPH_DOT_HPP + +#include <ostream> +#include <type_traits> +#include "fwd.hpp" + +namespace entt { + +/** + * @brief Outputs a graph in dot format. + * @tparam Graph Graph type, valid as long as it exposes edges and vertices. + * @tparam Writer Vertex decorator type. + * @param out A standard output stream. + * @param graph The graph to output. + * @param writer Vertex decorator object. + */ +template<typename Graph, typename Writer> +void dot(std::ostream &out, const Graph &graph, Writer writer) { + static_assert(std::is_base_of_v<directed_tag, typename Graph::graph_category>, "Invalid graph category"); + + if constexpr(std::is_same_v<typename Graph::graph_category, undirected_tag>) { + out << "graph{"; + } else { + out << "digraph{"; + } + + for(auto &&vertex: graph.vertices()) { + out << vertex << "["; + writer(out, vertex); + out << "];"; + } + + for(auto [lhs, rhs]: graph.edges()) { + if constexpr(std::is_same_v<typename Graph::graph_category, undirected_tag>) { + out << lhs << "--" << rhs << ";"; + } else { + out << lhs << "->" << rhs << ";"; + } + } + + out << "}"; +} + +/** + * @brief Outputs a graph in dot format. + * @tparam Graph Graph type, valid as long as it exposes edges and vertices. + * @param out A standard output stream. + * @param graph The graph to output. + */ +template<typename Graph> +void dot(std::ostream &out, const Graph &graph) { + return dot(out, graph, [](auto &&...) {}); +} + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/graph/flow.hpp b/external/entt/include/entt/graph/flow.hpp new file mode 100644 index 0000000..0e3721e --- /dev/null +++ b/external/entt/include/entt/graph/flow.hpp @@ -0,0 +1,351 @@ +#ifndef ENTT_GRAPH_FLOW_HPP +#define ENTT_GRAPH_FLOW_HPP + +#include <algorithm> +#include <cstddef> +#include <functional> +#include <iterator> +#include <memory> +#include <type_traits> +#include <utility> +#include <vector> +#include "../config/config.h" +#include "../container/dense_map.hpp" +#include "../container/dense_set.hpp" +#include "../core/compressed_pair.hpp" +#include "../core/fwd.hpp" +#include "../core/iterator.hpp" +#include "../core/utility.hpp" +#include "adjacency_matrix.hpp" +#include "fwd.hpp" + +namespace entt { + +/** + * @brief Utility class for creating task graphs. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Allocator> +class basic_flow { + using alloc_traits = std::allocator_traits<Allocator>; + static_assert(std::is_same_v<typename alloc_traits::value_type, id_type>, "Invalid value type"); + using task_container_type = dense_set<id_type, identity, std::equal_to<>, typename alloc_traits::template rebind_alloc<id_type>>; + using ro_rw_container_type = std::vector<std::pair<std::size_t, bool>, typename alloc_traits::template rebind_alloc<std::pair<std::size_t, bool>>>; + using deps_container_type = dense_map<id_type, ro_rw_container_type, identity, std::equal_to<>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, ro_rw_container_type>>>; + using adjacency_matrix_type = adjacency_matrix<directed_tag, typename alloc_traits::template rebind_alloc<std::size_t>>; + + void emplace(const id_type res, const bool is_rw) { + ENTT_ASSERT(index.first() < vertices.size(), "Invalid node"); + + if(!deps.contains(res) && sync_on != vertices.size()) { + deps[res].emplace_back(sync_on, true); + } + + deps[res].emplace_back(index.first(), is_rw); + } + + void setup_graph(adjacency_matrix_type &matrix) const { + for(const auto &elem: deps) { + const auto last = elem.second.cend(); + auto it = elem.second.cbegin(); + + while(it != last) { + if(it->second) { + // rw item + if(auto curr = it++; it != last) { + if(it->second) { + matrix.insert(curr->first, it->first); + } else if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { + for(; it != next; ++it) { + matrix.insert(curr->first, it->first); + matrix.insert(it->first, next->first); + } + } else { + for(; it != next; ++it) { + matrix.insert(curr->first, it->first); + } + } + } + } else { + // ro item (first iteration only) + if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { + for(; it != next; ++it) { + matrix.insert(it->first, next->first); + } + } else { + it = last; + } + } + } + } + } + + void transitive_closure(adjacency_matrix_type &matrix) const { + const auto length = matrix.size(); + + for(std::size_t vk{}; vk < length; ++vk) { + for(std::size_t vi{}; vi < length; ++vi) { + for(std::size_t vj{}; vj < length; ++vj) { + if(matrix.contains(vi, vk) && matrix.contains(vk, vj)) { + matrix.insert(vi, vj); + } + } + } + } + } + + void transitive_reduction(adjacency_matrix_type &matrix) const { + const auto length = matrix.size(); + + for(std::size_t vert{}; vert < length; ++vert) { + matrix.erase(vert, vert); + } + + for(std::size_t vj{}; vj < length; ++vj) { + for(std::size_t vi{}; vi < length; ++vi) { + if(matrix.contains(vi, vj)) { + for(std::size_t vk{}; vk < length; ++vk) { + if(matrix.contains(vj, vk)) { + matrix.erase(vi, vk); + } + } + } + } + } + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Iterable task list. */ + using iterable = iterable_adaptor<typename task_container_type::const_iterator>; + /*! @brief Adjacency matrix type. */ + using graph_type = adjacency_matrix_type; + + /*! @brief Default constructor. */ + basic_flow() + : basic_flow{allocator_type{}} {} + + /** + * @brief Constructs a flow builder with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_flow(const allocator_type &allocator) + : index{0u, allocator}, + vertices{allocator}, + deps{allocator} {} + + /*! @brief Default copy constructor. */ + basic_flow(const basic_flow &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + basic_flow(const basic_flow &other, const allocator_type &allocator) + : index{other.index.first(), allocator}, + vertices{other.vertices, allocator}, + deps{other.deps, allocator}, + sync_on{other.sync_on} {} + + /*! @brief Default move constructor. */ + basic_flow(basic_flow &&) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_flow(basic_flow &&other, const allocator_type &allocator) + : index{other.index.first(), allocator}, + vertices{std::move(other.vertices), allocator}, + deps{std::move(other.deps), allocator}, + sync_on{other.sync_on} {} + + /*! @brief Default destructor. */ + ~basic_flow() noexcept = default; + + /** + * @brief Default copy assignment operator. + * @return This flow builder. + */ + basic_flow &operator=(const basic_flow &) = default; + + /** + * @brief Default move assignment operator. + * @return This flow builder. + */ + basic_flow &operator=(basic_flow &&) noexcept = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return allocator_type{index.second()}; + } + + /** + * @brief Returns the identifier at specified location. + * @param pos Position of the identifier to return. + * @return The requested identifier. + */ + [[nodiscard]] id_type operator[](const size_type pos) const { + return vertices.cbegin()[pos]; + } + + /*! @brief Clears the flow builder. */ + void clear() noexcept { + index.first() = {}; + vertices.clear(); + deps.clear(); + sync_on = {}; + } + + /** + * @brief Exchanges the contents with those of a given flow builder. + * @param other Flow builder to exchange the content with. + */ + void swap(basic_flow &other) { + using std::swap; + std::swap(index, other.index); + std::swap(vertices, other.vertices); + std::swap(deps, other.deps); + std::swap(sync_on, other.sync_on); + } + + /** + * @brief Returns true if a flow builder contains no tasks, false otherwise. + * @return True if the flow builder contains no tasks, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return vertices.empty(); + } + + /** + * @brief Returns the number of tasks. + * @return The number of tasks. + */ + [[nodiscard]] size_type size() const noexcept { + return vertices.size(); + } + + /** + * @brief Binds a task to a flow builder. + * @param value Task identifier. + * @return This flow builder. + */ + basic_flow &bind(const id_type value) { + sync_on += (sync_on == vertices.size()); + const auto it = vertices.emplace(value).first; + index.first() = size_type(it - vertices.begin()); + return *this; + } + + /** + * @brief Turns the current task into a sync point. + * @return This flow builder. + */ + basic_flow &sync() { + ENTT_ASSERT(index.first() < vertices.size(), "Invalid node"); + sync_on = index.first(); + + for(const auto &elem: deps) { + elem.second.emplace_back(sync_on, true); + } + + return *this; + } + + /** + * @brief Assigns a resource to the current task with a given access mode. + * @param res Resource identifier. + * @param is_rw Access mode. + * @return This flow builder. + */ + basic_flow &set(const id_type res, bool is_rw = false) { + emplace(res, is_rw); + return *this; + } + + /** + * @brief Assigns a read-only resource to the current task. + * @param res Resource identifier. + * @return This flow builder. + */ + basic_flow &ro(const id_type res) { + emplace(res, false); + return *this; + } + + /** + * @brief Assigns a range of read-only resources to the current task. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return This flow builder. + */ + template<typename It> + std::enable_if_t<std::is_same_v<std::remove_const_t<typename std::iterator_traits<It>::value_type>, id_type>, basic_flow &> + ro(It first, It last) { + for(; first != last; ++first) { + emplace(*first, false); + } + + return *this; + } + + /** + * @brief Assigns a writable resource to the current task. + * @param res Resource identifier. + * @return This flow builder. + */ + basic_flow &rw(const id_type res) { + emplace(res, true); + return *this; + } + + /** + * @brief Assigns a range of writable resources to the current task. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return This flow builder. + */ + template<typename It> + std::enable_if_t<std::is_same_v<std::remove_const_t<typename std::iterator_traits<It>::value_type>, id_type>, basic_flow &> + rw(It first, It last) { + for(; first != last; ++first) { + emplace(*first, true); + } + + return *this; + } + + /** + * @brief Generates a task graph for the current content. + * @return The adjacency matrix of the task graph. + */ + [[nodiscard]] graph_type graph() const { + graph_type matrix{vertices.size(), get_allocator()}; + + setup_graph(matrix); + transitive_closure(matrix); + transitive_reduction(matrix); + + return matrix; + } + +private: + compressed_pair<size_type, allocator_type> index; + task_container_type vertices; + deps_container_type deps; + size_type sync_on{}; +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/graph/fwd.hpp b/external/entt/include/entt/graph/fwd.hpp new file mode 100644 index 0000000..963082f --- /dev/null +++ b/external/entt/include/entt/graph/fwd.hpp @@ -0,0 +1,27 @@ +#ifndef ENTT_GRAPH_FWD_HPP +#define ENTT_GRAPH_FWD_HPP + +#include <cstddef> +#include <memory> +#include "../core/fwd.hpp" + +namespace entt { + +/*! @brief Undirected graph category tag. */ +struct directed_tag {}; + +/*! @brief Directed graph category tag. */ +struct undirected_tag: directed_tag {}; + +template<typename, typename = std::allocator<std::size_t>> +class adjacency_matrix; + +template<typename = std::allocator<id_type>> +class basic_flow; + +/*! @brief Alias declaration for the most common use case. */ +using flow = basic_flow<>; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/locator/locator.hpp b/external/entt/include/entt/locator/locator.hpp new file mode 100644 index 0000000..dd80b5b --- /dev/null +++ b/external/entt/include/entt/locator/locator.hpp @@ -0,0 +1,158 @@ +#ifndef ENTT_LOCATOR_LOCATOR_HPP +#define ENTT_LOCATOR_LOCATOR_HPP + +#include <memory> +#include <utility> +#include "../config/config.h" + +namespace entt { + +/** + * @brief Service locator, nothing more. + * + * A service locator is used to do what it promises: locate services.<br/> + * Usually service locators are tightly bound to the services they expose and + * thus it's hard to define a general purpose class to do that. This tiny class + * tries to fill the gap and to get rid of the burden of defining a different + * specific locator for each application. + * + * @note + * Users shouldn't retain references to a service. The recommended way is to + * retrieve the service implementation currently set each and every time the + * need for it arises. The risk is to incur in unexpected behaviors otherwise. + * + * @tparam Service Service type. + */ +template<typename Service> +class locator final { + class service_handle { + friend class locator<Service>; + std::shared_ptr<Service> value{}; + }; + +public: + /*! @brief Service type. */ + using type = Service; + /*! @brief Service node type. */ + using node_type = service_handle; + + /*! @brief Default constructor, deleted on purpose. */ + locator() = delete; + + /*! @brief Default copy constructor, deleted on purpose. */ + locator(const locator &) = delete; + + /*! @brief Default destructor, deleted on purpose. */ + ~locator() noexcept = delete; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This locator. + */ + locator &operator=(const locator &) = delete; + + /** + * @brief Checks whether a service locator contains a value. + * @return True if the service locator contains a value, false otherwise. + */ + [[nodiscard]] static bool has_value() noexcept { + return (service != nullptr); + } + + /** + * @brief Returns a reference to a valid service, if any. + * + * @warning + * Invoking this function can result in undefined behavior if the service + * hasn't been set yet. + * + * @return A reference to the service currently set, if any. + */ + [[nodiscard]] static Service &value() noexcept { + ENTT_ASSERT(has_value(), "Service not available"); + return *service; + } + + /** + * @brief Returns a service if available or sets it from a fallback type. + * + * Arguments are used only if a service doesn't already exist. In all other + * cases, they are discarded. + * + * @tparam Args Types of arguments to use to construct the fallback service. + * @tparam Type Fallback service type. + * @param args Parameters to use to construct the fallback service. + * @return A reference to a valid service. + */ + template<typename Type = Service, typename... Args> + [[nodiscard]] static Service &value_or(Args &&...args) { + return service ? *service : emplace<Type>(std::forward<Args>(args)...); + } + + /** + * @brief Sets or replaces a service. + * @tparam Type Service type. + * @tparam Args Types of arguments to use to construct the service. + * @param args Parameters to use to construct the service. + * @return A reference to a valid service. + */ + template<typename Type = Service, typename... Args> + static Service &emplace(Args &&...args) { + service = std::make_shared<Type>(std::forward<Args>(args)...); + return *service; + } + + /** + * @brief Sets or replaces a service using a given allocator. + * @tparam Type Service type. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the service. + * @param alloc The allocator to use. + * @param args Parameters to use to construct the service. + * @return A reference to a valid service. + */ + template<typename Type = Service, typename Allocator, typename... Args> + static Service &emplace(std::allocator_arg_t, Allocator alloc, Args &&...args) { + service = std::allocate_shared<Type>(alloc, std::forward<Args>(args)...); + return *service; + } + + /** + * @brief Returns a handle to the underlying service. + * @return A handle to the underlying service. + */ + static node_type handle() noexcept { + node_type node{}; + node.value = service; + return node; + } + + /** + * @brief Resets or replaces a service. + * @param other Optional handle with which to replace the service. + */ + static void reset(const node_type &other = {}) noexcept { + service = other.value; + } + + /** + * @brief Resets or replaces a service. + * @tparam Type Service type. + * @tparam Deleter Deleter type. + * @param elem A pointer to a service to manage. + * @param deleter A deleter to use to destroy the service. + */ + template<typename Type, typename Deleter = std::default_delete<Type>> + static void reset(Type *elem, Deleter deleter = {}) { + service = std::shared_ptr<Service>{elem, std::move(deleter)}; + } + +private: + // std::shared_ptr because of its type erased allocator which is useful here + // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) + inline static std::shared_ptr<Service> service{}; +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/meta/adl_pointer.hpp b/external/entt/include/entt/meta/adl_pointer.hpp new file mode 100644 index 0000000..5bb768a --- /dev/null +++ b/external/entt/include/entt/meta/adl_pointer.hpp @@ -0,0 +1,35 @@ +#ifndef ENTT_META_ADL_POINTER_HPP +#define ENTT_META_ADL_POINTER_HPP + +namespace entt { + +/** + * @brief ADL based lookup function for dereferencing meta pointer-like types. + * @tparam Type Element type. + * @param value A pointer-like object. + * @return The value returned from the dereferenced pointer. + */ +template<typename Type> +decltype(auto) dereference_meta_pointer_like(const Type &value) { + return *value; +} + +/** + * @brief Fake ADL based lookup function for meta pointer-like types. + * @tparam Type Element type. + */ +template<typename Type> +struct adl_meta_pointer_like { + /** + * @brief Uses the default ADL based lookup method to resolve the call. + * @param value A pointer-like object. + * @return The value returned from the dereferenced pointer. + */ + static decltype(auto) dereference(const Type &value) { + return dereference_meta_pointer_like(value); + } +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/meta/container.hpp b/external/entt/include/entt/meta/container.hpp new file mode 100644 index 0000000..ca645a9 --- /dev/null +++ b/external/entt/include/entt/meta/container.hpp @@ -0,0 +1,388 @@ +// IWYU pragma: always_keep + +#ifndef ENTT_META_CONTAINER_HPP +#define ENTT_META_CONTAINER_HPP + +#include <array> +#include <deque> +#include <iterator> +#include <list> +#include <map> +#include <set> +#include <type_traits> +#include <unordered_map> +#include <unordered_set> +#include <vector> +#include "../container/dense_map.hpp" +#include "../container/dense_set.hpp" +#include "context.hpp" +#include "meta.hpp" +#include "type_traits.hpp" + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template<typename, typename = void> +struct fixed_size_sequence_container: std::true_type {}; + +template<typename Type> +struct fixed_size_sequence_container<Type, std::void_t<decltype(&Type::clear)>>: std::false_type {}; + +template<typename Type> +inline constexpr bool fixed_size_sequence_container_v = fixed_size_sequence_container<Type>::value; + +template<typename, typename = void> +struct key_only_associative_container: std::true_type {}; + +template<typename Type> +struct key_only_associative_container<Type, std::void_t<typename Type::mapped_type>>: std::false_type {}; + +template<typename Type> +inline constexpr bool key_only_associative_container_v = key_only_associative_container<Type>::value; + +template<typename, typename = void> +struct reserve_aware_container: std::false_type {}; + +template<typename Type> +struct reserve_aware_container<Type, std::void_t<decltype(&Type::reserve)>>: std::true_type {}; + +template<typename Type> +inline constexpr bool reserve_aware_container_v = reserve_aware_container<Type>::value; + +} // namespace internal +/*! @endcond */ + +/** + * @brief General purpose implementation of meta sequence container traits. + * @tparam Type Type of underlying sequence container. + */ +template<typename Type> +struct basic_meta_sequence_container_traits { + static_assert(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>, "Unexpected type"); + + /*! @brief Unsigned integer type. */ + using size_type = typename meta_sequence_container::size_type; + /*! @brief Meta iterator type. */ + using iterator = typename meta_sequence_container::iterator; + + /*! @brief True in case of key-only containers, false otherwise. */ + static constexpr bool fixed_size = internal::fixed_size_sequence_container_v<Type>; + + /** + * @brief Returns the number of elements in a container. + * @param container Opaque pointer to a container of the given type. + * @return Number of elements. + */ + [[nodiscard]] static size_type size(const void *container) { + return static_cast<const Type *>(container)->size(); + } + + /** + * @brief Clears a container. + * @param container Opaque pointer to a container of the given type. + * @return True in case of success, false otherwise. + */ + [[nodiscard]] static bool clear([[maybe_unused]] void *container) { + if constexpr(fixed_size) { + return false; + } else { + static_cast<Type *>(container)->clear(); + return true; + } + } + + /** + * @brief Increases the capacity of a container. + * @param container Opaque pointer to a container of the given type. + * @param sz Desired capacity. + * @return True in case of success, false otherwise. + */ + [[nodiscard]] static bool reserve([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) { + if constexpr(internal::reserve_aware_container_v<Type>) { + static_cast<Type *>(container)->reserve(sz); + return true; + } else { + return false; + } + } + + /** + * @brief Resizes a container. + * @param container Opaque pointer to a container of the given type. + * @param sz The new number of elements. + * @return True in case of success, false otherwise. + */ + [[nodiscard]] static bool resize([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) { + if constexpr(fixed_size || !std::is_default_constructible_v<typename Type::value_type>) { + return false; + } else { + static_cast<Type *>(container)->resize(sz); + return true; + } + } + + /** + * @brief Returns a possibly const iterator to the beginning. + * @param area The context to pass to the newly created iterator. + * @param container Opaque pointer to a container of the given type. + * @param as_const Const opaque pointer fallback. + * @return An iterator to the first element of the container. + */ + static iterator begin(const meta_ctx &area, void *container, const void *as_const) { + return container ? iterator{area, static_cast<Type *>(container)->begin()} + : iterator{area, static_cast<const Type *>(as_const)->begin()}; + } + + /** + * @brief Returns a possibly const iterator to the end. + * @param area The context to pass to the newly created iterator. + * @param container Opaque pointer to a container of the given type. + * @param as_const Const opaque pointer fallback. + * @return An iterator that is past the last element of the container. + */ + static iterator end(const meta_ctx &area, void *container, const void *as_const) { + return container ? iterator{area, static_cast<Type *>(container)->end()} + : iterator{area, static_cast<const Type *>(as_const)->end()}; + } + + /** + * @brief Assigns one element to a container and constructs its object from + * a given opaque instance. + * @param area The context to pass to the newly created iterator. + * @param container Opaque pointer to a container of the given type. + * @param value Optional opaque instance of the object to construct (as + * value type). + * @param cref Optional opaque instance of the object to construct (as + * decayed const reference type). + * @param it Iterator before which the element will be inserted. + * @return A possibly invalid iterator to the inserted element. + */ + [[nodiscard]] static iterator insert([[maybe_unused]] const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const void *value, [[maybe_unused]] const void *cref, [[maybe_unused]] const iterator &it) { + if constexpr(fixed_size) { + return iterator{}; + } else { + auto *const non_const = any_cast<typename Type::iterator>(&it.base()); + return {area, static_cast<Type *>(container)->insert( + non_const ? *non_const : any_cast<const typename Type::const_iterator &>(it.base()), + value ? *static_cast<const typename Type::value_type *>(value) : *static_cast<const std::remove_reference_t<typename Type::const_reference> *>(cref))}; + } + } + + /** + * @brief Erases an element from a container. + * @param area The context to pass to the newly created iterator. + * @param container Opaque pointer to a container of the given type. + * @param it An opaque iterator to the element to erase. + * @return A possibly invalid iterator following the last removed element. + */ + [[nodiscard]] static iterator erase([[maybe_unused]] const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const iterator &it) { + if constexpr(fixed_size) { + return iterator{}; + } else { + auto *const non_const = any_cast<typename Type::iterator>(&it.base()); + return {area, static_cast<Type *>(container)->erase(non_const ? *non_const : any_cast<const typename Type::const_iterator &>(it.base()))}; + } + } +}; + +/** + * @brief General purpose implementation of meta associative container traits. + * @tparam Type Type of underlying associative container. + */ +template<typename Type> +struct basic_meta_associative_container_traits { + static_assert(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>, "Unexpected type"); + + /*! @brief Unsigned integer type. */ + using size_type = typename meta_associative_container::size_type; + /*! @brief Meta iterator type. */ + using iterator = typename meta_associative_container::iterator; + + /*! @brief True in case of key-only containers, false otherwise. */ + static constexpr bool key_only = internal::key_only_associative_container_v<Type>; + + /** + * @brief Returns the number of elements in a container. + * @param container Opaque pointer to a container of the given type. + * @return Number of elements. + */ + [[nodiscard]] static size_type size(const void *container) { + return static_cast<const Type *>(container)->size(); + } + + /** + * @brief Clears a container. + * @param container Opaque pointer to a container of the given type. + * @return True in case of success, false otherwise. + */ + [[nodiscard]] static bool clear(void *container) { + static_cast<Type *>(container)->clear(); + return true; + } + + /** + * @brief Increases the capacity of a container. + * @param container Opaque pointer to a container of the given type. + * @param sz Desired capacity. + * @return True in case of success, false otherwise. + */ + [[nodiscard]] static bool reserve([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) { + if constexpr(internal::reserve_aware_container_v<Type>) { + static_cast<Type *>(container)->reserve(sz); + return true; + } else { + return false; + } + } + + /** + * @brief Returns a possibly const iterator to the beginning. + * @param area The context to pass to the newly created iterator. + * @param container Opaque pointer to a container of the given type. + * @param as_const Const opaque pointer fallback. + * @return An iterator to the first element of the container. + */ + static iterator begin(const meta_ctx &area, void *container, const void *as_const) { + return container ? iterator{area, std::bool_constant<key_only>{}, static_cast<Type *>(container)->begin()} + : iterator{area, std::bool_constant<key_only>{}, static_cast<const Type *>(as_const)->begin()}; + } + + /** + * @brief Returns a possibly const iterator to the end. + * @param area The context to pass to the newly created iterator. + * @param container Opaque pointer to a container of the given type. + * @param as_const Const opaque pointer fallback. + * @return An iterator that is past the last element of the container. + */ + static iterator end(const meta_ctx &area, void *container, const void *as_const) { + return container ? iterator{area, std::bool_constant<key_only>{}, static_cast<Type *>(container)->end()} + : iterator{area, std::bool_constant<key_only>{}, static_cast<const Type *>(as_const)->end()}; + } + + /** + * @brief Inserts an element into a container, if the key does not exist. + * @param container Opaque pointer to a container of the given type. + * @param key An opaque key value of an element to insert. + * @param value Optional opaque value to insert (key-value containers). + * @return True if the insertion took place, false otherwise. + */ + [[nodiscard]] static bool insert(void *container, const void *key, [[maybe_unused]] const void *value) { + if constexpr(key_only) { + return static_cast<Type *>(container)->insert(*static_cast<const typename Type::key_type *>(key)).second; + } else { + return static_cast<Type *>(container)->emplace(*static_cast<const typename Type::key_type *>(key), *static_cast<const typename Type::mapped_type *>(value)).second; + } + } + + /** + * @brief Removes an element from a container. + * @param container Opaque pointer to a container of the given type. + * @param key An opaque key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + [[nodiscard]] static size_type erase(void *container, const void *key) { + return static_cast<Type *>(container)->erase(*static_cast<const typename Type::key_type *>(key)); + } + + /** + * @brief Finds an element with a given key. + * @param area The context to pass to the newly created iterator. + * @param container Opaque pointer to a container of the given type. + * @param as_const Const opaque pointer fallback. + * @param key Opaque key value of an element to search for. + * @return An iterator to the element with the given key, if any. + */ + static iterator find(const meta_ctx &area, void *container, const void *as_const, const void *key) { + return container ? iterator{area, std::bool_constant<key_only>{}, static_cast<Type *>(container)->find(*static_cast<const typename Type::key_type *>(key))} + : iterator{area, std::bool_constant<key_only>{}, static_cast<const Type *>(as_const)->find(*static_cast<const typename Type::key_type *>(key))}; + } +}; + +/** + * @brief Meta sequence container traits for `std::vector`s of any type. + * @tparam Args Template arguments for the container. + */ +template<typename... Args> +struct meta_sequence_container_traits<std::vector<Args...>> + : basic_meta_sequence_container_traits<std::vector<Args...>> {}; + +/** + * @brief Meta sequence container traits for `std::array`s of any type. + * @tparam Type Template arguments for the container. + * @tparam N Template arguments for the container. + */ +template<typename Type, auto N> +struct meta_sequence_container_traits<std::array<Type, N>> + : basic_meta_sequence_container_traits<std::array<Type, N>> {}; + +/** + * @brief Meta sequence container traits for `std::list`s of any type. + * @tparam Args Template arguments for the container. + */ +template<typename... Args> +struct meta_sequence_container_traits<std::list<Args...>> + : basic_meta_sequence_container_traits<std::list<Args...>> {}; + +/** + * @brief Meta sequence container traits for `std::deque`s of any type. + * @tparam Args Template arguments for the container. + */ +template<typename... Args> +struct meta_sequence_container_traits<std::deque<Args...>> + : basic_meta_sequence_container_traits<std::deque<Args...>> {}; + +/** + * @brief Meta associative container traits for `std::map`s of any type. + * @tparam Args Template arguments for the container. + */ +template<typename... Args> +struct meta_associative_container_traits<std::map<Args...>> + : basic_meta_associative_container_traits<std::map<Args...>> {}; + +/** + * @brief Meta associative container traits for `std::unordered_map`s of any + * type. + * @tparam Args Template arguments for the container. + */ +template<typename... Args> +struct meta_associative_container_traits<std::unordered_map<Args...>> + : basic_meta_associative_container_traits<std::unordered_map<Args...>> {}; + +/** + * @brief Meta associative container traits for `std::set`s of any type. + * @tparam Args Template arguments for the container. + */ +template<typename... Args> +struct meta_associative_container_traits<std::set<Args...>> + : basic_meta_associative_container_traits<std::set<Args...>> {}; + +/** + * @brief Meta associative container traits for `std::unordered_set`s of any + * type. + * @tparam Args Template arguments for the container. + */ +template<typename... Args> +struct meta_associative_container_traits<std::unordered_set<Args...>> + : basic_meta_associative_container_traits<std::unordered_set<Args...>> {}; + +/** + * @brief Meta associative container traits for `dense_map`s of any type. + * @tparam Args Template arguments for the container. + */ +template<typename... Args> +struct meta_associative_container_traits<dense_map<Args...>> + : basic_meta_associative_container_traits<dense_map<Args...>> {}; + +/** + * @brief Meta associative container traits for `dense_set`s of any type. + * @tparam Args Template arguments for the container. + */ +template<typename... Args> +struct meta_associative_container_traits<dense_set<Args...>> + : basic_meta_associative_container_traits<dense_set<Args...>> {}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/meta/context.hpp b/external/entt/include/entt/meta/context.hpp new file mode 100644 index 0000000..cb26a72 --- /dev/null +++ b/external/entt/include/entt/meta/context.hpp @@ -0,0 +1,51 @@ +#ifndef ENTT_META_CTX_HPP +#define ENTT_META_CTX_HPP + +#include "../container/dense_map.hpp" +#include "../core/fwd.hpp" +#include "../core/utility.hpp" + +namespace entt { + +class meta_ctx; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct meta_type_node; + +struct meta_context { + dense_map<id_type, meta_type_node, identity> value{}; + + [[nodiscard]] inline static meta_context &from(meta_ctx &ctx); + [[nodiscard]] inline static const meta_context &from(const meta_ctx &ctx); +}; + +} // namespace internal +/*! @endcond */ + +/*! @brief Disambiguation tag for constructors and the like. */ +class meta_ctx_arg_t final {}; + +/*! @brief Constant of type meta_context_arg_t used to disambiguate calls. */ +inline constexpr meta_ctx_arg_t meta_ctx_arg{}; + +/*! @brief Opaque meta context type. */ +class meta_ctx: private internal::meta_context { + // attorney idiom like model to access the base class + friend struct internal::meta_context; +}; + +/*! @cond TURN_OFF_DOXYGEN */ +[[nodiscard]] inline internal::meta_context &internal::meta_context::from(meta_ctx &ctx) { + return ctx; +} + +[[nodiscard]] inline const internal::meta_context &internal::meta_context::from(const meta_ctx &ctx) { + return ctx; +} +/*! @endcond */ + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/meta/factory.hpp b/external/entt/include/entt/meta/factory.hpp new file mode 100644 index 0000000..1732d25 --- /dev/null +++ b/external/entt/include/entt/meta/factory.hpp @@ -0,0 +1,668 @@ +#ifndef ENTT_META_FACTORY_HPP +#define ENTT_META_FACTORY_HPP + +#include <cstddef> +#include <cstdint> +#include <functional> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> +#include "../config/config.h" +#include "../core/bit.hpp" +#include "../core/fwd.hpp" +#include "../core/type_info.hpp" +#include "../core/type_traits.hpp" +#include "../locator/locator.hpp" +#include "context.hpp" +#include "meta.hpp" +#include "node.hpp" +#include "policy.hpp" +#include "range.hpp" +#include "resolve.hpp" +#include "utility.hpp" + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +class basic_meta_factory { + using invoke_type = std::remove_pointer_t<decltype(meta_func_node::invoke)>; + + auto *find_overload() { + auto *curr = &details->func[bucket]; + + while(curr->invoke != invoke) { + curr = curr->next.get(); + } + + return curr; + } + +protected: + void type(const id_type id) noexcept { + auto &&elem = meta_context::from(*ctx).value[parent]; + ENTT_ASSERT(elem.id == id || !resolve(*ctx, id), "Duplicate identifier"); + invoke = nullptr; + bucket = parent; + elem.id = id; + } + + void base(const id_type id, meta_base_node node) { + details->base.insert_or_assign(id, node); + invoke = nullptr; + bucket = parent; + } + + void conv(const id_type id, meta_conv_node node) { + details->conv.insert_or_assign(id, node); + invoke = nullptr; + bucket = parent; + } + + void ctor(const id_type id, meta_ctor_node node) { + details->ctor.insert_or_assign(id, node); + invoke = nullptr; + bucket = parent; + } + + void dtor(meta_dtor_node node) { + meta_context::from(*ctx).value[parent].dtor = node; + invoke = nullptr; + bucket = parent; + } + + void data(const id_type id, meta_data_node node) { + if(auto it = details->data.find(id); it == details->data.end()) { + details->data.insert_or_assign(id, std::move(node)); + } else if(it->second.set != node.set || it->second.get != node.get) { + it->second = std::move(node); + } + + invoke = nullptr; + bucket = id; + } + + void func(const id_type id, meta_func_node node) { + if(auto it = details->func.find(id); it == details->func.end()) { + auto &&elem = details->func.insert_or_assign(id, std::move(node)).first; + invoke = elem->second.invoke; + bucket = id; + } else { + auto *curr = &it->second; + + while(curr->invoke != node.invoke && curr->next) { + curr = curr->next.get(); + } + + if(curr->invoke == node.invoke) { + invoke = curr->invoke; + } else { + invoke = node.invoke; + curr->next = std::make_shared<meta_func_node>(); + *curr->next = std::move(node); + } + + bucket = id; + } + } + + void prop(const id_type key, meta_prop_node value) { + if(bucket == parent) { + details->prop[key] = std::move(value); + } else if(invoke == nullptr) { + details->data[bucket].prop[key] = std::move(value); + } else { + find_overload()->prop[key] = std::move(value); + } + } + + void traits(const meta_traits value) { + if(bucket == parent) { + meta_context::from(*ctx).value[bucket].traits |= value; + } else if(invoke == nullptr) { + details->data[bucket].traits |= value; + } else { + find_overload()->traits |= value; + } + } + + void custom(meta_custom_node node) { + if(bucket == parent) { + meta_context::from(*ctx).value[bucket].custom = std::move(node); + } else if(invoke == nullptr) { + details->data[bucket].custom = std::move(node); + } else { + find_overload()->custom = std::move(node); + } + } + +public: + basic_meta_factory(const id_type id, meta_ctx &area) + : ctx{&area}, + parent{id}, + bucket{id} { + auto &&elem = meta_context::from(*ctx).value[parent]; + + if(!elem.details) { + elem.details = std::make_shared<meta_type_descriptor>(); + } + + details = elem.details.get(); + } + +private: + meta_ctx *ctx{}; + id_type parent{}; + id_type bucket{}; + invoke_type *invoke{}; + meta_type_descriptor *details{}; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Meta factory to be used for reflection purposes. + * @tparam Type Reflected type for which the factory was created. + */ +template<typename Type> +class meta_factory: private internal::basic_meta_factory { + using base_type = internal::basic_meta_factory; + + template<typename Setter, auto Getter, typename Policy, std::size_t... Index> + void data(const id_type id, std::index_sequence<Index...>) noexcept { + using data_type = std::invoke_result_t<decltype(Getter), Type &>; + using args_type = type_list<typename meta_function_helper_t<Type, decltype(value_list_element_v<Index, Setter>)>::args_type...>; + static_assert(Policy::template value<data_type>, "Invalid return type for the given policy"); + + base_type::data( + id, + internal::meta_data_node{ + /* this is never static */ + (std::is_member_object_pointer_v<decltype(value_list_element_v<Index, Setter>)> && ... && std::is_const_v<std::remove_reference_t<data_type>>) ? internal::meta_traits::is_const : internal::meta_traits::is_none, + Setter::size, + &internal::resolve<std::remove_cv_t<std::remove_reference_t<data_type>>>, + &meta_arg<type_list<type_list_element_t<type_list_element_t<Index, args_type>::size != 1u, type_list_element_t<Index, args_type>>...>>, + +[](meta_handle instance, meta_any value) { return (meta_setter<Type, value_list_element_v<Index, Setter>>(*instance.operator->(), value.as_ref()) || ...); }, + &meta_getter<Type, Getter, Policy>}); + } + +public: + /*! @brief Default constructor. */ + meta_factory() noexcept + : internal::basic_meta_factory{type_id<Type>(), locator<meta_ctx>::value_or()} {} + + /** + * @brief Context aware constructor. + * @param area The context into which to construct meta types. + */ + meta_factory(meta_ctx &area) noexcept + : internal::basic_meta_factory{type_id<Type>().hash(), area} {} + + /** + * @brief Assigns a custom unique identifier to a meta type. + * @param id A custom unique identifier. + * @return A meta factory for the given type. + */ + meta_factory type(const id_type id) noexcept { + base_type::type(id); + return *this; + } + + /** + * @brief Assigns a meta base to a meta type. + * + * A reflected base class must be a real base class of the reflected type. + * + * @tparam Base Type of the base class to assign to the meta type. + * @return A meta factory for the parent type. + */ + template<typename Base> + meta_factory base() noexcept { + static_assert(!std::is_same_v<Type, Base> && std::is_base_of_v<Base, Type>, "Invalid base type"); + auto *const op = +[](const void *instance) noexcept { return static_cast<const void *>(static_cast<const Base *>(static_cast<const Type *>(instance))); }; + base_type::base(type_id<Base>().hash(), internal::meta_base_node{&internal::resolve<Base>, op}); + return *this; + } + + /** + * @brief Assigns a meta conversion function to a meta type. + * + * Conversion functions can be either free functions or member + * functions.<br/> + * 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 Candidate> + auto conv() noexcept { + using conv_type = std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(Candidate), Type &>>>; + auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, std::invoke(Candidate, *static_cast<const Type *>(instance))); }; + base_type::conv(type_id<conv_type>().hash(), internal::meta_conv_node{op}); + return *this; + } + + /** + * @brief Assigns a meta conversion function to a meta type. + * + * The given type must be such that an instance of the reflected type can be + * converted to it. + * + * @tparam To Type of the conversion function to assign to the meta type. + * @return A meta factory for the parent type. + */ + template<typename To> + meta_factory conv() noexcept { + using conv_type = std::remove_cv_t<std::remove_reference_t<To>>; + auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, static_cast<To>(*static_cast<const Type *>(instance))); }; + base_type::conv(type_id<conv_type>().hash(), internal::meta_conv_node{op}); + return *this; + } + + /** + * @brief Assigns a meta constructor to a meta type. + * + * Both member functions and free function can be assigned to meta types in + * the role of constructors. All that is required is that they return an + * instance of the underlying type.<br/> + * From a client's point of view, nothing changes if a constructor of a meta + * type is a built-in one or not. + * + * @tparam Candidate The actual function to use as a constructor. + * @tparam Policy Optional policy (no policy set by default). + * @return A meta factory for the parent type. + */ + template<auto Candidate, typename Policy = as_is_t> + meta_factory ctor() noexcept { + using descriptor = meta_function_helper_t<Type, decltype(Candidate)>; + static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy"); + static_assert(std::is_same_v<std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>, Type>, "The function doesn't return an object of the required type"); + base_type::ctor(type_id<typename descriptor::args_type>().hash(), internal::meta_ctor_node{descriptor::args_type::size, &meta_arg<typename descriptor::args_type>, &meta_construct<Type, Candidate, Policy>}); + return *this; + } + + /** + * @brief Assigns a meta constructor to a meta type. + * + * A meta constructor is uniquely identified by the types of its arguments + * and is such that there exists an actual constructor of the underlying + * type that can be invoked with parameters whose types are those given. + * + * @tparam Args Types of arguments to use to construct an instance. + * @return A meta factory for the parent type. + */ + template<typename... Args> + meta_factory ctor() noexcept { + // default constructor is already implicitly generated, no need for redundancy + if constexpr(sizeof...(Args) != 0u) { + using descriptor = meta_function_helper_t<Type, Type (*)(Args...)>; + base_type::ctor(type_id<typename descriptor::args_type>().hash(), internal::meta_ctor_node{descriptor::args_type::size, &meta_arg<typename descriptor::args_type>, &meta_construct<Type, Args...>}); + } + + return *this; + } + + /** + * @brief Assigns a meta destructor to a meta type. + * + * Both free functions and member functions can be assigned to meta types in + * the role of destructors.<br/> + * The signature of a free function should be identical to the following: + * + * @code{.cpp} + * void(Type &); + * @endcode + * + * Member functions should not take arguments instead.<br/> + * 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 Func> + meta_factory dtor() noexcept { + static_assert(std::is_invocable_v<decltype(Func), Type &>, "The function doesn't accept an object of the type provided"); + auto *const op = +[](void *instance) { std::invoke(Func, *static_cast<Type *>(instance)); }; + base_type::dtor(internal::meta_dtor_node{op}); + return *this; + } + + /** + * @brief Assigns a meta data to a meta type. + * + * Both data members and static and global variables, as well as constants + * of any kind, can be assigned to a meta type.<br/> + * From a client's point of view, all the variables associated with the + * reflected object will appear as if they were part of the type itself. + * + * @tparam Data The actual variable to attach to the meta type. + * @tparam Policy Optional policy (no policy set by default). + * @param id Unique identifier. + * @return A meta factory for the parent type. + */ + template<auto Data, typename Policy = as_is_t> + meta_factory data(const id_type id) noexcept { + if constexpr(std::is_member_object_pointer_v<decltype(Data)>) { + using data_type = std::invoke_result_t<decltype(Data), Type &>; + static_assert(Policy::template value<data_type>, "Invalid return type for the given policy"); + + base_type::data( + id, + internal::meta_data_node{ + /* this is never static */ + std::is_const_v<std::remove_reference_t<data_type>> ? internal::meta_traits::is_const : internal::meta_traits::is_none, + 1u, + &internal::resolve<std::remove_cv_t<std::remove_reference_t<data_type>>>, + &meta_arg<type_list<std::remove_cv_t<std::remove_reference_t<data_type>>>>, + &meta_setter<Type, Data>, + &meta_getter<Type, Data, Policy>}); + } else { + using data_type = std::remove_pointer_t<decltype(Data)>; + + if constexpr(std::is_pointer_v<decltype(Data)>) { + static_assert(Policy::template value<decltype(*Data)>, "Invalid return type for the given policy"); + } else { + static_assert(Policy::template value<data_type>, "Invalid return type for the given policy"); + } + + base_type::data( + id, + internal::meta_data_node{ + ((std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<data_type>>> || std::is_const_v<std::remove_reference_t<data_type>>) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static, + 1u, + &internal::resolve<std::remove_cv_t<std::remove_reference_t<data_type>>>, + &meta_arg<type_list<std::remove_cv_t<std::remove_reference_t<data_type>>>>, + &meta_setter<Type, Data>, + &meta_getter<Type, Data, Policy>}); + } + + return *this; + } + + /** + * @brief Assigns a meta data to a meta type by means of its setter and + * getter. + * + * Setters and getters can be either free functions, member functions or a + * mix of them.<br/> + * 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.<br/> + * In case of member functions, getters have no arguments at all, while + * setters has an argument of a type convertible to that of the parameter to + * set. + * + * @tparam Setter The actual function to use as a setter. + * @tparam Getter The actual function to use as a getter. + * @tparam Policy Optional policy (no policy set by default). + * @param id Unique identifier. + * @return A meta factory for the parent type. + */ + template<auto Setter, auto Getter, typename Policy = as_is_t> + meta_factory data(const id_type id) noexcept { + using data_type = std::invoke_result_t<decltype(Getter), Type &>; + static_assert(Policy::template value<data_type>, "Invalid return type for the given policy"); + + if constexpr(std::is_same_v<decltype(Setter), std::nullptr_t>) { + base_type::data( + id, + internal::meta_data_node{ + /* this is never static */ + internal::meta_traits::is_const, + 0u, + &internal::resolve<std::remove_cv_t<std::remove_reference_t<data_type>>>, + &meta_arg<type_list<>>, + &meta_setter<Type, Setter>, + &meta_getter<Type, Getter, Policy>}); + } else { + using args_type = typename meta_function_helper_t<Type, decltype(Setter)>::args_type; + + base_type::data( + id, + internal::meta_data_node{ + /* this is never static nor const */ + internal::meta_traits::is_none, + 1u, + &internal::resolve<std::remove_cv_t<std::remove_reference_t<data_type>>>, + &meta_arg<type_list<type_list_element_t<args_type::size != 1u, args_type>>>, + &meta_setter<Type, Setter>, + &meta_getter<Type, Getter, Policy>}); + } + + return *this; + } + + /** + * @brief Assigns a meta data to a meta type by means of its setters and + * getter. + * + * Multi-setter support for meta data members. All setters are tried in the + * order of definition before returning to the caller.<br/> + * Setters can be either free functions, member functions or a mix of them + * and are provided via a `value_list` type. + * + * @sa data + * + * @tparam Setter The actual functions to use as setters. + * @tparam Getter The actual getter function. + * @tparam Policy Optional policy (no policy set by default). + * @param id Unique identifier. + * @return A meta factory for the parent type. + */ + template<typename Setter, auto Getter, typename Policy = as_is_t> + meta_factory data(const id_type id) noexcept { + data<Setter, Getter, Policy>(id, std::make_index_sequence<Setter::size>{}); + return *this; + } + + /** + * @brief Assigns a meta function to a meta type. + * + * Both member functions and free functions can be assigned to a meta + * type.<br/> + * From a client's point of view, all the functions associated with the + * reflected object will appear as if they were part of the type itself. + * + * @tparam Candidate The actual function to attach to the meta type. + * @tparam Policy Optional policy (no policy set by default). + * @param id Unique identifier. + * @return A meta factory for the parent type. + */ + template<auto Candidate, typename Policy = as_is_t> + meta_factory func(const id_type id) noexcept { + using descriptor = meta_function_helper_t<Type, decltype(Candidate)>; + static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy"); + + base_type::func( + id, + internal::meta_func_node{ + (descriptor::is_const ? internal::meta_traits::is_const : internal::meta_traits::is_none) | (descriptor::is_static ? internal::meta_traits::is_static : internal::meta_traits::is_none), + descriptor::args_type::size, + &internal::resolve<std::conditional_t<std::is_same_v<Policy, as_void_t>, void, std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>>>, + &meta_arg<typename descriptor::args_type>, + &meta_invoke<Type, Candidate, Policy>}); + + return *this; + } + + /** + * @brief Assigns a property to the last created meta object. + * + * Both the key and the value (if any) must be at least copy constructible. + * + * @tparam Value Optional type of the property value. + * @param id Property key. + * @param value Optional property value. + * @return A meta factory for the parent type. + */ + template<typename... Value> + meta_factory prop(id_type id, [[maybe_unused]] Value &&...value) { + if constexpr(sizeof...(Value) == 0u) { + base_type::prop(id, internal::meta_prop_node{&internal::resolve<void>}); + } else { + base_type::prop(id, internal::meta_prop_node{&internal::resolve<std::decay_t<Value>>..., std::make_shared<std::decay_t<Value>>(std::forward<Value>(value))...}); + } + + return *this; + } + + /** + * @brief Sets traits on the last created meta object. + * + * The assigned value must be an enum and intended as a bitmask. + * + * @tparam Value Type of the traits value. + * @param value Traits value. + * @return A meta factory for the parent type. + */ + template<typename Value> + meta_factory traits(const Value value) { + static_assert(std::is_enum_v<Value>, "Invalid enum type"); + base_type::traits(internal::user_to_meta_traits(value)); + return *this; + } + + /** + * @brief Sets user defined data that will never be used by the library. + * @tparam Value Type of user defined data to store. + * @tparam Args Types of arguments to use to construct the user data. + * @param args Parameters to use to initialize the user data. + * @return A meta factory for the parent type. + */ + template<typename Value, typename... Args> + meta_factory custom(Args &&...args) { + base_type::custom(internal::meta_custom_node{type_id<Value>().hash(), std::make_shared<Value>(std::forward<Args>(args)...)}); + return *this; + } +}; + +/** + * @brief Utility function to use for reflection. + * + * This is the point from which everything starts.<br/> + * By invoking this function with a type that is not yet reflected, a meta type + * is created to which it will be possible to attach meta objects through a + * dedicated factory. + * + * @tparam Type Type to reflect. + * @param ctx The context into which to construct meta types. + * @return A meta factory for the given type. + */ +template<typename Type> +[[nodiscard]] auto meta(meta_ctx &ctx) noexcept { + auto &&context = internal::meta_context::from(ctx); + // make sure the type exists in the context before returning a factory + context.value.try_emplace(type_id<Type>().hash(), internal::resolve<Type>(context)); + return meta_factory<Type>{ctx}; +} + +/** + * @brief Utility function to use for reflection. + * + * This is the point from which everything starts.<br/> + * 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<typename Type> +[[nodiscard]] auto meta() noexcept { + return meta<Type>(locator<meta_ctx>::value_or()); +} + +/** + * @brief Resets a type and all its parts. + * + * Resets a type and all its data members, member functions and properties, as + * well as its constructors, destructors and conversion functions if any.<br/> + * Base classes aren't reset but the link between the two types is removed. + * + * The type is also removed from the set of searchable types. + * + * @param id Unique identifier. + * @param ctx The context from which to reset meta types. + */ +inline void meta_reset(meta_ctx &ctx, const id_type id) noexcept { + auto &&context = internal::meta_context::from(ctx); + + for(auto it = context.value.begin(); it != context.value.end();) { + if(it->second.id == id) { + it = context.value.erase(it); + } else { + ++it; + } + } +} + +/** + * @brief Resets a type and all its parts. + * + * Resets a type and all its data members, member functions and properties, as + * well as its constructors, destructors and conversion functions if any.<br/> + * Base classes aren't reset but the link between the two types is removed. + * + * The type is also removed from the set of searchable types. + * + * @param id Unique identifier. + */ +inline void meta_reset(const id_type id) noexcept { + meta_reset(locator<meta_ctx>::value_or(), id); +} + +/** + * @brief Resets a type and all its parts. + * + * @sa meta_reset + * + * @tparam Type Type to reset. + * @param ctx The context from which to reset meta types. + */ +template<typename Type> +void meta_reset(meta_ctx &ctx) noexcept { + internal::meta_context::from(ctx).value.erase(type_id<Type>().hash()); +} + +/** + * @brief Resets a type and all its parts. + * + * @sa meta_reset + * + * @tparam Type Type to reset. + */ +template<typename Type> +void meta_reset() noexcept { + meta_reset<Type>(locator<meta_ctx>::value_or()); +} + +/** + * @brief Resets all meta types. + * + * @sa meta_reset + * + * @param ctx The context from which to reset meta types. + */ +inline void meta_reset(meta_ctx &ctx) noexcept { + internal::meta_context::from(ctx).value.clear(); +} + +/** + * @brief Resets all meta types. + * + * @sa meta_reset + */ +inline void meta_reset() noexcept { + meta_reset(locator<meta_ctx>::value_or()); +} + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/meta/fwd.hpp b/external/entt/include/entt/meta/fwd.hpp new file mode 100644 index 0000000..09d1051 --- /dev/null +++ b/external/entt/include/entt/meta/fwd.hpp @@ -0,0 +1,26 @@ +#ifndef ENTT_META_FWD_HPP +#define ENTT_META_FWD_HPP + +namespace entt { + +class meta_sequence_container; + +class meta_associative_container; + +class meta_any; + +struct meta_handle; + +struct meta_prop; + +struct meta_custom; + +struct meta_data; + +struct meta_func; + +class meta_type; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/meta/meta.hpp b/external/entt/include/entt/meta/meta.hpp new file mode 100644 index 0000000..b7b120a --- /dev/null +++ b/external/entt/include/entt/meta/meta.hpp @@ -0,0 +1,2026 @@ +#ifndef ENTT_META_META_HPP +#define ENTT_META_META_HPP + +#include <array> +#include <cstddef> +#include <iterator> +#include <memory> +#include <type_traits> +#include <utility> +#include "../config/config.h" +#include "../core/any.hpp" +#include "../core/fwd.hpp" +#include "../core/iterator.hpp" +#include "../core/type_info.hpp" +#include "../core/type_traits.hpp" +#include "../core/utility.hpp" +#include "../locator/locator.hpp" +#include "adl_pointer.hpp" +#include "context.hpp" +#include "fwd.hpp" +#include "node.hpp" +#include "range.hpp" +#include "type_traits.hpp" + +namespace entt { + +class meta_any; +class meta_type; + +/*! @brief Proxy object for sequence containers. */ +class meta_sequence_container { + class meta_iterator; + +public: + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Meta iterator type. */ + using iterator = meta_iterator; + + /*! @brief Default constructor. */ + meta_sequence_container() = default; + + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_sequence_container(const meta_ctx &area) noexcept + : ctx{&area} {} + + /** + * @brief Rebinds a proxy object to a sequence container type. + * @tparam Type Type of container to wrap. + * @param instance The container to wrap. + */ + template<typename Type> + void rebind(Type &instance) noexcept { + value_type_node = &internal::resolve<typename Type::value_type>; + const_reference_node = &internal::resolve<std::remove_const_t<std::remove_reference_t<typename Type::const_reference>>>; + size_fn = meta_sequence_container_traits<std::remove_const_t<Type>>::size; + clear_fn = meta_sequence_container_traits<std::remove_const_t<Type>>::clear; + reserve_fn = meta_sequence_container_traits<std::remove_const_t<Type>>::reserve; + resize_fn = meta_sequence_container_traits<std::remove_const_t<Type>>::resize; + begin_fn = meta_sequence_container_traits<std::remove_const_t<Type>>::begin; + end_fn = meta_sequence_container_traits<std::remove_const_t<Type>>::end; + insert_fn = meta_sequence_container_traits<std::remove_const_t<Type>>::insert; + erase_fn = meta_sequence_container_traits<std::remove_const_t<Type>>::erase; + const_only = std::is_const_v<Type>; + data = &instance; + } + + [[nodiscard]] inline meta_type value_type() const noexcept; + [[nodiscard]] inline size_type size() const noexcept; + inline bool resize(const size_type); + inline bool clear(); + inline bool reserve(const size_type); + [[nodiscard]] inline iterator begin(); + [[nodiscard]] inline iterator end(); + inline iterator insert(const iterator &, meta_any); + inline iterator erase(const iterator &); + [[nodiscard]] inline meta_any operator[](const size_type); + [[nodiscard]] inline explicit operator bool() const noexcept; + +private: + const meta_ctx *ctx{&locator<meta_ctx>::value_or()}; + internal::meta_type_node (*value_type_node)(const internal::meta_context &){}; + internal::meta_type_node (*const_reference_node)(const internal::meta_context &){}; + size_type (*size_fn)(const void *){}; + bool (*clear_fn)(void *){}; + bool (*reserve_fn)(void *, const size_type){}; + bool (*resize_fn)(void *, const size_type){}; + iterator (*begin_fn)(const meta_ctx &, void *, const void *){}; + iterator (*end_fn)(const meta_ctx &, void *, const void *){}; + iterator (*insert_fn)(const meta_ctx &, void *, const void *, const void *, const iterator &){}; + iterator (*erase_fn)(const meta_ctx &, void *, const iterator &){}; + const void *data{}; + bool const_only{}; +}; + +/*! @brief Proxy object for associative containers. */ +class meta_associative_container { + class meta_iterator; + +public: + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Meta iterator type. */ + using iterator = meta_iterator; + + /*! @brief Default constructor. */ + meta_associative_container() = default; + + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_associative_container(const meta_ctx &area) noexcept + : ctx{&area} {} + + /** + * @brief Rebinds a proxy object to an associative container type. + * @tparam Type Type of container to wrap. + * @param instance The container to wrap. + */ + template<typename Type> + void rebind(Type &instance) noexcept { + key_type_node = &internal::resolve<typename Type::key_type>; + value_type_node = &internal::resolve<typename Type::value_type>; + + if constexpr(!meta_associative_container_traits<std::remove_const_t<Type>>::key_only) { + mapped_type_node = &internal::resolve<typename Type::mapped_type>; + } + + size_fn = &meta_associative_container_traits<std::remove_const_t<Type>>::size; + clear_fn = &meta_associative_container_traits<std::remove_const_t<Type>>::clear; + reserve_fn = &meta_associative_container_traits<std::remove_const_t<Type>>::reserve; + begin_fn = &meta_associative_container_traits<std::remove_const_t<Type>>::begin; + end_fn = &meta_associative_container_traits<std::remove_const_t<Type>>::end; + insert_fn = &meta_associative_container_traits<std::remove_const_t<Type>>::insert; + erase_fn = &meta_associative_container_traits<std::remove_const_t<Type>>::erase; + find_fn = &meta_associative_container_traits<std::remove_const_t<Type>>::find; + const_only = std::is_const_v<Type>; + data = &instance; + } + + [[nodiscard]] inline meta_type key_type() const noexcept; + [[nodiscard]] inline meta_type mapped_type() const noexcept; + [[nodiscard]] inline meta_type value_type() const noexcept; + [[nodiscard]] inline size_type size() const noexcept; + inline bool clear(); + inline bool reserve(const size_type); + [[nodiscard]] inline iterator begin(); + [[nodiscard]] inline iterator end(); + inline bool insert(meta_any, meta_any); + inline size_type erase(meta_any); + [[nodiscard]] inline iterator find(meta_any); + [[nodiscard]] inline explicit operator bool() const noexcept; + +private: + const meta_ctx *ctx{&locator<meta_ctx>::value_or()}; + internal::meta_type_node (*key_type_node)(const internal::meta_context &){}; + internal::meta_type_node (*mapped_type_node)(const internal::meta_context &){}; + internal::meta_type_node (*value_type_node)(const internal::meta_context &){}; + size_type (*size_fn)(const void *){}; + bool (*clear_fn)(void *){}; + bool (*reserve_fn)(void *, const size_type){}; + iterator (*begin_fn)(const meta_ctx &, void *, const void *){}; + iterator (*end_fn)(const meta_ctx &, void *, const void *){}; + bool (*insert_fn)(void *, const void *, const void *){}; + size_type (*erase_fn)(void *, const void *){}; + iterator (*find_fn)(const meta_ctx &, void *, const void *, const void *){}; + const void *data{}; + bool const_only{}; +}; + +/*! @brief Possible modes of a meta any object. */ +using meta_any_policy = any_policy; + +/*! @brief Opaque wrapper for values of any type. */ +class meta_any { + using vtable_type = void(const internal::meta_traits op, const bool, const void *, void *); + + template<typename Type> + static std::enable_if_t<std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>> basic_vtable([[maybe_unused]] const internal::meta_traits req, [[maybe_unused]] const bool const_only, [[maybe_unused]] const void *value, [[maybe_unused]] void *other) { + if constexpr(is_meta_pointer_like_v<Type>) { + if(req == internal::meta_traits::is_meta_pointer_like) { + if constexpr(std::is_function_v<typename std::pointer_traits<Type>::element_type>) { + static_cast<meta_any *>(other)->emplace<Type>(*static_cast<const Type *>(value)); + } else if constexpr(!std::is_void_v<std::remove_const_t<typename std::pointer_traits<Type>::element_type>>) { + using in_place_type = decltype(adl_meta_pointer_like<Type>::dereference(*static_cast<const Type *>(value))); + + if constexpr(std::is_constructible_v<bool, Type>) { + if(const auto &pointer_like = *static_cast<const Type *>(value); pointer_like) { + static_cast<meta_any *>(other)->emplace<in_place_type>(adl_meta_pointer_like<Type>::dereference(pointer_like)); + } + } else { + static_cast<meta_any *>(other)->emplace<in_place_type>(adl_meta_pointer_like<Type>::dereference(*static_cast<const Type *>(value))); + } + } + } + } + + if constexpr(is_complete_v<meta_sequence_container_traits<Type>>) { + if(req == internal::meta_traits::is_meta_sequence_container) { + const_only ? static_cast<meta_sequence_container *>(other)->rebind(*static_cast<const Type *>(value)) : static_cast<meta_sequence_container *>(other)->rebind(*static_cast<Type *>(const_cast<void *>(value))); + } + } + + if constexpr(is_complete_v<meta_associative_container_traits<Type>>) { + if(req == internal::meta_traits::is_meta_associative_container) { + const_only ? static_cast<meta_associative_container *>(other)->rebind(*static_cast<const Type *>(value)) : static_cast<meta_associative_container *>(other)->rebind(*static_cast<Type *>(const_cast<void *>(value))); + } + } + } + + void release() { + if(node.dtor.dtor && (storage.policy() == any_policy::owner)) { + node.dtor.dtor(storage.data()); + } + } + + meta_any(const meta_any &other, any ref) noexcept + : storage{std::move(ref)}, + ctx{other.ctx}, + node{storage ? other.node : internal::meta_type_node{}}, + vtable{storage ? other.vtable : &basic_vtable<void>} {} + +public: + /*! Default constructor. */ + meta_any() = default; + + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_any(meta_ctx_arg_t, const meta_ctx &area) + : ctx{&area} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template<typename Type, typename... Args> + explicit meta_any(std::in_place_type_t<Type>, Args &&...args) + : meta_any{locator<meta_ctx>::value_or(), std::in_place_type<Type>, std::forward<Args>(args)...} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param area The context from which to search for meta types. + * @param args Parameters to use to construct the instance. + */ + template<typename Type, typename... Args> + explicit meta_any(const meta_ctx &area, std::in_place_type_t<Type>, Args &&...args) + : storage{std::in_place_type<Type>, std::forward<Args>(args)...}, + ctx{&area}, + node{internal::resolve<std::remove_cv_t<std::remove_reference_t<Type>>>(internal::meta_context::from(*ctx))}, + vtable{&basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>} {} + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_any>>> + meta_any(Type &&value) + : meta_any{locator<meta_ctx>::value_or(), std::forward<Type>(value)} {} + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param area The context from which to search for meta types. + * @param value An instance of an object to use to initialize the wrapper. + */ + template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_any>>> + meta_any(const meta_ctx &area, Type &&value) + : meta_any{area, std::in_place_type<std::decay_t<Type>>, std::forward<Type>(value)} {} + + /** + * @brief Context aware copy constructor. + * @param area The context from which to search for meta types. + * @param other The instance to copy from. + */ + meta_any(const meta_ctx &area, const meta_any &other) + : storage{other.storage}, + ctx{&area}, + node{other.node.resolve ? other.node.resolve(internal::meta_context::from(*ctx)) : other.node}, + vtable{other.vtable} {} + + /** + * @brief Context aware move constructor. + * @param area The context from which to search for meta types. + * @param other The instance to move from. + */ + meta_any(const meta_ctx &area, meta_any &&other) + : storage{std::move(other.storage)}, + ctx{&area}, + node{other.node.resolve ? std::exchange(other.node, internal::meta_type_node{}).resolve(internal::meta_context::from(*ctx)) : std::exchange(other.node, internal::meta_type_node{})}, + vtable{std::exchange(other.vtable, &basic_vtable<void>)} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + meta_any(const meta_any &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + meta_any(meta_any &&other) noexcept + : storage{std::move(other.storage)}, + ctx{other.ctx}, + node{std::exchange(other.node, internal::meta_type_node{})}, + vtable{std::exchange(other.vtable, &basic_vtable<void>)} {} + + /*! @brief Frees the internal storage, whatever it means. */ + ~meta_any() noexcept { + release(); + } + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This meta any object. + */ + meta_any &operator=(const meta_any &other) { + if(this != &other) { + release(); + storage = other.storage; + ctx = other.ctx; + node = other.node; + vtable = other.vtable; + } + + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This meta any object. + */ + meta_any &operator=(meta_any &&other) noexcept { + ENTT_ASSERT(this != &other, "Self move assignment"); + + release(); + storage = std::move(other.storage); + ctx = other.ctx; + node = std::exchange(other.node, internal::meta_type_node{}); + vtable = std::exchange(other.vtable, &basic_vtable<void>); + return *this; + } + + /** + * @brief Value assignment operator. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + * @return This meta any object. + */ + template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_any>>> + meta_any &operator=(Type &&value) { + emplace<std::decay_t<Type>>(std::forward<Type>(value)); + return *this; + } + + /*! @copydoc any::type */ + [[nodiscard]] inline meta_type type() const noexcept; + + /*! @copydoc any::data */ + [[nodiscard]] const void *data() const noexcept { + return storage.data(); + } + + /*! @copydoc any::data */ + [[nodiscard]] void *data() noexcept { + return storage.data(); + } + + /** + * @brief Invokes the underlying function, if possible. + * @tparam Args Types of arguments to use to invoke the function. + * @param id Unique identifier. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + template<typename... Args> + meta_any invoke(const id_type id, Args &&...args) const; + + /*! @copydoc invoke */ + template<typename... Args> + meta_any invoke(const id_type id, Args &&...args); + + /** + * @brief Sets the value of a given variable. + * @tparam Type Type of value to assign. + * @param id Unique identifier. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ + template<typename Type> + bool set(const id_type id, Type &&value); + + /** + * @brief Gets the value of a given variable. + * @param id Unique identifier. + * @return A wrapper containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(const id_type id) const; + + /*! @copydoc get */ + [[nodiscard]] meta_any get(const id_type id); + + /** + * @brief Tries to cast an instance to a given type. + * @tparam Type Type to which to cast the instance. + * @return A (possibly null) pointer to the contained instance. + */ + template<typename Type> + [[nodiscard]] const Type *try_cast() const { + const auto other = internal::resolve<std::remove_cv_t<Type>>(internal::meta_context::from(*ctx)); + return static_cast<const Type *>(internal::try_cast(internal::meta_context::from(*ctx), node, other, data())); + } + + /*! @copydoc try_cast */ + template<typename Type> + [[nodiscard]] Type *try_cast() { + if constexpr(std::is_const_v<Type>) { + return std::as_const(*this).try_cast<std::remove_const_t<Type>>(); + } else { + const auto other = internal::resolve<std::remove_cv_t<Type>>(internal::meta_context::from(*ctx)); + return static_cast<Type *>(const_cast<void *>(internal::try_cast(internal::meta_context::from(*ctx), node, other, data()))); + } + } + + /** + * @brief Tries to cast an instance to a given type. + * @tparam Type Type to which to cast the instance. + * @return A reference to the contained instance. + */ + template<typename Type> + [[nodiscard]] Type cast() const { + auto *const instance = try_cast<std::remove_reference_t<Type>>(); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast<Type>(*instance); + } + + /*! @copydoc cast */ + template<typename Type> + [[nodiscard]] Type cast() { + // forces const on non-reference types to make them work also with wrappers for const references + auto *const instance = try_cast<std::remove_reference_t<const Type>>(); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast<Type>(*instance); + } + + /** + * @brief Converts an object in such a way that a given cast becomes viable. + * @param type Meta type to which the cast is requested. + * @return A valid meta any object if there exists a viable conversion, an + * invalid one otherwise. + */ + [[nodiscard]] meta_any allow_cast(const meta_type &type) const; + + /** + * @brief Converts an object in such a way that a given cast becomes viable. + * @param type Meta type to which the cast is requested. + * @return True if there exists a viable conversion, false otherwise. + */ + [[nodiscard]] bool allow_cast(const meta_type &type) { + if(auto other = std::as_const(*this).allow_cast(type); other) { + if((other.storage.policy() == any_policy::owner)) { + std::swap(*this, other); + } + + return true; + } + + return false; + } + + /** + * @brief Converts an object in such a way that a given cast becomes viable. + * @tparam Type Type to which the cast is requested. + * @return A valid meta any object if there exists a viable conversion, an + * invalid one otherwise. + */ + template<typename Type> + [[nodiscard]] meta_any allow_cast() const { + if constexpr(std::is_reference_v<Type> && !std::is_const_v<std::remove_reference_t<Type>>) { + return meta_any{meta_ctx_arg, *ctx}; + } else { + auto other = internal::resolve<std::remove_cv_t<std::remove_reference_t<Type>>>(internal::meta_context::from(*ctx)); + return allow_cast(meta_type{*ctx, other}); + } + } + + /** + * @brief Converts an object in such a way that a given cast becomes viable. + * @tparam Type Type to which the cast is requested. + * @return True if there exists a viable conversion, false otherwise. + */ + template<typename Type> + [[nodiscard]] bool allow_cast() { + auto other = internal::resolve<std::remove_cv_t<std::remove_reference_t<Type>>>(internal::meta_context::from(*ctx)); + return allow_cast(meta_type{*ctx, other}) && (!(std::is_reference_v<Type> && !std::is_const_v<std::remove_reference_t<Type>>) || storage.data() != nullptr); + } + + /*! @copydoc any::emplace */ + template<typename Type, typename... Args> + void emplace(Args &&...args) { + release(); + storage.emplace<Type>(std::forward<Args>(args)...); + node = internal::resolve<std::remove_cv_t<std::remove_reference_t<Type>>>(internal::meta_context::from(*ctx)); + vtable = &basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>; + } + + /*! @copydoc any::assign */ + bool assign(const meta_any &other); + + /*! @copydoc any::assign */ + bool assign(meta_any &&other); + + /*! @copydoc any::reset */ + void reset() { + release(); + storage.reset(); + node = {}; + vtable = &basic_vtable<void>; + } + + /** + * @brief Returns a sequence container proxy. + * @return A sequence container proxy for the underlying object. + */ + [[nodiscard]] meta_sequence_container as_sequence_container() noexcept { + meta_sequence_container proxy{*ctx}; + vtable(internal::meta_traits::is_meta_sequence_container, policy() == meta_any_policy::cref, std::as_const(*this).data(), &proxy); + return proxy; + } + + /*! @copydoc as_sequence_container */ + [[nodiscard]] meta_sequence_container as_sequence_container() const noexcept { + meta_sequence_container proxy{*ctx}; + vtable(internal::meta_traits::is_meta_sequence_container, true, data(), &proxy); + return proxy; + } + + /** + * @brief Returns an associative container proxy. + * @return An associative container proxy for the underlying object. + */ + [[nodiscard]] meta_associative_container as_associative_container() noexcept { + meta_associative_container proxy{*ctx}; + vtable(internal::meta_traits::is_meta_associative_container, policy() == meta_any_policy::cref, std::as_const(*this).data(), &proxy); + return proxy; + } + + /*! @copydoc as_associative_container */ + [[nodiscard]] meta_associative_container as_associative_container() const noexcept { + meta_associative_container proxy{*ctx}; + vtable(internal::meta_traits::is_meta_associative_container, true, data(), &proxy); + return proxy; + } + + /** + * @brief Indirection operator for dereferencing opaque objects. + * @return A wrapper that shares a reference to an unmanaged object if the + * wrapped element is dereferenceable, an invalid meta any otherwise. + */ + [[nodiscard]] meta_any operator*() const noexcept { + meta_any ret{meta_ctx_arg, *ctx}; + vtable(internal::meta_traits::is_meta_pointer_like, true, storage.data(), &ret); + return ret; + } + + /** + * @brief Returns false if a wrapper is invalid, true otherwise. + * @return False if the wrapper is invalid, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return !(node.info == nullptr); + } + + /*! @copydoc any::operator== */ + [[nodiscard]] bool operator==(const meta_any &other) const noexcept { + return (ctx == other.ctx) && ((!node.info && !other.node.info) || (node.info && other.node.info && *node.info == *other.node.info && storage == other.storage)); + } + + /*! @copydoc any::operator!= */ + [[nodiscard]] bool operator!=(const meta_any &other) const noexcept { + return !(*this == other); + } + + /*! @copydoc any::as_ref */ + [[nodiscard]] meta_any as_ref() noexcept { + return meta_any{*this, storage.as_ref()}; + } + + /*! @copydoc any::as_ref */ + [[nodiscard]] meta_any as_ref() const noexcept { + return meta_any{*this, storage.as_ref()}; + } + + /** + * @brief Returns the current mode of a meta any object. + * @return The current mode of the meta any object. + */ + [[nodiscard]] meta_any_policy policy() const noexcept { + return storage.policy(); + } + +private: + any storage{}; + const meta_ctx *ctx{&locator<meta_ctx>::value_or()}; + internal::meta_type_node node{}; + vtable_type *vtable{&basic_vtable<void>}; +}; + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @param ctx The context from which to search for meta types. + * @return A properly initialized and not necessarily owning wrapper. + */ +template<typename Type> +[[nodiscard]] meta_any forward_as_meta(const meta_ctx &ctx, Type &&value) { + return meta_any{ctx, std::in_place_type<Type &&>, std::forward<Type>(value)}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @return A properly initialized and not necessarily owning wrapper. + */ +template<typename Type> +[[nodiscard]] meta_any forward_as_meta(Type &&value) { + return forward_as_meta(locator<meta_ctx>::value_or(), std::forward<Type>(value)); +} + +/** + * @brief Opaque pointers to instances of any type. + * + * A handle doesn't perform copies and isn't responsible for the contained + * object. It doesn't prolong the lifetime of the pointed instance. + */ +struct meta_handle { + /*! Default constructor. */ + meta_handle() = default; + + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_handle(meta_ctx_arg_t, const meta_ctx &area) + : any{meta_ctx_arg, area} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @param value An instance of an object to use to initialize the handle. + */ + meta_handle(meta_any &value) + : any{value.as_ref()} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @param value An instance of an object to use to initialize the handle. + */ + meta_handle(const meta_any &value) + : any{value.as_ref()} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @tparam Type Type of object to use to initialize the handle. + * @param ctx The context from which to search for meta types. + * @param value An instance of an object to use to initialize the handle. + */ + template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_handle>>> + meta_handle(const meta_ctx &ctx, Type &value) + : any{ctx, std::in_place_type<Type &>, value} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @tparam Type Type of object to use to initialize the handle. + * @param value An instance of an object to use to initialize the handle. + */ + template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_handle>>> + meta_handle(Type &value) + : meta_handle{locator<meta_ctx>::value_or(), value} {} + + /** + * @brief Context aware copy constructor. + * @param area The context from which to search for meta types. + * @param other The instance to copy from. + */ + meta_handle(const meta_ctx &area, const meta_handle &other) + : any{area, other.any} {} + + /** + * @brief Context aware move constructor. + * @param area The context from which to search for meta types. + * @param other The instance to move from. + */ + meta_handle(const meta_ctx &area, meta_handle &&other) + : any{area, std::move(other.any)} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + meta_handle(const meta_handle &) = delete; + + /*! @brief Default move constructor. */ + meta_handle(meta_handle &&) = default; + + /*! @brief Default destructor. */ + ~meta_handle() noexcept = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This meta handle. + */ + meta_handle &operator=(const meta_handle &) = delete; + + /** + * @brief Default move assignment operator. + * @return This meta handle. + */ + meta_handle &operator=(meta_handle &&) = default; + + /** + * @brief Returns false if a handle is invalid, true otherwise. + * @return False if the handle is invalid, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast<bool>(any); + } + + /*! @copydoc meta_any::operator== */ + [[nodiscard]] bool operator==(const meta_handle &other) const noexcept { + return (any == other.any); + } + + /*! @copydoc meta_any::operator!= */ + [[nodiscard]] bool operator!=(const meta_handle &other) const noexcept { + return !(*this == other); + } + + /** + * @brief Access operator for accessing the contained opaque object. + * @return A wrapper that shares a reference to an unmanaged object. + */ + [[nodiscard]] meta_any *operator->() { + return &any; + } + + /*! @copydoc operator-> */ + [[nodiscard]] const meta_any *operator->() const { + return &any; + } + +private: + meta_any any{meta_ctx_arg, locator<meta_ctx>::value_or()}; +}; + +/*! @brief Opaque wrapper for properties of any type. */ +struct meta_prop { + /*! @brief Default constructor. */ + meta_prop() noexcept = default; + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_prop(const meta_ctx &area, const internal::meta_prop_node &curr) noexcept + : node{&curr}, + ctx{&area} {} + + /** + * @brief Returns the stored value by const reference. + * @return A wrapper containing the value stored with the property. + */ + [[nodiscard]] meta_any value() const { + return node->value ? node->type(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, node->value.get()) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @brief Returns the stored value by reference. + * @return A wrapper containing the value stored with the property. + */ + [[nodiscard]] meta_any value() { + return node->value ? node->type(internal::meta_context::from(*ctx)).from_void(*ctx, node->value.get(), nullptr) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (node != nullptr); + } + + /** + * @brief Checks if two objects refer to the same type. + * @param other The object with which to compare. + * @return True if the objects refer to the same type, false otherwise. + */ + [[nodiscard]] bool operator==(const meta_prop &other) const noexcept { + return (ctx == other.ctx && node == other.node); + } + +private: + const internal::meta_prop_node *node{}; + const meta_ctx *ctx{}; +}; + +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_prop &lhs, const meta_prop &rhs) noexcept { + return !(lhs == rhs); +} + +/*! @brief Opaque wrapper for user defined data of any type. */ +struct meta_custom { + /*! @brief Default constructor. */ + meta_custom() noexcept = default; + + /** + * @brief Basic constructor for meta objects. + * @param curr The underlying node with which to construct the instance. + */ + meta_custom(internal::meta_custom_node curr) noexcept + : node{std::move(curr)} {} + + /** + * @brief Generic conversion operator. + * @tparam Type Type to which conversion is requested. + */ + template<typename Type> + [[nodiscard]] operator Type *() const noexcept { + return (type_id<Type>().hash() == node.type) ? std::static_pointer_cast<Type>(node.value).get() : nullptr; + } + + /** + * @brief Generic conversion operator. + * @tparam Type Type to which conversion is requested. + */ + template<typename Type> + [[nodiscard]] operator Type &() const noexcept { + ENTT_ASSERT(type_id<Type>().hash() == node.type, "Invalid type"); + return *std::static_pointer_cast<Type>(node.value); + } + +private: + internal::meta_custom_node node{}; +}; + +/*! @brief Opaque wrapper for data members. */ +struct meta_data { + /*! @brief Unsigned integer type. */ + using size_type = typename internal::meta_data_node::size_type; + + /*! @brief Default constructor. */ + meta_data() noexcept = default; + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_data(const meta_ctx &area, const internal::meta_data_node &curr) noexcept + : node{&curr}, + ctx{&area} {} + + /** + * @brief Returns the number of setters available. + * @return The number of setters available. + */ + [[nodiscard]] size_type arity() const noexcept { + return node->arity; + } + + /** + * @brief Indicates whether a data member is constant or not. + * @return True if the data member is constant, false otherwise. + */ + [[nodiscard]] bool is_const() const noexcept { + return static_cast<bool>(node->traits & internal::meta_traits::is_const); + } + + /** + * @brief Indicates whether a data member is static or not. + * @return True if the data member is static, false otherwise. + */ + [[nodiscard]] bool is_static() const noexcept { + return static_cast<bool>(node->traits & internal::meta_traits::is_static); + } + + /*! @copydoc meta_any::type */ + [[nodiscard]] inline meta_type type() const noexcept; + + /** + * @brief Sets the value of a given variable. + * @tparam Type Type of value to assign. + * @param instance An opaque instance of the underlying type. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ + template<typename Type> + // NOLINTNEXTLINE(modernize-use-nodiscard) + bool set(meta_handle instance, Type &&value) const { + return node->set && node->set(meta_handle{*ctx, std::move(instance)}, meta_any{*ctx, std::forward<Type>(value)}); + } + + /** + * @brief Gets the value of a given variable. + * @param instance An opaque instance of the underlying type. + * @return A wrapper containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(meta_handle instance) const { + return node->get(*ctx, meta_handle{*ctx, std::move(instance)}); + } + + /** + * @brief Returns the type accepted by the i-th setter. + * @param index Index of the setter of which to return the accepted type. + * @return The type accepted by the i-th setter. + */ + [[nodiscard]] inline meta_type arg(const size_type index) const noexcept; + + /** + * @brief Returns a range to visit registered meta properties. + * @return An iterable range to visit registered meta properties. + */ + [[nodiscard]] meta_range<meta_prop, typename decltype(internal::meta_data_node::prop)::const_iterator> prop() const noexcept { + return {{*ctx, node->prop.cbegin()}, {*ctx, node->prop.cend()}}; + } + + /** + * @brief Lookup utility for meta properties. + * @param key The key to use to search for a property. + * @return The registered meta property for the given key, if any. + */ + [[nodiscard]] meta_prop prop(const id_type key) const { + const auto it = node->prop.find(key); + return it != node->prop.cend() ? meta_prop{*ctx, it->second} : meta_prop{}; + } + + /** + * @brief Returns all meta traits for a given meta object. + * @tparam Type The type to convert the meta traits to. + * @return The registered meta traits, if any. + */ + template<typename Type> + [[nodiscard]] Type traits() const noexcept { + return internal::meta_to_user_traits<Type>(node->traits); + } + + /** + * @brief Returns user defined data for a given meta object. + * @return User defined arbitrary data. + */ + [[nodiscard]] meta_custom custom() const noexcept { + return {node->custom}; + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (node != nullptr); + } + + /*! @copydoc meta_prop::operator== */ + [[nodiscard]] bool operator==(const meta_data &other) const noexcept { + return (ctx == other.ctx && node == other.node); + } + +private: + const internal::meta_data_node *node{}; + const meta_ctx *ctx{}; +}; + +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_data &lhs, const meta_data &rhs) noexcept { + return !(lhs == rhs); +} + +/*! @brief Opaque wrapper for member functions. */ +struct meta_func { + /*! @brief Unsigned integer type. */ + using size_type = typename internal::meta_func_node::size_type; + + /*! @brief Default constructor. */ + meta_func() noexcept = default; + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_func(const meta_ctx &area, const internal::meta_func_node &curr) noexcept + : node{&curr}, + ctx{&area} {} + + /** + * @brief Returns the number of arguments accepted by a member function. + * @return The number of arguments accepted by the member function. + */ + [[nodiscard]] size_type arity() const noexcept { + return node->arity; + } + + /** + * @brief Indicates whether a member function is constant or not. + * @return True if the member function is constant, false otherwise. + */ + [[nodiscard]] bool is_const() const noexcept { + return static_cast<bool>(node->traits & internal::meta_traits::is_const); + } + + /** + * @brief Indicates whether a member function is static or not. + * @return True if the member function is static, false otherwise. + */ + [[nodiscard]] bool is_static() const noexcept { + return static_cast<bool>(node->traits & internal::meta_traits::is_static); + } + + /** + * @brief Returns the return type of a member function. + * @return The return type of the member function. + */ + [[nodiscard]] inline meta_type ret() const noexcept; + + /** + * @brief Returns the type of the i-th argument of a member function. + * @param index Index of the argument of which to return the type. + * @return The type of the i-th argument of a member function. + */ + [[nodiscard]] inline meta_type arg(const size_type index) const noexcept; + + /** + * @brief Invokes the underlying function, if possible. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @param sz Number of parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + meta_any invoke(meta_handle instance, meta_any *const args, const size_type sz) const { + return sz == arity() ? node->invoke(*ctx, meta_handle{*ctx, std::move(instance)}, args) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @copybrief invoke + * @tparam Args Types of arguments to use to invoke the function. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + template<typename... Args> + // NOLINTNEXTLINE(modernize-use-nodiscard) + meta_any invoke(meta_handle instance, Args &&...args) const { + std::array<meta_any, sizeof...(Args)> arguments{meta_any{*ctx, std::forward<Args>(args)}...}; + return invoke(std::move(instance), arguments.data(), sizeof...(Args)); + } + + /*! @copydoc meta_data::prop */ + [[nodiscard]] meta_range<meta_prop, typename decltype(internal::meta_func_node::prop)::const_iterator> prop() const noexcept { + return {{*ctx, node->prop.cbegin()}, {*ctx, node->prop.cend()}}; + } + + /** + * @brief Lookup utility for meta properties. + * @param key The key to use to search for a property. + * @return The registered meta property for the given key, if any. + */ + [[nodiscard]] meta_prop prop(const id_type key) const { + const auto it = node->prop.find(key); + return it != node->prop.cend() ? meta_prop{*ctx, it->second} : meta_prop{}; + } + + /*! @copydoc meta_data::traits */ + template<typename Type> + [[nodiscard]] Type traits() const noexcept { + return internal::meta_to_user_traits<Type>(node->traits); + } + + /*! @copydoc meta_data::custom */ + [[nodiscard]] meta_custom custom() const noexcept { + return {node->custom}; + } + + /** + * @brief Returns the next overload of a given function, if any. + * @return The next overload of the given function, if any. + */ + [[nodiscard]] meta_func next() const { + return node->next ? meta_func{*ctx, *node->next} : meta_func{}; + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (node != nullptr); + } + + /*! @copydoc meta_prop::operator== */ + [[nodiscard]] bool operator==(const meta_func &other) const noexcept { + return (ctx == other.ctx && node == other.node); + } + +private: + const internal::meta_func_node *node{}; + const meta_ctx *ctx{}; +}; + +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_func &lhs, const meta_func &rhs) noexcept { + return !(lhs == rhs); +} + +/*! @brief Opaque wrapper for types. */ +class meta_type { + template<typename Func> + [[nodiscard]] auto lookup(meta_any *const args, const typename internal::meta_type_node::size_type sz, [[maybe_unused]] bool constness, Func next) const { + decltype(next()) candidate = nullptr; + size_type same{}; + bool ambiguous{}; + + for(auto curr = next(); curr; curr = next()) { + if constexpr(std::is_same_v<std::decay_t<decltype(*curr)>, internal::meta_func_node>) { + if(constness && !static_cast<bool>(curr->traits & internal::meta_traits::is_const)) { + continue; + } + } + + if(curr->arity == sz) { + size_type match{}; + size_type pos{}; + + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) - waiting for C++20 (and std::span) + for(; pos < sz && args[pos]; ++pos) { + const auto other = curr->arg(*ctx, pos); + const auto type = args[pos].type(); + + if(const auto &info = other.info(); info == type.info()) { + ++match; + } else if(!((type.node.details && (type.node.details->base.contains(info.hash()) || type.node.details->conv.contains(info.hash()))) || (type.node.conversion_helper && other.node.conversion_helper))) { + break; + } + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + + if(pos == sz) { + if(!candidate || match > same) { + candidate = curr; + same = match; + ambiguous = false; + } else if(match == same) { + if constexpr(std::is_same_v<std::decay_t<decltype(*curr)>, internal::meta_func_node>) { + if(static_cast<bool>(curr->traits & internal::meta_traits::is_const) != static_cast<bool>(candidate->traits & internal::meta_traits::is_const)) { + candidate = static_cast<bool>(candidate->traits & internal::meta_traits::is_const) ? curr : candidate; + ambiguous = false; + continue; + } + } + + ambiguous = true; + } + } + } + } + + return ambiguous ? nullptr : candidate; + } + +public: + /*! @brief Unsigned integer type. */ + using size_type = typename internal::meta_type_node::size_type; + + /*! @brief Default constructor. */ + meta_type() noexcept = default; + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_type(const meta_ctx &area, internal::meta_type_node curr) noexcept + : node{std::move(curr)}, + ctx{&area} {} + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_type(const meta_ctx &area, const internal::meta_base_node &curr) noexcept + : meta_type{area, curr.type(internal::meta_context::from(area))} {} + + /** + * @brief Returns the type info object of the underlying type. + * @return The type info object of the underlying type. + */ + [[nodiscard]] const type_info &info() const noexcept { + return *node.info; + } + + /** + * @brief Returns the identifier assigned to a type. + * @return The identifier assigned to the type. + */ + [[nodiscard]] id_type id() const noexcept { + return node.id; + } + + /** + * @brief Returns the size of the underlying type if known. + * @return The size of the underlying type if known, 0 otherwise. + */ + [[nodiscard]] size_type size_of() const noexcept { + return node.size_of; + } + + /** + * @brief Checks whether a type refers to an arithmetic type or not. + * @return True if the underlying type is an arithmetic type, false + * otherwise. + */ + [[nodiscard]] bool is_arithmetic() const noexcept { + return static_cast<bool>(node.traits & internal::meta_traits::is_arithmetic); + } + + /** + * @brief Checks whether a type refers to an integral type or not. + * @return True if the underlying type is an integral type, false otherwise. + */ + [[nodiscard]] bool is_integral() const noexcept { + return static_cast<bool>(node.traits & internal::meta_traits::is_integral); + } + + /** + * @brief Checks whether a type refers to a signed type or not. + * @return True if the underlying type is a signed type, false otherwise. + */ + [[nodiscard]] bool is_signed() const noexcept { + return static_cast<bool>(node.traits & internal::meta_traits::is_signed); + } + + /** + * @brief Checks whether a type refers to an array type or not. + * @return True if the underlying type is an array type, false otherwise. + */ + [[nodiscard]] bool is_array() const noexcept { + return static_cast<bool>(node.traits & internal::meta_traits::is_array); + } + + /** + * @brief Checks whether a type refers to an enum or not. + * @return True if the underlying type is an enum, false otherwise. + */ + [[nodiscard]] bool is_enum() const noexcept { + return static_cast<bool>(node.traits & internal::meta_traits::is_enum); + } + + /** + * @brief Checks whether a type refers to a class or not. + * @return True if the underlying type is a class, false otherwise. + */ + [[nodiscard]] bool is_class() const noexcept { + return static_cast<bool>(node.traits & internal::meta_traits::is_class); + } + + /** + * @brief Checks whether a type refers to a pointer or not. + * @return True if the underlying type is a pointer, false otherwise. + */ + [[nodiscard]] bool is_pointer() const noexcept { + return static_cast<bool>(node.traits & internal::meta_traits::is_pointer); + } + + /** + * @brief Provides the type for which the pointer is defined. + * @return The type for which the pointer is defined or this type if it + * doesn't refer to a pointer type. + */ + [[nodiscard]] meta_type remove_pointer() const noexcept { + return {*ctx, node.remove_pointer(internal::meta_context::from(*ctx))}; + } + + /** + * @brief Checks whether a type is a pointer-like type or not. + * @return True if the underlying type is pointer-like, false otherwise. + */ + [[nodiscard]] bool is_pointer_like() const noexcept { + return static_cast<bool>(node.traits & internal::meta_traits::is_meta_pointer_like); + } + + /** + * @brief Checks whether a type refers to a sequence container or not. + * @return True if the type is a sequence container, false otherwise. + */ + [[nodiscard]] bool is_sequence_container() const noexcept { + return static_cast<bool>(node.traits & internal::meta_traits::is_meta_sequence_container); + } + + /** + * @brief Checks whether a type refers to an associative container or not. + * @return True if the type is an associative container, false otherwise. + */ + [[nodiscard]] bool is_associative_container() const noexcept { + return static_cast<bool>(node.traits & internal::meta_traits::is_meta_associative_container); + } + + /** + * @brief Checks whether a type refers to a recognized class template + * specialization or not. + * @return True if the type is a recognized class template specialization, + * false otherwise. + */ + [[nodiscard]] bool is_template_specialization() const noexcept { + return (node.templ.arity != 0u); + } + + /** + * @brief Returns the number of template arguments. + * @return The number of template arguments. + */ + [[nodiscard]] size_type template_arity() const noexcept { + return node.templ.arity; + } + + /** + * @brief Returns a tag for the class template of the underlying type. + * @return The tag for the class template of the underlying type. + */ + [[nodiscard]] inline meta_type template_type() const noexcept { + return node.templ.type ? meta_type{*ctx, node.templ.type(internal::meta_context::from(*ctx))} : meta_type{}; + } + + /** + * @brief Returns the type of the i-th template argument of a type. + * @param index Index of the template argument of which to return the type. + * @return The type of the i-th template argument of a type. + */ + [[nodiscard]] inline meta_type template_arg(const size_type index) const noexcept { + return index < template_arity() ? meta_type{*ctx, node.templ.arg(internal::meta_context::from(*ctx), index)} : meta_type{}; + } + + /** + * @brief Checks if a type supports direct casting to another type. + * @param other The meta type to test for. + * @return True if direct casting is allowed, false otherwise. + */ + [[nodiscard]] bool can_cast(const meta_type &other) const noexcept { + // casting this is UB in all cases but we aren't going to use the resulting pointer, so... + return (internal::try_cast(internal::meta_context::from(*ctx), node, other.node, this) != nullptr); + } + + /** + * @brief Checks if a type supports conversion it to another type. + * @param other The meta type to test for. + * @return True if the conversion is allowed, false otherwise. + */ + [[nodiscard]] bool can_convert(const meta_type &other) const noexcept { + return (internal::try_convert(internal::meta_context::from(*ctx), node, other.info(), other.is_arithmetic() || other.is_enum(), nullptr, [](const void *, auto &&...args) { return ((static_cast<void>(args), 1) + ... + 0u); }) != 0u); + } + + /** + * @brief Returns a range to visit registered top-level base meta types. + * @return An iterable range to visit registered top-level base meta types. + */ + [[nodiscard]] meta_range<meta_type, typename decltype(internal::meta_type_descriptor::base)::const_iterator> base() const noexcept { + using range_type = meta_range<meta_type, typename decltype(internal::meta_type_descriptor::base)::const_iterator>; + return node.details ? range_type{{*ctx, node.details->base.cbegin()}, {*ctx, node.details->base.cend()}} : range_type{}; + } + + /** + * @brief Returns a range to visit registered top-level meta data. + * @return An iterable range to visit registered top-level meta data. + */ + [[nodiscard]] meta_range<meta_data, typename decltype(internal::meta_type_descriptor::data)::const_iterator> data() const noexcept { + using range_type = meta_range<meta_data, typename decltype(internal::meta_type_descriptor::data)::const_iterator>; + return node.details ? range_type{{*ctx, node.details->data.cbegin()}, {*ctx, node.details->data.cend()}} : range_type{}; + } + + /** + * @brief Lookup utility for meta data (bases are also visited). + * @param id Unique identifier. + * @return The registered meta data for the given identifier, if any. + */ + [[nodiscard]] meta_data data(const id_type id) const { + const auto *elem = internal::look_for<&internal::meta_type_descriptor::data>(internal::meta_context::from(*ctx), node, id); + return elem ? meta_data{*ctx, *elem} : meta_data{}; + } + + /** + * @brief Returns a range to visit registered top-level functions. + * @return An iterable range to visit registered top-level functions. + */ + [[nodiscard]] meta_range<meta_func, typename decltype(internal::meta_type_descriptor::func)::const_iterator> func() const noexcept { + using return_type = meta_range<meta_func, typename decltype(internal::meta_type_descriptor::func)::const_iterator>; + return node.details ? return_type{{*ctx, node.details->func.cbegin()}, {*ctx, node.details->func.cend()}} : return_type{}; + } + + /** + * @brief Lookup utility for meta functions (bases are also visited). + * + * In case of overloaded functions, a random one is returned. + * + * @param id Unique identifier. + * @return The registered meta function for the given identifier, if any. + */ + [[nodiscard]] meta_func func(const id_type id) const { + const auto *elem = internal::look_for<&internal::meta_type_descriptor::func>(internal::meta_context::from(*ctx), node, id); + return elem ? meta_func{*ctx, *elem} : meta_func{}; + } + + /** + * @brief Creates an instance of the underlying type, if possible. + * @param args Parameters to use to construct the instance. + * @param sz Number of parameters to use to construct the instance. + * @return A wrapper containing the new instance, if any. + */ + [[nodiscard]] meta_any construct(meta_any *const args, const size_type sz) const { + if(node.details) { + if(const auto *candidate = lookup(args, sz, false, [first = node.details->ctor.cbegin(), last = node.details->ctor.cend()]() mutable { return first == last ? nullptr : &(first++)->second; }); candidate) { + return candidate->invoke(*ctx, args); + } + } + + if(sz == 0u && node.default_constructor) { + return node.default_constructor(*ctx); + } + + return meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @copybrief construct + * @tparam Args Types of arguments to use to construct the instance. + * @param args Parameters to use to construct the instance. + * @return A wrapper containing the new instance, if any. + */ + template<typename... Args> + [[nodiscard]] meta_any construct(Args &&...args) const { + std::array<meta_any, sizeof...(Args)> arguments{meta_any{*ctx, std::forward<Args>(args)}...}; + return construct(arguments.data(), sizeof...(Args)); + } + + /** + * @brief Wraps an opaque element of the underlying type. + * @param elem A valid pointer to an element of the underlying type. + * @return A wrapper that references the given instance. + */ + [[nodiscard]] meta_any from_void(void *elem) const { + return (elem && node.from_void) ? node.from_void(*ctx, elem, nullptr) : meta_any{meta_ctx_arg, *ctx}; + } + + /*! @copydoc from_void */ + [[nodiscard]] meta_any from_void(const void *elem) const { + return (elem && node.from_void) ? node.from_void(*ctx, nullptr, elem) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @brief Invokes a function given an identifier, if possible. + * @param id Unique identifier. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @param sz Number of parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + meta_any invoke(const id_type id, meta_handle instance, meta_any *const args, const size_type sz) const { + if(node.details) { + if(auto it = node.details->func.find(id); it != node.details->func.cend()) { + if(const auto *candidate = lookup(args, sz, instance && (instance->data() == nullptr), [curr = &it->second]() mutable { return curr ? std::exchange(curr, curr->next.get()) : nullptr; }); candidate) { + return candidate->invoke(*ctx, meta_handle{*ctx, std::move(instance)}, args); + } + } + } + + for(auto &&curr: base()) { + if(auto elem = curr.second.invoke(id, *instance.operator->(), args, sz); elem) { + return elem; + } + } + + return meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @copybrief invoke + * @param id Unique identifier. + * @tparam Args Types of arguments to use to invoke the function. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + template<typename... Args> + // NOLINTNEXTLINE(modernize-use-nodiscard) + meta_any invoke(const id_type id, meta_handle instance, Args &&...args) const { + std::array<meta_any, sizeof...(Args)> arguments{meta_any{*ctx, std::forward<Args>(args)}...}; + return invoke(id, std::move(instance), arguments.data(), sizeof...(Args)); + } + + /** + * @brief Sets the value of a given variable. + * @tparam Type Type of value to assign. + * @param id Unique identifier. + * @param instance An opaque instance of the underlying type. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ + template<typename Type> + bool set(const id_type id, meta_handle instance, Type &&value) const { + const auto candidate = data(id); + return candidate && candidate.set(std::move(instance), std::forward<Type>(value)); + } + + /** + * @brief Gets the value of a given variable. + * @param id Unique identifier. + * @param instance An opaque instance of the underlying type. + * @return A wrapper containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(const id_type id, meta_handle instance) const { + const auto candidate = data(id); + return candidate ? candidate.get(std::move(instance)) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @brief Returns a range to visit registered top-level meta properties. + * @return An iterable range to visit registered top-level meta properties. + */ + [[nodiscard]] meta_range<meta_prop, typename decltype(internal::meta_type_descriptor::prop)::const_iterator> prop() const noexcept { + using range_type = meta_range<meta_prop, typename decltype(internal::meta_type_descriptor::prop)::const_iterator>; + return node.details ? range_type{{*ctx, node.details->prop.cbegin()}, {*ctx, node.details->prop.cend()}} : range_type{}; + } + + /** + * @brief Lookup utility for meta properties (bases are also visited). + * @param key The key to use to search for a property. + * @return The registered meta property for the given key, if any. + */ + [[nodiscard]] meta_prop prop(const id_type key) const { + const auto *elem = internal::look_for<&internal::meta_type_descriptor::prop>(internal::meta_context::from(*ctx), node, key); + return elem ? meta_prop{*ctx, *elem} : meta_prop{}; + } + + /*! @copydoc meta_data::traits */ + template<typename Type> + [[nodiscard]] Type traits() const noexcept { + return internal::meta_to_user_traits<Type>(node.traits); + } + + /*! @copydoc meta_data::custom */ + [[nodiscard]] meta_custom custom() const noexcept { + return {node.custom}; + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return !(ctx == nullptr); + } + + /*! @copydoc meta_prop::operator== */ + [[nodiscard]] bool operator==(const meta_type &other) const noexcept { + return (ctx == other.ctx) && ((!node.info && !other.node.info) || (node.info && other.node.info && *node.info == *other.node.info)); + } + +private: + internal::meta_type_node node{}; + const meta_ctx *ctx{}; +}; + +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_type &lhs, const meta_type &rhs) noexcept { + return !(lhs == rhs); +} + +[[nodiscard]] inline meta_type meta_any::type() const noexcept { + return node.info ? meta_type{*ctx, node} : meta_type{}; +} + +template<typename... Args> +// NOLINTNEXTLINE(modernize-use-nodiscard) +meta_any meta_any::invoke(const id_type id, Args &&...args) const { + return type().invoke(id, *this, std::forward<Args>(args)...); +} + +template<typename... Args> +meta_any meta_any::invoke(const id_type id, Args &&...args) { + return type().invoke(id, *this, std::forward<Args>(args)...); +} + +template<typename Type> +bool meta_any::set(const id_type id, Type &&value) { + return type().set(id, *this, std::forward<Type>(value)); +} + +[[nodiscard]] inline meta_any meta_any::get(const id_type id) const { + return type().get(id, *this); +} + +[[nodiscard]] inline meta_any meta_any::get(const id_type id) { + return type().get(id, *this); +} + +[[nodiscard]] inline meta_any meta_any::allow_cast(const meta_type &type) const { + return internal::try_convert(internal::meta_context::from(*ctx), node, type.info(), type.is_arithmetic() || type.is_enum(), data(), [this, &type]([[maybe_unused]] const void *instance, auto &&...args) { + if constexpr((std::is_same_v<std::remove_const_t<std::remove_reference_t<decltype(args)>>, internal::meta_type_node> || ...)) { + return (args.from_void(*ctx, nullptr, instance), ...); + } else if constexpr((std::is_same_v<std::remove_const_t<std::remove_reference_t<decltype(args)>>, internal::meta_conv_node> || ...)) { + return (args.conv(*ctx, instance), ...); + } else if constexpr((std::is_same_v<std::remove_const_t<std::remove_reference_t<decltype(args)>>, decltype(internal::meta_type_node::conversion_helper)> || ...)) { + // exploits the fact that arithmetic types and enums are also default constructible + auto other = type.construct(); + const auto value = (args(nullptr, instance), ...); + other.node.conversion_helper(other.data(), &value); + return other; + } else { + // forwards to force a compile-time error in case of available arguments + return meta_any{meta_ctx_arg, *ctx, std::forward<decltype(args)>(args)...}; + } + }); +} + +inline bool meta_any::assign(const meta_any &other) { + auto value = other.allow_cast({*ctx, node}); + return value && storage.assign(value.storage); +} + +inline bool meta_any::assign(meta_any &&other) { + if(*node.info == *other.node.info) { + return storage.assign(std::move(other.storage)); + } + + return assign(std::as_const(other)); +} + +[[nodiscard]] inline meta_type meta_data::type() const noexcept { + return meta_type{*ctx, node->type(internal::meta_context::from(*ctx))}; +} + +[[nodiscard]] inline meta_type meta_data::arg(const size_type index) const noexcept { + return index < arity() ? node->arg(*ctx, index) : meta_type{}; +} + +[[nodiscard]] inline meta_type meta_func::ret() const noexcept { + return meta_type{*ctx, node->ret(internal::meta_context::from(*ctx))}; +} + +[[nodiscard]] inline meta_type meta_func::arg(const size_type index) const noexcept { + return index < arity() ? node->arg(*ctx, index) : meta_type{}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +class meta_sequence_container::meta_iterator final { + using vtable_type = void(const void *, const std::ptrdiff_t, meta_any *); + + template<typename It> + static void basic_vtable(const void *value, const std::ptrdiff_t offset, meta_any *other) { + const auto &it = *static_cast<const It *>(value); + other ? other->emplace<decltype(*it)>(*it) : std::advance(const_cast<It &>(it), offset); + } + +public: + using value_type = meta_any; + using pointer = input_iterator_pointer<value_type>; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::bidirectional_iterator_tag; + + meta_iterator() = default; + + template<typename It> + meta_iterator(const meta_ctx &area, It iter) noexcept + : ctx{&area}, + vtable{&basic_vtable<It>}, + handle{iter} {} + + meta_iterator &operator++() noexcept { + vtable(handle.data(), 1, nullptr); + return *this; + } + + meta_iterator operator++(int value) noexcept { + meta_iterator orig = *this; + vtable(handle.data(), ++value, nullptr); + return orig; + } + + meta_iterator &operator--() noexcept { + vtable(handle.data(), -1, nullptr); + return *this; + } + + meta_iterator operator--(int value) noexcept { + meta_iterator orig = *this; + vtable(handle.data(), --value, nullptr); + return orig; + } + + [[nodiscard]] reference operator*() const { + reference other{meta_ctx_arg, *ctx}; + vtable(handle.data(), 0, &other); + return other; + } + + [[nodiscard]] pointer operator->() const { + return operator*(); + } + + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast<bool>(handle); + } + + [[nodiscard]] bool operator==(const meta_iterator &other) const noexcept { + return handle == other.handle; + } + + [[nodiscard]] bool operator!=(const meta_iterator &other) const noexcept { + return !(*this == other); + } + + [[nodiscard]] const any &base() const noexcept { + return handle; + } + +private: + const meta_ctx *ctx{&locator<meta_ctx>::value_or()}; + vtable_type *vtable{}; + any handle{}; +}; + +class meta_associative_container::meta_iterator final { + using vtable_type = void(const void *, std::pair<meta_any, meta_any> *); + + template<bool KeyOnly, typename It> + static void basic_vtable(const void *value, std::pair<meta_any, meta_any> *other) { + if(const auto &it = *static_cast<const It *>(value); other) { + if constexpr(KeyOnly) { + other->first.emplace<decltype(*it)>(*it); + } else { + other->first.emplace<decltype((it->first))>(it->first); + other->second.emplace<decltype((it->second))>(it->second); + } + } else { + ++const_cast<It &>(it); + } + } + +public: + using value_type = std::pair<meta_any, meta_any>; + using pointer = input_iterator_pointer<value_type>; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + meta_iterator() = default; + + template<bool KeyOnly, typename It> + meta_iterator(const meta_ctx &area, std::bool_constant<KeyOnly>, It iter) noexcept + : ctx{&area}, + vtable{&basic_vtable<KeyOnly, It>}, + handle{iter} {} + + meta_iterator &operator++() noexcept { + vtable(handle.data(), nullptr); + return *this; + } + + meta_iterator operator++(int) noexcept { + meta_iterator orig = *this; + vtable(handle.data(), nullptr); + return orig; + } + + [[nodiscard]] reference operator*() const { + reference other{{meta_ctx_arg, *ctx}, {meta_ctx_arg, *ctx}}; + vtable(handle.data(), &other); + return other; + } + + [[nodiscard]] pointer operator->() const { + return operator*(); + } + + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast<bool>(handle); + } + + [[nodiscard]] bool operator==(const meta_iterator &other) const noexcept { + return handle == other.handle; + } + + [[nodiscard]] bool operator!=(const meta_iterator &other) const noexcept { + return !(*this == other); + } + +private: + const meta_ctx *ctx{&locator<meta_ctx>::value_or()}; + vtable_type *vtable{}; + any handle{}; +}; +/*! @endcond */ + +/** + * @brief Returns the meta value type of a container. + * @return The meta value type of the container. + */ +[[nodiscard]] inline meta_type meta_sequence_container::value_type() const noexcept { + return value_type_node ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{}; +} + +/** + * @brief Returns the size of a container. + * @return The size of the container. + */ +[[nodiscard]] inline meta_sequence_container::size_type meta_sequence_container::size() const noexcept { + return size_fn(data); +} + +/** + * @brief Resizes a container to contain a given number of elements. + * @param sz The new size of the container. + * @return True in case of success, false otherwise. + */ +inline bool meta_sequence_container::resize(const size_type sz) { + return !const_only && resize_fn(const_cast<void *>(data), sz); +} + +/** + * @brief Clears the content of a container. + * @return True in case of success, false otherwise. + */ +inline bool meta_sequence_container::clear() { + return !const_only && clear_fn(const_cast<void *>(data)); +} + +/** + * @brief Reserves storage for at least the given number of elements. + * @param sz The new capacity of the container. + * @return True in case of success, false otherwise. + */ +inline bool meta_sequence_container::reserve(const size_type sz) { + return !const_only && reserve_fn(const_cast<void *>(data), sz); +} + +/** + * @brief Returns an iterator to the first element of a container. + * @return An iterator to the first element of the container. + */ +[[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::begin() { + return begin_fn(*ctx, const_only ? nullptr : const_cast<void *>(data), data); +} + +/** + * @brief Returns an iterator that is past the last element of a container. + * @return An iterator that is past the last element of the container. + */ +[[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::end() { + return end_fn(*ctx, const_only ? nullptr : const_cast<void *>(data), data); +} + +/** + * @brief Inserts an element at a specified location of a container. + * @param it Iterator before which the element will be inserted. + * @param value Element value to insert. + * @return A possibly invalid iterator to the inserted element. + */ +inline meta_sequence_container::iterator meta_sequence_container::insert(const iterator &it, meta_any value) { + // this abomination is necessary because only on macos value_type and const_reference are different types for std::vector<bool> + if(const auto vtype = value_type_node(internal::meta_context::from(*ctx)); !const_only && (value.allow_cast({*ctx, vtype}) || value.allow_cast({*ctx, const_reference_node(internal::meta_context::from(*ctx))}))) { + const bool is_value_type = (value.type().info() == *vtype.info); + return insert_fn(*ctx, const_cast<void *>(data), is_value_type ? std::as_const(value).data() : nullptr, is_value_type ? nullptr : std::as_const(value).data(), it); + } + + return iterator{}; +} + +/** + * @brief Removes a given element from a container. + * @param it Iterator to the element to remove. + * @return A possibly invalid iterator following the last removed element. + */ +inline meta_sequence_container::iterator meta_sequence_container::erase(const iterator &it) { + return const_only ? iterator{} : erase_fn(*ctx, const_cast<void *>(data), it); +} + +/** + * @brief Returns a reference to the element at a given location of a container. + * @param pos The position of the element to return. + * @return A reference to the requested element properly wrapped. + */ +[[nodiscard]] inline meta_any meta_sequence_container::operator[](const size_type pos) { + auto it = begin(); + it.operator++(static_cast<int>(pos) - 1); + return *it; +} + +/** + * @brief Returns false if a proxy is invalid, true otherwise. + * @return False if the proxy is invalid, true otherwise. + */ +[[nodiscard]] inline meta_sequence_container::operator bool() const noexcept { + return (data != nullptr); +} + +/** + * @brief Returns the meta key type of a container. + * @return The meta key type of the a container. + */ +[[nodiscard]] inline meta_type meta_associative_container::key_type() const noexcept { + return key_type_node ? meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))} : meta_type{}; +} + +/** + * @brief Returns the meta mapped type of a container. + * @return The meta mapped type of the a container. + */ +[[nodiscard]] inline meta_type meta_associative_container::mapped_type() const noexcept { + return mapped_type_node ? meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))} : meta_type{}; +} + +/*! @copydoc meta_sequence_container::value_type */ +[[nodiscard]] inline meta_type meta_associative_container::value_type() const noexcept { + return value_type_node ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{}; +} + +/*! @copydoc meta_sequence_container::size */ +[[nodiscard]] inline meta_associative_container::size_type meta_associative_container::size() const noexcept { + return size_fn(data); +} + +/*! @copydoc meta_sequence_container::clear */ +inline bool meta_associative_container::clear() { + return !const_only && clear_fn(const_cast<void *>(data)); +} + +/*! @copydoc meta_sequence_container::reserve */ +inline bool meta_associative_container::reserve(const size_type sz) { + return !const_only && reserve_fn(const_cast<void *>(data), sz); +} + +/*! @copydoc meta_sequence_container::begin */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::begin() { + return begin_fn(*ctx, const_only ? nullptr : const_cast<void *>(data), data); +} + +/*! @copydoc meta_sequence_container::end */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::end() { + return end_fn(*ctx, const_only ? nullptr : const_cast<void *>(data), data); +} + +/** + * @brief Inserts a key-only or key/value element into a container. + * @param key The key of the element to insert. + * @param value The value of the element to insert, if needed. + * @return A bool denoting whether the insertion took place. + */ +inline bool meta_associative_container::insert(meta_any key, meta_any value = {}) { + return !const_only && key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))}) + && (!mapped_type_node || value.allow_cast(meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))})) + && insert_fn(const_cast<void *>(data), std::as_const(key).data(), std::as_const(value).data()); +} + +/** + * @brief Removes the specified element from a container. + * @param key The key of the element to remove. + * @return A bool denoting whether the removal took place. + */ +inline meta_associative_container::size_type meta_associative_container::erase(meta_any key) { + return (!const_only && key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))})) ? erase_fn(const_cast<void *>(data), std::as_const(key).data()) : 0u; +} + +/** + * @brief Returns an iterator to the element with a given key, if any. + * @param key The key of the element to search. + * @return An iterator to the element with the given key, if any. + */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::find(meta_any key) { + return key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))}) ? find_fn(*ctx, const_only ? nullptr : const_cast<void *>(data), data, std::as_const(key).data()) : iterator{}; +} + +/** + * @brief Returns false if a proxy is invalid, true otherwise. + * @return False if the proxy is invalid, true otherwise. + */ +[[nodiscard]] inline meta_associative_container::operator bool() const noexcept { + return (data != nullptr); +} + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/meta/node.hpp b/external/entt/include/entt/meta/node.hpp new file mode 100644 index 0000000..5c4e25c --- /dev/null +++ b/external/entt/include/entt/meta/node.hpp @@ -0,0 +1,298 @@ +#ifndef ENTT_META_NODE_HPP +#define ENTT_META_NODE_HPP + +#include <cstddef> +#include <memory> +#include <type_traits> +#include <utility> +#include "../config/config.h" +#include "../container/dense_map.hpp" +#include "../core/attribute.h" +#include "../core/bit.hpp" +#include "../core/enum.hpp" +#include "../core/fwd.hpp" +#include "../core/type_info.hpp" +#include "../core/type_traits.hpp" +#include "../core/utility.hpp" +#include "context.hpp" +#include "type_traits.hpp" + +namespace entt { + +class meta_any; +class meta_type; +struct meta_handle; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +enum class meta_traits : std::uint32_t { + is_none = 0x0000, + is_const = 0x0001, + is_static = 0x0002, + is_arithmetic = 0x0004, + is_integral = 0x0008, + is_signed = 0x0010, + is_array = 0x0020, + is_enum = 0x0040, + is_class = 0x0080, + is_pointer = 0x0100, + is_meta_pointer_like = 0x0200, + is_meta_sequence_container = 0x0400, + is_meta_associative_container = 0x0800, + _user_defined_traits = 0xFFFF, + _entt_enum_as_bitmask = 0xFFFF +}; + +template<typename Type> +[[nodiscard]] auto meta_to_user_traits(const meta_traits traits) noexcept { + static_assert(std::is_enum_v<Type>, "Invalid enum type"); + constexpr auto shift = popcount(static_cast<std::underlying_type_t<meta_traits>>(meta_traits::_user_defined_traits)); + return Type{static_cast<std::underlying_type_t<Type>>(static_cast<std::underlying_type_t<meta_traits>>(traits) >> shift)}; +} + +template<typename Type> +[[nodiscard]] auto user_to_meta_traits(const Type value) noexcept { + static_assert(std::is_enum_v<Type>, "Invalid enum type"); + constexpr auto shift = popcount(static_cast<std::underlying_type_t<meta_traits>>(meta_traits::_user_defined_traits)); + const auto traits = static_cast<std::underlying_type_t<internal::meta_traits>>(static_cast<std::underlying_type_t<Type>>(value)); + ENTT_ASSERT(traits < ((~static_cast<std::underlying_type_t<meta_traits>>(meta_traits::_user_defined_traits)) >> shift), "Invalid traits"); + return meta_traits{traits << shift}; +} + +struct meta_type_node; + +struct meta_custom_node { + id_type type{}; + std::shared_ptr<void> value{}; +}; + +struct meta_prop_node { + meta_type_node (*type)(const meta_context &) noexcept {}; + std::shared_ptr<void> value{}; +}; + +struct meta_base_node { + meta_type_node (*type)(const meta_context &) noexcept {}; + const void *(*cast)(const void *) noexcept {}; +}; + +struct meta_conv_node { + meta_any (*conv)(const meta_ctx &, const void *){}; +}; + +struct meta_ctor_node { + using size_type = std::size_t; + + size_type arity{0u}; + meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; + meta_any (*invoke)(const meta_ctx &, meta_any *const){}; +}; + +struct meta_dtor_node { + void (*dtor)(void *){}; +}; + +struct meta_data_node { + using size_type = std::size_t; + + meta_traits traits{meta_traits::is_none}; + size_type arity{0u}; + meta_type_node (*type)(const meta_context &) noexcept {}; + meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; + bool (*set)(meta_handle, meta_any){}; + meta_any (*get)(const meta_ctx &, meta_handle){}; + meta_custom_node custom{}; + dense_map<id_type, meta_prop_node, identity> prop{}; +}; + +struct meta_func_node { + using size_type = std::size_t; + + meta_traits traits{meta_traits::is_none}; + size_type arity{0u}; + meta_type_node (*ret)(const meta_context &) noexcept {}; + meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; + meta_any (*invoke)(const meta_ctx &, meta_handle, meta_any *const){}; + std::shared_ptr<meta_func_node> next{}; + meta_custom_node custom{}; + dense_map<id_type, meta_prop_node, identity> prop{}; +}; + +struct meta_template_node { + using size_type = std::size_t; + + size_type arity{0u}; + meta_type_node (*type)(const meta_context &) noexcept {}; + meta_type_node (*arg)(const meta_context &, const size_type) noexcept {}; +}; + +struct meta_type_descriptor { + dense_map<id_type, meta_ctor_node, identity> ctor{}; + dense_map<id_type, meta_base_node, identity> base{}; + dense_map<id_type, meta_conv_node, identity> conv{}; + dense_map<id_type, meta_data_node, identity> data{}; + dense_map<id_type, meta_func_node, identity> func{}; + dense_map<id_type, meta_prop_node, identity> prop{}; +}; + +struct meta_type_node { + using size_type = std::size_t; + + const type_info *info{}; + id_type id{}; + meta_traits traits{meta_traits::is_none}; + size_type size_of{0u}; + meta_type_node (*resolve)(const meta_context &) noexcept {}; + meta_type_node (*remove_pointer)(const meta_context &) noexcept {}; + meta_any (*default_constructor)(const meta_ctx &){}; + double (*conversion_helper)(void *, const void *){}; + meta_any (*from_void)(const meta_ctx &, void *, const void *){}; + meta_template_node templ{}; + meta_dtor_node dtor{}; + meta_custom_node custom{}; + std::shared_ptr<meta_type_descriptor> details{}; +}; + +template<auto Member> +auto *look_for(const meta_context &context, const meta_type_node &node, const id_type id) { + if(node.details) { + if(const auto it = (node.details.get()->*Member).find(id); it != (node.details.get()->*Member).cend()) { + return &it->second; + } + + for(auto &&curr: node.details->base) { + if(auto *elem = look_for<Member>(context, curr.second.type(context), id); elem) { + return elem; + } + } + } + + return static_cast<typename std::remove_reference_t<decltype(node.details.get()->*Member)>::mapped_type *>(nullptr); +} + +template<typename Type> +meta_type_node resolve(const meta_context &) noexcept; + +template<typename... Args> +[[nodiscard]] auto meta_arg_node(const meta_context &context, type_list<Args...>, [[maybe_unused]] const std::size_t index) noexcept { + [[maybe_unused]] std::size_t pos{}; + meta_type_node (*value)(const meta_context &) noexcept = nullptr; + ((value = (pos++ == index ? &resolve<std::remove_cv_t<std::remove_reference_t<Args>>> : value)), ...); + ENTT_ASSERT(value != nullptr, "Out of bounds"); + return value(context); +} + +[[nodiscard]] inline const void *try_cast(const meta_context &context, const meta_type_node &from, const meta_type_node &to, const void *instance) noexcept { + if(from.info && to.info && *from.info == *to.info) { + return instance; + } + + if(from.details) { + for(auto &&curr: from.details->base) { + if(const void *elem = try_cast(context, curr.second.type(context), to, curr.second.cast(instance)); elem) { + return elem; + } + } + } + + return nullptr; +} + +template<typename Func> +[[nodiscard]] inline auto try_convert(const meta_context &context, const meta_type_node &from, const type_info &to, const bool arithmetic_or_enum, const void *instance, Func func) { + if(from.info && *from.info == to) { + return func(instance, from); + } + + if(from.details) { + if(auto it = from.details->conv.find(to.hash()); it != from.details->conv.cend()) { + return func(instance, it->second); + } + + for(auto &&curr: from.details->base) { + if(auto other = try_convert(context, curr.second.type(context), to, arithmetic_or_enum, curr.second.cast(instance), func); other) { + return other; + } + } + } + + if(from.conversion_helper && arithmetic_or_enum) { + return func(instance, from.conversion_helper); + } + + return func(instance); +} + +[[nodiscard]] inline const meta_type_node *try_resolve(const meta_context &context, const type_info &info) noexcept { + const auto it = context.value.find(info.hash()); + return it != context.value.end() ? &it->second : nullptr; +} + +template<typename Type> +[[nodiscard]] meta_type_node resolve(const meta_context &context) noexcept { + static_assert(std::is_same_v<Type, std::remove_const_t<std::remove_reference_t<Type>>>, "Invalid type"); + + if(auto *elem = try_resolve(context, type_id<Type>()); elem) { + return *elem; + } + + meta_type_node node{ + &type_id<Type>(), + type_id<Type>().hash(), + (std::is_arithmetic_v<Type> ? meta_traits::is_arithmetic : meta_traits::is_none) + | (std::is_integral_v<Type> ? meta_traits::is_integral : meta_traits::is_none) + | (std::is_signed_v<Type> ? meta_traits::is_signed : meta_traits::is_none) + | (std::is_array_v<Type> ? meta_traits::is_array : meta_traits::is_none) + | (std::is_enum_v<Type> ? meta_traits::is_enum : meta_traits::is_none) + | (std::is_class_v<Type> ? meta_traits::is_class : meta_traits::is_none) + | (std::is_pointer_v<Type> ? meta_traits::is_pointer : meta_traits::is_none) + | (is_meta_pointer_like_v<Type> ? meta_traits::is_meta_pointer_like : meta_traits::is_none) + | (is_complete_v<meta_sequence_container_traits<Type>> ? meta_traits::is_meta_sequence_container : meta_traits::is_none) + | (is_complete_v<meta_associative_container_traits<Type>> ? meta_traits::is_meta_associative_container : meta_traits::is_none), + size_of_v<Type>, + &resolve<Type>, + &resolve<std::remove_cv_t<std::remove_pointer_t<Type>>>}; + + if constexpr(std::is_default_constructible_v<Type>) { + node.default_constructor = +[](const meta_ctx &ctx) { + return meta_any{ctx, std::in_place_type<Type>}; + }; + } + + if constexpr(std::is_arithmetic_v<Type>) { + node.conversion_helper = +[](void *lhs, const void *rhs) { + return lhs ? static_cast<double>(*static_cast<Type *>(lhs) = static_cast<Type>(*static_cast<const double *>(rhs))) : static_cast<double>(*static_cast<const Type *>(rhs)); + }; + } else if constexpr(std::is_enum_v<Type>) { + node.conversion_helper = +[](void *lhs, const void *rhs) { + return lhs ? static_cast<double>(*static_cast<Type *>(lhs) = static_cast<Type>(static_cast<std::underlying_type_t<Type>>(*static_cast<const double *>(rhs)))) : static_cast<double>(*static_cast<const Type *>(rhs)); + }; + } + + if constexpr(!std::is_void_v<Type> && !std::is_function_v<Type>) { + node.from_void = +[](const meta_ctx &ctx, void *elem, const void *celem) { + if(elem) { + return meta_any{ctx, std::in_place_type<std::decay_t<Type> &>, *static_cast<std::decay_t<Type> *>(elem)}; + } + + return meta_any{ctx, std::in_place_type<const std::decay_t<Type> &>, *static_cast<const std::decay_t<Type> *>(celem)}; + }; + } + + if constexpr(is_complete_v<meta_template_traits<Type>>) { + node.templ = meta_template_node{ + meta_template_traits<Type>::args_type::size, + &resolve<typename meta_template_traits<Type>::class_type>, + +[](const meta_context &area, const std::size_t index) noexcept { return meta_arg_node(area, typename meta_template_traits<Type>::args_type{}, index); }}; + } + + return node; +} + +} // namespace internal +/*! @endcond */ + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/meta/pointer.hpp b/external/entt/include/entt/meta/pointer.hpp new file mode 100644 index 0000000..e90b0a1 --- /dev/null +++ b/external/entt/include/entt/meta/pointer.hpp @@ -0,0 +1,51 @@ +// IWYU pragma: always_keep + +#ifndef ENTT_META_POINTER_HPP +#define ENTT_META_POINTER_HPP + +#include <memory> +#include <type_traits> +#include "type_traits.hpp" + +namespace entt { + +/** + * @brief Makes plain pointers pointer-like types for the meta system. + * @tparam Type Element type. + */ +template<typename Type> +struct is_meta_pointer_like<Type *> + : std::true_type {}; + +/** + * @brief Partial specialization used to reject pointers to arrays. + * @tparam Type Type of elements of the array. + * @tparam N Number of elements of the array. + */ +template<typename Type, std::size_t N> +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +struct is_meta_pointer_like<Type (*)[N]> + : std::false_type {}; + +/** + * @brief Makes `std::shared_ptr`s of any type pointer-like types for the meta + * system. + * @tparam Type Element type. + */ +template<typename Type> +struct is_meta_pointer_like<std::shared_ptr<Type>> + : 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<typename Type, typename... Args> +struct is_meta_pointer_like<std::unique_ptr<Type, Args...>> + : std::true_type {}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/meta/policy.hpp b/external/entt/include/entt/meta/policy.hpp new file mode 100644 index 0000000..b939599 --- /dev/null +++ b/external/entt/include/entt/meta/policy.hpp @@ -0,0 +1,58 @@ +#ifndef ENTT_META_POLICY_HPP +#define ENTT_META_POLICY_HPP + +#include <type_traits> + +namespace entt { + +/*! @brief Empty class type used to request the _as ref_ policy. */ +struct as_ref_t final { + /*! @cond TURN_OFF_DOXYGEN */ + template<typename Type> + static constexpr bool value = std::is_reference_v<Type> && !std::is_const_v<std::remove_reference_t<Type>>; + /*! @endcond */ +}; + +/*! @brief Empty class type used to request the _as cref_ policy. */ +struct as_cref_t final { + /*! @cond TURN_OFF_DOXYGEN */ + template<typename Type> + static constexpr bool value = std::is_reference_v<Type>; + /*! @endcond */ +}; + +/*! @brief Empty class type used to request the _as-is_ policy. */ +struct as_is_t final { + /*! @cond TURN_OFF_DOXYGEN */ + template<typename> + static constexpr bool value = true; + /*! @endcond */ +}; + +/*! @brief Empty class type used to request the _as void_ policy. */ +struct as_void_t final { + /*! @cond TURN_OFF_DOXYGEN */ + template<typename> + static constexpr bool value = true; + /*! @endcond */ +}; + +/** + * @brief Provides the member constant `value` to true if a type also is a meta + * policy, false otherwise. + * @tparam Type Type to check. + */ +template<typename Type> +struct is_meta_policy + : std::bool_constant<std::is_same_v<Type, as_ref_t> || std::is_same_v<Type, as_cref_t> || std::is_same_v<Type, as_is_t> || std::is_same_v<Type, as_void_t>> {}; + +/** + * @brief Helper variable template. + * @tparam Type Type to check. + */ +template<typename Type> +inline constexpr bool is_meta_policy_v = is_meta_policy<Type>::value; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/meta/range.hpp b/external/entt/include/entt/meta/range.hpp new file mode 100644 index 0000000..f53b572 --- /dev/null +++ b/external/entt/include/entt/meta/range.hpp @@ -0,0 +1,143 @@ +#ifndef ENTT_META_RANGE_HPP +#define ENTT_META_RANGE_HPP + +#include <cstddef> +#include <iterator> +#include <utility> +#include "../core/fwd.hpp" +#include "../core/iterator.hpp" +#include "context.hpp" + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template<typename Type, typename It> +struct meta_range_iterator final { + using value_type = std::pair<id_type, Type>; + using pointer = input_iterator_pointer<value_type>; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr meta_range_iterator() noexcept + : it{}, + ctx{} {} + + constexpr meta_range_iterator(const meta_ctx &area, const It iter) noexcept + : it{iter}, + ctx{&area} {} + + constexpr meta_range_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr meta_range_iterator operator++(int) noexcept { + meta_range_iterator orig = *this; + return ++(*this), orig; + } + + constexpr meta_range_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr meta_range_iterator operator--(int) noexcept { + meta_range_iterator orig = *this; + return operator--(), orig; + } + + constexpr meta_range_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr meta_range_iterator operator+(const difference_type value) const noexcept { + meta_range_iterator copy = *this; + return (copy += value); + } + + constexpr meta_range_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr meta_range_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].first, Type{*ctx, it[value].second}}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + template<typename... Args> + friend constexpr std::ptrdiff_t operator-(const meta_range_iterator<Args...> &, const meta_range_iterator<Args...> &) noexcept; + + template<typename... Args> + friend constexpr bool operator==(const meta_range_iterator<Args...> &, const meta_range_iterator<Args...> &) noexcept; + + template<typename... Args> + friend constexpr bool operator<(const meta_range_iterator<Args...> &, const meta_range_iterator<Args...> &) noexcept; + +private: + It it; + const meta_ctx *ctx; +}; + +template<typename... Args> +[[nodiscard]] constexpr std::ptrdiff_t operator-(const meta_range_iterator<Args...> &lhs, const meta_range_iterator<Args...> &rhs) noexcept { + return lhs.it - rhs.it; +} + +template<typename... Args> +[[nodiscard]] constexpr bool operator==(const meta_range_iterator<Args...> &lhs, const meta_range_iterator<Args...> &rhs) noexcept { + return lhs.it == rhs.it; +} + +template<typename... Args> +[[nodiscard]] constexpr bool operator!=(const meta_range_iterator<Args...> &lhs, const meta_range_iterator<Args...> &rhs) noexcept { + return !(lhs == rhs); +} + +template<typename... Args> +[[nodiscard]] constexpr bool operator<(const meta_range_iterator<Args...> &lhs, const meta_range_iterator<Args...> &rhs) noexcept { + return lhs.it < rhs.it; +} + +template<typename... Args> +[[nodiscard]] constexpr bool operator>(const meta_range_iterator<Args...> &lhs, const meta_range_iterator<Args...> &rhs) noexcept { + return rhs < lhs; +} + +template<typename... Args> +[[nodiscard]] constexpr bool operator<=(const meta_range_iterator<Args...> &lhs, const meta_range_iterator<Args...> &rhs) noexcept { + return !(lhs > rhs); +} + +template<typename... Args> +[[nodiscard]] constexpr bool operator>=(const meta_range_iterator<Args...> &lhs, const meta_range_iterator<Args...> &rhs) noexcept { + return !(lhs < rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Iterable range to use to iterate all types of meta objects. + * @tparam Type Type of meta objects returned. + * @tparam It Type of forward iterator. + */ +template<typename Type, typename It> +using meta_range = iterable_adaptor<internal::meta_range_iterator<Type, It>>; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/meta/resolve.hpp b/external/entt/include/entt/meta/resolve.hpp new file mode 100644 index 0000000..9360db5 --- /dev/null +++ b/external/entt/include/entt/meta/resolve.hpp @@ -0,0 +1,102 @@ +#ifndef ENTT_META_RESOLVE_HPP +#define ENTT_META_RESOLVE_HPP + +#include <type_traits> +#include "../core/type_info.hpp" +#include "../locator/locator.hpp" +#include "context.hpp" +#include "meta.hpp" +#include "node.hpp" +#include "range.hpp" + +namespace entt { + +/** + * @brief Returns the meta type associated with a given type. + * @tparam Type Type to use to search for a meta type. + * @param ctx The context from which to search for meta types. + * @return The meta type associated with the given type, if any. + */ +template<typename Type> +[[nodiscard]] meta_type resolve(const meta_ctx &ctx) noexcept { + auto &&context = internal::meta_context::from(ctx); + return {ctx, internal::resolve<std::remove_cv_t<std::remove_reference_t<Type>>>(context)}; +} + +/** + * @brief Returns the meta type associated with a given type. + * @tparam Type Type to use to search for a meta type. + * @return The meta type associated with the given type, if any. + */ +template<typename Type> +[[nodiscard]] meta_type resolve() noexcept { + return resolve<Type>(locator<meta_ctx>::value_or()); +} + +/** + * @brief Returns a range to use to visit all meta types. + * @param ctx The context from which to search for meta types. + * @return An iterable range to use to visit all meta types. + */ +[[nodiscard]] inline meta_range<meta_type, typename decltype(internal::meta_context::value)::const_iterator> resolve(const meta_ctx &ctx) noexcept { + auto &&context = internal::meta_context::from(ctx); + return {{ctx, context.value.cbegin()}, {ctx, context.value.cend()}}; +} + +/** + * @brief Returns a range to use to visit all meta types. + * @return An iterable range to use to visit all meta types. + */ +[[nodiscard]] inline meta_range<meta_type, typename decltype(internal::meta_context::value)::const_iterator> resolve() noexcept { + return resolve(locator<meta_ctx>::value_or()); +} + +/** + * @brief Returns the meta type associated with a given identifier, if any. + * @param ctx The context from which to search for meta types. + * @param id Unique identifier. + * @return The meta type associated with the given identifier, if any. + */ +[[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const id_type id) noexcept { + for(auto &&curr: resolve(ctx)) { + if(curr.second.id() == id) { + return curr.second; + } + } + + return meta_type{}; +} + +/** + * @brief Returns the meta type associated with a given identifier, if any. + * @param id Unique identifier. + * @return The meta type associated with the given identifier, if any. + */ +[[nodiscard]] inline meta_type resolve(const id_type id) noexcept { + return resolve(locator<meta_ctx>::value_or(), id); +} + +/** + * @brief Returns the meta type associated with a given type info object. + * @param ctx The context from which to search for meta types. + * @param info The type info object of the requested type. + * @return The meta type associated with the given type info object, if any. + */ +[[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const type_info &info) noexcept { + auto &&context = internal::meta_context::from(ctx); + const auto *elem = internal::try_resolve(context, info); + return elem ? meta_type{ctx, *elem} : meta_type{}; +} + +/** + * @brief Returns the meta type associated with a given type info object. + * @param info The type info object of the requested type. + * @return The meta type associated with the given type info object, if any. + */ +[[nodiscard]] inline meta_type resolve(const type_info &info) noexcept { + return resolve(locator<meta_ctx>::value_or(), info); +} + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/meta/template.hpp b/external/entt/include/entt/meta/template.hpp new file mode 100644 index 0000000..5eab2af --- /dev/null +++ b/external/entt/include/entt/meta/template.hpp @@ -0,0 +1,29 @@ +// IWYU pragma: always_keep + +#ifndef ENTT_META_TEMPLATE_HPP +#define ENTT_META_TEMPLATE_HPP + +#include "../core/type_traits.hpp" + +namespace entt { + +/*! @brief Utility class to disambiguate class templates. */ +template<template<typename...> class> +struct meta_class_template_tag {}; + +/** + * @brief General purpose traits class for generating meta template information. + * @tparam Clazz Type of class template. + * @tparam Args Types of template arguments. + */ +template<template<typename...> class Clazz, typename... Args> +struct meta_template_traits<Clazz<Args...>> { + /*! @brief Wrapped class template. */ + using class_type = meta_class_template_tag<Clazz>; + /*! @brief List of template arguments. */ + using args_type = type_list<Args...>; +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/meta/type_traits.hpp b/external/entt/include/entt/meta/type_traits.hpp new file mode 100644 index 0000000..9ba167f --- /dev/null +++ b/external/entt/include/entt/meta/type_traits.hpp @@ -0,0 +1,54 @@ +#ifndef ENTT_META_TYPE_TRAITS_HPP +#define ENTT_META_TYPE_TRAITS_HPP + +#include <type_traits> +#include <utility> + +namespace entt { + +/** + * @brief Traits class template to be specialized to enable support for meta + * template information. + */ +template<typename> +struct meta_template_traits; + +/** + * @brief Traits class template to be specialized to enable support for meta + * sequence containers. + */ +template<typename> +struct meta_sequence_container_traits; + +/** + * @brief Traits class template to be specialized to enable support for meta + * associative containers. + */ +template<typename> +struct meta_associative_container_traits; + +/** + * @brief Provides the member constant `value` to true if a given type is a + * pointer-like type from the point of view of the meta system, false otherwise. + */ +template<typename> +struct is_meta_pointer_like: std::false_type {}; + +/** + * @brief Partial specialization to ensure that const pointer-like types are + * also accepted. + * @tparam Type Potentially pointer-like type. + */ +template<typename Type> +struct is_meta_pointer_like<const Type>: is_meta_pointer_like<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type Potentially pointer-like type. + */ +template<typename Type> +inline constexpr auto is_meta_pointer_like_v = is_meta_pointer_like<Type>::value; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/meta/utility.hpp b/external/entt/include/entt/meta/utility.hpp new file mode 100644 index 0000000..b2e45c6 --- /dev/null +++ b/external/entt/include/entt/meta/utility.hpp @@ -0,0 +1,539 @@ +#ifndef ENTT_META_UTILITY_HPP +#define ENTT_META_UTILITY_HPP + +#include <cstddef> +#include <functional> +#include <type_traits> +#include <utility> +#include "../core/type_traits.hpp" +#include "../locator/locator.hpp" +#include "meta.hpp" +#include "node.hpp" +#include "policy.hpp" + +namespace entt { + +/** + * @brief Meta function descriptor traits. + * @tparam Ret Function return type. + * @tparam Args Function arguments. + * @tparam Static Function staticness. + * @tparam Const Function constness. + */ +template<typename Ret, typename Args, bool Static, bool Const> +struct meta_function_descriptor_traits { + /*! @brief Meta function return type. */ + using return_type = Ret; + /*! @brief Meta function arguments. */ + using args_type = Args; + + /*! @brief True if the meta function is static, false otherwise. */ + static constexpr bool is_static = Static; + /*! @brief True if the meta function is const, false otherwise. */ + static constexpr bool is_const = Const; +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename, typename> +struct meta_function_descriptor; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam Class Actual owner of the member function. + * @tparam Args Function arguments. + */ +template<typename Type, typename Ret, typename Class, typename... Args> +struct meta_function_descriptor<Type, Ret (Class::*)(Args...) const> + : meta_function_descriptor_traits< + Ret, + std::conditional_t<std::is_base_of_v<Class, Type>, type_list<Args...>, type_list<const Class &, Args...>>, + !std::is_base_of_v<Class, Type>, + true> {}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam Class Actual owner of the member function. + * @tparam Args Function arguments. + */ +template<typename Type, typename Ret, typename Class, typename... Args> +struct meta_function_descriptor<Type, Ret (Class::*)(Args...)> + : meta_function_descriptor_traits< + Ret, + std::conditional_t<std::is_base_of_v<Class, Type>, type_list<Args...>, type_list<Class &, Args...>>, + !std::is_base_of_v<Class, Type>, + false> {}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta data is associated. + * @tparam Class Actual owner of the data member. + * @tparam Ret Data member type. + */ +template<typename Type, typename Ret, typename Class> +struct meta_function_descriptor<Type, Ret Class::*> + : meta_function_descriptor_traits< + Ret &, + std::conditional_t<std::is_base_of_v<Class, Type>, type_list<>, type_list<Class &>>, + !std::is_base_of_v<Class, Type>, + false> {}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam MaybeType First function argument. + * @tparam Args Other function arguments. + */ +template<typename Type, typename Ret, typename MaybeType, typename... Args> +struct meta_function_descriptor<Type, Ret (*)(MaybeType, Args...)> + : meta_function_descriptor_traits< + Ret, + std::conditional_t< + std::is_same_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>, + type_list<Args...>, + type_list<MaybeType, Args...>>, + !(std::is_same_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>), + std::is_const_v<std::remove_reference_t<MaybeType>> && (std::is_same_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>)> {}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + */ +template<typename Type, typename Ret> +struct meta_function_descriptor<Type, Ret (*)()> + : meta_function_descriptor_traits< + Ret, + type_list<>, + true, + false> {}; + +/** + * @brief Meta function helper. + * + * Converts a function type to be associated with a reflected type into its meta + * function descriptor. + * + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Candidate The actual function to associate with the reflected type. + */ +template<typename Type, typename Candidate> +class meta_function_helper { + template<typename Ret, typename... Args, typename Class> + static constexpr meta_function_descriptor<Type, Ret (Class::*)(Args...) const> get_rid_of_noexcept(Ret (Class::*)(Args...) const); + + template<typename Ret, typename... Args, typename Class> + static constexpr meta_function_descriptor<Type, Ret (Class::*)(Args...)> get_rid_of_noexcept(Ret (Class::*)(Args...)); + + template<typename Ret, typename Class> + static constexpr meta_function_descriptor<Type, Ret Class::*> get_rid_of_noexcept(Ret Class::*); + + template<typename Ret, typename... Args> + static constexpr meta_function_descriptor<Type, Ret (*)(Args...)> get_rid_of_noexcept(Ret (*)(Args...)); + + template<typename Class> + static constexpr meta_function_descriptor<Class, decltype(&Class::operator())> get_rid_of_noexcept(Class); + +public: + /*! @brief The meta function descriptor of the given function. */ + using type = decltype(get_rid_of_noexcept(std::declval<Candidate>())); +}; + +/** + * @brief Helper type. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Candidate The actual function to associate with the reflected type. + */ +template<typename Type, typename Candidate> +using meta_function_helper_t = typename meta_function_helper<Type, Candidate>::type; + +/** + * @brief Wraps a value depending on the given policy. + * + * This function always returns a wrapped value in the requested context.<br/> + * Therefore, if the passed value is itself a wrapped object with a different + * context, it undergoes a rebinding to the requested context. + * + * @tparam Policy Optional policy (no policy set by default). + * @tparam Type Type of value to wrap. + * @param ctx The context from which to search for meta types. + * @param value Value to wrap. + * @return A meta any containing the returned value, if any. + */ +template<typename Policy = as_is_t, typename Type> +[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) { + if constexpr(std::is_same_v<Policy, as_void_t>) { + return meta_any{ctx, std::in_place_type<void>}; + } else if constexpr(std::is_same_v<Policy, as_ref_t>) { + return meta_any{ctx, std::in_place_type<Type>, value}; + } else if constexpr(std::is_same_v<Policy, as_cref_t>) { + static_assert(std::is_lvalue_reference_v<Type>, "Invalid type"); + return meta_any{ctx, std::in_place_type<const std::remove_reference_t<Type> &>, std::as_const(value)}; + } else { + return meta_any{ctx, std::forward<Type>(value)}; + } +} + +/** + * @brief Wraps a value depending on the given policy. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Type Type of value to wrap. + * @param value Value to wrap. + * @return A meta any containing the returned value, if any. + */ +template<typename Policy = as_is_t, typename Type> +[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(Type &&value) { + return meta_dispatch<Policy, Type>(locator<meta_ctx>::value_or(), std::forward<Type>(value)); +} + +/** + * @brief Returns the meta type of the i-th element of a list of arguments. + * @tparam Type Type list of the actual types of arguments. + * @param ctx The context from which to search for meta types. + * @param index The index of the element for which to return the meta type. + * @return The meta type of the i-th element of the list of arguments. + */ +template<typename Type> +[[nodiscard]] static meta_type meta_arg(const meta_ctx &ctx, const std::size_t index) noexcept { + auto &&context = internal::meta_context::from(ctx); + return {ctx, internal::meta_arg_node(context, Type{}, index)}; +} + +/** + * @brief Returns the meta type of the i-th element of a list of arguments. + * @tparam Type Type list of the actual types of arguments. + * @param index The index of the element for which to return the meta type. + * @return The meta type of the i-th element of the list of arguments. + */ +template<typename Type> +[[nodiscard]] static meta_type meta_arg(const std::size_t index) noexcept { + return meta_arg<Type>(locator<meta_ctx>::value_or(), index); +} + +/** + * @brief Sets the value of a given variable. + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to set. + * @param instance An opaque instance of the underlying type, if required. + * @param value Parameter to use to set the variable. + * @return True in case of success, false otherwise. + */ +template<typename Type, auto Data> +[[nodiscard]] bool meta_setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) { + if constexpr(!std::is_same_v<decltype(Data), Type> && !std::is_same_v<decltype(Data), std::nullptr_t>) { + if constexpr(std::is_member_function_pointer_v<decltype(Data)> || std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>>) { + using descriptor = meta_function_helper_t<Type, decltype(Data)>; + using data_type = type_list_element_t<descriptor::is_static, typename descriptor::args_type>; + + if(auto *const clazz = instance->try_cast<Type>(); clazz && value.allow_cast<data_type>()) { + std::invoke(Data, *clazz, value.cast<data_type>()); + return true; + } + } else if constexpr(std::is_member_object_pointer_v<decltype(Data)>) { + using data_type = std::remove_reference_t<typename meta_function_helper_t<Type, decltype(Data)>::return_type>; + + if constexpr(!std::is_array_v<data_type> && !std::is_const_v<data_type>) { + if(auto *const clazz = instance->try_cast<Type>(); clazz && value.allow_cast<data_type>()) { + std::invoke(Data, *clazz) = value.cast<data_type>(); + return true; + } + } + } else { + using data_type = std::remove_reference_t<decltype(*Data)>; + + if constexpr(!std::is_array_v<data_type> && !std::is_const_v<data_type>) { + if(value.allow_cast<data_type>()) { + *Data = value.cast<data_type>(); + return true; + } + } + } + } + + return false; +} + +/** + * @brief Gets the value of a given variable. + * + * @warning + * The context provided is used only for the return type.<br/> + * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to get. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @param instance An opaque instance of the underlying type, if required. + * @return A meta any containing the value of the underlying variable. + */ +template<typename Type, auto Data, typename Policy = as_is_t> +[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_getter(const meta_ctx &ctx, [[maybe_unused]] meta_handle instance) { + if constexpr(std::is_member_pointer_v<decltype(Data)> || std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>>) { + if constexpr(!std::is_array_v<std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(Data), Type &>>>>) { + if constexpr(std::is_invocable_v<decltype(Data), Type &>) { + if(auto *clazz = instance->try_cast<Type>(); clazz) { + return meta_dispatch<Policy>(ctx, std::invoke(Data, *clazz)); + } + } + + if constexpr(std::is_invocable_v<decltype(Data), const Type &>) { + if(auto *fallback = instance->try_cast<const Type>(); fallback) { + return meta_dispatch<Policy>(ctx, std::invoke(Data, *fallback)); + } + } + } + + return meta_any{meta_ctx_arg, ctx}; + } else if constexpr(std::is_pointer_v<decltype(Data)>) { + if constexpr(std::is_array_v<std::remove_pointer_t<decltype(Data)>>) { + return meta_any{meta_ctx_arg, ctx}; + } else { + return meta_dispatch<Policy>(ctx, *Data); + } + } else { + return meta_dispatch<Policy>(ctx, Data); + } +} + +/** + * @brief Gets the value of a given variable. + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to get. + * @tparam Policy Optional policy (no policy set by default). + * @param instance An opaque instance of the underlying type, if required. + * @return A meta any containing the value of the underlying variable. + */ +template<typename Type, auto Data, typename Policy = as_is_t> +[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_getter(meta_handle instance) { + return meta_getter<Type, Data, Policy>(locator<meta_ctx>::value_or(), std::move(instance)); +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template<typename Policy, typename Candidate, typename... Args> +[[nodiscard]] meta_any meta_invoke_with_args(const meta_ctx &ctx, Candidate &&candidate, Args &&...args) { + if constexpr(std::is_void_v<decltype(std::invoke(std::forward<Candidate>(candidate), args...))>) { + std::invoke(std::forward<Candidate>(candidate), args...); + return meta_any{ctx, std::in_place_type<void>}; + } else { + return meta_dispatch<Policy>(ctx, std::invoke(std::forward<Candidate>(candidate), args...)); + } +} + +template<typename Type, typename Policy, typename Candidate, std::size_t... Index> +[[nodiscard]] meta_any meta_invoke(const meta_ctx &ctx, [[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *const args, std::index_sequence<Index...>) { + using descriptor = meta_function_helper_t<Type, std::remove_reference_t<Candidate>>; + + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) - waiting for C++20 (and std::span) + if constexpr(std::is_invocable_v<std::remove_reference_t<Candidate>, const Type &, type_list_element_t<Index, typename descriptor::args_type>...>) { + if(const auto *const clazz = instance->try_cast<const Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) { + return meta_invoke_with_args<Policy>(ctx, std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...); + } + } else if constexpr(std::is_invocable_v<std::remove_reference_t<Candidate>, Type &, type_list_element_t<Index, typename descriptor::args_type>...>) { + if(auto *const clazz = instance->try_cast<Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) { + return meta_invoke_with_args<Policy>(ctx, std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...); + } + } else { + if(((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) { + return meta_invoke_with_args<Policy>(ctx, std::forward<Candidate>(candidate), (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...); + } + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + + return meta_any{meta_ctx_arg, ctx}; +} + +template<typename Type, typename... Args, std::size_t... Index> +[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, meta_any *const args, std::index_sequence<Index...>) { + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) - waiting for C++20 (and std::span) + if(((args + Index)->allow_cast<Args>() && ...)) { + return meta_any{ctx, std::in_place_type<Type>, (args + Index)->cast<Args>()...}; + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + + return meta_any{meta_ctx_arg, ctx}; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Tries to _invoke_ an object given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.<br/> + * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @tparam Candidate The type of the actual object to _invoke_. + * @param instance An opaque instance of the underlying type, if required. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template<typename Type, typename Policy = as_is_t, typename Candidate> +[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_invoke(const meta_ctx &ctx, meta_handle instance, Candidate &&candidate, meta_any *const args) { + return internal::meta_invoke<Type, Policy>(ctx, std::move(instance), std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{}); +} + +/** + * @brief Tries to _invoke_ an object given a list of erased parameters. + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Candidate The type of the actual object to _invoke_. + * @param instance An opaque instance of the underlying type, if required. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template<typename Type, typename Policy = as_is_t, typename Candidate> +[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_invoke(meta_handle instance, Candidate &&candidate, meta_any *const args) { + return meta_invoke<Type, Policy>(locator<meta_ctx>::value_or(), std::move(instance), std::forward<Candidate>(candidate), args); +} + +/** + * @brief Tries to invoke a function given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.<br/> + * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @param instance An opaque instance of the underlying type, if required. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template<typename Type, auto Candidate, typename Policy = as_is_t> +[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_invoke(const meta_ctx &ctx, meta_handle instance, meta_any *const args) { + return internal::meta_invoke<Type, Policy>(ctx, std::move(instance), Candidate, args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<decltype(Candidate)>>::args_type::size>{}); +} + +/** + * @brief Tries to invoke a function given a list of erased parameters. + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param instance An opaque instance of the underlying type, if required. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template<typename Type, auto Candidate, typename Policy = as_is_t> +[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_invoke(meta_handle instance, meta_any *const args) { + return meta_invoke<Type, Candidate, Policy>(locator<meta_ctx>::value_or(), std::move(instance), args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.<br/> + * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Actual type of the instance to construct. + * @tparam Args Types of arguments expected. + * @param ctx The context from which to search for meta types. + * @param args Parameters to use to construct the instance. + * @return A meta any containing the new instance, if any. + */ +template<typename Type, typename... Args> +[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, meta_any *const args) { + return internal::meta_construct<Type, Args...>(ctx, args, std::index_sequence_for<Args...>{}); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * @tparam Type Actual type of the instance to construct. + * @tparam Args Types of arguments expected. + * @param args Parameters to use to construct the instance. + * @return A meta any containing the new instance, if any. + */ +template<typename Type, typename... Args> +[[nodiscard]] meta_any meta_construct(meta_any *const args) { + return meta_construct<Type, Args...>(locator<meta_ctx>::value_or(), args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.<br/> + * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Candidate The type of the actual object to _invoke_. + * @param ctx The context from which to search for meta types. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template<typename Type, typename Policy = as_is_t, typename Candidate> +[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, Candidate &&candidate, meta_any *const args) { + if constexpr(meta_function_helper_t<Type, Candidate>::is_static || std::is_class_v<std::remove_cv_t<std::remove_reference_t<Candidate>>>) { + return internal::meta_invoke<Type, Policy>(ctx, {}, std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{}); + } else { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - waiting for C++20 (and std::span) + return internal::meta_invoke<Type, Policy>(ctx, *args, std::forward<Candidate>(candidate), args + 1u, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{}); + } +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Candidate The type of the actual object to _invoke_. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template<typename Type, typename Policy = as_is_t, typename Candidate> +[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_construct(Candidate &&candidate, meta_any *const args) { + return meta_construct<Type, Policy>(locator<meta_ctx>::value_or(), std::forward<Candidate>(candidate), args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.<br/> + * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template<typename Type, auto Candidate, typename Policy = as_is_t> +[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_construct(const meta_ctx &ctx, meta_any *const args) { + return meta_construct<Type, Policy>(ctx, Candidate, args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template<typename Type, auto Candidate, typename Policy = as_is_t> +[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_construct(meta_any *const args) { + return meta_construct<Type, Candidate, Policy>(locator<meta_ctx>::value_or(), args); +} + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/platform/android-ndk-r17.hpp b/external/entt/include/entt/platform/android-ndk-r17.hpp new file mode 100644 index 0000000..5696123 --- /dev/null +++ b/external/entt/include/entt/platform/android-ndk-r17.hpp @@ -0,0 +1,67 @@ +#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 <android/ndk-version.h> +# if __NDK_MAJOR__ == 17 + +# include <functional> +# include <type_traits> +# include <utility> + +namespace std { + +namespace internal { + +template<typename Func, typename... Args> +constexpr auto is_invocable(int) -> decltype(std::invoke(std::declval<Func>(), std::declval<Args>()...), std::true_type{}); + +template<typename, typename...> +constexpr std::false_type is_invocable(...); + +template<typename Ret, typename Func, typename... Args> +constexpr auto is_invocable_r(int) +-> std::enable_if_t<decltype(std::is_convertible_v<decltype(std::invoke(std::declval<Func>(), std::declval<Args>()...)), Ret>, std::true_type>; + + +template<typename, typename, typename...> +constexpr std::false_type is_invocable_r(...); + +} // namespace internal + +template<typename Func, typename... Args> +struct is_invocable: decltype(internal::is_invocable<Func, Args...>(0)) {}; + +template<typename Func, typename... Argsv> +inline constexpr bool is_invocable_v = std::is_invocable<Func, Args...>::value; + +template<typename Ret, typename Func, typename... Args> +struct is_invocable_r: decltype(internal::is_invocable_r<Ret, Func, Args...>(0)) {}; + +template<typename Ret, typename Func, typename... Args> +inline constexpr bool is_invocable_r_v = std::is_invocable_r<Ret, Func, Args...>::value; + +template<typename Func, typename... Args> +struct invoke_result { + using type = decltype(std::invoke(std::declval<Func>(), std::declval<Args>()...)); +}; + +template<typename Func, typename... Args> +using invoke_result_t = typename std::invoke_result<Func, Args...>::type; + +} // namespace std + +# endif +#endif + +/** + * Internal details not to be documented. + * @endcond + */ + +#endif diff --git a/external/entt/include/entt/poly/fwd.hpp b/external/entt/include/entt/poly/fwd.hpp new file mode 100644 index 0000000..34b096d --- /dev/null +++ b/external/entt/include/entt/poly/fwd.hpp @@ -0,0 +1,21 @@ +#ifndef ENTT_POLY_FWD_HPP +#define ENTT_POLY_FWD_HPP + +#include <cstddef> + +namespace entt { + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +template<typename, std::size_t Len = sizeof(double[2]), std::size_t = alignof(double[2])> +class basic_poly; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Concept Concept descriptor. + */ +template<typename Concept> +using poly = basic_poly<Concept>; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/poly/poly.hpp b/external/entt/include/entt/poly/poly.hpp new file mode 100644 index 0000000..977148a --- /dev/null +++ b/external/entt/include/entt/poly/poly.hpp @@ -0,0 +1,310 @@ +#ifndef ENTT_POLY_POLY_HPP +#define ENTT_POLY_POLY_HPP + +#include <cstddef> +#include <functional> +#include <tuple> +#include <type_traits> +#include <utility> +#include "../core/any.hpp" +#include "../core/type_info.hpp" +#include "../core/type_traits.hpp" +#include "fwd.hpp" + +namespace entt { + +/*! @brief Inspector class used to infer the type of the virtual table. */ +struct poly_inspector { + /** + * @brief Generic conversion operator (definition only). + * @tparam Type Type to which conversion is requested. + */ + template<typename Type> + operator Type &&() const; + + /** + * @brief Dummy invocation function (definition only). + * @tparam Member Index of the function to invoke. + * @tparam Args Types of arguments to pass to the function. + * @param args The arguments to pass to the function. + * @return A poly inspector convertible to any type. + */ + template<std::size_t Member, typename... Args> + [[nodiscard]] poly_inspector invoke(Args &&...args) const; + + /*! @copydoc invoke */ + template<std::size_t Member, typename... Args> + [[nodiscard]] poly_inspector invoke(Args &&...args); +}; + +/** + * @brief Static virtual table factory. + * @tparam Concept Concept descriptor. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + */ +template<typename Concept, std::size_t Len, std::size_t Align> +class poly_vtable { + using inspector = typename Concept::template type<poly_inspector>; + + template<typename Ret, typename... Args> + static auto vtable_entry(Ret (*)(inspector &, Args...)) -> Ret (*)(basic_any<Len, Align> &, Args...); + + template<typename Ret, typename... Args> + static auto vtable_entry(Ret (*)(const inspector &, Args...)) -> Ret (*)(const basic_any<Len, Align> &, Args...); + + template<typename Ret, typename... Args> + static auto vtable_entry(Ret (*)(Args...)) -> Ret (*)(const basic_any<Len, Align> &, Args...); + + template<typename Ret, typename... Args> + static auto vtable_entry(Ret (inspector::*)(Args...)) -> Ret (*)(basic_any<Len, Align> &, Args...); + + template<typename Ret, typename... Args> + static auto vtable_entry(Ret (inspector::*)(Args...) const) -> Ret (*)(const basic_any<Len, Align> &, Args...); + + template<auto... Candidate> + static auto make_vtable(value_list<Candidate...>) noexcept + -> decltype(std::make_tuple(vtable_entry(Candidate)...)); + + template<typename... Func> + [[nodiscard]] static constexpr auto make_vtable(type_list<Func...>) noexcept { + if constexpr(sizeof...(Func) == 0u) { + return decltype(make_vtable(typename Concept::template impl<inspector>{})){}; + } else if constexpr((std::is_function_v<Func> && ...)) { + return decltype(std::make_tuple(vtable_entry(std::declval<Func inspector::*>())...)){}; + } + } + + template<typename Type, auto Candidate, typename Ret, typename Any, typename... Args> + static void fill_vtable_entry(Ret (*&entry)(Any &, Args...)) noexcept { + if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Args...>) { + entry = +[](Any &, Args... args) -> Ret { + return std::invoke(Candidate, std::forward<Args>(args)...); + }; + } else { + entry = +[](Any &instance, Args... args) -> Ret { + return static_cast<Ret>(std::invoke(Candidate, any_cast<constness_as_t<Type, Any> &>(instance), std::forward<Args>(args)...)); + }; + } + } + + template<typename Type, auto... Index> + [[nodiscard]] static auto fill_vtable(std::index_sequence<Index...>) noexcept { + vtable_type impl{}; + (fill_vtable_entry<Type, value_list_element_v<Index, typename Concept::template impl<Type>>>(std::get<Index>(impl)), ...); + return impl; + } + + using vtable_type = decltype(make_vtable(Concept{})); + static constexpr bool is_mono_v = std::tuple_size_v<vtable_type> == 1u; + +public: + /*! @brief Virtual table type. */ + using type = std::conditional_t<is_mono_v, std::tuple_element_t<0u, vtable_type>, const vtable_type *>; + + /** + * @brief Returns a static virtual table for a specific concept and type. + * @tparam Type The type for which to generate the virtual table. + * @return A static virtual table for the given concept and type. + */ + template<typename Type> + [[nodiscard]] static type instance() noexcept { + static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Type differs from its decayed form"); + static const vtable_type vtable = fill_vtable<Type>(std::make_index_sequence<Concept::template impl<Type>::size>{}); + + if constexpr(is_mono_v) { + return std::get<0>(vtable); + } else { + return &vtable; + } + } +}; + +/** + * @brief Poly base class used to inject functionalities into concepts. + * @tparam Poly The outermost poly class. + */ +template<typename Poly> +struct poly_base { + /** + * @brief Invokes a function from the static virtual table. + * @tparam Member Index of the function to invoke. + * @tparam Args Types of arguments to pass to the function. + * @param self A reference to the poly object that made the call. + * @param args The arguments to pass to the function. + * @return The return value of the invoked function, if any. + */ + template<std::size_t Member, typename... Args> + [[nodiscard]] decltype(auto) invoke(const poly_base &self, Args &&...args) const { + const auto &poly = static_cast<const Poly &>(self); + + if constexpr(std::is_function_v<std::remove_pointer_t<decltype(poly.vtable)>>) { + return poly.vtable(poly.storage, std::forward<Args>(args)...); + } else { + return std::get<Member>(*poly.vtable)(poly.storage, std::forward<Args>(args)...); + } + } + + /*! @copydoc invoke */ + template<std::size_t Member, typename... Args> + [[nodiscard]] decltype(auto) invoke(poly_base &self, Args &&...args) { + auto &poly = static_cast<Poly &>(self); + + if constexpr(std::is_function_v<std::remove_pointer_t<decltype(poly.vtable)>>) { + static_assert(Member == 0u, "Unknown member"); + return poly.vtable(poly.storage, std::forward<Args>(args)...); + } else { + return std::get<Member>(*poly.vtable)(poly.storage, std::forward<Args>(args)...); + } + } +}; + +/** + * @brief Shortcut for calling `poly_base<Type>::invoke`. + * @tparam Member Index of the function to invoke. + * @tparam Poly A fully defined poly object. + * @tparam Args Types of arguments to pass to the function. + * @param self A reference to the poly object that made the call. + * @param args The arguments to pass to the function. + * @return The return value of the invoked function, if any. + */ +template<std::size_t Member, typename Poly, typename... Args> +decltype(auto) poly_call(Poly &&self, Args &&...args) { + return std::forward<Poly>(self).template invoke<Member>(self, std::forward<Args>(args)...); +} + +/** + * @brief Static polymorphism made simple and within everyone's reach. + * + * Static polymorphism is a very powerful tool in C++, albeit sometimes + * cumbersome to obtain.<br/> + * This class aims to make it simple and easy to use. + * + * @note + * Both deduced and defined static virtual tables are supported.<br/> + * Moreover, the `poly` class template also works with unmanaged objects. + * + * @tparam Concept Concept descriptor. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + */ +template<typename Concept, std::size_t Len, std::size_t Align> +class basic_poly: private Concept::template type<poly_base<basic_poly<Concept, Len, Align>>> { + friend struct poly_base<basic_poly>; + +public: + /*! @brief Concept type. */ + using concept_type = typename Concept::template type<poly_base<basic_poly>>; + /*! @brief Virtual table type. */ + using vtable_type = typename poly_vtable<Concept, Len, Align>::type; + + /*! @brief Default constructor. */ + basic_poly() noexcept = default; + + /** + * @brief Constructs a poly by directly initializing the new object. + * @tparam Type Type of object to use to initialize the poly. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template<typename Type, typename... Args> + explicit basic_poly(std::in_place_type_t<Type>, Args &&...args) + : storage{std::in_place_type<Type>, std::forward<Args>(args)...}, + vtable{poly_vtable<Concept, Len, Align>::template instance<std::remove_cv_t<std::remove_reference_t<Type>>>()} {} + + /** + * @brief Constructs a poly from a given value. + * @tparam Type Type of object to use to initialize the poly. + * @param value An instance of an object to use to initialize the poly. + */ + template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, basic_poly>>> + basic_poly(Type &&value) noexcept + : basic_poly{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)} {} + + /** + * @brief Returns the object type if any, `type_id<void>()` otherwise. + * @return The object type if any, `type_id<void>()` otherwise. + */ + [[nodiscard]] const type_info &type() const noexcept { + return storage.type(); + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data() const noexcept { + return storage.data(); + } + + /*! @copydoc data */ + [[nodiscard]] void *data() noexcept { + return storage.data(); + } + + /** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the poly. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template<typename Type, typename... Args> + void emplace(Args &&...args) { + storage.template emplace<Type>(std::forward<Args>(args)...); + vtable = poly_vtable<Concept, Len, Align>::template instance<std::remove_cv_t<std::remove_reference_t<Type>>>(); + } + + /*! @brief Destroys contained object */ + void reset() { + storage.reset(); + vtable = {}; + } + + /** + * @brief Returns false if a poly is empty, true otherwise. + * @return False if the poly is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast<bool>(storage); + } + + /** + * @brief Returns a pointer to the underlying concept. + * @return A pointer to the underlying concept. + */ + [[nodiscard]] concept_type *operator->() noexcept { + return this; + } + + /*! @copydoc operator-> */ + [[nodiscard]] const concept_type *operator->() const noexcept { + return this; + } + + /** + * @brief Aliasing constructor. + * @return A poly that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_poly as_ref() noexcept { + basic_poly ref{}; + ref.storage = storage.as_ref(); + ref.vtable = vtable; + return ref; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_poly as_ref() const noexcept { + basic_poly ref{}; + ref.storage = storage.as_ref(); + ref.vtable = vtable; + return ref; + } + +private: + basic_any<Len, Align> storage{}; + vtable_type vtable{}; +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/process/fwd.hpp b/external/entt/include/entt/process/fwd.hpp new file mode 100644 index 0000000..6d033a3 --- /dev/null +++ b/external/entt/include/entt/process/fwd.hpp @@ -0,0 +1,20 @@ +#ifndef ENTT_PROCESS_FWD_HPP +#define ENTT_PROCESS_FWD_HPP + +#include <cstdint> +#include <memory> + +namespace entt { + +template<typename, typename> +class process; + +template<typename = std::uint32_t, typename = std::allocator<void>> +class basic_scheduler; + +/*! @brief Alias declaration for the most common use case. */ +using scheduler = basic_scheduler<>; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/process/process.hpp b/external/entt/include/entt/process/process.hpp new file mode 100644 index 0000000..e95ad0c --- /dev/null +++ b/external/entt/include/entt/process/process.hpp @@ -0,0 +1,354 @@ +#ifndef ENTT_PROCESS_PROCESS_HPP +#define ENTT_PROCESS_PROCESS_HPP + +#include <cstdint> +#include <type_traits> +#include <utility> +#include "fwd.hpp" + +namespace entt { + +/** + * @brief Base class for processes. + * + * This class stays true to the CRTP idiom. Derived classes must specify what's + * the intended type for elapsed times.<br/> + * 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<typename Derived, typename Delta> +class process { + enum class state : std::uint8_t { + uninitialized = 0, + running, + paused, + succeeded, + failed, + aborted, + finished, + rejected + }; + + template<typename Target = Derived> + auto next(std::integral_constant<state, state::uninitialized>) + -> decltype(std::declval<Target>().init(), void()) { + static_cast<Target *>(this)->init(); + } + + template<typename Target = Derived> + auto next(std::integral_constant<state, state::running>, Delta delta, void *data) + -> decltype(std::declval<Target>().update(delta, data), void()) { + static_cast<Target *>(this)->update(delta, data); + } + + template<typename Target = Derived> + auto next(std::integral_constant<state, state::succeeded>) + -> decltype(std::declval<Target>().succeeded(), void()) { + static_cast<Target *>(this)->succeeded(); + } + + template<typename Target = Derived> + auto next(std::integral_constant<state, state::failed>) + -> decltype(std::declval<Target>().failed(), void()) { + static_cast<Target *>(this)->failed(); + } + + template<typename Target = Derived> + auto next(std::integral_constant<state, state::aborted>) + -> decltype(std::declval<Target>().aborted(), void()) { + static_cast<Target *>(this)->aborted(); + } + + template<typename... Args> + void next(Args &&...) const noexcept {} + +protected: + /** + * @brief Terminates a process with success if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + */ + void succeed() noexcept { + if(alive()) { + current = state::succeeded; + } + } + + /** + * @brief Terminates a process with errors if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + */ + void fail() noexcept { + if(alive()) { + current = state::failed; + } + } + + /** + * @brief Stops a process if it's in a running state. + * + * The function is idempotent and it does nothing if the process isn't + * running. + */ + void pause() noexcept { + if(current == state::running) { + current = state::paused; + } + } + + /** + * @brief Restarts a process if it's paused. + * + * The function is idempotent and it does nothing if the process isn't + * paused. + */ + void unpause() noexcept { + if(current == state::paused) { + current = state::running; + } + } + +public: + /*! @brief Type used to provide elapsed time. */ + using delta_type = Delta; + + /*! @brief Default constructor. */ + constexpr process() = default; + + /*! @brief Default copy constructor. */ + process(const process &) = default; + + /*! @brief Default move constructor. */ + process(process &&) noexcept = default; + + /** + * @brief Default copy assignment operator. + * @return This process. + */ + process &operator=(const process &) = default; + + /** + * @brief Default move assignment operator. + * @return This process. + */ + process &operator=(process &&) noexcept = default; + + /*! @brief Default destructor. */ + virtual ~process() noexcept { + static_assert(std::is_base_of_v<process, Derived>, "Incorrect use of the class template"); + } + + /** + * @brief Aborts a process if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + * + * @param immediate Requests an immediate operation. + */ + void abort(const bool immediate = false) { + if(alive()) { + current = state::aborted; + + if(immediate) { + tick({}); + } + } + } + + /** + * @brief Returns true if a process is either running or paused. + * @return True if the process is still alive, false otherwise. + */ + [[nodiscard]] bool alive() const noexcept { + return current == state::running || current == state::paused; + } + + /** + * @brief Returns true if a process is already terminated. + * @return True if the process is terminated, false otherwise. + */ + [[nodiscard]] bool finished() const noexcept { + return current == state::finished; + } + + /** + * @brief Returns true if a process is currently paused. + * @return True if the process is paused, false otherwise. + */ + [[nodiscard]] bool paused() const noexcept { + return current == state::paused; + } + + /** + * @brief Returns true if a process terminated with errors. + * @return True if the process terminated with errors, false otherwise. + */ + [[nodiscard]] bool rejected() const noexcept { + return current == state::rejected; + } + + /** + * @brief Updates a process and its internal state if required. + * @param delta Elapsed time. + * @param data Optional data. + */ + void tick(const Delta delta, void *data = nullptr) { + switch(current) { + case state::uninitialized: + next(std::integral_constant<state, state::uninitialized>{}); + current = state::running; + break; + case state::running: + next(std::integral_constant<state, state::running>{}, delta, data); + break; + default: + // suppress warnings + break; + } + + // if it's dead, it must be notified and removed immediately + switch(current) { + case state::succeeded: + next(std::integral_constant<state, state::succeeded>{}); + current = state::finished; + break; + case state::failed: + next(std::integral_constant<state, state::failed>{}); + current = state::rejected; + break; + case state::aborted: + next(std::integral_constant<state, state::aborted>{}); + current = state::rejected; + break; + default: + // suppress warnings + break; + } + } + +private: + state current{state::uninitialized}; +}; + +/** + * @brief Adaptor for lambdas and functors to turn them into processes. + * + * Lambdas and functors can't be used directly with a scheduler for they are not + * properly defined processes with managed life cycles.<br/> + * 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<typename Func, typename Delta> +struct process_adaptor: process<process_adaptor<Func, Delta>, 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<typename... Args> + process_adaptor(Args &&...args) + : Func{std::forward<Args>(args)...} {} + + /** + * @brief Updates a process and its internal state if required. + * @param delta Elapsed time. + * @param data Optional data. + */ + void update(const Delta delta, void *data) { + Func::operator()( + delta, + data, + [this]() { this->succeed(); }, + [this]() { this->fail(); }); + } +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/process/scheduler.hpp b/external/entt/include/entt/process/scheduler.hpp new file mode 100644 index 0000000..770059d --- /dev/null +++ b/external/entt/include/entt/process/scheduler.hpp @@ -0,0 +1,364 @@ +#ifndef ENTT_PROCESS_SCHEDULER_HPP +#define ENTT_PROCESS_SCHEDULER_HPP + +#include <cstddef> +#include <memory> +#include <type_traits> +#include <utility> +#include <vector> +#include "../config/config.h" +#include "../core/compressed_pair.hpp" +#include "fwd.hpp" +#include "process.hpp" + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template<typename Delta> +struct basic_process_handler { + virtual ~basic_process_handler() noexcept = default; + + virtual bool update(const Delta, void *) = 0; + virtual void abort(const bool) = 0; + + // std::shared_ptr because of its type erased allocator which is useful here + std::shared_ptr<basic_process_handler> next; +}; + +template<typename Delta, typename Type> +struct process_handler final: basic_process_handler<Delta> { + template<typename... Args> + process_handler(Args &&...args) + : process{std::forward<Args>(args)...} {} + + bool update(const Delta delta, void *data) override { + if(process.tick(delta, data); process.rejected()) { + this->next.reset(); + } + + return (process.rejected() || process.finished()); + } + + void abort(const bool immediate) override { + process.abort(immediate); + } + + Type process; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Cooperative scheduler for processes. + * + * A cooperative scheduler runs processes and helps managing their life cycles. + * + * Each process is invoked once per tick. If a process terminates, it's + * removed automatically from the scheduler and it's never invoked again.<br/> + * 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<my_process>(arguments...); + * @endcode + * + * In order to invoke all scheduled processes, call the `update` member function + * passing it the elapsed time to forward to the tasks. + * + * @sa process + * + * @tparam Delta Type to use to provide elapsed time. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Delta, typename Allocator> +class basic_scheduler { + template<typename Type> + using handler_type = internal::process_handler<Delta, Type>; + + // std::shared_ptr because of its type erased allocator which is useful here + using process_type = std::shared_ptr<internal::basic_process_handler<Delta>>; + + using alloc_traits = std::allocator_traits<Allocator>; + using container_allocator = typename alloc_traits::template rebind_alloc<process_type>; + using container_type = std::vector<process_type, container_allocator>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Unsigned integer type. */ + using delta_type = Delta; + + /*! @brief Default constructor. */ + basic_scheduler() + : basic_scheduler{allocator_type{}} {} + + /** + * @brief Constructs a scheduler with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_scheduler(const allocator_type &allocator) + : handlers{allocator, allocator} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_scheduler(const basic_scheduler &) = delete; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_scheduler(basic_scheduler &&other) noexcept + : handlers{std::move(other.handlers)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_scheduler(basic_scheduler &&other, const allocator_type &allocator) + : handlers{container_type{std::move(other.handlers.first()), allocator}, allocator} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a scheduler is not allowed"); + } + + /*! @brief Default destructor. */ + ~basic_scheduler() noexcept = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This process scheduler. + */ + basic_scheduler &operator=(const basic_scheduler &) = delete; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This process scheduler. + */ + basic_scheduler &operator=(basic_scheduler &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a scheduler is not allowed"); + handlers = std::move(other.handlers); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given scheduler. + * @param other Scheduler to exchange the content with. + */ + void swap(basic_scheduler &other) { + using std::swap; + swap(handlers, other.handlers); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return handlers.second(); + } + + /** + * @brief Number of processes currently scheduled. + * @return Number of processes currently scheduled. + */ + [[nodiscard]] size_type size() const noexcept { + return handlers.first().size(); + } + + /** + * @brief Returns true if at least a process is currently scheduled. + * @return True if there are scheduled processes, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return handlers.first().empty(); + } + + /** + * @brief Discards all scheduled processes. + * + * Processes aren't aborted. They are discarded along with their children + * and never executed again. + */ + void clear() { + handlers.first().clear(); + } + + /** + * @brief Schedules a process for the next tick. + * + * Returned value can be used to attach a continuation for the last process. + * The continutation is scheduled automatically when the process terminates + * and only if the process returns with success. + * + * Example of use (pseudocode): + * + * @code{.cpp} + * // schedules a task in the form of a process class + * scheduler.attach<my_process>(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<my_other_process>(); + * @endcode + * + * @tparam Proc Type of process to schedule. + * @tparam Args Types of arguments to use to initialize the process. + * @param args Parameters to use to initialize the process. + * @return This process scheduler. + */ + template<typename Proc, typename... Args> + basic_scheduler &attach(Args &&...args) { + static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type"); + auto &ref = handlers.first().emplace_back(std::allocate_shared<handler_type<Proc>>(handlers.second(), std::forward<Args>(args)...)); + // forces the process to exit the uninitialized state + ref->update({}, nullptr); + return *this; + } + + /** + * @brief Schedules a process for the next tick. + * + * A process can be either a lambda or a functor. The scheduler wraps both + * of them in a process adaptor internally.<br/> + * The signature of the function call operator should be equivalent to the + * following: + * + * @code{.cpp} + * void(Delta delta, void *data, auto succeed, auto fail); + * @endcode + * + * Where: + * + * * `delta` is the elapsed time. + * * `data` is an opaque pointer to user data if any, `nullptr` otherwise. + * * `succeed` is a function to call when a process terminates with success. + * * `fail` is a function to call when a process terminates with errors. + * + * The signature of the function call operator of both `succeed` and `fail` + * is equivalent to the following: + * + * @code{.cpp} + * void(); + * @endcode + * + * Returned value can be used to attach a continuation for the last process. + * The continutation is scheduled automatically when the process terminates + * and only if the process returns with success. + * + * Example of use (pseudocode): + * + * @code{.cpp} + * // schedules a task in the form of a lambda function + * scheduler.attach([](auto delta, void *, auto succeed, auto fail) { + * // code + * }) + * // appends a child in the form of another lambda function + * .then([](auto delta, void *, auto succeed, auto fail) { + * // code + * }) + * // appends a child in the form of a process class + * .then<my_process>(arguments...); + * @endcode + * + * @sa process_adaptor + * + * @tparam Func Type of process to schedule. + * @param func Either a lambda or a functor to use as a process. + * @return This process scheduler. + */ + template<typename Func> + basic_scheduler &attach(Func &&func) { + using Proc = process_adaptor<std::decay_t<Func>, Delta>; + return attach<Proc>(std::forward<Func>(func)); + } + + /** + * @brief Sets a process as a continuation of the last scheduled process. + * @tparam Proc Type of process to use as a continuation. + * @tparam Args Types of arguments to use to initialize the process. + * @param args Parameters to use to initialize the process. + * @return This process scheduler. + */ + template<typename Proc, typename... Args> + basic_scheduler &then(Args &&...args) { + static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type"); + ENTT_ASSERT(!handlers.first().empty(), "Process not available"); + auto *curr = handlers.first().back().get(); + for(; curr->next; curr = curr->next.get()) {} + curr->next = std::allocate_shared<handler_type<Proc>>(handlers.second(), std::forward<Args>(args)...); + return *this; + } + + /** + * @brief Sets a process as a continuation of the last scheduled process. + * @tparam Func Type of process to use as a continuation. + * @param func Either a lambda or a functor to use as a process. + * @return This process scheduler. + */ + template<typename Func> + basic_scheduler &then(Func &&func) { + using Proc = process_adaptor<std::decay_t<Func>, Delta>; + return then<Proc>(std::forward<Func>(func)); + } + + /** + * @brief Updates all scheduled processes. + * + * All scheduled processes are executed in no specific order.<br/> + * If a process terminates with success, it's replaced with its child, if + * any. Otherwise, if a process terminates with an error, it's removed along + * with its child. + * + * @param delta Elapsed time. + * @param data Optional data. + */ + void update(const delta_type delta, void *data = nullptr) { + for(auto next = handlers.first().size(); next; --next) { + if(const auto pos = next - 1u; handlers.first()[pos]->update(delta, data)) { + // updating might spawn/reallocate, cannot hold refs until here + if(auto &curr = handlers.first()[pos]; curr->next) { + curr = std::move(curr->next); + // forces the process to exit the uninitialized state + curr->update({}, nullptr); + } else { + curr = std::move(handlers.first().back()); + handlers.first().pop_back(); + } + } + } + } + + /** + * @brief Aborts all scheduled processes. + * + * Unless an immediate operation is requested, the abort is scheduled for + * the next tick. Processes won't be executed anymore in any case.<br/> + * Once a process is fully aborted and thus finished, it's discarded along + * with its child, if any. + * + * @param immediate Requests an immediate operation. + */ + void abort(const bool immediate = false) { + for(auto &&curr: handlers.first()) { + curr->abort(immediate); + } + } + +private: + compressed_pair<container_type, allocator_type> handlers; +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/resource/cache.hpp b/external/entt/include/entt/resource/cache.hpp new file mode 100644 index 0000000..4650c63 --- /dev/null +++ b/external/entt/include/entt/resource/cache.hpp @@ -0,0 +1,413 @@ +#ifndef ENTT_RESOURCE_RESOURCE_CACHE_HPP +#define ENTT_RESOURCE_RESOURCE_CACHE_HPP + +#include <cstddef> +#include <functional> +#include <iterator> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> +#include "../container/dense_map.hpp" +#include "../core/compressed_pair.hpp" +#include "../core/fwd.hpp" +#include "../core/iterator.hpp" +#include "../core/utility.hpp" +#include "fwd.hpp" +#include "loader.hpp" +#include "resource.hpp" + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template<typename Type, typename It> +class resource_cache_iterator final { + template<typename, typename> + friend class resource_cache_iterator; + +public: + using value_type = std::pair<id_type, resource<Type>>; + using pointer = input_iterator_pointer<value_type>; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr resource_cache_iterator() noexcept = default; + + constexpr resource_cache_iterator(const It iter) noexcept + : it{iter} {} + + template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>> + constexpr resource_cache_iterator(const resource_cache_iterator<std::remove_const_t<Type>, Other> &other) noexcept + : it{other.it} {} + + constexpr resource_cache_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr resource_cache_iterator operator++(int) noexcept { + resource_cache_iterator orig = *this; + return ++(*this), orig; + } + + constexpr resource_cache_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr resource_cache_iterator operator--(int) noexcept { + resource_cache_iterator orig = *this; + return operator--(), orig; + } + + constexpr resource_cache_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr resource_cache_iterator operator+(const difference_type value) const noexcept { + resource_cache_iterator copy = *this; + return (copy += value); + } + + constexpr resource_cache_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr resource_cache_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].first, resource<Type>{it[value].second}}; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + template<typename... Lhs, typename... Rhs> + friend constexpr std::ptrdiff_t operator-(const resource_cache_iterator<Lhs...> &, const resource_cache_iterator<Rhs...> &) noexcept; + + template<typename... Lhs, typename... Rhs> + friend constexpr bool operator==(const resource_cache_iterator<Lhs...> &, const resource_cache_iterator<Rhs...> &) noexcept; + + template<typename... Lhs, typename... Rhs> + friend constexpr bool operator<(const resource_cache_iterator<Lhs...> &, const resource_cache_iterator<Rhs...> &) noexcept; + +private: + It it; +}; + +template<typename... Lhs, typename... Rhs> +[[nodiscard]] constexpr std::ptrdiff_t operator-(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept { + return lhs.it - rhs.it; +} + +template<typename... Lhs, typename... Rhs> +[[nodiscard]] constexpr bool operator==(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept { + return lhs.it == rhs.it; +} + +template<typename... Lhs, typename... Rhs> +[[nodiscard]] constexpr bool operator!=(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept { + return !(lhs == rhs); +} + +template<typename... Lhs, typename... Rhs> +[[nodiscard]] constexpr bool operator<(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept { + return lhs.it < rhs.it; +} + +template<typename... Lhs, typename... Rhs> +[[nodiscard]] constexpr bool operator>(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept { + return rhs < lhs; +} + +template<typename... Lhs, typename... Rhs> +[[nodiscard]] constexpr bool operator<=(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept { + return !(lhs > rhs); +} + +template<typename... Lhs, typename... Rhs> +[[nodiscard]] constexpr bool operator>=(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept { + return !(lhs < rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic cache for resources of any type. + * @tparam Type Type of resources managed by a cache. + * @tparam Loader Type of loader used to create the resources. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Type, typename Loader, typename Allocator> +class resource_cache { + using alloc_traits = std::allocator_traits<Allocator>; + static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type"); + using container_allocator = typename alloc_traits::template rebind_alloc<std::pair<const id_type, typename Loader::result_type>>; + using container_type = dense_map<id_type, typename Loader::result_type, identity, std::equal_to<>, container_allocator>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Resource type. */ + using value_type = Type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Loader type. */ + using loader_type = Loader; + /*! @brief Input iterator type. */ + using iterator = internal::resource_cache_iterator<Type, typename container_type::iterator>; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::resource_cache_iterator<const Type, typename container_type::const_iterator>; + + /*! @brief Default constructor. */ + resource_cache() + : resource_cache{loader_type{}} {} + + /** + * @brief Constructs an empty cache with a given allocator. + * @param allocator The allocator to use. + */ + explicit resource_cache(const allocator_type &allocator) + : resource_cache{loader_type{}, allocator} {} + + /** + * @brief Constructs an empty cache with a given allocator and loader. + * @param callable The loader to use. + * @param allocator The allocator to use. + */ + explicit resource_cache(const loader_type &callable, const allocator_type &allocator = allocator_type{}) + : pool{container_type{allocator}, callable} {} + + /*! @brief Default copy constructor. */ + resource_cache(const resource_cache &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + resource_cache(const resource_cache &other, const allocator_type &allocator) + : pool{std::piecewise_construct, std::forward_as_tuple(other.pool.first(), allocator), std::forward_as_tuple(other.pool.second())} {} + + /*! @brief Default move constructor. */ + resource_cache(resource_cache &&) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + resource_cache(resource_cache &&other, const allocator_type &allocator) + : pool{std::piecewise_construct, std::forward_as_tuple(std::move(other.pool.first()), allocator), std::forward_as_tuple(std::move(other.pool.second()))} {} + + /*! @brief Default destructor. */ + ~resource_cache() noexcept = default; + + /** + * @brief Default copy assignment operator. + * @return This cache. + */ + resource_cache &operator=(const resource_cache &) = default; + + /** + * @brief Default move assignment operator. + * @return This cache. + */ + resource_cache &operator=(resource_cache &&) noexcept = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return pool.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the cache is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal cache. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return pool.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return pool.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal cache. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return pool.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return pool.first().end(); + } + + /** + * @brief Returns true if a cache contains no resources, false otherwise. + * @return True if the cache contains no resources, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return pool.first().empty(); + } + + /** + * @brief Number of resources managed by a cache. + * @return Number of resources currently stored. + */ + [[nodiscard]] size_type size() const noexcept { + return pool.first().size(); + } + + /*! @brief Clears a cache. */ + void clear() noexcept { + pool.first().clear(); + } + + /** + * @brief Loads a resource, if its identifier does not exist. + * + * Arguments are forwarded directly to the loader and _consumed_ only if the + * resource doesn't already exist. + * + * @warning + * If the resource isn't loaded correctly, the returned handle could be + * invalid and any use of it will result in undefined behavior. + * + * @tparam Args Types of arguments to use to load the resource if required. + * @param id Unique resource identifier. + * @param args Arguments to use to load the resource if required. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template<typename... Args> + std::pair<iterator, bool> load(const id_type id, Args &&...args) { + if(auto it = pool.first().find(id); it != pool.first().end()) { + return {it, false}; + } + + return pool.first().emplace(id, pool.second()(std::forward<Args>(args)...)); + } + + /** + * @brief Force loads a resource, if its identifier does not exist. + * @copydetails load + */ + template<typename... Args> + std::pair<iterator, bool> force_load(const id_type id, Args &&...args) { + return {pool.first().insert_or_assign(id, pool.second()(std::forward<Args>(args)...)).first, true}; + } + + /** + * @brief Returns a handle for a given resource identifier. + * + * @warning + * There is no guarantee that the returned handle is valid.<br/> + * If it is not, any use will result in indefinite behavior. + * + * @param id Unique resource identifier. + * @return A handle for the given resource. + */ + [[nodiscard]] resource<const value_type> operator[](const id_type id) const { + if(auto it = pool.first().find(id); it != pool.first().cend()) { + return resource<const value_type>{it->second}; + } + + return {}; + } + + /*! @copydoc operator[] */ + [[nodiscard]] resource<value_type> operator[](const id_type id) { + if(auto it = pool.first().find(id); it != pool.first().end()) { + return resource<value_type>{it->second}; + } + + return {}; + } + + /** + * @brief Checks if a cache contains a given identifier. + * @param id Unique resource identifier. + * @return True if the cache contains the resource, false otherwise. + */ + [[nodiscard]] bool contains(const id_type id) const { + return pool.first().contains(id); + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto it = pool.first().begin(); + return pool.first().erase(it + (pos - const_iterator{it})); + } + + /** + * @brief Removes the given elements from a cache. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto it = pool.first().begin(); + return pool.first().erase(it + (first - const_iterator{it}), it + (last - const_iterator{it})); + } + + /** + * @brief Removes the given elements from a cache. + * @param id Unique resource identifier. + * @return Number of resources erased (either 0 or 1). + */ + size_type erase(const id_type id) { + return pool.first().erase(id); + } + + /** + * @brief Returns the loader used to create resources. + * @return The loader used to create resources. + */ + [[nodiscard]] loader_type loader() const { + return pool.second(); + } + +private: + compressed_pair<container_type, loader_type> pool; +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/resource/fwd.hpp b/external/entt/include/entt/resource/fwd.hpp new file mode 100644 index 0000000..ed63da5 --- /dev/null +++ b/external/entt/include/entt/resource/fwd.hpp @@ -0,0 +1,19 @@ +#ifndef ENTT_RESOURCE_FWD_HPP +#define ENTT_RESOURCE_FWD_HPP + +#include <memory> + +namespace entt { + +template<typename> +struct resource_loader; + +template<typename Type, typename = resource_loader<Type>, typename = std::allocator<Type>> +class resource_cache; + +template<typename> +class resource; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/resource/loader.hpp b/external/entt/include/entt/resource/loader.hpp new file mode 100644 index 0000000..f59d748 --- /dev/null +++ b/external/entt/include/entt/resource/loader.hpp @@ -0,0 +1,33 @@ +#ifndef ENTT_RESOURCE_LOADER_HPP +#define ENTT_RESOURCE_LOADER_HPP + +#include <memory> +#include <utility> +#include "fwd.hpp" + +namespace entt { + +/** + * @brief Transparent loader for shared resources. + * @tparam Type Type of resources created by the loader. + */ +template<typename Type> +struct resource_loader { + /*! @brief Result type. */ + using result_type = std::shared_ptr<Type>; + + /** + * @brief Constructs a shared pointer to a resource from its arguments. + * @tparam Args Types of arguments to use to construct the resource. + * @param args Parameters to use to construct the resource. + * @return A shared pointer to a resource of the given type. + */ + template<typename... Args> + result_type operator()(Args &&...args) const { + return std::make_shared<Type>(std::forward<Args>(args)...); + } +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/resource/resource.hpp b/external/entt/include/entt/resource/resource.hpp new file mode 100644 index 0000000..323d738 --- /dev/null +++ b/external/entt/include/entt/resource/resource.hpp @@ -0,0 +1,259 @@ +#ifndef ENTT_RESOURCE_RESOURCE_HPP +#define ENTT_RESOURCE_RESOURCE_HPP + +#include <memory> +#include <type_traits> +#include <utility> +#include "fwd.hpp" + +namespace entt { + +/** + * @brief Basic resource handle. + * + * A handle wraps a resource and extends its lifetime. It also shares the same + * resource with all other handles constructed from the same element.<br/> + * As a rule of thumb, resources should never be copied nor moved. Handles are + * the way to go to push references around. + * + * @tparam Type Type of resource managed by a handle. + */ +template<typename Type> +class resource { + template<typename> + friend class resource; + + template<typename Other> + static constexpr bool is_acceptable_v = !std::is_same_v<Type, Other> && std::is_constructible_v<Type &, Other &>; + +public: + /*! @brief Resource type. */ + using element_type = Type; + /*! @brief Handle type. */ + using handle_type = std::shared_ptr<element_type>; + + /*! @brief Default constructor. */ + resource() noexcept + : value{} {} + + /** + * @brief Creates a new resource handle. + * @param res A handle to a resource. + */ + explicit resource(handle_type res) noexcept + : value{std::move(res)} {} + + /*! @brief Default copy constructor. */ + resource(const resource &) noexcept = default; + + /*! @brief Default move constructor. */ + resource(resource &&) noexcept = default; + + /** + * @brief Aliasing constructor. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle with which to share ownership information. + * @param res Unrelated and unmanaged resources. + */ + template<typename Other> + resource(const resource<Other> &other, element_type &res) noexcept + : value{other.value, std::addressof(res)} {} + + /** + * @brief Copy constructs a handle which shares ownership of the resource. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to copy from. + */ + template<typename Other, typename = std::enable_if_t<is_acceptable_v<Other>>> + resource(const resource<Other> &other) noexcept + : value{other.value} {} + + /** + * @brief Move constructs a handle which takes ownership of the resource. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to move from. + */ + template<typename Other, typename = std::enable_if_t<is_acceptable_v<Other>>> + resource(resource<Other> &&other) noexcept + : value{std::move(other.value)} {} + + /*! @brief Default destructor. */ + ~resource() noexcept = default; + + /** + * @brief Default copy assignment operator. + * @return This resource handle. + */ + resource &operator=(const resource &) noexcept = default; + + /** + * @brief Default move assignment operator. + * @return This resource handle. + */ + resource &operator=(resource &&) noexcept = default; + + /** + * @brief Copy assignment operator from foreign handle. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to copy from. + * @return This resource handle. + */ + template<typename Other, typename = std::enable_if_t<is_acceptable_v<Other>>> + resource &operator=(const resource<Other> &other) noexcept { + value = other.value; + return *this; + } + + /** + * @brief Move assignment operator from foreign handle. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to move from. + * @return This resource handle. + */ + template<typename Other, typename = std::enable_if_t<is_acceptable_v<Other>>> + resource &operator=(resource<Other> &&other) noexcept { + value = std::move(other.value); + return *this; + } + + /** + * @brief Returns a reference to the managed resource. + * + * @warning + * The behavior is undefined if the handle doesn't contain a resource. + * + * @return A reference to the managed resource. + */ + [[nodiscard]] element_type &operator*() const noexcept { + return *value; + } + + /*! @copydoc operator* */ + [[nodiscard]] operator element_type &() const noexcept { + return *value; + } + + /** + * @brief Returns a pointer to the managed resource. + * @return A pointer to the managed resource. + */ + [[nodiscard]] element_type *operator->() const noexcept { + return value.get(); + } + + /** + * @brief Returns true if a handle contains a resource, false otherwise. + * @return True if the handle contains a resource, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast<bool>(value); + } + + /*! @brief Releases the ownership of the managed resource. */ + void reset() { + value.reset(); + } + + /** + * @brief Replaces the managed resource. + * @param other A handle to a resource. + */ + void reset(handle_type other) { + value = std::move(other); + } + + /** + * @brief Returns the underlying resource handle. + * @return The underlying resource handle. + */ + [[nodiscard]] const handle_type &handle() const noexcept { + return value; + } + +private: + handle_type value; +}; + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if both handles refer to the same resource, false otherwise. + */ +template<typename Lhs, typename Rhs> +[[nodiscard]] bool operator==(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept { + return (std::addressof(*lhs) == std::addressof(*rhs)); +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return False if both handles refer to the same resource, true otherwise. + */ +template<typename Lhs, typename Rhs> +[[nodiscard]] bool operator!=(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if the first handle is less than the second, false otherwise. + */ +template<typename Lhs, typename Rhs> +[[nodiscard]] bool operator<(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept { + return (std::addressof(*lhs) < std::addressof(*rhs)); +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if the first handle is greater than the second, false otherwise. + */ +template<typename Lhs, typename Rhs> +[[nodiscard]] bool operator>(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if the first handle is less than or equal to the second, false + * otherwise. + */ +template<typename Lhs, typename Rhs> +[[nodiscard]] bool operator<=(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept { + return !(lhs > rhs); +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if the first handle is greater than or equal to the second, + * false otherwise. + */ +template<typename Lhs, typename Rhs> +[[nodiscard]] bool operator>=(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept { + return !(lhs < rhs); +} + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/signal/delegate.hpp b/external/entt/include/entt/signal/delegate.hpp new file mode 100644 index 0000000..0060649 --- /dev/null +++ b/external/entt/include/entt/signal/delegate.hpp @@ -0,0 +1,323 @@ +#ifndef ENTT_SIGNAL_DELEGATE_HPP +#define ENTT_SIGNAL_DELEGATE_HPP + +#include <cstddef> +#include <functional> +#include <tuple> +#include <type_traits> +#include <utility> +#include "../config/config.h" +#include "../core/type_traits.hpp" +#include "fwd.hpp" + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template<typename Ret, typename... Args> +constexpr auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); + +template<typename Ret, typename Type, typename... Args, typename Other> +constexpr auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); + +template<typename Class, typename Ret, typename... Args, typename... Other> +constexpr auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); + +template<typename Class, typename Ret, typename... Args, typename... Other> +constexpr auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); + +template<typename Class, typename Type, typename... Other> +constexpr auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); + +template<typename... Type> +using function_pointer_t = decltype(function_pointer(std::declval<Type>()...)); + +template<typename... Class, typename Ret, typename... Args> +[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { + return std::index_sequence_for<Class..., Args...>{}; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic delegate implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + */ +template<typename> +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<typename Ret, typename... Args> +class delegate<Ret(Args...)> { + template<auto Candidate, std::size_t... Index> + [[nodiscard]] auto wrap(std::index_sequence<Index...>) noexcept { + return [](const void *, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...); + [[maybe_unused]] constexpr auto offset = !std::is_invocable_r_v<Ret, decltype(Candidate), type_list_element_t<Index, type_list<Args...>>...> * (sizeof...(Args) - sizeof...(Index)); + return static_cast<Ret>(std::invoke(Candidate, std::forward<type_list_element_t<Index + offset, type_list<Args...>>>(std::get<Index + offset>(arguments))...)); + }; + } + + template<auto Candidate, typename Type, std::size_t... Index> + [[nodiscard]] auto wrap(Type &, std::index_sequence<Index...>) noexcept { + return [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload)); + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...); + [[maybe_unused]] constexpr auto offset = !std::is_invocable_r_v<Ret, decltype(Candidate), Type &, type_list_element_t<Index, type_list<Args...>>...> * (sizeof...(Args) - sizeof...(Index)); + return static_cast<Ret>(std::invoke(Candidate, *curr, std::forward<type_list_element_t<Index + offset, type_list<Args...>>>(std::get<Index + offset>(arguments))...)); + }; + } + + template<auto Candidate, typename Type, std::size_t... Index> + [[nodiscard]] auto wrap(Type *, std::index_sequence<Index...>) noexcept { + return [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload)); + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...); + [[maybe_unused]] constexpr auto offset = !std::is_invocable_r_v<Ret, decltype(Candidate), Type *, type_list_element_t<Index, type_list<Args...>>...> * (sizeof...(Args) - sizeof...(Index)); + return static_cast<Ret>(std::invoke(Candidate, curr, std::forward<type_list_element_t<Index + offset, type_list<Args...>>>(std::get<Index + offset>(arguments))...)); + }; + } + +public: + /*! @brief Function type of the contained target. */ + using function_type = Ret(const void *, Args...); + /*! @brief Function type of the delegate. */ + using type = Ret(Args...); + /*! @brief Return type of the delegate. */ + using result_type = Ret; + + /*! @brief Default constructor. */ + delegate() noexcept = default; + + /** + * @brief Constructs a delegate with a given object or payload, if any. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance Optional valid object that fits the purpose. + */ + template<auto Candidate, typename... Type> + delegate(connect_arg_t<Candidate>, Type &&...value_or_instance) noexcept { + connect<Candidate>(std::forward<Type>(value_or_instance)...); + } + + /** + * @brief Constructs a delegate and connects an user defined function with + * optional payload. + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ + delegate(function_type *function, const void *payload = nullptr) noexcept { + connect(function, payload); + } + + /** + * @brief Connects a free function or an unbound member to a delegate. + * @tparam Candidate Function or member to connect to the delegate. + */ + template<auto Candidate> + void connect() noexcept { + instance = nullptr; + + if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Args...>) { + fn = [](const void *, Args... args) -> Ret { + return Ret(std::invoke(Candidate, std::forward<Args>(args)...)); + }; + } else if constexpr(std::is_member_pointer_v<decltype(Candidate)>) { + fn = wrap<Candidate>(internal::index_sequence_for<type_list_element_t<0, type_list<Args...>>>(internal::function_pointer_t<decltype(Candidate)>{})); + } else { + fn = wrap<Candidate>(internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate)>{})); + } + } + + /** + * @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.<br/> + * 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<auto Candidate, typename Type> + void connect(Type &value_or_instance) noexcept { + instance = &value_or_instance; + + if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type &, Args...>) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload)); + return Ret(std::invoke(Candidate, *curr, std::forward<Args>(args)...)); + }; + } else { + fn = wrap<Candidate>(value_or_instance, internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate), Type>{})); + } + } + + /** + * @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<auto Candidate, typename Type> + void connect(Type *value_or_instance) noexcept { + instance = value_or_instance; + + if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type *, Args...>) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload)); + return Ret(std::invoke(Candidate, curr, std::forward<Args>(args)...)); + }; + } else { + fn = wrap<Candidate>(value_or_instance, internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate), Type>{})); + } + } + + /** + * @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.<br/> + * The payload is returned as the first argument to the target function in + * all cases. + * + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ + void connect(function_type *function, const void *payload = nullptr) noexcept { + ENTT_ASSERT(function != nullptr, "Uninitialized function pointer"); + instance = payload; + fn = function; + } + + /** + * @brief Resets a delegate. + * + * After a reset, a delegate cannot be invoked anymore. + */ + void reset() noexcept { + instance = nullptr; + fn = nullptr; + } + + /** + * @brief Returns a pointer to the stored callable function target, if any. + * @return An opaque pointer to the stored callable function target. + */ + [[nodiscard]] function_type *target() const noexcept { + return fn; + } + + /** + * @brief Returns the instance or the payload linked to a delegate, if any. + * @return An opaque pointer to the underlying data. + */ + [[nodiscard]] const void *data() const noexcept { + return instance; + } + + /** + * @brief Triggers a delegate. + * + * The delegate invokes the underlying function and returns the result. + * + * @warning + * Attempting to trigger an invalid delegate results in undefined + * behavior. + * + * @param args Arguments to use to invoke the underlying function. + * @return The value returned by the underlying function. + */ + Ret operator()(Args... args) const { + ENTT_ASSERT(static_cast<bool>(*this), "Uninitialized delegate"); + return fn(instance, std::forward<Args>(args)...); + } + + /** + * @brief Checks whether a delegate actually stores a listener. + * @return False if the delegate is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + // no need to also test instance + return !(fn == nullptr); + } + + /** + * @brief Compares the contents of two delegates. + * @param other Delegate with which to compare. + * @return False if the two contents differ, true otherwise. + */ + [[nodiscard]] bool operator==(const delegate<Ret(Args...)> &other) const noexcept { + return fn == other.fn && instance == other.instance; + } + +private: + const void *instance{}; + function_type *fn{}; +}; + +/** + * @brief Compares the contents of two delegates. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @param lhs A valid delegate object. + * @param rhs A valid delegate object. + * @return True if the two contents differ, false otherwise. + */ +template<typename Ret, typename... Args> +[[nodiscard]] bool operator!=(const delegate<Ret(Args...)> &lhs, const delegate<Ret(Args...)> &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + */ +template<auto Candidate> +delegate(connect_arg_t<Candidate>) -> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate)>>>; + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + */ +template<auto Candidate, typename Type> +delegate(connect_arg_t<Candidate>, Type &&) -> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate), Type>>>; + +/** + * @brief Deduction guide. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template<typename Ret, typename... Args> +delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate<Ret(Args...)>; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/signal/dispatcher.hpp b/external/entt/include/entt/signal/dispatcher.hpp new file mode 100644 index 0000000..fbd758e --- /dev/null +++ b/external/entt/include/entt/signal/dispatcher.hpp @@ -0,0 +1,397 @@ +#ifndef ENTT_SIGNAL_DISPATCHER_HPP +#define ENTT_SIGNAL_DISPATCHER_HPP + +#include <cstddef> +#include <functional> +#include <memory> +#include <type_traits> +#include <utility> +#include <vector> +#include "../container/dense_map.hpp" +#include "../core/compressed_pair.hpp" +#include "../core/fwd.hpp" +#include "../core/type_info.hpp" +#include "../core/utility.hpp" +#include "fwd.hpp" +#include "sigh.hpp" + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct basic_dispatcher_handler { + virtual ~basic_dispatcher_handler() noexcept = default; + virtual void publish() = 0; + virtual void disconnect(void *) = 0; + virtual void clear() noexcept = 0; + [[nodiscard]] virtual std::size_t size() const noexcept = 0; +}; + +template<typename Type, typename Allocator> +class dispatcher_handler final: public basic_dispatcher_handler { + static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Invalid type"); + + using alloc_traits = std::allocator_traits<Allocator>; + using signal_type = sigh<void(Type &), Allocator>; + using container_type = std::vector<Type, typename alloc_traits::template rebind_alloc<Type>>; + +public: + using allocator_type = Allocator; + + dispatcher_handler(const allocator_type &allocator) + : signal{allocator}, + events{allocator} {} + + void publish() override { + const auto length = events.size(); + + for(std::size_t pos{}; pos < length; ++pos) { + signal.publish(events[pos]); + } + + events.erase(events.cbegin(), events.cbegin() + length); + } + + void disconnect(void *instance) override { + bucket().disconnect(instance); + } + + void clear() noexcept override { + events.clear(); + } + + [[nodiscard]] auto bucket() noexcept { + return typename signal_type::sink_type{signal}; + } + + void trigger(Type event) { + signal.publish(event); + } + + template<typename... Args> + void enqueue(Args &&...args) { + if constexpr(std::is_aggregate_v<Type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<Type>)) { + events.push_back(Type{std::forward<Args>(args)...}); + } else { + events.emplace_back(std::forward<Args>(args)...); + } + } + + [[nodiscard]] std::size_t size() const noexcept override { + return events.size(); + } + +private: + signal_type signal; + container_type events; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic dispatcher implementation. + * + * A dispatcher can be used either to trigger an immediate event or to enqueue + * events to be published all together once per tick.<br/> + * Listeners are provided in the form of member functions. For each event of + * type `Type`, listeners are such that they can be invoked with an argument of + * type `Type &`, no matter what the return type is. + * + * The dispatcher creates instances of the `sigh` class internally. Refer to the + * documentation of the latter for more details. + * + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Allocator> +class basic_dispatcher { + template<typename Type> + using handler_type = internal::dispatcher_handler<Type, Allocator>; + + using key_type = id_type; + // std::shared_ptr because of its type erased allocator which is useful here + using mapped_type = std::shared_ptr<internal::basic_dispatcher_handler>; + + using alloc_traits = std::allocator_traits<Allocator>; + using container_allocator = typename alloc_traits::template rebind_alloc<std::pair<const key_type, mapped_type>>; + using container_type = dense_map<key_type, mapped_type, identity, std::equal_to<>, container_allocator>; + + template<typename Type> + [[nodiscard]] handler_type<Type> &assure(const id_type id) { + static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Non-decayed types not allowed"); + auto &&ptr = pools.first()[id]; + + if(!ptr) { + const auto &allocator = get_allocator(); + ptr = std::allocate_shared<handler_type<Type>>(allocator, allocator); + } + + return static_cast<handler_type<Type> &>(*ptr); + } + + template<typename Type> + [[nodiscard]] const handler_type<Type> *assure(const id_type id) const { + static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Non-decayed types not allowed"); + + if(auto it = pools.first().find(id); it != pools.first().cend()) { + return static_cast<const handler_type<Type> *>(it->second.get()); + } + + return nullptr; + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + + /*! @brief Default constructor. */ + basic_dispatcher() + : basic_dispatcher{allocator_type{}} {} + + /** + * @brief Constructs a dispatcher with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_dispatcher(const allocator_type &allocator) + : pools{allocator, allocator} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_dispatcher(const basic_dispatcher &) = delete; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_dispatcher(basic_dispatcher &&other) noexcept + : pools{std::move(other.pools)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_dispatcher(basic_dispatcher &&other, const allocator_type &allocator) + : pools{container_type{std::move(other.pools.first()), allocator}, allocator} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a dispatcher is not allowed"); + } + + /*! @brief Default destructor. */ + ~basic_dispatcher() noexcept = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This dispatcher. + */ + basic_dispatcher &operator=(const basic_dispatcher &) = delete; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This dispatcher. + */ + basic_dispatcher &operator=(basic_dispatcher &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a dispatcher is not allowed"); + pools = std::move(other.pools); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given dispatcher. + * @param other Dispatcher to exchange the content with. + */ + void swap(basic_dispatcher &other) { + using std::swap; + swap(pools, other.pools); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return pools.second(); + } + + /** + * @brief Returns the number of pending events for a given type. + * @tparam Type Type of event for which to return the count. + * @param id Name used to map the event queue within the dispatcher. + * @return The number of pending events for the given type. + */ + template<typename Type> + [[nodiscard]] size_type size(const id_type id = type_hash<Type>::value()) const noexcept { + const auto *cpool = assure<std::decay_t<Type>>(id); + return cpool ? cpool->size() : 0u; + } + + /** + * @brief Returns the total number of pending events. + * @return The total number of pending events. + */ + [[nodiscard]] size_type size() const noexcept { + size_type count{}; + + for(auto &&cpool: pools.first()) { + count += cpool.second->size(); + } + + return count; + } + + /** + * @brief Returns a sink object for the given event and queue. + * + * A sink is an opaque object used to connect listeners to events. + * + * The function type for a listener is _compatible_ with: + * + * @code{.cpp} + * void(Type &); + * @endcode + * + * The order of invocation of the listeners isn't guaranteed. + * + * @sa sink + * + * @tparam Type Type of event of which to get the sink. + * @param id Name used to map the event queue within the dispatcher. + * @return A temporary sink object. + */ + template<typename Type> + [[nodiscard]] auto sink(const id_type id = type_hash<Type>::value()) { + return assure<Type>(id).bucket(); + } + + /** + * @brief Triggers an immediate event of a given type. + * @tparam Type Type of event to trigger. + * @param value An instance of the given type of event. + */ + template<typename Type> + void trigger(Type &&value = {}) { + trigger(type_hash<std::decay_t<Type>>::value(), std::forward<Type>(value)); + } + + /** + * @brief Triggers an immediate event on a queue of a given type. + * @tparam Type Type of event to trigger. + * @param value An instance of the given type of event. + * @param id Name used to map the event queue within the dispatcher. + */ + template<typename Type> + void trigger(const id_type id, Type &&value = {}) { + assure<std::decay_t<Type>>(id).trigger(std::forward<Type>(value)); + } + + /** + * @brief Enqueues an event of the given type. + * @tparam Type Type of event to enqueue. + * @tparam Args Types of arguments to use to construct the event. + * @param args Arguments to use to construct the event. + */ + template<typename Type, typename... Args> + void enqueue(Args &&...args) { + enqueue_hint<Type>(type_hash<Type>::value(), std::forward<Args>(args)...); + } + + /** + * @brief Enqueues an event of the given type. + * @tparam Type Type of event to enqueue. + * @param value An instance of the given type of event. + */ + template<typename Type> + void enqueue(Type &&value) { + enqueue_hint(type_hash<std::decay_t<Type>>::value(), std::forward<Type>(value)); + } + + /** + * @brief Enqueues an event of the given type. + * @tparam Type Type of event to enqueue. + * @tparam Args Types of arguments to use to construct the event. + * @param id Name used to map the event queue within the dispatcher. + * @param args Arguments to use to construct the event. + */ + template<typename Type, typename... Args> + void enqueue_hint(const id_type id, Args &&...args) { + assure<Type>(id).enqueue(std::forward<Args>(args)...); + } + + /** + * @brief Enqueues an event of the given type. + * @tparam Type Type of event to enqueue. + * @param id Name used to map the event queue within the dispatcher. + * @param value An instance of the given type of event. + */ + template<typename Type> + void enqueue_hint(const id_type id, Type &&value) { + assure<std::decay_t<Type>>(id).enqueue(std::forward<Type>(value)); + } + + /** + * @brief Utility function to disconnect everything related to a given value + * or instance from a dispatcher. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template<typename Type> + 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<typename Type> + void disconnect(Type *value_or_instance) { + for(auto &&cpool: pools.first()) { + cpool.second->disconnect(value_or_instance); + } + } + + /** + * @brief Discards all the events stored so far in a given queue. + * @tparam Type Type of event to discard. + * @param id Name used to map the event queue within the dispatcher. + */ + template<typename Type> + void clear(const id_type id = type_hash<Type>::value()) { + assure<Type>(id).clear(); + } + + /*! @brief Discards all the events queued so far. */ + void clear() noexcept { + for(auto &&cpool: pools.first()) { + cpool.second->clear(); + } + } + + /** + * @brief Delivers all the pending events of a given queue. + * @tparam Type Type of event to send. + * @param id Name used to map the event queue within the dispatcher. + */ + template<typename Type> + void update(const id_type id = type_hash<Type>::value()) { + assure<Type>(id).publish(); + } + + /*! @brief Delivers all the pending events. */ + void update() const { + for(auto &&cpool: pools.first()) { + cpool.second->publish(); + } + } + +private: + compressed_pair<container_type, allocator_type> pools; +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/signal/emitter.hpp b/external/entt/include/entt/signal/emitter.hpp new file mode 100644 index 0000000..de69ed4 --- /dev/null +++ b/external/entt/include/entt/signal/emitter.hpp @@ -0,0 +1,182 @@ +#ifndef ENTT_SIGNAL_EMITTER_HPP +#define ENTT_SIGNAL_EMITTER_HPP + +#include <functional> +#include <type_traits> +#include <utility> +#include "../container/dense_map.hpp" +#include "../core/compressed_pair.hpp" +#include "../core/fwd.hpp" +#include "../core/type_info.hpp" +#include "../core/utility.hpp" +#include "fwd.hpp" + +namespace entt { + +/** + * @brief General purpose event emitter. + * + * To create an emitter type, derived classes must inherit from the base as: + * + * @code{.cpp} + * struct my_emitter: emitter<my_emitter> { + * // ... + * } + * @endcode + * + * Handlers for the different events are created internally on the fly. It's not + * required to specify in advance the full list of accepted events.<br/> + * Moreover, whenever an event is published, an emitter also passes a reference + * to itself to its listeners. + * + * @tparam Derived Emitter type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Derived, typename Allocator> +class emitter { + using key_type = id_type; + using mapped_type = std::function<void(void *)>; + + using alloc_traits = std::allocator_traits<Allocator>; + using container_allocator = typename alloc_traits::template rebind_alloc<std::pair<const key_type, mapped_type>>; + using container_type = dense_map<key_type, mapped_type, identity, std::equal_to<>, container_allocator>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + + /*! @brief Default constructor. */ + emitter() + : emitter{allocator_type{}} {} + + /** + * @brief Constructs an emitter with a given allocator. + * @param allocator The allocator to use. + */ + explicit emitter(const allocator_type &allocator) + : handlers{allocator, allocator} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + emitter(const emitter &) = delete; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + emitter(emitter &&other) noexcept + : handlers{std::move(other.handlers)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + emitter(emitter &&other, const allocator_type &allocator) + : handlers{container_type{std::move(other.handlers.first()), allocator}, allocator} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying an emitter is not allowed"); + } + + /*! @brief Default destructor. */ + virtual ~emitter() noexcept { + static_assert(std::is_base_of_v<emitter<Derived, Allocator>, Derived>, "Invalid emitter type"); + } + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This emitter. + */ + emitter &operator=(const emitter &) = delete; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This emitter. + */ + emitter &operator=(emitter &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying an emitter is not allowed"); + handlers = std::move(other.handlers); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given emitter. + * @param other Emitter to exchange the content with. + */ + void swap(emitter &other) { + using std::swap; + swap(handlers, other.handlers); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return handlers.second(); + } + + /** + * @brief Publishes a given event. + * @tparam Type Type of event to trigger. + * @param value An instance of the given type of event. + */ + template<typename Type> + void publish(Type &&value) { + if(const auto id = type_id<Type>().hash(); handlers.first().contains(id)) { + handlers.first()[id](&value); + } + } + + /** + * @brief Registers a listener with the event emitter. + * @tparam Type Type of event to which to connect the listener. + * @param func The listener to register. + */ + template<typename Type> + void on(std::function<void(Type &, Derived &)> func) { + handlers.first().insert_or_assign(type_id<Type>().hash(), [func = std::move(func), this](void *value) { + func(*static_cast<Type *>(value), static_cast<Derived &>(*this)); + }); + } + + /** + * @brief Disconnects a listener from the event emitter. + * @tparam Type Type of event of the listener. + */ + template<typename Type> + void erase() { + handlers.first().erase(type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()); + } + + /*! @brief Disconnects all the listeners. */ + void clear() noexcept { + handlers.first().clear(); + } + + /** + * @brief Checks if there are listeners registered for the specific event. + * @tparam Type Type of event to test. + * @return True if there are no listeners registered, false otherwise. + */ + template<typename Type> + [[nodiscard]] bool contains() const { + return handlers.first().contains(type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()); + } + + /** + * @brief Checks if there are listeners registered with the event emitter. + * @return True if there are no listeners registered, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return handlers.first().empty(); + } + +private: + compressed_pair<container_type, allocator_type> handlers; +}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/signal/fwd.hpp b/external/entt/include/entt/signal/fwd.hpp new file mode 100644 index 0000000..b0964b6 --- /dev/null +++ b/external/entt/include/entt/signal/fwd.hpp @@ -0,0 +1,46 @@ +#ifndef ENTT_SIGNAL_FWD_HPP +#define ENTT_SIGNAL_FWD_HPP + +#include <memory> + +namespace entt { + +template<typename> +class delegate; + +template<typename = std::allocator<void>> +class basic_dispatcher; + +template<typename, typename = std::allocator<void>> +class emitter; + +class connection; + +struct scoped_connection; + +template<typename> +class sink; + +template<typename Type, typename = std::allocator<void>> +class sigh; + +/*! @brief Alias declaration for the most common use case. */ +using dispatcher = basic_dispatcher<>; + +/*! @brief Disambiguation tag for constructors and the like. */ +template<auto> +struct connect_arg_t { + /*! @brief Default constructor. */ + explicit connect_arg_t() = default; +}; + +/** + * @brief Constant of type connect_arg_t used to disambiguate calls. + * @tparam Candidate Element to connect (likely a free or member function). + */ +template<auto Candidate> +inline constexpr connect_arg_t<Candidate> connect_arg{}; + +} // namespace entt + +#endif diff --git a/external/entt/include/entt/signal/sigh.hpp b/external/entt/include/entt/signal/sigh.hpp new file mode 100644 index 0000000..38da4a2 --- /dev/null +++ b/external/entt/include/entt/signal/sigh.hpp @@ -0,0 +1,471 @@ +#ifndef ENTT_SIGNAL_SIGH_HPP +#define ENTT_SIGNAL_SIGH_HPP + +#include <cstddef> +#include <memory> +#include <type_traits> +#include <utility> +#include <vector> +#include "delegate.hpp" +#include "fwd.hpp" + +namespace entt { + +/** + * @brief Sink class. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Type A valid signal handler type. + */ +template<typename Type> +class sink; + +/** + * @brief Unmanaged signal handler. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Type A valid function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Type, typename Allocator> +class sigh; + +/** + * @brief Unmanaged signal handler. + * + * It works directly with references to classes and pointers to member functions + * as well as pointers to free functions. Users of this class are in charge of + * disconnecting instances before deleting them. + * + * This class serves mainly two purposes: + * + * * Creating signals to use later to notify a bunch of listeners. + * * Collecting results from a set of functions like in a voting system. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Ret, typename... Args, typename Allocator> +class sigh<Ret(Args...), Allocator> { + friend class sink<sigh<Ret(Args...), Allocator>>; + + using alloc_traits = std::allocator_traits<Allocator>; + using delegate_type = delegate<Ret(Args...)>; + using container_type = std::vector<delegate_type, typename alloc_traits::template rebind_alloc<delegate_type>>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Sink type. */ + using sink_type = sink<sigh<Ret(Args...), Allocator>>; + + /*! @brief Default constructor. */ + sigh() noexcept(std::is_nothrow_default_constructible_v<allocator_type> && std::is_nothrow_constructible_v<container_type, const allocator_type &>) + : sigh{allocator_type{}} {} + + /** + * @brief Constructs a signal handler with a given allocator. + * @param allocator The allocator to use. + */ + explicit sigh(const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v<container_type, const allocator_type &>) + : calls{allocator} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + sigh(const sigh &other) noexcept(std::is_nothrow_copy_constructible_v<container_type>) + : calls{other.calls} {} + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + sigh(const sigh &other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v<container_type, const container_type &, const allocator_type &>) + : calls{other.calls, allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + sigh(sigh &&other) noexcept(std::is_nothrow_move_constructible_v<container_type>) + : calls{std::move(other.calls)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + sigh(sigh &&other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v<container_type, container_type &&, const allocator_type &>) + : calls{std::move(other.calls), allocator} {} + + /*! @brief Default destructor. */ + ~sigh() noexcept = default; + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This signal handler. + */ + sigh &operator=(const sigh &other) noexcept(std::is_nothrow_copy_assignable_v<container_type>) { + calls = other.calls; + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This signal handler. + */ + sigh &operator=(sigh &&other) noexcept(std::is_nothrow_move_assignable_v<container_type>) { + calls = std::move(other.calls); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given signal handler. + * @param other Signal handler to exchange the content with. + */ + void swap(sigh &other) noexcept(std::is_nothrow_swappable_v<container_type>) { + using std::swap; + swap(calls, other.calls); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return calls.get_allocator(); + } + + /** + * @brief Number of listeners connected to the signal. + * @return Number of listeners currently connected. + */ + [[nodiscard]] size_type size() const noexcept { + return calls.size(); + } + + /** + * @brief Returns false if at least a listener is connected to the signal. + * @return True if the signal has no listeners connected, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return calls.empty(); + } + + /** + * @brief Triggers a signal. + * + * All the listeners are notified. Order isn't guaranteed. + * + * @param args Arguments to use to invoke listeners. + */ + void publish(Args... args) const { + for(auto pos = calls.size(); pos; --pos) { + calls[pos - 1u](args...); + } + } + + /** + * @brief Collects return values from the listeners. + * + * The collector must expose a call operator with the following properties: + * + * * The return type is either `void` or such that it's convertible to + * `bool`. In the second case, a true value will stop the iteration. + * * The list of parameters is empty if `Ret` is `void`, otherwise it + * contains a single element such that `Ret` is convertible to it. + * + * @tparam Func Type of collector to use, if any. + * @param func A valid function object. + * @param args Arguments to use to invoke listeners. + */ + template<typename Func> + void collect(Func func, Args... args) const { + for(auto pos = calls.size(); pos; --pos) { + if constexpr(std::is_void_v<Ret> || !std::is_invocable_v<Func, Ret>) { + calls[pos - 1u](args...); + + if constexpr(std::is_invocable_r_v<bool, Func>) { + if(func()) { + break; + } + } else { + func(); + } + } else { + if constexpr(std::is_invocable_r_v<bool, Func, Ret>) { + if(func(calls[pos - 1u](args...))) { + break; + } + } else { + func(calls[pos - 1u](args...)); + } + } + } + } + +private: + container_type calls; +}; + +/** + * @brief Connection class. + * + * Opaque object the aim of which is to allow users to release an already + * estabilished connection without having to keep a reference to the signal or + * the sink that generated it. + */ +class connection { + template<typename> + friend class sink; + + connection(delegate<void(void *)> fn, void *ref) + : disconnect{fn}, signal{ref} {} + +public: + /*! @brief Default constructor. */ + connection() + : disconnect{}, + signal{} {} + + /** + * @brief Checks whether a connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast<bool>(disconnect); + } + + /*! @brief Breaks the connection. */ + void release() { + if(disconnect) { + disconnect(signal); + disconnect.reset(); + } + } + +private: + delegate<void(void *)> 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.<br/> + * A scoped connection automatically breaks the link between the two objects + * when it goes out of scope. + */ +struct scoped_connection { + /*! @brief Default constructor. */ + scoped_connection() = default; + + /** + * @brief Constructs a scoped connection from a basic connection. + * @param other A valid connection object. + */ + scoped_connection(const connection &other) + : conn{other} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + scoped_connection(const scoped_connection &) = delete; + + /** + * @brief Move constructor. + * @param other The scoped connection to move from. + */ + scoped_connection(scoped_connection &&other) noexcept + : conn{std::exchange(other.conn, {})} {} + + /*! @brief Automatically breaks the link on destruction. */ + ~scoped_connection() noexcept { + conn.release(); + } + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This scoped connection. + */ + scoped_connection &operator=(const scoped_connection &) = delete; + + /** + * @brief Move assignment operator. + * @param other The scoped connection to move from. + * @return This scoped connection. + */ + scoped_connection &operator=(scoped_connection &&other) noexcept { + conn = std::exchange(other.conn, {}); + return *this; + } + + /** + * @brief Acquires a connection. + * @param other The connection object to acquire. + * @return This scoped connection. + */ + scoped_connection &operator=(connection other) { + conn = other; + return *this; + } + + /** + * @brief Checks whether a scoped connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast<bool>(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.<br/> + * The function type for a listener is the one of the signal to which it + * belongs. + * + * The clear separation between a signal and a sink permits to store the former + * as private data member without exposing the publish functionality to the + * users of the class. + * + * @warning + * Lifetime of a sink must not overcome that of the signal to which it refers. + * In any other case, attempting to use a sink results in undefined behavior. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Ret, typename... Args, typename Allocator> +class sink<sigh<Ret(Args...), Allocator>> { + using signal_type = sigh<Ret(Args...), Allocator>; + using delegate_type = typename signal_type::delegate_type; + using difference_type = typename signal_type::container_type::difference_type; + + template<auto Candidate, typename Type> + static void release(Type value_or_instance, void *signal) { + sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>(value_or_instance); + } + + template<auto Candidate> + static void release(void *signal) { + sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>(); + } + + template<typename Func> + void disconnect_if(Func callback) { + for(auto pos = signal->calls.size(); pos; --pos) { + if(auto &elem = signal->calls[pos - 1u]; callback(elem)) { + elem = std::move(signal->calls.back()); + signal->calls.pop_back(); + } + } + } + +public: + /** + * @brief Constructs a sink that is allowed to modify a given signal. + * @param ref A valid reference to a signal object. + */ + sink(sigh<Ret(Args...), Allocator> &ref) noexcept + : signal{&ref} {} + + /** + * @brief Returns false if at least a listener is connected to the sink. + * @return True if the sink has no listeners connected, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return signal->calls.empty(); + } + + /** + * @brief Connects a free function (with or without payload), a bound or an + * unbound member to a signal. + * @tparam Candidate Function or member to connect to the signal. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance A valid object that fits the purpose, if any. + * @return A properly initialized connection object. + */ + template<auto Candidate, typename... Type> + connection connect(Type &&...value_or_instance) { + disconnect<Candidate>(value_or_instance...); + + delegate_type call{}; + call.template connect<Candidate>(value_or_instance...); + signal->calls.push_back(std::move(call)); + + delegate<void(void *)> conn{}; + conn.template connect<&release<Candidate, Type...>>(value_or_instance...); + return {conn, signal}; + } + + /** + * @brief Disconnects a free function (with or without payload), a bound or + * an unbound member from a signal. + * @tparam Candidate Function or member to disconnect from the signal. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance A valid object that fits the purpose, if any. + */ + template<auto Candidate, typename... Type> + void disconnect(Type &&...value_or_instance) { + delegate_type call{}; + call.template connect<Candidate>(value_or_instance...); + disconnect_if([&call](const auto &elem) { return elem == call; }); + } + + /** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @param value_or_instance A valid object that fits the purpose. + */ + void disconnect(const void *value_or_instance) { + ENTT_ASSERT(value_or_instance != nullptr, "Invalid value or instance"); + disconnect_if([value_or_instance](const auto &elem) { return elem.data() == value_or_instance; }); + } + + /*! @brief Disconnects all the listeners from a signal. */ + void disconnect() { + signal->calls.clear(); + } + +private: + signal_type *signal; +}; + +/** + * @brief Deduction guide. + * + * It allows to deduce the signal handler type of a sink directly from the + * signal it refers to. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Ret, typename... Args, typename Allocator> +sink(sigh<Ret(Args...), Allocator> &) -> sink<sigh<Ret(Args...), Allocator>>; + +} // namespace entt + +#endif |
