tomato/test/entt/entity/group.cpp
Green Sky 90ce4bda4e Squashed 'external/entt/entt/' changes from fef921132..344e03ac6
344e03ac6 update single include file to v3.12.2
da56665b0 registry: make ::valid backward compatible
f6f01ef1b snapshot: avoid warnings due to deprecated functions
0ed514628 now working on v3.12.2
a41421d86 update single include file to v3.12.1
c1f6b11f7 snapshot: reintroduce support to storage listeners
b2233064a now working on version v3.12.1
cb974bf56 adjacency_matrix: fix in_edges() is off by 1 in some cases (close #1019)
7b7d82e6f doc: snapshot (close #984)
05c6898fc test: self-fixing archive example for snapshot classes
7ffa459a6 snapshot: drop ::get member template parameter
93e8e94e6 test: basic continuous loader
c4e241662 snapshot: review basic_continuous_loader (and drop shrink)
9c25419b9 test: more on basic_snapshot_loader
1879830df snapshot: drop pointless assert
29298c0eb test: guarantee code coverage, we'll update the test later on
247abef1d test: rollback for code coverage purposes on the snapshot class
6994d98d2 test: typo
9a600ece2 test: snapshot
f91226ef4 snapshot: share ::orphans implementation (to deprecate in future though)
e366ffbd3 doc: snapshot
63b300d39 snapshot: again, dense_map::contains is a thing
afb70d157 test: avoid warnings due to unused variables
49534eec0 snapshot: dense_map::contains is a thing fortunately
3f1277f7b snapshot: use the right allocator for the remote-local mapping
26fad4c38 test: basic snapshot loader
25b3afacf test: basic snapshot
2d25bbb09 snapshot: check registry type
0eb834582 snapshot: small cleanup
124a44052 test: use the new snapshot get functions in the test suite
5c704636e test: use the new snapshot get functions in the test suite
31fd94cc3 snapshot: cleanup to get ready to drop an internal function
573e43272 snapshot: reduce storage lookups
1d8943481 snapshot: drop useless function
e0a1ef7c1 snapshot: check on member type class
48ac0e0eb snapshot: add basic_continuous_loader::get, deprecate ::entities and ::component
bcb6234d9 snapshot: add basic_snapshot_loader::get, deprecate ::entities and ::component
f96796326 snapshot: reject entity type in the range-get (now get instead of get_sparse)
b22c55dd2 doc: typo
4ff5a536c snapshot: add basic_snapshot::get, deprecate ::entities and ::component
fff5f578a test: avoid using deprecated functions in an example
0f44c8c92 doc: reflect recent changes
0b6ad0315 snapshot: * single element only archive functions required * avoid iterating elements more than once
2450b0bc6 test: minor changes (waiting for a rework)
fc8eebf36 snapshot: use component_traits instead of is_empty_v
e4f51f2b7 snapshot: avoid multiple lookups of the same storage
2c2216a89 doc: typo
cafe85180 snapshot: deprecate multi-type component loading function
35e338cc9 snapshot: deprecate multi-type component loading function
8feeaaef7 doc: minor changes
e7a3c4e37 snapshot: add missing [[deprecate(...)]]
ea5c558bd snapshot: cleanup (waiting for further improvements)
94f0ed179 snapshot: deprecate multi-type component loading function
244c35949 snapshot: deprecate multi-type component loading function
1f24fea21 type_traits: formatting
8deaa09b2 test: perform static checks at compile-time
85bffb714 type_traits: std::tuple traits specialization for entt::type_list and entt::value_list (#1011)
325ca310d view: updated ::refresh
d903e268f snapshot: minor changes
f4b26756c snapshot: improved basic_snapshot::component
fb3a34ee9 *: updated TODO
6902bb6c4 doc: typo
379819b2b test: cleanup
59abfbfb5 meta: refine policy check on value types for non-member data
6e2d87184 registry: avoid casting return types directly to better support empty storage
57ec3c85c registry: erase_if (close #977)
4afdf287f doc: minor changes
2810ac7cb registry: suppress a warning on msvc
e0d27f9bf *: updated TODO
de303c999 test: reverse-each for storage entity
1619e780f test: reverse each for plain storage classes
a1e37eca6 storage: reverse-each
c345e7456 doc: note on reverse iterations
d166c026f snapshot: minor changes
5e639996d doc: minor changes
dac2ef5a9 doc: typo
71d7888e8 snapshot: drop redundant check
84a4df9c4 doc: exclude-only views
95bc20319 doc: entity lifecycle
5a9f6d211 doc: cleanup
a29302faa test: more on entity signals
75efa72c6 registry: cleanup ::erase
58a84665b registry: cleanup ::remove
a5263384d doc: drop redundant comments
c0e6759c6 doc: cleanup a little further
d754f7431 doc: cleanup
1df539943 doc: drop pointless tags
c284e6fee doc: minor changes
500239758 test: typo
319ecd808 organizer: fix organizer::vertex::prepare not creating component pools (#1014)
d7891fabc doc: mention named pools support when registering listeners
e287dd041 helper: minor changes
4dee9dde1 registry: named pools support for on_construct/on_update/on_destroy
9bae6e67b doc: update connection helper doc
aa7a7ce25 doc: minor changes
a969468c5 registry: de-deprecate :) on_construct/on_update/on_destroy
a1e76fc63 doc: more about entity storage
d8ed4ca35 registry: refine how entity storage is used internally
3248e3f91 helper: make sigh_helper work with named pools
f00687e6f doc: updated registry documentation
5240c6b60 registry: deprecate on_construct/on_update/on_destroy
67604a88e natvis: update registry snippet
4242dfb8b registry: use entity storage directly as much as possible
f96d8ee83 registry: prepare to split component storage and entity storage
c147ec37c test: try to make gcc happy again
094ddbba3 meta: avoid shadow warnings
634630ca2 test: add missing template keywords (thanks msvc for ignoring them)
d78c26f26 *: updated TODO
fabc6c9bd test: full cross-registry entity-copy example with meta (not strictly required)
b6e8ddd2a meta: fight against the small nuances of the language :)
cf2bbae6e mixin: make it simpler to modify the underlying type
08799616d *: updated TODO
58bebf78d meta: reduce symbols and their sizes if possible
d534fad3e doc: more about views
871dc7a40 doc: drop references to storage placeholders
1fe7c78f7 test: minor changes
22a65f80f test: cleanup
756ea8a38 *: updated TODO
12186cb40 registry: drop internal static storage variables from ::assure
aa9ffb9ee registry: const ::storage<T>(...) returns a pointer to possibly null storage
dcb5aed90 registry: lazily/partially initialize views in the ::view const function
34f6a747a registry: add support for non-existent pools to try_get
912cb2ad5 snapshot: constness review
885488b3d registry: any_of supports non-existing pools now
3d3d3ef2d registry: all_of supports non-existing pools now
a7120b340 registry: coding style
51915205b test: cover stable multi-type model
4a3ee042e view: refine ::storage function
88a1b8d0d view: stable multi-type view ::each(cb) function
7e18a0f96 view: update ::use function
c367082dd view: unchecked_refresh function
9f94b5306 view: double check on none_of
44ed10c50 view: stable multi type view ::find/::back/::front functions
1b2280941 view: stable multi type view ::begin/::end functions
bdabbaa63 view: stable multi type view ::contains function
c79c109b7 view: stable multi type view ::size_hint function
f1a213382 registry: prepare to remove static storage from const assure
17dc06149 view: stable single type view ::each(cb) function
3b8d82330 view: drop unused return
a20829e70 view: ::handle returns a pointer rather than a reference
5be2fdc15 view: stable single type view ::each() function
873b107e6 -: updated TODO
356bbbe53 view: stable single type view ::find function
e3ce4e156 view: stable single type view ::front/::back functions
e02050c51 view: stable single type view ::rbegin/::rend functions
26930633f view: stable single type view ::begin/::end functions
b7a485767 view: stable single type view ::contains function
f54cdccd4 view: stable single type view ::empty function
41c9a32f3 view: stable single type view ::size function
736ef3580 view: make operator bool work properly with partially initialized views
0128cbb4f test: minor changes
ff0a40715 test: prepare test suite for safe invalid views
34f440386 view: avoid using storage, further prepare for empty safe views
b1c78efb6 nativs: updated signal file
28f03ff9c meta: add missing checks on factory<...>::data
a5fe61adb *: minor changes
457f5e59e view: rollback handle() usage and prepare to safe empty views
422fd284e group: refine group ::find function
6f3222573 view: refine single type view ::find function
366bbceb0 doc: use doxygen-awesome-css
7b7f81e08 doc: update reference.md
cfe955f97 doc: update links.md
684ddc9de doc: minor changes
f5d38a9ae doc: drop redundant doxy variable
447e3693f doc: updated doxy file (doxygen 1.9.6)
909490bf6 view: try to make g++ happy again
d90363e4a view: make view pack also work with empty views
ee5de744c view: add missing [[nodiscard]]
d401c88a0 view: assert on null handles
80563b955 view: allow swapping storage elements of a view
c74900057 sigh_mixin: avoid shadow warnings
78867d5c9 group: make msvc happy with constness on virtual functions
d435fc779 basic_entt_traits: suppress a warning by gcc
e6f76e0f9 view: try to make VS happy again :)
1c6b53360 test: minor changes
5c3d8360c view: turn ::use into a self-contained, non-const function
3882c7d9a view: turn ::refresh into a self contained, non-const function
15726218b view: doc
869bfc82c test: minor changes
0eb3d54b2 group: change signature of ::storage to return a (maybe null) pointer rather than a reference
f83290f76 view: change signature of ::storage to return a (maybe null) pointer rather than a reference
686a3b9d7 registry: make storage_for_type available to the final user
4d57d5c32 registry: make ::storage<T> return type explicit
36c21cf7f registry: drop redundant traits usage
7ab10e193 test: minor changes
41467d35a -: updated TODO
d351252a1 doc: entity storage
c6cd4f701 doc: refine storage section
65889cca4 doc: brief mention of void storage
f1914fd94 doc: rearrange a few things
e53af7bef registry: minor changes
b910cd261 *: updated TODO
58d331ca0 registry: minor changes
17f5b0a33 registry: avoid bumping version on destroy if not requested
de386292b registry: deprecate ::each
88bf26a2f registry: deprecate ::assign
3caad4100 mixin: common internal owner_or_assert function
916203a24 test: stress assert on entity limit
62f1971f7 test: minor changes
4fde96357 natvis: updated registry snippet
c3730b65f group: * unified model * drop group handler's size function (no longer required)
1ea072cd3 group: back to the unified model for group handlers
bbe4582ee meta: minor changes
89ab5c328 meta: operator==/!= for meta_func
3a4672793 meta: operator==/!= for meta_prop
0a0446f35 meta: operator==/!= for meta_data (close #1002)
fc58ff74b meta: operator==/!= for meta_handle (see #1002)
fed6831cd locator: support to opaque structures (close #956)
1605c8d9d natvis: updated entity file
d6641c7d8 -: updated TODO file
5079f38e9 storage: allow on_update signals on entity storage
1eab2a4a8 meta: fix constness detection for static functions in meta_type::invoke
c33110765 test: cleanup
117b0bd67 test: more about storage<...>::patch
9b4a6f877 storage: use allocator_traits::destroy rather than destroy_at
f4e6f2b37 group: suppress shadow warning
5971fb7aa -: updated TODO
10dfe7e93 sigh: allow disconnecting listeners during iterations (close #986)
a9208a956 doc: fixed typo
1cc5b32ca test: cleanup
f8a972a3c signal: drop sink::before
5b7cc2002 group: rollback some (no longer required) changes to the owning_group_descriptor
bd34e7f2c group: drop nested groups support, prepare to the large group review and multi storage support
46fe29c3f group: make matching functions virtual for owning groups
c50e2815c group: make owning_group_descriptor depend on the storage base type
fbfee632d group: minor changes
77c59aabf group: group_handler::size function for owning groups
ebb1e8a72 group: single check function for group handlers
1646217f0 group: make types explicit for the next/prev functions
645edfb2b group: decouple constructing and setting prev/next links
61f28298c group/registry: minor changes
d19f97bf2 group: use ::handle() if possible
70c611a84 group: cleanup
286428c19 group: make common_type base of non-owning group handlers
6ec719bcf group: reduce the footprint of non-owning group handlers
11f9bb2d7 registry: use shared_ptr<void> for non-owning groups (prepare to drop the basic handler dependency)
5a1ba5ad7 regisrtry: decouple container types for groups
cf094e7ef registry: finally split owning and non-owning groups as it ought to be
31808bd9a sigh: flip the last commit on its head and drop redundant functions rather than merging them
61a5173a7 sigh: merge a couple of functions
ed6fe9e65 sigh/sink: refine internal definition
e30fa8520 doc: cleanup
ca1069e18 snapshot: avoid allocations if possible
70f73a094 snapshot: drop pointless checks
710fff0e3 entity: make get_t, exclude_t and owned_t constexpr constructible
660bc5843 entity: turn get_t, exclude_t and owned_t into proper classes (close #998)
13295a14e type_traits: v141 toolset workaround for value_list_diff
9ce07ff61 type_traits: value_list_diff[_t]
b272e04ba type_traits: value_list_contains[_v]
28b11912a test: cleanup
b9f096d12 type_traits: value_list_unique[_t]
8c60faa1d type_traits: value_list_index[_v]
1f93ea4ee snapshot: avoid unnecessary lookups
7ca77e53f snapshot: avoid unnecessary lookups
69397f365 snapshot: avoid unnecessary lookups
f907bc066 snapshot: drop redundant checks and avoid unnecessary lookups
bda52701f snapshot: avoid unnecessary lookups
d26f7684c snapshot: minor changes
63d6c2bff snapshot: avoid unnecessary lookups
cc45e7341 snapshot: also avoid using views if not required
5d092bcb1 snapshot: avoid unnecessary lookups
295c68841 snapshot: review ::orphans functions
2664b5255 observer: allocator support
dd3632833 observer: configurable mask type
c8c929e4a group: use type members properly
d1ef7bf15 view: use type members properly
1ab23f17d group: early exit on signal races
a72eb4693 group: minor changes
67579d062 -: updated TODO
766a233f3 view: base_type -> common_type
905671c23 runtime_view: base_type -> common_type
27c1383e4 group: base_type -> common_type
029ccc8f7 registry: base_type -> common_type
cde40d586 group: drop unused using decl
6a16a8a20 group: auto init for owning groups
1a12dede6 group: auto init for non-owning groups
35a78b65e group: cleanup
ada19432f group: support for index based sort
4998e9087 doc: minor changes
471c11c6d sparse_set: respect -> sort_as (naming is hard, you know)
3e13e0b59 group: sort/respect -> sort_as (also decoupled from group types)
53cd105f2 group: reuse pools as much as possible
24b31c379 group: reuse pools as much as possible
def82b534 group: index based get
a424f4ebf view: review get
b8f0a8d8e doc: a couple of interesting articles/series (close #994)
7941226ef group: try to reuse pools when sorting and also please all compilers out there at the same time (aka me figthing ICEs again)
86bbb2f6b group: reuse pools when sorting
3c176f725 test: suppress warnings due to unused variables
3642c8a78 registry: drop [[nodiscard]] from ::group (close #991)
0e80d90a7 group: use storage<idx> as much as possible
4fdf2dccd group: update doc
f8a997e6c group: minor changes
40f676ed1 test: drop unused include
5e346748e test: code coverage for groups and registry
3ef61fe01 meta: support meta member functions on primitive types
3885d280d test: cleanup
f41b91419 meta: allow updating values on meta properties
e0684f634 registry: cleanup/minor changes
fb980a78c registry: further refine the group function(s)
c2430ab48 doc: minor changes
d36d9cb39 registry: further cleanup group functions
0017c08bb group: get pools from handlers
e737ff747 group: get filter from handlers
945dc4093 group: split group handler functions
7ef008511 registry: drop group_data
d2fa68813 registry/group: prepare to get rid of group_data
f22a09a9a group: in-between change to simplify dropping group_data
b0aba79a5 snapshot: minor changes
7c23e4a2f registry: minor changes
7fe035ce4 group: move group size from registry group_data to basic_group_handler
3e7160eda group: minor changes
aaeb686ec group: common base class for group handlers
3fdf4884d group: prepare for group handler common base class
1b23ff4b9 registry: use common group handler types as keys for the group set
88dac318e group: wrap the len of owning groups to avoid changing it by mistake
520c2e660 group: make group handlers work with multiple storage of the same type
f5d0d451b group: split pools and filter in the group handlers
8af6fc0cc group: use ::handle internally if possible
c04b97a31 group: add ::handle function to all group types
1d85414dc doc: drop refs to registry::version (close #992)
c6533827f group: fight with clang format from time to time :)
b5803451b group: make owning groups work with their handlers
3417d66b2 group: make non-owning groups work with their handlers
1e61204e8 registry: deduce group handler type from group type
19c4857ef group: cleanup
66ea94898 registry/group: move group handler to group file as it ought to be
ced6d21c3 registry: break dependency between registry and group handlers
429c7c45c registry: further cleanup things
c03b1111a registry: small cleanup
ebd7d3acd registry: storage based model with pools for groups
5aeec60cf registry: prepare to switch to storage based group handlers
620b4f751 registry: pass handlers to group callbacks
6d58004c1 registry: minor changes to simplify the implementation slightly
df6d926de registry: prepare for a storage based group handler
e63af24cb registry: turn the non-owning group handler in a storage
068d9f8ae registry: discard unused arguments from listeners if possible
c19c848c4 test: suppress warnings due to unused variables
0bf0a0a8f doc: delegate
743e8678e delegate: also support functions that skip first elements (on second attempt only)
a7ad1c06f delegate: prepare to support filtering on both sides
b1af70e70 registry: avoid checking pools in the group handler if possible
c87c3533e registry: avoid checking pools in the group handler if possible
4839a0ee6 registry: cleanup
a0f0c44e6 registry: minor changes
74691dc1d group: just use meaningful names :)
e4957badb registry: split group handler to further refine group management
46791c4c3 registry: turn group handler functions into static ones
56c391784 registry: prepare to rework groups
1fb13d3e9 doc: minor changes
535beb4e2 storage: drop unnecessary use of integral_constant
2d318b88c -: updated TODO
b7f0b76ce entity/mixin: add missing include
d30312f51 entity/helper: add missing include, drop unnecessary traits calls
30772848e meta: avoid unnecessary calls to std::move
eca01a397 doc: add vcpkg badge and vcpkg.link (#985)
35ef0b7ac core: reduces the number of instantiations a bit
19ccba3a6 meta: reduces the number of instantiations a bit
207b7674a doc: fix typo
631c55ba9 storage: minor changes/tests
e7b30fd36 storage: return iterator to elements rather than entities and only if it makes sense
3e959007b storage: ::insert returns an iterator to the range of inserted entities
07ec4ca23 -: updated TODO
6e4946b68 storage: uniform interface to simplify mixin implementation
47ea16f17 test: signals on entity creation/destruction
722857fc0 test: get rid of pointless template parameters
2125b3838 test: minor changes
289de7d57 test: exclude only views
25ecd8e79 test: minor changes
319dfdb07 test: filtered registry view
9dbbcac01 -: updated TODO
f545c8e05 registry: deprecate ::release
c68fa6a65 registry: make ::destroy work without ::release (the latter to be deprecated)
d288ecd70 registry: make ::release use ::bump return value
312d3aba8 sparse_set: bump returns the version in use (for convenience)
4d2b2c6de registry: use traits_type::next if possible
80d55a226 test: increase code coverage
d86a53935 test: suppress warnings due to unused variables
0f7098d0e -: updated TODO
8c96be1e9 registry: deprecate a bunch of functions because of the entity storage
37f396bfe registry: make entity storage storage as any other
75894dc40 storage: update traits_type for entity storage
cdee000ce any: rollback a change that turns vs toolset v141 crazy
54ca62600 dispatcher: refine aggregate check
6f4280ed5 any: refine aggregate check
ddf56b78c storage: backward compatibility on component requirements
53a854f54 any: just cleanup the code to make it easier to work with
4896acac7 storage: typo
e3defeba2 test: suppress warnings due to unused variables
62079908c storage: use proper value type for entity storage
e65a8f2e5 doc: add link to koala engine :)
9f27fb1e5 registry: further prepare to turn the entity storage into a plain pool
04d734e76 registry: prepare to turn the entity pool in a plain storage
df50fa1b5 natvis: cleanup
051872b8c natvis: update registry definition
57ab9e7be registry: avoid using assure if not required
69d95ba75 test: more bench to stress a little an upcoming feature
9caf66d7c test: cleanup
74cb0d40c test: internal rework
deac7f34b dispatcher: refine aggregate support
a9883f27c storage: refine transparent aggregate support
85b1e57d8 sparse_set: drop fast_compact, expect full clear
b7d8e0186 storage: make the entity storage perform a full clear rather than a fake one (still viable via erase)
390a56176 -: updated TODO file
a1b888cce natvis: add optiona storage length item for entity storage
2107dd689 natvis: fix already existing errors due to renaming or design changes
1fca56afe storage: make it easier to refine the natvis file
c0762a6a5 storage: add get/get_as_tuple to entity storage to make it suitable for use with views
f48de1bac test: stress get/get_as_tuple for empty types
c7dfce89e sigh_mixin: refine pop_all
822fafcd4 view: uniform implementation to simplify upcoming changes
1476d4ea9 sparse_set: refine ::respect
c1c63777e -: updated TODO
2fab25ae8 registry: refine internal check
75d449152 -: updated TODO
c7866fb21 storage: use entt traits next function if possible
87987bacd entity: added basic_entt_traits::next (with tests)
bde0219fe snapshot: review basic_continuous_loader::entities
ad64c849b storage: suppress warnings
b808bb83b test: suppress warnings
d0090d35f snapshot: try to make sizes an opaque value to the caller
7a1a06a24 sigh_mixin: avoid shadow warnings
000b17881 -: updated TODO
068b6ed49 registry: first (almost) backward compatible version with opaque hidden entity storage
0187fb48a test: sigh mixin for entity storage types
35a2b3844 sigh_mixin: also support entity storage types
4747c9a4c registry: extended checks to support swap-only entity storage types
7be8d8327 registry: make a couple of conditions opaque
a5d6757d6 registry: prepare to get rid of the vector of entities
3f09d47c8 storage: remove redundant typename keyword
9c06d6ba0 registry: use type member names
b7c819bf4 test: entity storage
9f31803ba storage: swap-only entity storage
1e7deff9c test: drop redundant checks
04ac15d8d test: minor changes
376218991 sigh_mixin: make pop_all use narrow view iterators if any
18d6e466d -: [[nodiscard]]  as appropriate
095ecf314 group: extended_group_iterator::base to return the underlying iterator
433ed863e view: extended_view_iterator::base to return the underlying iterator
0dba68e75 storage: coding style/minor changes
1ab281582 storage: extended_storage_iterator::base to return the underlying iterator
2af5a725e doc: * updated copyright * udpated TODO list
a86bf1332 test: try to make lcov happy
831054bff test: share as much as possible
f94de1c06 test: rework lib stuff to share common files
a3d9503a1 test: try to make lcov happy
3f2b15f9f test: try to make lcov happy
e48817d51 test: try to make lcov happy
d11cebe30 view: uniform design to also help natvis without having to poke into stl internals
77a5efb32 natvis: updated to_entity intrinsic
851006efe -: updated TODO
6fc6b2fb3 sigh_mixin: further improve ::pop_all
ed17a2c48 sparse_set: ::contiguous function
bd00e797a sparse_set: further refine pop_all to make it even faster
e645c4928 -: updated TODO
a425878e8 sparse_set/storage: clear is backward compatible now
f3cd9d374 storage: fixed clear_all counter
b3e93b084 registry: naming convention
314c189c4 test: minor changes
2bb2c5566 build: try to make lcov happy again
d13c126e9 view: avoid name clashes
9b54ee37a flow: propagate allocator to generated graph + internal rework
e1ead9d3e build: update coverage workflow
cf61068dc mixin: suppress a warning with gcc11
82863f829 test: code coverage for range functionalities
e4de59827 test: try to make lcov happy
ccea4c920 memory: code coverage
89166f0e4 build: refine analyzer workflow
7a05a16c5 registry: slightly better destroy (yet not quite there though)
d0854646c test: yet another test to stress the upcoming changes
1e9c9fe5f registry: better, faster range-remove + refine range-erase
80fac8d8e test: minor changes
c774b9838 -: updated TODO
3fd0403cc registry: faster, better range-erase
6eb3347a3 test: a couple of extra functions to stress the upcoming changes
89bceaff7 -: updated TODO
dc25c9c1a sparse_set: invoke release_sparse_pages before clearing the sparse array
e68ba5870 sigh_mixin: add a missing include
c68cb3375 entity: make deletion_policy publicly available via fwd.hpp
59f807fd0 sparse_set: suppress warnings due to unused expressions
232ffebc1 sparse_set: internal clear_all function
3cea845a0 sparse_set: sparse_set_iterator::data function
295f3b32e registry: a couple of extra move calls here and there
254da2c3c sparse_set: better, faster range remove
ecd3b8d93 sparse_set: prevent rework errors as much as possible
c673b9b17 sigh_mixin: slightly improved pop + review insert
cd28de0d6 test: clear-stable bench
672f6a711 test: minor changes
3b50672b7 storage: restore storage_for/storage_type duality, it turned out to be very useful in practice
f0613b1c6 sparse_set/storage: minor changes to reuse type members
2197e160e -: drop file pushed by mistake :)
2dccd9016 handle: discard entity on destruction
2f873f2dd -: storage_mixin.hpp -> mixin.hpp (non-storage mixins are also a thing)
fde1a524e sparse_set: ::get -> ::value (to avoid hiding from derived classes)
055801047 doc: drop references to docsforge + minor changes
79a054a52 sigh_mixin: scope base_type properly
d94e443a1 doc: drop outdated section
3862184e8 sigh_mixin: support self managed storage classes
f40fa3c2f test: * use range destroy * avoid compiler optimizations
01bc93459 test (bench): the new entity storage enables the fast path in all cases
151bd0739 sparse_set: revert optmized range push, it prevents self-managed storage classes
935393aae sparse_set: better, faster range push
fbfde4347 snapshot: avoid unused variable warnings
2ffbe115b component_traits: revert entity customization support
645973eb7 sparse_set: insert -> push
133230797 sparse_set: emplace -> push
b700f5eb5 doc: typo
e60dbdc52 sparse_set/storage: * rename swap_at in swap_or_move to capture the real purpose * define swap_at as a protected function to allow swapping from above
c66623b33 sigh_mixin: avoid hiding basic_iterator type meber
62246d879 storage: avoid hiding basic_iterator type meber
b35f13130 sparse_set: support swap-only mixins
3dd82633a -: drop storage_mixin.cpp, I forgot to do it a couple of commits ago :)
00231bf8a storage: make swap_at non-final to support checks on derived classes
58d392e81 -: minor changes
1d4d99d09 mixin: sigh_storage_mixin -> sigh_mixin
fe3edf2c8 -: minor changes
0864ba042 -: drop useless typename
3a9698001 build: minor changes
423f7a555 is_equality_comparable: detect C-style arrays directly
5db8ad53a build: update gh workflow
c2ab35780 view: make also VS toolset v141 happy
4fb558f14 view: further reduce instantiations
5762a8a08 view: reuse internal functions if possible
ed4c67521 sparse_set/storage: drop move_element
f15789846 config: ENTT_FAIL(msg) -> ENTT_ASSERT(false, msg)
6d20709e0 storage: minor changes
a9a9853c0 sigh_storage_mixin: use entity_type from Type
af14aa4c9 doc: more about signals (sigh_storage_mixin)
24d6b9881 test: minor changes
899f4baa6 storage: * drop storage_for]_t] * make storage_type[_t] deal with constness
c1ab7ba02 sigh_storage_mixin: make all virtual member functions final
9d38f6020 registry: thanks MSVC for accepting invalid C++ code
0efa25cf6 sigh: cool, I keep doing the same error again and again apparently :)
6316b6045 registry: make it work with storage<void> also in C++17
f268fb60a entity: avoid breaking changes due to type members renaming
3520d6915 entity: add base_type
4da7a8451 entity: make checks work with 64b identifiers :)
382dfc3bb entity: strict check on entity/version masks
b6dcdc816 entity: * also expose entity_mask and version mask to the final user * avoid default args with entt_traits::construct for backward compatibility
c9d544089 doc: review/cleanup entity.md a bit (done)
3eb5faeed doc: review/cleanup entity.md a bit (work in progress)
7a328c7ed doc: updated links
6567aa195 doc: a note about listeners disconnection (close #958)
92319f011 entt_traits: split basic impl, simplify def
782d86b6e entt_traits: value_type -> type (cuz it's not a value type after all)
c2cae37c1 entity_traits: make page_size type explicit
1026d26ec entt_traits: drop reserved value
7156803db test: local non-static constexpr variables
f54ed5424 helper: local non-static constexpr variables
f30b50195 algorithm: local non-static constexpr variables
c90ab9aff sparse_set: * break dependency on traits_type::reserved * use a tombstone if all I need is a tombstone
c2f6ca43f doc: graph (close #957)
3e5e41d88 test: cover some corner cases of the flow class
9eafc0431 flow: minor changes
0a82b777b component_traits: support specializations based on entity type
32bcc01a4 component: * make component_traits treat void properly * drop ignore_as_empty_v
9c3fe3546 nativs: entity module
83f8aed58 helper: use traits_type from storage class directly
2fd660274 snapshot: use public registry traits_type member type
a554d406e registry: * public traits_type member type * break dependency on component_traits * use public storage traits_type member type
5f12f872e test: minor changes
be4eb68a3 helper: * break dependency on component_traits * use public storage traits_type member type
df5284d9e view: * break dependency on component_traits * use public storage traits_type member type
0e27d33e7 storage: public traits_type member type
fe6e6ae73 sparse_set: public traits_type member type
9d29713ea entity: naming convention
270d0277d group: cleanup
0bd06c8d5 hashed_string: naming convention
733f215cc storage: break dependency between component_traits and storage_iterator
ad01a69fe *: renaming/coding style
dd9c1dade sparse_set: no need to differentiate template args for sparse_set_iterator
b8f70519f doc: fixed typo
9b9d212dd *: coding style
3fe15969d doc: cleanup
ec4bf222c meta: avoid the +1u trick for 0-sized arrays
1173908ee meta: avoid rebinding when forwarding requests
2595b8a92 doc: sigh_helper
f4e2a8c76 sigh_builder: add all missing .template that msvc kindly accepted anyway
66e1a0565 entity: sigh_helper utility with tests (close #928)
87283dc41 storage: simplified impl in order to introduce multi-type storage more easily
a802ebffe storage: * move storage_type[_t] and storage_for[_t] to fwd.hpp * no need to include storage.hpp when forward defining views
b84b09421 doc: add Arch ECS to references.md (#954)
940fd0939 todo: add a note for a (soon to be released) change
920338be5 doc: add ecsact to links.md (thanks @zaucy for pointing this out)
bcd1155b7 gh: add more gcc and clang versions
1dc88109e gh: update workflows
262c1f53c cmake: only enable -Wdocumentation for clang-cl
4af0a3a0d doc: cleanup
be1641828 doc: cleanup
b54a52fbf doc: fixed typo
ae8815995 doc: fixed typo
62c764f68 doc: fixed typo
2c48cc10a cmake: enable documentation diagnostic for clang
82f286678 sigh: drop redundant function
d56e5a269 registry: propagate allocator to context
1517b2951 doc: document delegate raw access
bea7b43a1 delegate: target member function
2f878f8b5 sigh: refine ::collect
fc68c1b29 view/group: cleanup
9081c185d meta: minor changes
7c4493f23 group: make filter storage available
da4e73ab8 view: make filter storage available
f3e7f98b4 registry: extra check when moving a registry
3925fc612 emitter: extra allocator check when moving
c639130c1 dispatcher: extra allocator check when moving
75c311600 registry: cleanup
e9e14eb49 meta: [[nodiscard]]
d1558304f any: [[nodiscard]]
0531b530b snapshot: minor changes
f9d0178dd workflow: bump iwyu version
b66b8d37e test: suppress warning
05ef4c29d storage: minor changes
9c3d75669 test: cleanup include directives
93651e46f registry: drop [[deprecated]] functions
ea901cbfa test: code coverage
d5dc4f43e doc: meta.md
498e02f15 doc: core.md
d0ea8f4f9 cmake: suppress some warnings for clang-cl, it goes a little wrong otherwise
dec3b7bb3 test: suppress warnings
10bc8b05a test: use /W1 with VS (but for toolset v141, too bugged for that)
ad77b54dc cmake: bump version to get some cool feature/update
b6724b028 group: pass filter storage to groups (in-between change for full storage access)
54270b103 group: make them easily copyable/movable
31dc732a7 doc: graph.md
f0e02d6d3 doc: container.md
156d6e4ea doc: poly.md
4375c1c3d doc: lib.md
24a9cd67e scheduler: forgot to add the fwd file to the previous commit :)
ba8d522c1 doc: add the worst engine (love the name) to the list of links
3ae46214a doc: review process.md
5119fe8d7 scheduler: basic type model with default for common cases
ed0319cdd view: avoid shadow warnings
bc50da6a7 storage: suppress warnings with non copyable nor default constructible types
52b3b4c24 group: suppress warnings for unused variables in case of empty types
74bab529d test: minor changes
b1b143917 meta: [[maybe_unused]] variable to avoid warnings with corner cases
7beb4c85c test: suppress a few warnings (entity)
f3beb5670 test: suppress a few warnings (container)
446c67b69 test: suppress a few warnings (resource)
c4507bd17 test: suppress a few warnings (poly)
61e872bb4 test: suppress a few warnings (meta)
9f22a3e23 test: suppress a few warnings (memory)
653dd5cd4 test: suppress a few warnings (tuple)
bc53ed3be test: suppress a few warnings (flow)
f935bbcce dense_set: suppress warnings due to possible narrowing conversions
c7d505353 dense_map: suppress warnings due to possible narrowing conversions
ea78f1d97 now working on version 3.12
REVERT: fef921132 update single include file
REVERT: e52a93f8a ready to cut v3.11.1
REVERT: cd541f335 storage: * move storage_type[_t] and storage_for[_t] to fwd.hpp * no need to include storage.hpp when forward defining views
REVERT: 255b8be8c view: avoid shadow warnings
REVERT: 8cd7f064a storage: suppress warnings with non copyable nor default constructible types
REVERT: 58ae4117c group: suppress warnings for unused variables in case of empty types
REVERT: cfa1e805b meta: [[maybe_unused]] variable to avoid warnings with corner cases
REVERT: ccedacec8 dense_set: suppress warnings due to possible narrowing conversions
REVERT: 17578dc8c dense_map: suppress warnings due to possible narrowing conversions

git-subtree-dir: external/entt/entt
git-subtree-split: 344e03ac64a1f78424ab1150e2d4778e8df8431d
2023-10-02 15:30:10 +02:00

1645 lines
55 KiB
C++

#include <algorithm>
#include <cstddef>
#include <functional>
#include <iterator>
#include <tuple>
#include <type_traits>
#include <utility>
#include <gtest/gtest.h>
#include <entt/entity/group.hpp>
#include <entt/entity/registry.hpp>
#include "../common/config.h"
struct empty_type {};
struct boxed_int {
int value;
};
inline bool operator==(const boxed_int &lhs, const boxed_int &rhs) {
return lhs.value == rhs.value;
}
TEST(NonOwningGroup, Functionalities) {
entt::registry registry;
auto group = registry.group(entt::get<int, char>);
auto cgroup = std::as_const(registry).group_if_exists(entt::get<const int, const char>);
ASSERT_TRUE(group.empty());
const auto e0 = registry.create();
registry.emplace<char>(e0, '1');
const auto e1 = registry.create();
registry.emplace<int>(e1, 42);
registry.emplace<char>(e1, '2');
ASSERT_FALSE(group.empty());
ASSERT_NO_FATAL_FAILURE(group.begin()++);
ASSERT_NO_FATAL_FAILURE(++cgroup.begin());
ASSERT_NO_FATAL_FAILURE([](auto it) { return it++; }(group.rbegin()));
ASSERT_NO_FATAL_FAILURE([](auto it) { return ++it; }(cgroup.rbegin()));
ASSERT_NE(group.begin(), group.end());
ASSERT_NE(cgroup.begin(), cgroup.end());
ASSERT_NE(group.rbegin(), group.rend());
ASSERT_NE(cgroup.rbegin(), cgroup.rend());
ASSERT_EQ(group.size(), 1u);
registry.emplace<int>(e0);
ASSERT_EQ(group.size(), 2u);
registry.erase<int>(e0);
ASSERT_EQ(group.size(), 1u);
for(auto entity: group) {
ASSERT_EQ(std::get<0>(cgroup.get<const int, const char>(entity)), 42);
ASSERT_EQ(std::get<1>(group.get<int, char>(entity)), '2');
ASSERT_EQ(cgroup.get<1>(entity), '2');
}
ASSERT_EQ(group.handle().data()[0u], e1);
registry.erase<char>(e0);
registry.erase<char>(e1);
ASSERT_EQ(group.begin(), group.end());
ASSERT_EQ(cgroup.begin(), cgroup.end());
ASSERT_EQ(group.rbegin(), group.rend());
ASSERT_EQ(cgroup.rbegin(), cgroup.rend());
ASSERT_TRUE(group.empty());
ASSERT_NE(group.capacity(), 0u);
group.shrink_to_fit();
ASSERT_EQ(group.capacity(), 0u);
decltype(group) invalid{};
ASSERT_TRUE(group);
ASSERT_TRUE(cgroup);
ASSERT_FALSE(invalid);
}
TEST(NonOwningGroup, Handle) {
entt::registry registry;
const auto entity = registry.create();
auto group = registry.group(entt::get<int, char>);
auto &&handle = group.handle();
ASSERT_TRUE(handle.empty());
ASSERT_FALSE(handle.contains(entity));
ASSERT_EQ(&handle, &group.handle());
ASSERT_NE(&handle, group.storage<int>());
registry.emplace<int>(entity);
registry.emplace<char>(entity);
ASSERT_FALSE(handle.empty());
ASSERT_TRUE(handle.contains(entity));
ASSERT_EQ(&handle, &group.handle());
}
TEST(NonOwningGroup, Invalid) {
entt::registry registry{};
auto group = std::as_const(registry).group_if_exists(entt::get<const empty_type, const int>);
const auto entity = registry.create();
registry.emplace<empty_type>(entity);
registry.emplace<int>(entity);
ASSERT_FALSE(group);
ASSERT_TRUE(group.empty());
ASSERT_EQ(group.size(), 0u);
ASSERT_EQ(group.capacity(), 0u);
ASSERT_NO_FATAL_FAILURE(group.shrink_to_fit());
ASSERT_EQ(group.begin(), group.end());
ASSERT_EQ(group.rbegin(), group.rend());
ASSERT_FALSE(group.contains(entity));
ASSERT_EQ(group.find(entity), group.end());
ASSERT_EQ(group.front(), entt::entity{entt::null});
ASSERT_EQ(group.back(), entt::entity{entt::null});
}
TEST(NonOwningGroup, ElementAccess) {
entt::registry registry;
auto group = registry.group(entt::get<int, char>);
auto cgroup = std::as_const(registry).group_if_exists(entt::get<const int, const char>);
const auto e0 = registry.create();
registry.emplace<int>(e0);
registry.emplace<char>(e0);
const auto e1 = registry.create();
registry.emplace<int>(e1);
registry.emplace<char>(e1);
for(auto i = 0u; i < group.size(); ++i) {
ASSERT_EQ(group[i], i ? e0 : e1);
ASSERT_EQ(cgroup[i], i ? e0 : e1);
}
}
TEST(NonOwningGroup, Contains) {
entt::registry registry;
auto group = registry.group(entt::get<int, char>);
const auto e0 = registry.create();
registry.emplace<int>(e0);
registry.emplace<char>(e0);
const auto e1 = registry.create();
registry.emplace<int>(e1);
registry.emplace<char>(e1);
registry.destroy(e0);
ASSERT_FALSE(group.contains(e0));
ASSERT_TRUE(group.contains(e1));
}
TEST(NonOwningGroup, Empty) {
entt::registry registry;
const auto e0 = registry.create();
registry.emplace<double>(e0);
registry.emplace<int>(e0);
registry.emplace<float>(e0);
const auto e1 = registry.create();
registry.emplace<char>(e1);
registry.emplace<float>(e1);
ASSERT_TRUE(registry.group(entt::get<char, int, float>).empty());
ASSERT_TRUE(registry.group(entt::get<double, char, int, float>).empty());
}
TEST(NonOwningGroup, Each) {
entt::registry registry;
entt::entity entity[2]{registry.create(), registry.create()};
auto group = registry.group(entt::get<int, char>);
auto cgroup = std::as_const(registry).group_if_exists(entt::get<const int, const char>);
registry.emplace<int>(entity[0u], 0);
registry.emplace<char>(entity[0u], static_cast<char>(0));
registry.emplace<int>(entity[1u], 1);
registry.emplace<char>(entity[1u], static_cast<char>(1));
auto iterable = group.each();
auto citerable = cgroup.each();
ASSERT_NE(citerable.begin(), citerable.end());
ASSERT_NO_FATAL_FAILURE(iterable.begin()->operator=(*iterable.begin()));
ASSERT_EQ(decltype(iterable.end()){}, iterable.end());
auto it = iterable.begin();
ASSERT_EQ(it.base(), group.begin());
ASSERT_EQ((it++, ++it), iterable.end());
ASSERT_EQ(it.base(), group.end());
group.each([expected = 1](auto entt, int &ivalue, char &cvalue) mutable {
ASSERT_EQ(static_cast<int>(entt::to_integral(entt)), expected);
ASSERT_EQ(ivalue, expected);
ASSERT_EQ(cvalue, expected);
--expected;
});
cgroup.each([expected = 1](const int &ivalue, const char &cvalue) mutable {
ASSERT_EQ(ivalue, expected);
ASSERT_EQ(cvalue, expected);
--expected;
});
ASSERT_EQ(std::get<0>(*iterable.begin()), entity[1u]);
ASSERT_EQ(std::get<0>(*++citerable.begin()), entity[0u]);
static_assert(std::is_same_v<decltype(std::get<1>(*iterable.begin())), int &>);
static_assert(std::is_same_v<decltype(std::get<2>(*citerable.begin())), const char &>);
// do not use iterable, make sure an iterable group works when created from a temporary
for(auto [entt, ivalue, cvalue]: registry.group(entt::get<int, char>).each()) {
ASSERT_EQ(static_cast<int>(entt::to_integral(entt)), ivalue);
ASSERT_EQ(static_cast<char>(entt::to_integral(entt)), cvalue);
}
}
TEST(NonOwningGroup, Sort) {
entt::registry registry;
auto group = registry.group(entt::get<const int, unsigned int>);
const auto e0 = registry.create();
const auto e1 = registry.create();
const auto e2 = registry.create();
const auto e3 = registry.create();
registry.emplace<unsigned int>(e0, 0u);
registry.emplace<unsigned int>(e1, 1u);
registry.emplace<unsigned int>(e2, 2u);
registry.emplace<unsigned int>(e3, 3u);
registry.emplace<int>(e0, 0);
registry.emplace<int>(e1, 1);
registry.emplace<int>(e2, 2);
ASSERT_EQ(group.handle().data()[0u], e0);
ASSERT_EQ(group.handle().data()[1u], e1);
ASSERT_EQ(group.handle().data()[2u], e2);
group.sort([](const entt::entity lhs, const entt::entity rhs) {
return entt::to_integral(lhs) < entt::to_integral(rhs);
});
ASSERT_EQ(group.handle().data()[0u], e2);
ASSERT_EQ(group.handle().data()[1u], e1);
ASSERT_EQ(group.handle().data()[2u], e0);
ASSERT_EQ((group.get<const int, unsigned int>(e0)), (std::make_tuple(0, 0u)));
ASSERT_EQ((group.get<const int, unsigned int>(e1)), (std::make_tuple(1, 1u)));
ASSERT_EQ((group.get<const int, unsigned int>(e2)), (std::make_tuple(2, 2u)));
ASSERT_FALSE(group.contains(e3));
group.sort<const int>([](const int lhs, const int rhs) {
return lhs > rhs;
});
ASSERT_EQ(group.handle().data()[0u], e0);
ASSERT_EQ(group.handle().data()[1u], e1);
ASSERT_EQ(group.handle().data()[2u], e2);
ASSERT_EQ((group.get<0, 1>(e0)), (std::make_tuple(0, 0u)));
ASSERT_EQ((group.get<0, 1>(e1)), (std::make_tuple(1, 1u)));
ASSERT_EQ((group.get<0, 1>(e2)), (std::make_tuple(2, 2u)));
ASSERT_FALSE(group.contains(e3));
group.sort<const int, unsigned int>([](const auto lhs, const auto rhs) {
static_assert(std::is_same_v<decltype(std::get<0>(lhs)), const int &>);
static_assert(std::is_same_v<decltype(std::get<1>(rhs)), unsigned int &>);
return std::get<0>(lhs) < std::get<0>(rhs);
});
ASSERT_EQ(group.handle().data()[0u], e2);
ASSERT_EQ(group.handle().data()[1u], e1);
ASSERT_EQ(group.handle().data()[2u], e0);
ASSERT_EQ((group.get<const int, unsigned int>(e0)), (std::make_tuple(0, 0u)));
ASSERT_EQ((group.get<const int, unsigned int>(e1)), (std::make_tuple(1, 1u)));
ASSERT_EQ((group.get<const int, unsigned int>(e2)), (std::make_tuple(2, 2u)));
ASSERT_FALSE(group.contains(e3));
}
TEST(NonOwningGroup, SortAsAPool) {
entt::registry registry;
auto group = registry.group(entt::get<const int, unsigned int>);
const auto e0 = registry.create();
const auto e1 = registry.create();
const auto e2 = registry.create();
const auto e3 = registry.create();
auto uval = 0u;
auto ival = 0;
registry.emplace<unsigned int>(e0, uval++);
registry.emplace<unsigned int>(e1, uval++);
registry.emplace<unsigned int>(e2, uval++);
registry.emplace<unsigned int>(e3, uval + 1);
registry.emplace<int>(e0, ival++);
registry.emplace<int>(e1, ival++);
registry.emplace<int>(e2, ival++);
for(auto entity: group) {
ASSERT_EQ(group.get<unsigned int>(entity), --uval);
ASSERT_EQ(group.get<const int>(entity), --ival);
}
registry.sort<unsigned int>(std::less<unsigned int>{});
group.sort_as(*group.storage<unsigned int>());
ASSERT_EQ((group.get<const int, unsigned int>(e0)), (std::make_tuple(0, 0u)));
ASSERT_EQ((group.get<0, 1>(e1)), (std::make_tuple(1, 1u)));
ASSERT_EQ((group.get<const int, unsigned int>(e2)), (std::make_tuple(2, 2u)));
ASSERT_FALSE(group.contains(e3));
for(auto entity: group) {
ASSERT_EQ(group.get<unsigned int>(entity), uval++);
ASSERT_EQ(group.get<const int>(entity), ival++);
}
}
TEST(NonOwningGroup, IndexRebuiltOnDestroy) {
entt::registry registry;
auto group = registry.group(entt::get<int, unsigned int>);
const auto e0 = registry.create();
const auto e1 = registry.create();
registry.emplace<unsigned int>(e0, 0u);
registry.emplace<unsigned int>(e1, 1u);
registry.emplace<int>(e0, 0);
registry.emplace<int>(e1, 1);
registry.destroy(e0);
registry.emplace<int>(registry.create(), 42);
ASSERT_EQ(group.size(), 1u);
ASSERT_EQ(group[{}], e1);
ASSERT_EQ(group.get<int>(e1), 1);
ASSERT_EQ(group.get<unsigned int>(e1), 1u);
group.each([e1](auto entity, auto ivalue, auto uivalue) {
ASSERT_EQ(entity, e1);
ASSERT_EQ(ivalue, 1);
ASSERT_EQ(uivalue, 1u);
});
for(auto &&curr: group.each()) {
ASSERT_EQ(std::get<0>(curr), e1);
ASSERT_EQ(std::get<1>(curr), 1);
ASSERT_EQ(std::get<2>(curr), 1u);
}
}
TEST(NonOwningGroup, ConstNonConstAndAllInBetween) {
entt::registry registry;
auto group = registry.group(entt::get<int, empty_type, const char>);
ASSERT_EQ(group.size(), 0u);
const auto entity = registry.create();
registry.emplace<int>(entity, 0);
registry.emplace<empty_type>(entity);
registry.emplace<char>(entity, 'c');
ASSERT_EQ(group.size(), 1u);
static_assert(std::is_same_v<decltype(group.get<0>({})), int &>);
static_assert(std::is_same_v<decltype(group.get<int>({})), int &>);
static_assert(std::is_same_v<decltype(group.get<1>({})), void>);
static_assert(std::is_same_v<decltype(group.get<empty_type>({})), void>);
static_assert(std::is_same_v<decltype(group.get<2>({})), const char &>);
static_assert(std::is_same_v<decltype(group.get<const char>({})), const char &>);
static_assert(std::is_same_v<decltype(group.get<int, empty_type, const char>({})), std::tuple<int &, const char &>>);
static_assert(std::is_same_v<decltype(group.get<0, 1, 2>({})), std::tuple<int &, const char &>>);
static_assert(std::is_same_v<decltype(group.get({})), std::tuple<int &, const char &>>);
static_assert(std::is_same_v<decltype(std::as_const(registry).group_if_exists(entt::get<int, char>)), decltype(std::as_const(registry).group_if_exists(entt::get<const int, const char>))>);
static_assert(std::is_same_v<decltype(std::as_const(registry).group_if_exists(entt::get<const int, char>)), decltype(std::as_const(registry).group_if_exists(entt::get<const int, const char>))>);
static_assert(std::is_same_v<decltype(std::as_const(registry).group_if_exists(entt::get<int, const char>)), decltype(std::as_const(registry).group_if_exists(entt::get<const int, const char>))>);
group.each([](auto &&i, auto &&c) {
static_assert(std::is_same_v<decltype(i), int &>);
static_assert(std::is_same_v<decltype(c), const char &>);
});
for(auto [entt, iv, cv]: group.each()) {
static_assert(std::is_same_v<decltype(entt), entt::entity>);
static_assert(std::is_same_v<decltype(iv), int &>);
static_assert(std::is_same_v<decltype(cv), const char &>);
}
}
TEST(NonOwningGroup, Find) {
entt::registry registry;
auto group = registry.group(entt::get<int, const char>);
const auto e0 = registry.create();
registry.emplace<int>(e0);
registry.emplace<char>(e0);
const auto e1 = registry.create();
registry.emplace<int>(e1);
registry.emplace<char>(e1);
const auto e2 = registry.create();
registry.emplace<int>(e2);
registry.emplace<char>(e2);
const auto e3 = registry.create();
registry.emplace<int>(e3);
registry.emplace<char>(e3);
registry.erase<int>(e1);
ASSERT_NE(group.find(e0), group.end());
ASSERT_EQ(group.find(e1), group.end());
ASSERT_NE(group.find(e2), group.end());
ASSERT_NE(group.find(e3), group.end());
auto it = group.find(e2);
ASSERT_EQ(*it, e2);
ASSERT_EQ(*(++it), e3);
ASSERT_EQ(*(++it), e0);
ASSERT_EQ(++it, group.end());
ASSERT_EQ(++group.find(e0), group.end());
const auto e4 = registry.create();
registry.destroy(e4);
const auto e5 = registry.create();
registry.emplace<int>(e5);
registry.emplace<char>(e5);
ASSERT_NE(group.find(e5), group.end());
ASSERT_EQ(group.find(e4), group.end());
}
TEST(NonOwningGroup, ExcludedComponents) {
entt::registry registry;
const auto e0 = registry.create();
registry.emplace<int>(e0, 0);
const auto e1 = registry.create();
registry.emplace<int>(e1, 1);
registry.emplace<char>(e1);
const auto group = registry.group(entt::get<int>, entt::exclude<char>);
const auto e2 = registry.create();
registry.emplace<int>(e2, 2);
const auto e3 = registry.create();
registry.emplace<int>(e3, 3);
registry.emplace<char>(e3);
for(const auto entity: group) {
ASSERT_TRUE(entity == e0 || entity == e2);
if(entity == e0) {
ASSERT_EQ(group.get<int>(e0), 0);
} else if(entity == e2) {
ASSERT_EQ(group.get<0>(e2), 2);
}
}
registry.emplace<char>(e0);
registry.emplace<char>(e2);
ASSERT_TRUE(group.empty());
registry.erase<char>(e1);
registry.erase<char>(e3);
for(const auto entity: group) {
ASSERT_TRUE(entity == e1 || entity == e3);
if(entity == e1) {
ASSERT_EQ(group.get<int>(e1), 1);
} else if(entity == e3) {
ASSERT_EQ(group.get<0>(e3), 3);
}
}
}
TEST(NonOwningGroup, EmptyAndNonEmptyTypes) {
entt::registry registry;
const auto group = registry.group(entt::get<int, empty_type>);
const auto e0 = registry.create();
registry.emplace<empty_type>(e0);
registry.emplace<int>(e0);
const auto e1 = registry.create();
registry.emplace<empty_type>(e1);
registry.emplace<int>(e1);
registry.emplace<int>(registry.create());
for(const auto entity: group) {
ASSERT_TRUE(entity == e0 || entity == e1);
}
group.each([e0, e1](const auto entity, const int &) {
ASSERT_TRUE(entity == e0 || entity == e1);
});
for(auto [entt, iv]: group.each()) {
static_assert(std::is_same_v<decltype(entt), entt::entity>);
static_assert(std::is_same_v<decltype(iv), int &>);
ASSERT_TRUE(entt == e0 || entt == e1);
}
ASSERT_EQ(group.size(), 2u);
}
TEST(NonOwningGroup, TrackEntitiesOnComponentDestruction) {
entt::registry registry;
const auto group = registry.group(entt::get<int>, entt::exclude<char>);
const auto cgroup = std::as_const(registry).group_if_exists(entt::get<const int>, entt::exclude<char>);
const auto entity = registry.create();
registry.emplace<int>(entity);
registry.emplace<char>(entity);
ASSERT_TRUE(group.empty());
ASSERT_TRUE(cgroup.empty());
registry.erase<char>(entity);
ASSERT_FALSE(group.empty());
ASSERT_FALSE(cgroup.empty());
}
TEST(NonOwningGroup, EmptyTypes) {
entt::registry registry;
const auto entity = registry.create();
registry.emplace<int>(entity);
registry.emplace<char>(entity);
registry.emplace<empty_type>(entity);
registry.group(entt::get<int, char, empty_type>).each([entity](const auto entt, int, char) {
ASSERT_EQ(entity, entt);
});
for(auto [entt, iv, cv]: registry.group(entt::get<int, char, empty_type>).each()) {
static_assert(std::is_same_v<decltype(entt), entt::entity>);
static_assert(std::is_same_v<decltype(iv), int &>);
static_assert(std::is_same_v<decltype(cv), char &>);
ASSERT_EQ(entity, entt);
}
registry.group(entt::get<int, empty_type, char>).each([check = true](int, char) mutable {
ASSERT_TRUE(check);
check = false;
});
for(auto [entt, iv, cv]: registry.group(entt::get<int, empty_type, char>).each()) {
static_assert(std::is_same_v<decltype(entt), entt::entity>);
static_assert(std::is_same_v<decltype(iv), int &>);
static_assert(std::is_same_v<decltype(cv), char &>);
ASSERT_EQ(entity, entt);
}
registry.group(entt::get<empty_type, int, char>).each([entity](const auto entt, int, char) {
ASSERT_EQ(entity, entt);
});
for(auto [entt, iv, cv]: registry.group(entt::get<empty_type, int, char>).each()) {
static_assert(std::is_same_v<decltype(entt), entt::entity>);
static_assert(std::is_same_v<decltype(iv), int &>);
static_assert(std::is_same_v<decltype(cv), char &>);
ASSERT_EQ(entity, entt);
}
auto iterable = registry.group(entt::get<int, char, double>).each();
ASSERT_EQ(iterable.begin(), iterable.end());
}
TEST(NonOwningGroup, FrontBack) {
entt::registry registry;
auto group = registry.group<>(entt::get<const int, const char>);
ASSERT_EQ(group.front(), static_cast<entt::entity>(entt::null));
ASSERT_EQ(group.back(), static_cast<entt::entity>(entt::null));
const auto e0 = registry.create();
registry.emplace<int>(e0);
registry.emplace<char>(e0);
const auto e1 = registry.create();
registry.emplace<int>(e1);
registry.emplace<char>(e1);
const auto entity = registry.create();
registry.emplace<char>(entity);
ASSERT_EQ(group.front(), e1);
ASSERT_EQ(group.back(), e0);
}
TEST(NonOwningGroup, SignalRace) {
entt::registry registry;
registry.on_construct<double>().connect<&entt::registry::emplace_or_replace<int>>();
const auto group = registry.group(entt::get<int, double>);
auto entity = registry.create();
registry.emplace<double>(entity);
ASSERT_EQ(group.size(), 1u);
}
TEST(NonOwningGroup, ExtendedGet) {
using type = decltype(std::declval<entt::registry>().group(entt::get<int, empty_type, char>).get({}));
static_assert(std::tuple_size_v<type> == 2u);
static_assert(std::is_same_v<std::tuple_element_t<0, type>, int &>);
static_assert(std::is_same_v<std::tuple_element_t<1, type>, char &>);
entt::registry registry;
const auto entity = registry.create();
registry.emplace<int>(entity, 42);
registry.emplace<char>(entity, 'c');
const auto tup = registry.group(entt::get<int, char>).get(entity);
ASSERT_EQ(std::get<0>(tup), 42);
ASSERT_EQ(std::get<1>(tup), 'c');
}
TEST(NonOwningGroup, IterableGroupAlgorithmCompatibility) {
entt::registry registry;
const auto entity = registry.create();
registry.emplace<int>(entity);
registry.emplace<char>(entity);
const auto group = registry.group(entt::get<int, char>);
const auto iterable = group.each();
const auto it = std::find_if(iterable.begin(), iterable.end(), [entity](auto args) { return std::get<0>(args) == entity; });
ASSERT_EQ(std::get<0>(*it), entity);
}
TEST(NonOwningGroup, Storage) {
entt::registry registry;
const auto entity = registry.create();
auto group = registry.group(entt::get<int, const char>, entt::exclude<double, const float>);
static_assert(std::is_same_v<decltype(group.storage<0u>()), entt::storage_type_t<int> *>);
static_assert(std::is_same_v<decltype(group.storage<int>()), entt::storage_type_t<int> *>);
static_assert(std::is_same_v<decltype(group.storage<const int>()), entt::storage_type_t<int> *>);
static_assert(std::is_same_v<decltype(group.storage<1u>()), const entt::storage_type_t<char> *>);
static_assert(std::is_same_v<decltype(group.storage<char>()), const entt::storage_type_t<char> *>);
static_assert(std::is_same_v<decltype(group.storage<const char>()), const entt::storage_type_t<char> *>);
static_assert(std::is_same_v<decltype(group.storage<2u>()), entt::storage_type_t<double> *>);
static_assert(std::is_same_v<decltype(group.storage<double>()), entt::storage_type_t<double> *>);
static_assert(std::is_same_v<decltype(group.storage<const double>()), entt::storage_type_t<double> *>);
static_assert(std::is_same_v<decltype(group.storage<3u>()), const entt::storage_type_t<float> *>);
static_assert(std::is_same_v<decltype(group.storage<float>()), const entt::storage_type_t<float> *>);
static_assert(std::is_same_v<decltype(group.storage<const float>()), const entt::storage_type_t<float> *>);
ASSERT_TRUE(group);
ASSERT_NE(group.storage<int>(), nullptr);
ASSERT_NE(group.storage<1u>(), nullptr);
ASSERT_NE(group.storage<double>(), nullptr);
ASSERT_NE(group.storage<3u>(), nullptr);
ASSERT_EQ(group.size(), 0u);
group.storage<int>()->emplace(entity);
group.storage<double>()->emplace(entity);
registry.emplace<char>(entity);
registry.emplace<float>(entity);
ASSERT_EQ(group.size(), 0u);
ASSERT_EQ(group.begin(), group.end());
ASSERT_TRUE(group.storage<int>()->contains(entity));
ASSERT_TRUE(group.storage<const char>()->contains(entity));
ASSERT_TRUE(group.storage<double>()->contains(entity));
ASSERT_TRUE(group.storage<const float>()->contains(entity));
ASSERT_TRUE((registry.all_of<int, char, double, float>(entity)));
group.storage<double>()->erase(entity);
registry.erase<float>(entity);
ASSERT_EQ(group.size(), 1u);
ASSERT_NE(group.begin(), group.end());
ASSERT_TRUE(group.storage<const int>()->contains(entity));
ASSERT_TRUE(group.storage<char>()->contains(entity));
ASSERT_FALSE(group.storage<const double>()->contains(entity));
ASSERT_FALSE(group.storage<float>()->contains(entity));
ASSERT_TRUE((registry.all_of<int, char>(entity)));
ASSERT_FALSE((registry.any_of<double, float>(entity)));
group.storage<0u>()->erase(entity);
ASSERT_EQ(group.size(), 0u);
ASSERT_EQ(group.begin(), group.end());
ASSERT_FALSE(group.storage<0u>()->contains(entity));
ASSERT_TRUE(group.storage<1u>()->contains(entity));
ASSERT_FALSE(group.storage<2u>()->contains(entity));
ASSERT_FALSE(group.storage<3u>()->contains(entity));
ASSERT_TRUE((registry.all_of<char>(entity)));
ASSERT_FALSE((registry.any_of<int, double, float>(entity)));
group = {};
ASSERT_FALSE(group);
ASSERT_EQ(group.storage<0u>(), nullptr);
ASSERT_EQ(group.storage<const char>(), nullptr);
ASSERT_EQ(group.storage<2u>(), nullptr);
ASSERT_EQ(group.storage<const float>(), nullptr);
}
TEST(NonOwningGroup, Overlapping) {
entt::registry registry;
auto group = registry.group(entt::get<char>, entt::exclude<double>);
auto other = registry.group<int>(entt::get<char>, entt::exclude<double>);
ASSERT_TRUE(group.empty());
ASSERT_TRUE(other.empty());
const auto entity = registry.create();
registry.emplace<char>(entity, '1');
ASSERT_FALSE(group.empty());
ASSERT_TRUE(other.empty());
registry.emplace<int>(entity, 42);
ASSERT_FALSE(group.empty());
ASSERT_FALSE(other.empty());
registry.emplace<double>(entity, 3.);
ASSERT_TRUE(group.empty());
ASSERT_TRUE(other.empty());
}
TEST(OwningGroup, Functionalities) {
entt::registry registry;
auto group = registry.group<int>(entt::get<char>);
auto cgroup = std::as_const(registry).group_if_exists<const int>(entt::get<const char>);
ASSERT_TRUE(group.empty());
const auto e0 = registry.create();
registry.emplace<char>(e0, '1');
const auto e1 = registry.create();
registry.emplace<int>(e1, 42);
registry.emplace<char>(e1, '2');
ASSERT_FALSE(group.empty());
ASSERT_NO_FATAL_FAILURE(group.begin()++);
ASSERT_NO_FATAL_FAILURE(++cgroup.begin());
ASSERT_NO_FATAL_FAILURE([](auto it) { return it++; }(group.rbegin()));
ASSERT_NO_FATAL_FAILURE([](auto it) { return ++it; }(cgroup.rbegin()));
ASSERT_NE(group.begin(), group.end());
ASSERT_NE(cgroup.begin(), cgroup.end());
ASSERT_NE(group.rbegin(), group.rend());
ASSERT_NE(cgroup.rbegin(), cgroup.rend());
ASSERT_EQ(group.size(), 1u);
registry.emplace<int>(e0);
ASSERT_EQ(group.size(), 2u);
registry.erase<int>(e0);
ASSERT_EQ(group.size(), 1u);
ASSERT_EQ(cgroup.storage<const int>()->raw()[0u][0u], 42);
ASSERT_EQ(group.storage<int>()->raw()[0u][0u], 42);
for(auto entity: group) {
ASSERT_EQ(std::get<0>(cgroup.get<const int, const char>(entity)), 42);
ASSERT_EQ(std::get<1>(group.get<int, char>(entity)), '2');
ASSERT_EQ(cgroup.get<1>(entity), '2');
}
ASSERT_EQ(group.handle().data()[0u], e1);
ASSERT_EQ(group.storage<int>()->raw()[0u][0u], 42);
registry.erase<char>(e0);
registry.erase<char>(e1);
ASSERT_EQ(group.begin(), group.end());
ASSERT_EQ(cgroup.begin(), cgroup.end());
ASSERT_EQ(group.rbegin(), group.rend());
ASSERT_EQ(cgroup.rbegin(), cgroup.rend());
ASSERT_TRUE(group.empty());
decltype(group) invalid{};
ASSERT_TRUE(group);
ASSERT_TRUE(cgroup);
ASSERT_FALSE(invalid);
}
TEST(OwningGroup, Handle) {
entt::registry registry;
const auto entity = registry.create();
auto group = registry.group<int>(entt::get<char>);
auto &&handle = group.handle();
ASSERT_TRUE(handle.empty());
ASSERT_FALSE(handle.contains(entity));
ASSERT_EQ(&handle, &group.handle());
ASSERT_EQ(&handle, group.storage<int>());
registry.emplace<int>(entity);
registry.emplace<char>(entity);
ASSERT_FALSE(handle.empty());
ASSERT_TRUE(handle.contains(entity));
ASSERT_EQ(&handle, &group.handle());
}
TEST(OwningGroup, Invalid) {
entt::registry registry{};
auto group = std::as_const(registry).group_if_exists<const int>(entt::get<const empty_type>);
const auto entity = registry.create();
registry.emplace<empty_type>(entity);
registry.emplace<int>(entity);
ASSERT_FALSE(group);
ASSERT_TRUE(group.empty());
ASSERT_EQ(group.size(), 0u);
ASSERT_EQ(group.begin(), group.end());
ASSERT_EQ(group.rbegin(), group.rend());
ASSERT_FALSE(group.contains(entity));
ASSERT_EQ(group.find(entity), group.end());
ASSERT_EQ(group.front(), entt::entity{entt::null});
ASSERT_EQ(group.back(), entt::entity{entt::null});
}
TEST(OwningGroup, ElementAccess) {
entt::registry registry;
auto group = registry.group<int>(entt::get<char>);
auto cgroup = std::as_const(registry).group_if_exists<const int>(entt::get<const char>);
const auto e0 = registry.create();
registry.emplace<int>(e0);
registry.emplace<char>(e0);
const auto e1 = registry.create();
registry.emplace<int>(e1);
registry.emplace<char>(e1);
for(auto i = 0u; i < group.size(); ++i) {
ASSERT_EQ(group[i], i ? e0 : e1);
ASSERT_EQ(cgroup[i], i ? e0 : e1);
}
}
TEST(OwningGroup, Contains) {
entt::registry registry;
auto group = registry.group<int>(entt::get<char>);
const auto e0 = registry.create();
registry.emplace<int>(e0);
registry.emplace<char>(e0);
const auto e1 = registry.create();
registry.emplace<int>(e1);
registry.emplace<char>(e1);
registry.destroy(e0);
ASSERT_FALSE(group.contains(e0));
ASSERT_TRUE(group.contains(e1));
}
TEST(OwningGroup, Empty) {
entt::registry registry;
const auto e0 = registry.create();
registry.emplace<double>(e0);
registry.emplace<int>(e0);
registry.emplace<float>(e0);
const auto e1 = registry.create();
registry.emplace<char>(e1);
registry.emplace<float>(e1);
ASSERT_TRUE((registry.group<char, int>(entt::get<float>).empty()));
ASSERT_TRUE((registry.group<double, float>(entt::get<char, int>).empty()));
}
TEST(OwningGroup, Each) {
entt::registry registry;
entt::entity entity[2]{registry.create(), registry.create()};
auto group = registry.group<int>(entt::get<char>);
auto cgroup = std::as_const(registry).group_if_exists<const int>(entt::get<const char>);
registry.emplace<int>(entity[0u], 0);
registry.emplace<char>(entity[0u], static_cast<char>(0));
registry.emplace<int>(entity[1u], 1);
registry.emplace<char>(entity[1u], static_cast<char>(1));
auto iterable = group.each();
auto citerable = cgroup.each();
ASSERT_NE(citerable.begin(), citerable.end());
ASSERT_NO_FATAL_FAILURE(iterable.begin()->operator=(*iterable.begin()));
ASSERT_EQ(decltype(iterable.end()){}, iterable.end());
auto it = iterable.begin();
ASSERT_EQ(it.base(), group.begin());
ASSERT_EQ((it++, ++it), iterable.end());
ASSERT_EQ(it.base(), group.end());
group.each([expected = 1](auto entt, int &ivalue, char &cvalue) mutable {
ASSERT_EQ(static_cast<int>(entt::to_integral(entt)), expected);
ASSERT_EQ(ivalue, expected);
ASSERT_EQ(cvalue, expected);
--expected;
});
cgroup.each([expected = 1](const int &ivalue, const char &cvalue) mutable {
ASSERT_EQ(ivalue, expected);
ASSERT_EQ(cvalue, expected);
--expected;
});
ASSERT_EQ(std::get<0>(*iterable.begin()), entity[1u]);
ASSERT_EQ(std::get<0>(*++citerable.begin()), entity[0u]);
static_assert(std::is_same_v<decltype(std::get<1>(*iterable.begin())), int &>);
static_assert(std::is_same_v<decltype(std::get<2>(*citerable.begin())), const char &>);
// do not use iterable, make sure an iterable group works when created from a temporary
for(auto [entt, ivalue, cvalue]: registry.group<int>(entt::get<char>).each()) {
ASSERT_EQ(static_cast<int>(entt::to_integral(entt)), ivalue);
ASSERT_EQ(static_cast<char>(entt::to_integral(entt)), cvalue);
}
}
TEST(OwningGroup, SortOrdered) {
entt::registry registry;
auto group = registry.group<boxed_int, char>();
entt::entity entities[5]{};
registry.create(std::begin(entities), std::end(entities));
registry.emplace<boxed_int>(entities[0], 12);
registry.emplace<char>(entities[0], 'a');
registry.emplace<boxed_int>(entities[1], 9);
registry.emplace<char>(entities[1], 'b');
registry.emplace<boxed_int>(entities[2], 6);
registry.emplace<char>(entities[2], 'c');
registry.emplace<boxed_int>(entities[3], 1);
registry.emplace<boxed_int>(entities[4], 2);
group.sort([&group](const entt::entity lhs, const entt::entity rhs) {
return group.get<boxed_int>(lhs).value < group.get<0>(rhs).value;
});
ASSERT_EQ(group.handle().data()[0u], entities[0]);
ASSERT_EQ(group.handle().data()[1u], entities[1]);
ASSERT_EQ(group.handle().data()[2u], entities[2]);
ASSERT_EQ(group.handle().data()[3u], entities[3]);
ASSERT_EQ(group.handle().data()[4u], entities[4]);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][0u].value, 12);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][1u].value, 9);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][2u].value, 6);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][3u].value, 1);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][4u].value, 2);
ASSERT_EQ(group.storage<char>()->raw()[0u][0u], 'a');
ASSERT_EQ(group.storage<char>()->raw()[0u][1u], 'b');
ASSERT_EQ(group.storage<char>()->raw()[0u][2u], 'c');
ASSERT_EQ((group.get<boxed_int, char>(entities[0])), (std::make_tuple(boxed_int{12}, 'a')));
ASSERT_EQ((group.get<0, 1>(entities[1])), (std::make_tuple(boxed_int{9}, 'b')));
ASSERT_EQ((group.get<boxed_int, char>(entities[2])), (std::make_tuple(boxed_int{6}, 'c')));
ASSERT_FALSE(group.contains(entities[3]));
ASSERT_FALSE(group.contains(entities[4]));
}
TEST(OwningGroup, SortReverse) {
entt::registry registry;
auto group = registry.group<boxed_int, char>();
entt::entity entities[5]{};
registry.create(std::begin(entities), std::end(entities));
registry.emplace<boxed_int>(entities[0], 6);
registry.emplace<char>(entities[0], 'a');
registry.emplace<boxed_int>(entities[1], 9);
registry.emplace<char>(entities[1], 'b');
registry.emplace<boxed_int>(entities[2], 12);
registry.emplace<char>(entities[2], 'c');
registry.emplace<boxed_int>(entities[3], 1);
registry.emplace<boxed_int>(entities[4], 2);
group.sort<boxed_int>([](const auto &lhs, const auto &rhs) {
return lhs.value < rhs.value;
});
ASSERT_EQ(group.handle().data()[0u], entities[2]);
ASSERT_EQ(group.handle().data()[1u], entities[1]);
ASSERT_EQ(group.handle().data()[2u], entities[0]);
ASSERT_EQ(group.handle().data()[3u], entities[3]);
ASSERT_EQ(group.handle().data()[4u], entities[4]);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][0u].value, 12);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][1u].value, 9);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][2u].value, 6);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][3u].value, 1);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][4u].value, 2);
ASSERT_EQ(group.storage<char>()->raw()[0u][0u], 'c');
ASSERT_EQ(group.storage<char>()->raw()[0u][1u], 'b');
ASSERT_EQ(group.storage<char>()->raw()[0u][2u], 'a');
ASSERT_EQ((group.get<boxed_int, char>(entities[0])), (std::make_tuple(boxed_int{6}, 'a')));
ASSERT_EQ((group.get<0, 1>(entities[1])), (std::make_tuple(boxed_int{9}, 'b')));
ASSERT_EQ((group.get<boxed_int, char>(entities[2])), (std::make_tuple(boxed_int{12}, 'c')));
ASSERT_FALSE(group.contains(entities[3]));
ASSERT_FALSE(group.contains(entities[4]));
}
TEST(OwningGroup, SortUnordered) {
entt::registry registry;
auto group = registry.group<boxed_int>(entt::get<char>);
entt::entity entities[7]{};
registry.create(std::begin(entities), std::end(entities));
registry.emplace<boxed_int>(entities[0], 6);
registry.emplace<char>(entities[0], 'c');
registry.emplace<boxed_int>(entities[1], 3);
registry.emplace<char>(entities[1], 'b');
registry.emplace<boxed_int>(entities[2], 1);
registry.emplace<char>(entities[2], 'a');
registry.emplace<boxed_int>(entities[3], 9);
registry.emplace<char>(entities[3], 'd');
registry.emplace<boxed_int>(entities[4], 12);
registry.emplace<char>(entities[4], 'e');
registry.emplace<boxed_int>(entities[5], 4);
registry.emplace<boxed_int>(entities[6], 5);
group.sort<boxed_int, char>([](const auto lhs, const auto rhs) {
static_assert(std::is_same_v<decltype(std::get<0>(lhs)), boxed_int &>);
static_assert(std::is_same_v<decltype(std::get<1>(rhs)), char &>);
return std::get<1>(lhs) < std::get<1>(rhs);
});
ASSERT_EQ(group.handle().data()[0u], entities[4]);
ASSERT_EQ(group.handle().data()[1u], entities[3]);
ASSERT_EQ(group.handle().data()[2u], entities[0]);
ASSERT_EQ(group.handle().data()[3u], entities[1]);
ASSERT_EQ(group.handle().data()[4u], entities[2]);
ASSERT_EQ(group.handle().data()[5u], entities[5]);
ASSERT_EQ(group.handle().data()[6u], entities[6]);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][0u].value, 12);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][1u].value, 9);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][2u].value, 6);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][3u].value, 3);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][4u].value, 1);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][5u].value, 4);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][6u].value, 5);
ASSERT_EQ(group.get<char>(group.handle().data()[0u]), 'e');
ASSERT_EQ(group.get<1>(group.handle().data()[1u]), 'd');
ASSERT_EQ(group.get<char>(group.handle().data()[2u]), 'c');
ASSERT_EQ(group.get<1>(group.handle().data()[3u]), 'b');
ASSERT_EQ(group.get<char>(group.handle().data()[4u]), 'a');
ASSERT_FALSE(group.contains(entities[5]));
ASSERT_FALSE(group.contains(entities[6]));
}
TEST(OwningGroup, SortWithExclusionList) {
entt::registry registry;
auto group = registry.group<boxed_int>(entt::get<>, entt::exclude<char>);
entt::entity entities[5]{};
registry.create(std::begin(entities), std::end(entities));
registry.emplace<boxed_int>(entities[0], 0);
registry.emplace<boxed_int>(entities[1], 1);
registry.emplace<boxed_int>(entities[2], 2);
registry.emplace<boxed_int>(entities[3], 3);
registry.emplace<boxed_int>(entities[4], 4);
registry.emplace<char>(entities[2]);
group.sort([](const entt::entity lhs, const entt::entity rhs) {
return lhs < rhs;
});
ASSERT_EQ(group.handle().data()[0u], entities[4]);
ASSERT_EQ(group.handle().data()[1u], entities[3]);
ASSERT_EQ(group.handle().data()[2u], entities[1]);
ASSERT_EQ(group.handle().data()[3u], entities[0]);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][0u].value, 4);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][1u].value, 3);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][2u].value, 1);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][3u].value, 0);
ASSERT_EQ(group.get<boxed_int>(entities[0]).value, 0);
ASSERT_EQ(group.get<0>(entities[1]).value, 1);
ASSERT_EQ(group.get<boxed_int>(entities[3]).value, 3);
ASSERT_EQ(group.get<0>(entities[4]).value, 4);
ASSERT_FALSE(group.contains(entities[2]));
}
TEST(OwningGroup, IndexRebuiltOnDestroy) {
entt::registry registry;
auto group = registry.group<int>(entt::get<unsigned int>);
const auto e0 = registry.create();
const auto e1 = registry.create();
registry.emplace<unsigned int>(e0, 0u);
registry.emplace<unsigned int>(e1, 1u);
registry.emplace<int>(e0, 0);
registry.emplace<int>(e1, 1);
registry.destroy(e0);
registry.emplace<int>(registry.create(), 42);
ASSERT_EQ(group.size(), 1u);
ASSERT_EQ(group[{}], e1);
ASSERT_EQ(group.get<int>(e1), 1);
ASSERT_EQ(group.get<unsigned int>(e1), 1u);
group.each([e1](auto entity, auto ivalue, auto uivalue) {
ASSERT_EQ(entity, e1);
ASSERT_EQ(ivalue, 1);
ASSERT_EQ(uivalue, 1u);
});
for(auto &&curr: group.each()) {
ASSERT_EQ(std::get<0>(curr), e1);
ASSERT_EQ(std::get<1>(curr), 1);
ASSERT_EQ(std::get<2>(curr), 1u);
}
}
TEST(OwningGroup, ConstNonConstAndAllInBetween) {
entt::registry registry;
auto group = registry.group<int, const char>(entt::get<empty_type, double, const float>);
ASSERT_EQ(group.size(), 0u);
const auto entity = registry.create();
registry.emplace<int>(entity, 0);
registry.emplace<char>(entity, 'c');
registry.emplace<empty_type>(entity);
registry.emplace<double>(entity, 0.);
registry.emplace<float>(entity, 0.f);
ASSERT_EQ(group.size(), 1u);
static_assert(std::is_same_v<decltype(group.get<0>({})), int &>);
static_assert(std::is_same_v<decltype(group.get<int>({})), int &>);
static_assert(std::is_same_v<decltype(group.get<1>({})), const char &>);
static_assert(std::is_same_v<decltype(group.get<const char>({})), const char &>);
static_assert(std::is_same_v<decltype(group.get<2>({})), void>);
static_assert(std::is_same_v<decltype(group.get<empty_type>({})), void>);
static_assert(std::is_same_v<decltype(group.get<3>({})), double &>);
static_assert(std::is_same_v<decltype(group.get<double>({})), double &>);
static_assert(std::is_same_v<decltype(group.get<4>({})), const float &>);
static_assert(std::is_same_v<decltype(group.get<const float>({})), const float &>);
static_assert(std::is_same_v<decltype(group.get<int, const char, empty_type, double, const float>({})), std::tuple<int &, const char &, double &, const float &>>);
static_assert(std::is_same_v<decltype(group.get<0, 1, 2, 3, 4>({})), std::tuple<int &, const char &, double &, const float &>>);
static_assert(std::is_same_v<decltype(group.get({})), std::tuple<int &, const char &, double &, const float &>>);
static_assert(std::is_same_v<decltype(std::as_const(registry).group_if_exists<int>(entt::get<char>)), decltype(std::as_const(registry).group_if_exists<const int>(entt::get<const char>))>);
static_assert(std::is_same_v<decltype(std::as_const(registry).group_if_exists<const int>(entt::get<char>)), decltype(std::as_const(registry).group_if_exists<const int>(entt::get<const char>))>);
static_assert(std::is_same_v<decltype(std::as_const(registry).group_if_exists<int>(entt::get<const char>)), decltype(std::as_const(registry).group_if_exists<const int>(entt::get<const char>))>);
group.each([](auto &&i, auto &&c, auto &&d, auto &&f) {
static_assert(std::is_same_v<decltype(i), int &>);
static_assert(std::is_same_v<decltype(c), const char &>);
static_assert(std::is_same_v<decltype(d), double &>);
static_assert(std::is_same_v<decltype(f), const float &>);
});
for(auto [entt, iv, cv, dv, fv]: group.each()) {
static_assert(std::is_same_v<decltype(entt), entt::entity>);
static_assert(std::is_same_v<decltype(iv), int &>);
static_assert(std::is_same_v<decltype(cv), const char &>);
static_assert(std::is_same_v<decltype(dv), double &>);
static_assert(std::is_same_v<decltype(fv), const float &>);
}
}
TEST(OwningGroup, Find) {
entt::registry registry;
auto group = registry.group<int>(entt::get<const char>);
const auto e0 = registry.create();
registry.emplace<int>(e0);
registry.emplace<char>(e0);
const auto e1 = registry.create();
registry.emplace<int>(e1);
registry.emplace<char>(e1);
const auto e2 = registry.create();
registry.emplace<int>(e2);
registry.emplace<char>(e2);
const auto e3 = registry.create();
registry.emplace<int>(e3);
registry.emplace<char>(e3);
registry.erase<int>(e1);
ASSERT_NE(group.find(e0), group.end());
ASSERT_EQ(group.find(e1), group.end());
ASSERT_NE(group.find(e2), group.end());
ASSERT_NE(group.find(e3), group.end());
auto it = group.find(e2);
ASSERT_EQ(*it, e2);
ASSERT_EQ(*(++it), e3);
ASSERT_EQ(*(++it), e0);
ASSERT_EQ(++it, group.end());
ASSERT_EQ(++group.find(e0), group.end());
const auto e4 = registry.create();
registry.destroy(e4);
const auto e5 = registry.create();
registry.emplace<int>(e5);
registry.emplace<char>(e5);
ASSERT_NE(group.find(e5), group.end());
ASSERT_EQ(group.find(e4), group.end());
}
TEST(OwningGroup, ExcludedComponents) {
entt::registry registry;
const auto e0 = registry.create();
registry.emplace<int>(e0, 0);
const auto e1 = registry.create();
registry.emplace<int>(e1, 1);
registry.emplace<char>(e1);
const auto group = registry.group<int>(entt::get<>, entt::exclude<char, double>);
const auto e2 = registry.create();
registry.emplace<int>(e2, 2);
const auto e3 = registry.create();
registry.emplace<int>(e3, 3);
registry.emplace<double>(e3);
for(const auto entity: group) {
ASSERT_TRUE(entity == e0 || entity == e2);
if(entity == e0) {
ASSERT_EQ(group.get<int>(e0), 0);
} else if(entity == e2) {
ASSERT_EQ(group.get<0>(e2), 2);
}
}
registry.emplace<char>(e0);
registry.emplace<double>(e2);
ASSERT_TRUE(group.empty());
registry.erase<char>(e1);
registry.erase<double>(e3);
for(const auto entity: group) {
ASSERT_TRUE(entity == e1 || entity == e3);
if(entity == e1) {
ASSERT_EQ(group.get<int>(e1), 1);
} else if(entity == e3) {
ASSERT_EQ(group.get<0>(e3), 3);
}
}
}
TEST(OwningGroup, EmptyAndNonEmptyTypes) {
entt::registry registry;
const auto group = registry.group<int>(entt::get<empty_type>);
const auto e0 = registry.create();
registry.emplace<empty_type>(e0);
registry.emplace<int>(e0);
const auto e1 = registry.create();
registry.emplace<empty_type>(e1);
registry.emplace<int>(e1);
registry.emplace<int>(registry.create());
for(const auto entity: group) {
ASSERT_TRUE(entity == e0 || entity == e1);
}
group.each([e0, e1](const auto entity, const int &) {
ASSERT_TRUE(entity == e0 || entity == e1);
});
for(auto [entt, iv]: group.each()) {
static_assert(std::is_same_v<decltype(entt), entt::entity>);
static_assert(std::is_same_v<decltype(iv), int &>);
ASSERT_TRUE(entt == e0 || entt == e1);
}
ASSERT_EQ(group.size(), 2u);
}
TEST(OwningGroup, TrackEntitiesOnComponentDestruction) {
entt::registry registry;
const auto group = registry.group<int>(entt::get<>, entt::exclude<char>);
const auto cgroup = std::as_const(registry).group_if_exists<const int>(entt::get<>, entt::exclude<char>);
const auto entity = registry.create();
registry.emplace<int>(entity);
registry.emplace<char>(entity);
ASSERT_TRUE(group.empty());
ASSERT_TRUE(cgroup.empty());
registry.erase<char>(entity);
ASSERT_FALSE(group.empty());
ASSERT_FALSE(cgroup.empty());
}
TEST(OwningGroup, EmptyTypes) {
entt::registry registry;
const auto entity = registry.create();
registry.emplace<int>(entity);
registry.emplace<char>(entity);
registry.emplace<empty_type>(entity);
registry.group<int>(entt::get<char, empty_type>).each([entity](const auto entt, int, char) {
ASSERT_EQ(entity, entt);
});
for(auto [entt, iv, cv]: registry.group<int>(entt::get<char, empty_type>).each()) {
static_assert(std::is_same_v<decltype(entt), entt::entity>);
static_assert(std::is_same_v<decltype(iv), int &>);
static_assert(std::is_same_v<decltype(cv), char &>);
ASSERT_EQ(entity, entt);
}
registry.group<char>(entt::get<empty_type, int>).each([check = true](char, int) mutable {
ASSERT_TRUE(check);
check = false;
});
for(auto [entt, cv, iv]: registry.group<char>(entt::get<empty_type, int>).each()) {
static_assert(std::is_same_v<decltype(entt), entt::entity>);
static_assert(std::is_same_v<decltype(cv), char &>);
static_assert(std::is_same_v<decltype(iv), int &>);
ASSERT_EQ(entity, entt);
}
registry.group<empty_type>(entt::get<int, char>).each([entity](const auto entt, int, char) {
ASSERT_EQ(entity, entt);
});
for(auto [entt, iv, cv]: registry.group<empty_type>(entt::get<int, char>).each()) {
static_assert(std::is_same_v<decltype(entt), entt::entity>);
static_assert(std::is_same_v<decltype(iv), int &>);
static_assert(std::is_same_v<decltype(cv), char &>);
ASSERT_EQ(entity, entt);
}
auto iterable = registry.group<double>(entt::get<int, char>).each();
ASSERT_EQ(iterable.begin(), iterable.end());
}
TEST(OwningGroup, FrontBack) {
entt::registry registry;
auto group = registry.group<const char>(entt::get<const int>);
ASSERT_EQ(group.front(), static_cast<entt::entity>(entt::null));
ASSERT_EQ(group.back(), static_cast<entt::entity>(entt::null));
const auto e0 = registry.create();
registry.emplace<int>(e0);
registry.emplace<char>(e0);
const auto e1 = registry.create();
registry.emplace<int>(e1);
registry.emplace<char>(e1);
const auto entity = registry.create();
registry.emplace<char>(entity);
ASSERT_EQ(group.front(), e1);
ASSERT_EQ(group.back(), e0);
}
TEST(OwningGroup, SignalRace) {
entt::registry registry;
registry.on_construct<double>().connect<&entt::registry::emplace_or_replace<int>>();
const auto group = registry.group<int>(entt::get<double>);
auto entity = registry.create();
registry.emplace<double>(entity);
ASSERT_EQ(group.size(), 1u);
}
TEST(OwningGroup, StableLateInitialization) {
entt::registry registry;
for(std::size_t i{}; i < 30u; ++i) {
auto entity = registry.create();
if(!(i % 2u)) registry.emplace<int>(entity);
if(!(i % 3u)) registry.emplace<char>(entity);
}
// thanks to @pgruenbacher for pointing out this corner case
ASSERT_EQ((registry.group<int, char>().size()), 5u);
}
TEST(OwningGroup, PreventEarlyOptOut) {
entt::registry registry;
registry.emplace<int>(registry.create(), 3);
const auto entity = registry.create();
registry.emplace<char>(entity, 'c');
registry.emplace<int>(entity, 2);
// thanks to @pgruenbacher for pointing out this corner case
registry.group<char, int>().each([entity](const auto entt, const auto &c, const auto &i) {
ASSERT_EQ(entity, entt);
ASSERT_EQ(c, 'c');
ASSERT_EQ(i, 2);
});
}
TEST(OwningGroup, SwappingValuesIsAllowed) {
entt::registry registry;
const auto group = registry.group<boxed_int>(entt::get<empty_type>);
for(std::size_t i{}; i < 2u; ++i) {
const auto entity = registry.create();
registry.emplace<boxed_int>(entity, static_cast<int>(i));
registry.emplace<empty_type>(entity);
}
registry.destroy(group.back());
// thanks to @andranik3949 for pointing out this missing test
registry.view<const boxed_int>().each([](const auto entity, const auto &value) {
ASSERT_EQ(static_cast<int>(entt::to_integral(entity)), value.value);
});
}
TEST(OwningGroup, ExtendedGet) {
using type = decltype(std::declval<entt::registry>().group<int, empty_type>(entt::get<char>).get({}));
static_assert(std::tuple_size_v<type> == 2u);
static_assert(std::is_same_v<std::tuple_element_t<0, type>, int &>);
static_assert(std::is_same_v<std::tuple_element_t<1, type>, char &>);
entt::registry registry;
const auto entity = registry.create();
registry.emplace<int>(entity, 42);
registry.emplace<char>(entity, 'c');
const auto tup = registry.group<int>(entt::get<char>).get(entity);
ASSERT_EQ(std::get<0>(tup), 42);
ASSERT_EQ(std::get<1>(tup), 'c');
}
TEST(OwningGroup, IterableGroupAlgorithmCompatibility) {
entt::registry registry;
const auto entity = registry.create();
registry.emplace<int>(entity);
registry.emplace<char>(entity);
const auto group = registry.group<int>(entt::get<char>);
const auto iterable = group.each();
const auto it = std::find_if(iterable.begin(), iterable.end(), [entity](auto args) { return std::get<0>(args) == entity; });
ASSERT_EQ(std::get<0>(*it), entity);
}
TEST(OwningGroup, Storage) {
entt::registry registry;
const auto entity = registry.create();
auto group = registry.group<int>(entt::get<const char>, entt::exclude<double, const float>);
static_assert(std::is_same_v<decltype(group.storage<0u>()), entt::storage_type_t<int> *>);
static_assert(std::is_same_v<decltype(group.storage<int>()), entt::storage_type_t<int> *>);
static_assert(std::is_same_v<decltype(group.storage<const int>()), entt::storage_type_t<int> *>);
static_assert(std::is_same_v<decltype(group.storage<1u>()), const entt::storage_type_t<char> *>);
static_assert(std::is_same_v<decltype(group.storage<char>()), const entt::storage_type_t<char> *>);
static_assert(std::is_same_v<decltype(group.storage<const char>()), const entt::storage_type_t<char> *>);
static_assert(std::is_same_v<decltype(group.storage<2u>()), entt::storage_type_t<double> *>);
static_assert(std::is_same_v<decltype(group.storage<double>()), entt::storage_type_t<double> *>);
static_assert(std::is_same_v<decltype(group.storage<const double>()), entt::storage_type_t<double> *>);
static_assert(std::is_same_v<decltype(group.storage<3u>()), const entt::storage_type_t<float> *>);
static_assert(std::is_same_v<decltype(group.storage<float>()), const entt::storage_type_t<float> *>);
static_assert(std::is_same_v<decltype(group.storage<const float>()), const entt::storage_type_t<float> *>);
ASSERT_TRUE(group);
ASSERT_NE(group.storage<int>(), nullptr);
ASSERT_NE(group.storage<1u>(), nullptr);
ASSERT_NE(group.storage<double>(), nullptr);
ASSERT_NE(group.storage<3u>(), nullptr);
ASSERT_EQ(group.size(), 0u);
group.storage<int>()->emplace(entity);
group.storage<double>()->emplace(entity);
registry.emplace<char>(entity);
registry.emplace<float>(entity);
ASSERT_EQ(group.size(), 0u);
ASSERT_EQ(group.begin(), group.end());
ASSERT_TRUE(group.storage<int>()->contains(entity));
ASSERT_TRUE(group.storage<const char>()->contains(entity));
ASSERT_TRUE(group.storage<double>()->contains(entity));
ASSERT_TRUE(group.storage<const float>()->contains(entity));
ASSERT_TRUE((registry.all_of<int, char, double, float>(entity)));
group.storage<double>()->erase(entity);
registry.erase<float>(entity);
ASSERT_EQ(group.size(), 1u);
ASSERT_NE(group.begin(), group.end());
ASSERT_TRUE(group.storage<const int>()->contains(entity));
ASSERT_TRUE(group.storage<char>()->contains(entity));
ASSERT_FALSE(group.storage<const double>()->contains(entity));
ASSERT_FALSE(group.storage<float>()->contains(entity));
ASSERT_TRUE((registry.all_of<int, char>(entity)));
ASSERT_FALSE((registry.any_of<double, float>(entity)));
group.storage<0u>()->erase(entity);
ASSERT_EQ(group.size(), 0u);
ASSERT_EQ(group.begin(), group.end());
ASSERT_FALSE(group.storage<0u>()->contains(entity));
ASSERT_TRUE(group.storage<1u>()->contains(entity));
ASSERT_FALSE(group.storage<2u>()->contains(entity));
ASSERT_FALSE(group.storage<3u>()->contains(entity));
ASSERT_TRUE((registry.all_of<char>(entity)));
ASSERT_FALSE((registry.any_of<int, double, float>(entity)));
group = {};
ASSERT_FALSE(group);
ASSERT_EQ(group.storage<0u>(), nullptr);
ASSERT_EQ(group.storage<const char>(), nullptr);
ASSERT_EQ(group.storage<2u>(), nullptr);
ASSERT_EQ(group.storage<const float>(), nullptr);
}
ENTT_DEBUG_TEST(OwningGroupDeathTest, Overlapping) {
entt::registry registry;
registry.group<char>(entt::get<int>, entt::exclude<double>);
ASSERT_DEATH((registry.group<char, float>(entt::get<float>, entt::exclude<double>)), "");
ASSERT_DEATH(registry.group<char>(entt::get<int, float>, entt::exclude<double>), "");
ASSERT_DEATH(registry.group<char>(entt::get<int>, entt::exclude<double, float>), "");
}