1 #ifndef ENTT_ENTITY_STORAGE_HPP
2 #define ENTT_ENTITY_STORAGE_HPP
11 #include "../config/config.h"
12 #include "../core/compressed_pair.hpp"
13 #include "../core/iterator.hpp"
14 #include "../core/memory.hpp"
15 #include "../core/type_info.hpp"
16 #include "component.hpp"
19 #include "sigh_storage_mixin.hpp"
20 #include "sparse_set.hpp"
31 template<
typename Container>
32 class storage_iterator final {
33 friend storage_iterator<const Container>;
35 using container_type = std::remove_const_t<Container>;
36 using alloc_traits = std::allocator_traits<typename container_type::allocator_type>;
37 using comp_traits = component_traits<typename container_type::value_type>;
39 using iterator_traits = std::iterator_traits<std::conditional_t<
40 std::is_const_v<Container>,
41 typename alloc_traits::template rebind_traits<typename std::pointer_traits<typename container_type::value_type>::element_type>::const_pointer,
42 typename alloc_traits::template rebind_traits<typename std::pointer_traits<typename container_type::value_type>::element_type>::pointer>>;
45 using value_type =
typename iterator_traits::value_type;
46 using pointer =
typename iterator_traits::pointer;
47 using reference =
typename iterator_traits::reference;
48 using difference_type =
typename iterator_traits::difference_type;
49 using iterator_category = std::random_access_iterator_tag;
51 storage_iterator() ENTT_NOEXCEPT = default;
53 storage_iterator(Container *ref, difference_type idx) ENTT_NOEXCEPT
57 template<
bool Const = std::is_const_v<Container>,
typename = std::enable_if_t<Const>>
58 storage_iterator(
const storage_iterator<std::remove_const_t<Container>> &other) ENTT_NOEXCEPT
59 : packed{other.packed},
60 offset{other.offset} {}
62 storage_iterator &operator++() ENTT_NOEXCEPT {
63 return --offset, *
this;
66 storage_iterator operator++(
int) ENTT_NOEXCEPT {
67 storage_iterator orig = *
this;
68 return ++(*this), orig;
71 storage_iterator &operator--() ENTT_NOEXCEPT {
72 return ++offset, *
this;
75 storage_iterator operator--(
int) ENTT_NOEXCEPT {
76 storage_iterator orig = *
this;
77 return operator--(), orig;
80 storage_iterator &operator+=(
const difference_type value) ENTT_NOEXCEPT {
85 storage_iterator
operator+(
const difference_type value)
const ENTT_NOEXCEPT {
86 storage_iterator copy = *
this;
87 return (copy += value);
90 storage_iterator &operator-=(
const difference_type value) ENTT_NOEXCEPT {
91 return (*
this += -value);
94 storage_iterator operator-(
const difference_type value)
const ENTT_NOEXCEPT {
95 return (*
this + -value);
98 [[nodiscard]] reference operator[](
const difference_type value)
const ENTT_NOEXCEPT {
99 const auto pos = index() - value;
100 return (*packed)[pos / comp_traits::page_size][
fast_mod(pos, comp_traits::page_size)];
103 [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
104 const auto pos = index();
105 return (*packed)[pos / comp_traits::page_size] +
fast_mod(pos, comp_traits::page_size);
108 [[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
109 return *operator->();
112 [[nodiscard]] difference_type index() const ENTT_NOEXCEPT {
118 difference_type offset;
121 template<
typename CLhs,
typename CRhs>
122 [[nodiscard]] std::ptrdiff_t operator-(
const storage_iterator<CLhs> &lhs,
const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT {
123 return rhs.index() - lhs.index();
126 template<
typename CLhs,
typename CRhs>
127 [[nodiscard]]
bool operator==(
const storage_iterator<CLhs> &lhs,
const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT {
128 return lhs.index() == rhs.index();
131 template<
typename CLhs,
typename CRhs>
132 [[nodiscard]]
bool operator!=(
const storage_iterator<CLhs> &lhs,
const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT {
133 return !(lhs == rhs);
136 template<
typename CLhs,
typename CRhs>
137 [[nodiscard]]
bool operator<(
const storage_iterator<CLhs> &lhs,
const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT {
138 return lhs.index() > rhs.index();
141 template<
typename CLhs,
typename CRhs>
142 [[nodiscard]]
bool operator>(
const storage_iterator<CLhs> &lhs,
const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT {
143 return lhs.index() < rhs.index();
146 template<
typename CLhs,
typename CRhs>
147 [[nodiscard]]
bool operator<=(
const storage_iterator<CLhs> &lhs,
const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT {
151 template<
typename CLhs,
typename CRhs>
152 [[nodiscard]]
bool operator>=(
const storage_iterator<CLhs> &lhs,
const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT {
156 template<
typename It,
typename... Other>
157 class extended_storage_iterator final {
158 template<
typename Iter,
typename... Args>
159 friend class extended_storage_iterator;
162 using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::forward_as_tuple(*std::declval<Other>()...)));
163 using pointer = input_iterator_pointer<value_type>;
164 using reference = value_type;
165 using difference_type = std::ptrdiff_t;
166 using iterator_category = std::input_iterator_tag;
168 extended_storage_iterator() =
default;
170 extended_storage_iterator(It base, Other... other)
171 : it{base, other...} {}
173 template<
typename... Args,
typename = std::enable_if_t<(!std::is_same_v<Other, Args> && ...) && (std::is_constructible_v<Other, Args> && ...)>>
174 extended_storage_iterator(
const extended_storage_iterator<It, Args...> &other)
177 extended_storage_iterator &operator++() ENTT_NOEXCEPT {
178 return ++std::get<It>(it), (++std::get<Other>(it), ...), *
this;
181 extended_storage_iterator operator++(
int) ENTT_NOEXCEPT {
182 extended_storage_iterator orig = *
this;
183 return ++(*this), orig;
186 [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
190 [[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
191 return {*std::get<It>(it), *std::get<Other>(it)...};
194 template<
typename... CLhs,
typename... CRhs>
195 friend bool operator==(
const extended_storage_iterator<CLhs...> &,
const extended_storage_iterator<CRhs...> &) ENTT_NOEXCEPT;
198 std::tuple<It, Other...> it;
201 template<
typename... CLhs,
typename... CRhs>
202 [[nodiscard]]
bool operator==(
const extended_storage_iterator<CLhs...> &lhs,
const extended_storage_iterator<CRhs...> &rhs) ENTT_NOEXCEPT {
203 return std::get<0>(lhs.it) == std::get<0>(rhs.it);
206 template<
typename... CLhs,
typename... CRhs>
207 [[nodiscard]]
bool operator!=(
const extended_storage_iterator<CLhs...> &lhs,
const extended_storage_iterator<CRhs...> &rhs) ENTT_NOEXCEPT {
208 return !(lhs == rhs);
233 template<
typename Entity,
typename Type,
typename Allocator,
typename>
235 static_assert(std::is_move_constructible_v<Type> && std::is_move_assignable_v<Type>,
"The type must be at least move constructible/assignable");
237 using alloc_traits = std::allocator_traits<Allocator>;
238 static_assert(std::is_same_v<typename alloc_traits::value_type, Type>,
"Invalid value type");
240 using container_type = std::vector<typename alloc_traits::pointer, typename alloc_traits::template rebind_alloc<typename alloc_traits::pointer>>;
243 [[nodiscard]]
auto &element_at(
const std::size_t pos)
const {
247 auto assure_at_least(
const std::size_t pos) {
248 auto &&container = packed.
first();
251 if(!(idx < container.size())) {
252 auto curr = container.size();
253 container.resize(idx + 1u,
nullptr);
256 for(
const auto last = container.size(); curr < last; ++curr) {
261 container.resize(curr);
269 template<
typename... Args>
270 auto emplace_element(
const Entity
entt,
const bool force_back, Args &&...args) {
274 auto elem = assure_at_least(
static_cast<size_type>(it.index()));
290 void shrink_to_size(
const std::size_t sz) {
294 std::destroy_at(std::addressof(element_at(pos)));
297 std::destroy_at(std::addressof(element_at(pos)));
301 auto &&container = packed.
first();
302 auto page_allocator{packed.
second()};
305 for(
auto pos = from, last = container.size(); pos < last; ++pos) {
309 container.resize(from);
313 const void *get_at(
const std::size_t pos)
const final {
314 return std::addressof(element_at(pos));
317 void swap_at(
const std::size_t lhs,
const std::size_t rhs)
final {
319 swap(element_at(lhs), element_at(rhs));
322 void move_element(
const std::size_t from,
const std::size_t to)
final {
323 auto &elem = element_at(from);
325 std::destroy_at(std::addressof(elem));
335 for(; first != last; ++first) {
338 [[maybe_unused]]
auto unused = std::exchange(element_at(
static_cast<size_type>(first.index())), std::move(elem));
339 std::destroy_at(std::addressof(elem));
350 for(; first != last; ++first) {
352 std::destroy_at(std::addressof(element_at(
static_cast<size_type>(first.index()))));
365 if constexpr(std::is_copy_constructible_v<value_type>) {
366 return emplace_element(
entt, force_back, *
static_cast<const value_type *
>(value));
371 if constexpr(std::is_default_constructible_v<value_type>) {
372 return emplace_element(
entt, force_back);
391 using pointer =
typename container_type::pointer;
393 using const_pointer =
typename alloc_traits::template rebind_traits<typename alloc_traits::const_pointer>::const_pointer;
395 using iterator = internal::storage_iterator<container_type>;
417 packed{container_type{allocator}, allocator} {}
425 packed{std::move(other.packed)} {}
433 :
base_type{std::move(other), allocator},
434 packed{container_type{std::move(other.packed.first()), allocator}, allocator} {
435 ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.
second() == other.packed.second(),
"Copying a storage is not allowed");
449 ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.
second() == other.packed.second(),
"Copying a storage is not allowed");
453 packed.
first() = std::move(other.packed.first());
488 assure_at_least(cap - 1u);
512 return packed.
first().data();
517 return packed.
first().data();
529 const auto pos =
static_cast<typename iterator::difference_type
>(
base_type::size());
540 const auto pos =
static_cast<typename iterator::difference_type
>(
base_type::size());
578 return std::make_reverse_iterator(
cend());
588 return std::make_reverse_iterator(
end());
602 return std::make_reverse_iterator(
cbegin());
612 return std::make_reverse_iterator(
begin());
640 return std::forward_as_tuple(
get(
entt));
645 return std::forward_as_tuple(
get(
entt));
660 template<
typename... Args>
662 if constexpr(std::is_aggregate_v<value_type>) {
663 const auto it = emplace_element(
entt,
false, Type{std::forward<Args>(args)...});
664 return element_at(
static_cast<size_type>(it.index()));
666 const auto it = emplace_element(
entt,
false, std::forward<Args>(args)...);
667 return element_at(
static_cast<size_type>(it.index()));
678 template<
typename... Func>
681 auto &elem = element_at(idx);
682 (std::forward<Func>(func)(elem), ...);
699 template<
typename It>
701 for(; first != last; ++first) {
702 emplace_element(*first,
true, value);
718 template<typename EIt, typename CIt, typename = std::enable_if_t<std::is_same_v<typename std::iterator_traits<CIt>::value_type,
value_type>>>
719 void insert(EIt first, EIt last, CIt from) {
720 for(; first != last; ++first, ++from) {
721 emplace_element(*first,
true, *from);
747 template<
typename Entity,
typename Type,
typename Allocator>
748 class basic_storage<Entity, Type, Allocator, std::enable_if_t<ignore_as_empty_v<Type>>>
749 :
public basic_sparse_set<Entity, typename std::allocator_traits<Allocator>::template rebind_alloc<Entity>> {
750 using alloc_traits = std::allocator_traits<Allocator>;
751 static_assert(std::is_same_v<typename alloc_traits::value_type, Type>,
"Invalid value type");
794 :
base_type{std::move(other), allocator} {}
849 template<
typename... Args>
860 template<
typename... Func>
863 (std::forward<Func>(func)(), ...);
873 template<
typename It,
typename... Args>
874 void insert(It first, It last, Args &&...) {
875 for(; first != last; ++first) {
902 template<
typename Entity,
typename Type,
typename =
void>
Basic sparse set implementation.
void swap(basic_sparse_set &other)
Exchanges the contents with those of a given sparse set.
size_type index(const entity_type entt) const
Returns the position of an entity in a sparse set.
virtual basic_iterator try_emplace(const Entity entt, const bool force_back, const void *=nullptr)
Assigns an entity to a sparse set.
iterator end() const
Returns an iterator to the end.
virtual void reserve(const size_type cap)
Increases the capacity of a sparse set.
size_type size() const
Returns the number of elements in a sparse set.
virtual void shrink_to_fit()
Requests the removal of unused capacity.
virtual void swap_and_pop(basic_iterator first, basic_iterator last)
Erases entities from a sparse set.
entity_type at(const size_type pos) const
Returns the entity at specified location, with bounds checking.
const_iterator begin() const
Returns an iterator to the beginning.
const_iterator cbegin() const
Returns an iterator to the beginning.
internal::sparse_set_iterator< packed_container_type > basic_iterator
Random access iterator type.
bool contains(const entity_type entt) const
Checks if a sparse set contains an entity.
constexpr allocator_type get_allocator() const
Returns the associated allocator.
const_iterator cend() const
Returns an iterator to the end.
basic_sparse_set & operator=(basic_sparse_set &&other)
Move assignment operator.
virtual void in_place_pop(basic_iterator first, basic_iterator last)
Erases entities from a sparse set.
basic_storage(const allocator_type &allocator)
Constructs an empty container with a given allocator.
basic_storage(basic_storage &&other, const allocator_type &allocator)
Allocator-extended move constructor.
constexpr allocator_type get_allocator() const
Returns the associated allocator.
const_iterable each() const
Returns an iterable object to use to visit a storage.
basic_storage(basic_storage &&other)=default
Move constructor.
Type value_type
Type of the objects assigned to entities.
iterable each()
Returns an iterable object to use to visit a storage.
std::size_t size_type
Unsigned integer type.
void insert(It first, It last, Args &&...)
Assigns entities to a storage.
std::tuple get_as_tuple([[maybe_unused]] const entity_type entt) const
Returns an empty tuple.
void patch([[maybe_unused]] const entity_type entt, Func &&...func)
Updates the instance assigned to a given entity in-place.
void get([[maybe_unused]] const entity_type entt) const
Returns the object assigned to an entity, that is void.
Entity entity_type
Underlying entity identifier.
basic_storage()
Default constructor.
Allocator allocator_type
Allocator type.
void emplace(const entity_type entt, Args &&...)
Assigns an entity to a storage and constructs its object.
basic_storage & operator=(basic_storage &&other)=default
Move assignment operator.
Basic storage implementation.
value_type & get(const entity_type entt)
Returns the object assigned to an entity.
const_reverse_iterator crend() const
Returns a reverse iterator to the end.
void shrink_to_fit() override
Requests the removal of unused capacity.
iterator end()
Returns an iterator to the end.
const_iterator cbegin() const
Returns an iterator to the beginning.
const_iterator cend() const
Returns an iterator to the end.
std::reverse_iterator< iterator > reverse_iterator
Reverse iterator type.
const_reverse_iterator rbegin() const
Returns a reverse iterator to the beginning.
Allocator allocator_type
Allocator type.
basic_storage(basic_storage &&other, const allocator_type &allocator)
Allocator-extended move constructor.
underlying_type::basic_iterator try_emplace([[maybe_unused]] const Entity entt, const bool force_back, const void *value) override
Assigns an entity to a storage.
std::tuple< value_type & > get_as_tuple(const entity_type entt)
Returns the object assigned to an entity as a tuple.
iterable each()
Returns an iterable object to use to visit a storage.
internal::storage_iterator< container_type > iterator
Random access iterator type.
internal::storage_iterator< const container_type > const_iterator
Constant random access iterator type.
reverse_iterator rend()
Returns a reverse iterator to the end.
basic_storage & operator=(basic_storage &&other)
Move assignment operator.
const_iterator begin() const
Returns an iterator to the beginning.
value_type & emplace(const entity_type entt, Args &&...args)
Assigns an entity to a storage and constructs its object.
reverse_iterator rbegin()
Returns a reverse iterator to the beginning.
const_pointer raw() const
Direct access to the array of objects.
basic_storage(basic_storage &&other)
Move constructor.
typename container_type::pointer pointer
Pointer type to contained elements.
void reserve(const size_type cap) override
Increases the capacity of a storage.
value_type & patch(const entity_type entt, Func &&...func)
Updates the instance assigned to a given entity in-place.
void in_place_pop(typename underlying_type::basic_iterator first, typename underlying_type::basic_iterator last) override
Erases elements from a storage.
const_reverse_iterator rend() const
Returns a reverse iterator to the end.
std::size_t size_type
Unsigned integer type.
std::reverse_iterator< const_iterator > const_reverse_iterator
Constant reverse iterator type.
typename alloc_traits::template rebind_traits< typename alloc_traits::const_pointer >::const_pointer const_pointer
Constant pointer type to contained elements.
const_reverse_iterator crbegin() const
Returns a reverse iterator to the beginning.
basic_storage(const allocator_type &allocator)
Constructs an empty storage with a given allocator.
const_iterator end() const
Returns an iterator to the end.
pointer raw()
Direct access to the array of objects.
Entity entity_type
Underlying entity identifier.
void insert(It first, It last, const value_type &value={})
Assigns one or more entities to a storage and constructs their objects from a given instance.
iterator begin()
Returns an iterator to the beginning.
basic_storage()
Default constructor.
void swap(basic_storage &other)
Exchanges the contents with those of a given storage.
constexpr allocator_type get_allocator() const
Returns the associated allocator.
void insert(EIt first, EIt last, CIt from)
Assigns one or more entities to a storage and constructs their objects from a given range.
void swap_and_pop(typename underlying_type::basic_iterator first, typename underlying_type::basic_iterator last) override
Erases elements from a storage.
~basic_storage() override
Default destructor.
const_iterable each() const
Returns an iterable object to use to visit a storage.
Type value_type
Type of the objects assigned to entities.
std::tuple< const value_type & > get_as_tuple(const entity_type entt) const
Returns the object assigned to an entity as a tuple.
const value_type & get(const entity_type entt) const
Returns the object assigned to an entity.
size_type capacity() const override
Returns the number of elements that a storage has currently allocated space for.
second_type & second()
Returns the second element that a pair stores.
first_type & first()
Returns the first element that a pair stores.
Mixin type used to add signal support to storage types.
constexpr bool operator==(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs)
Compares two hashed strings.
constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs)
Utility function to design allocation-aware containers.
constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs)
Utility function to design allocation-aware containers.
constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod)
Fast module utility function (powers of two only).
const type_info & type_id()
Returns the type info object associated to a given type.
constexpr bool operator<=(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs)
Compares two hashed strings.
constexpr bool operator>=(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs)
Compares two hashed strings.
constexpr Type * uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args)
Uses-allocator construction utility (waiting for C++20).
constexpr tombstone_t tombstone
Compile-time constant for tombstone entities.
constexpr type_list< Type..., Other... > operator+(type_list< Type... >, type_list< Other... >)
Concatenates multiple type lists.
constexpr auto to_address(Type &&ptr)
Unwraps fancy pointers, does nothing otherwise (waiting for C++20).
constexpr bool operator<(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs)
Compares two hashed strings.
deletion_policy
Sparse set deletion policy.
bool operator!=(const basic_any< Len, Align > &lhs, const basic_any< Len, Align > &rhs)
Checks if two wrappers differ in their content.
constexpr bool operator>(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs)
Compares two hashed strings.
Common way to access various properties of components.
static constexpr std::size_t page_size
Page size, default is ENTT_PACKED_PAGE for non-empty types.
static constexpr bool in_place_delete
Pointer stability, default is false.
Utility class to create an iterable object from a pair of iterators.
Provides a common way to access certain properties of storage types.