Compare commits

..

1298 Commits

Author SHA1 Message Date
Michele Caini
7821307565 update single include file 2024-04-10 08:52:08 +02:00
Michele Caini
27efd6fdf4 snapshot: suppress a warning by clang 2024-04-10 08:29:17 +02:00
Michele Caini
4dc3f650e8 snapshot: full support to stable types - close #1118 2024-04-10 08:29:14 +02:00
Michele Caini
d5a2e353cd sigh_mixin: avoid using weak ranges twice - close #1123 2024-04-10 08:29:10 +02:00
Michele Caini
3fd8b84d04 now working on version v3.13.2 2024-04-10 08:28:47 +02:00
Michele Caini
2909e7ab1f update single include file 2024-02-02 10:09:52 +01:00
Ezekiel Warren
60703f6845 build: don't install include/BUILD.bazel (#1107) 2024-01-22 09:55:58 +01:00
Michele Caini
062c95fc11 test: avoid warnings due to unused variables 2024-01-19 14:35:45 +01:00
Michele Caini
15ce55bc60 group: avoid swapping observed types - close #1100 2024-01-19 14:23:02 +01:00
Michele Caini
2d3fa3e51a snapshot: refine the check in the constructor of the loader class - close #1106 2024-01-19 09:22:58 +01:00
Michele Caini
7f49768739 now working on version v3.13.1 2024-01-19 08:52:00 +01:00
Michele Caini
fedcb920ce update single include file 2024-01-16 15:55:41 +01:00
Michele Caini
6e0c2baf9d test: drop a bunch of NOLINT 2024-01-15 14:26:23 +01:00
Michele Caini
20aa62c584 test: drop a bunch of NOLINT 2024-01-15 14:26:19 +01:00
Michele Caini
9fbd167bd0 test: make all compilers happy again 2024-01-15 10:04:14 +01:00
Michele Caini
2562bf76f6 test: drop a bunch of NOLINT 2024-01-15 09:26:51 +01:00
Michele Caini
b5ac3c5f41 test: drop a bunch of NOLINT 2024-01-15 09:26:46 +01:00
Michele Caini
7af8d015bc test: drop a bunch of NOLINT 2024-01-12 16:11:20 +01:00
Michele Caini
f01fe06b7a test: drop a bunch of NOLINT 2024-01-12 16:11:16 +01:00
Michele Caini
5618fd9549 test: drop a bunch of NOLINT 2024-01-12 11:34:38 +01:00
Michele Caini
76397ad2e6 test: drop a bunch of NOLINT 2024-01-12 11:34:30 +01:00
Michele Caini
6ca1bca26c test: drop a bunch of NOLINT 2024-01-12 11:34:21 +01:00
Michele Caini
b0b5b2ec4d test: drop a bunch of NOLINT 2024-01-12 10:35:32 +01:00
Michele Caini
1c460130a5 test: drop a bunch of NOLINT 2024-01-12 10:35:10 +01:00
Michele Caini
31b2a2129c test: drop a bunch of NOLINT 2024-01-12 10:34:41 +01:00
Michele Caini
f3a6fdbc02 test: linter utilities 2024-01-12 10:34:11 +01:00
Michele Caini
f6232603c4 test: minor changes 2024-01-12 10:33:50 +01:00
Michele Caini
15d63ec358 test: drop a bunch of NOLINT 2024-01-11 14:11:43 +01:00
Michele Caini
9800bfa680 test: drop a bunch of NOLINT 2024-01-11 14:11:31 +01:00
Michele Caini
6b91bbab28 test: drop a bunch of NOLINT 2024-01-11 14:11:13 +01:00
Michele Caini
b7e929b0cc test: drop a bunch of NOLINT 2024-01-11 14:11:01 +01:00
Michele Caini
f38ab24b7a test: drop a bunch of NOLINT 2024-01-11 14:10:47 +01:00
Michele Caini
b698e53131 test: drop a bunch of NOLINT 2024-01-11 14:10:39 +01:00
Michele Caini
346165e4f9 test: drop a bunch of NOLINT 2024-01-11 14:10:26 +01:00
Michele Caini
9708727269 test: drop a bunch of NOLINT 2024-01-11 14:09:51 +01:00
Michele Caini
0f6e1d8aef test: drop a bunch of NOLINT 2024-01-10 17:44:34 +01:00
Michele Caini
51932b4459 linter: cleanup clang-tidy config 2024-01-10 17:35:51 +01:00
Michele Caini
2b092c2fce test: prepare to cleanup clang-tidy config and track things to review in future 2024-01-10 17:35:27 +01:00
Michele Caini
b152c63dd6 test: suppress linter warnings on reinterpret_cast 2024-01-09 19:28:46 +01:00
Michele Caini
f920a84ac0 test: suppress non-const global variable warnings by the linter 2024-01-09 16:26:34 +01:00
Michele Caini
4e9cc9773d *: updated TODO 2024-01-09 16:25:53 +01:00
Michele Caini
4d32c1ba58 test: avoid non-const global variables 2024-01-09 12:38:01 +01:00
Michele Caini
2776f9bad9 test: avoid using C-style arrays 2024-01-09 11:36:19 +01:00
Michele Caini
fc381b72d3 test: minor changes 2024-01-09 11:36:11 +01:00
Michele Caini
a77a223223 test: avoid using C-style arrays 2024-01-09 11:35:51 +01:00
Michele Caini
b249fa252d *: updated TODO 2024-01-09 10:33:36 +01:00
Michele Caini
b32b8002d3 test: drop unused variable 2024-01-09 10:33:22 +01:00
Michele Caini
8177f8934c test: reintroduce meaningful checks behind a NOLINT directive 2024-01-09 10:32:40 +01:00
Michele Caini
ac4c38a10a test: reintroduce meaningful checks behind a NOLINT directive 2024-01-09 10:31:19 +01:00
Michele Caini
854dedfda0 test: reintroduce meaningful checks behind a NOLINT directive 2024-01-09 10:29:50 +01:00
Michele Caini
52f8e6db30 test: improve code coverage on deprecated function 2024-01-08 15:23:49 +01:00
Michele Caini
140d0dab6a test: drop unnecessary checks 2024-01-08 15:08:29 +01:00
Michele Caini
1ff2db8aa2 test: drop unnecessary checks 2024-01-08 14:25:03 +01:00
Michele Caini
34f700fcca test: suppress linter false warnings 2024-01-08 14:18:36 +01:00
Michele Caini
8c53e0514e test: avoid unnecessary NOLINT 2024-01-08 13:18:15 +01:00
Michele Caini
b6cea84910 test: avoid using ASSERT_NO_FATAL_FAILURE improperly 2024-01-08 12:42:13 +01:00
Michele Caini
f4d9a7729f test: drop unnecessary checks 2024-01-08 12:09:20 +01:00
Michele Caini
47ab69f394 test: drop unnecessary NOLINT 2024-01-08 11:43:57 +01:00
Michele Caini
aab9277d8d build: temporary workaround to a GH issue with C++20 2024-01-08 10:32:53 +01:00
Michele Caini
6d21fdcc78 *: updated TODO 2024-01-08 08:52:49 +01:00
Michele Caini
1d9f771af3 test: avoid using deprecated functions 2024-01-01 17:11:09 +01:00
Michele Caini
18330eb330 test: avoid using deprecated functions 2024-01-01 16:59:10 +01:00
Michele Caini
6f38fdaea6 *: updated TODO 2024-01-01 16:40:27 +01:00
Michele Caini
bd75e33694 test: refine linter directives 2024-01-01 16:40:20 +01:00
Michele Caini
010f0b9402 storage: avoid using base_type::at internally 2024-01-01 16:39:51 +01:00
Michele Caini
776c541d1c saprse_set: deprecate .at as ambiguous 2024-01-01 16:39:33 +01:00
Michele Caini
f20abd5767 test: suppress linter warnings on deprecated functions 2024-01-01 16:02:53 +01:00
Michele Caini
8cb0c8802d *: updated TODO 2024-01-01 15:46:19 +01:00
Michele Caini
a4a0abdbd6 view: avoid forcing non-integral entity types 2024-01-01 15:46:07 +01:00
Michele Caini
4bd0f26a26 doc: a few links 2024-01-01 10:55:41 +01:00
Michele Caini
5ea8f0382b test: NOLINT review 2023-12-23 18:01:35 +01:00
Michele Caini
8beaea89f2 test: comment expected NOLINT for deprecated feature 2023-12-23 17:49:43 +01:00
Michele Caini
433fca98a3 test: minor changes (code coverage purposes) 2023-12-23 17:49:04 +01:00
Michele Caini
24aac47ce5 test: delete unused functions (code coverage purposes) 2023-12-23 15:45:32 +01:00
Michele Caini
d5518c63e9 test: avoid non-private member variables in classes (linter) 2023-12-22 16:34:24 +01:00
Michele Caini
4a06c41a33 test: minor changes 2023-12-22 14:42:05 +01:00
Michele Caini
4d63ac5c88 *: updated TODO 2023-12-22 14:31:00 +01:00
Michele Caini
6e31855717 build: work-in-progress clang-tidy config 2023-12-22 14:30:46 +01:00
Michele Caini
76e668034c test: special member functions warnings (linter) 2023-12-22 14:30:17 +01:00
Michele Caini
28424709d5 sparse_set: refine assure_at_least to better support non-enum identifiers 2023-12-22 14:30:02 +01:00
Michele Caini
fc3df2b0ac test: special member functions warnings (linter) 2023-12-22 14:29:33 +01:00
Michele Caini
27698a2a0a test: use default member init as needed 2023-12-21 10:21:08 +01:00
Michele Caini
9b7aa49adc test: use equals default as needed 2023-12-21 09:45:31 +01:00
Michele Caini
8022cf4200 test: avoid confusable identifiers 2023-12-21 08:59:26 +01:00
Michele Caini
bd06d384e0 test: try to make all compilers happy again 2023-12-21 08:22:01 +01:00
Michele Caini
17913a7a14 test: avoid uninitialized variables 2023-12-20 17:30:19 +01:00
Michele Caini
ef914bad8c test: avoid static global functions 2023-12-20 14:04:20 +01:00
Michele Caini
867656f549 test: use transparent functors 2023-12-20 10:34:54 +01:00
Michele Caini
6e665b27bf test: handle linter warning on string literals 2023-12-20 10:08:13 +01:00
Michele Caini
f34dbcd7d8 test: handle linter warning due to unnecessary copy 2023-12-20 09:49:16 +01:00
Michele Caini
6b608f51fb test: avoid moving trivially copyable types 2023-12-20 09:19:19 +01:00
Michele Caini
cbf853fa75 test: comment expected NOLINT for deprecated feature 2023-12-20 09:16:22 +01:00
Michele Caini
14450df1ee test: more by the linter (work in progress) 2023-12-19 18:59:40 +01:00
Michele Caini
27a46a97a6 test: linter related stuff 2023-12-19 18:58:47 +01:00
Michele Caini
d2344e8e62 hashed_string: linter related stuff 2023-12-19 11:51:29 +01:00
Michele Caini
4b5a6cd2c6 test: minor changes 2023-12-19 11:25:53 +01:00
Michele Caini
f3ce121be3 test: try to make all compilers happy at once 2023-12-19 11:21:10 +01:00
Michele Caini
eef8b42c77 test: make gcc happy again 2023-12-19 11:13:10 +01:00
Michele Caini
03551c242c *: updated TODO 2023-12-19 11:06:48 +01:00
Michele Caini
f5a8feb47f meta: linter related stuff 2023-12-19 11:06:24 +01:00
Michele Caini
3208281d94 test: [[nodiscard]], const-correctness and other linter related stuff 2023-12-19 11:06:08 +01:00
Michele Caini
2271fb8c0f *: updated TODO 2023-12-19 11:05:51 +01:00
Michele Caini
feb0bb02ae test: minor changes 2023-12-19 11:05:23 +01:00
Michele Caini
6c99ca50e7 test: minor changes 2023-12-19 11:05:08 +01:00
Michele Caini
cadcd9ce43 test: [[nodiscard]], const-correctness and other linter related stuff 2023-12-19 11:04:55 +01:00
Michele Caini
0bf1817b9a test: const-correctness and other linter related stuff 2023-12-19 11:04:21 +01:00
Michele Caini
cd887237b8 test: const-correctness and other linter related stuff 2023-12-19 11:04:07 +01:00
Michele Caini
218cb02492 test: [[nodiscard]] as needed 2023-12-19 11:03:47 +01:00
Michele Caini
5a2be7d6eb test: [[nodiscard]] and other linter related stuff 2023-12-19 11:03:25 +01:00
Michele Caini
c6f3a5408e test: [[nodiscard]], const-correctness and other linter related stuff 2023-12-19 10:58:43 +01:00
Michele Caini
b6d0921177 registry: use sort_as rather than pack 2023-12-19 10:53:19 +01:00
Michele Caini
6ac7a02172 test: const-correctness and other linter related stuff 2023-12-19 10:23:45 +01:00
Michele Caini
a35612a4f8 test: const-correctness and other linter related stuff 2023-12-19 10:23:11 +01:00
Michele Caini
4d9ff926b6 test: [[nodiscard]], const-correctness and other linter related stuff 2023-12-18 17:48:16 +01:00
Michele Caini
636749eff3 test: const correctness 2023-12-18 14:34:01 +01:00
Michele Caini
685467acba test: claenup (and use static_assert as needed) 2023-12-18 14:33:48 +01:00
Michele Caini
9605af3f54 test: const correctness 2023-12-18 14:33:01 +01:00
Michele Caini
a3db63342d test: minor changes 2023-12-18 14:32:48 +01:00
Michele Caini
57ec8d9ca3 test: const correctness 2023-12-18 14:32:33 +01:00
Michele Caini
f11d93ab25 test: const correctness (and [[nodiscard]] as needed) 2023-12-18 14:32:18 +01:00
Michele Caini
b3489857ad test: const correctness (and [[nodiscard]] as needed) 2023-12-18 14:32:06 +01:00
Michele Caini
205e21e313 doc: minor changes 2023-12-18 14:13:05 +01:00
Michele Caini
cd59efab8f test: const-correctness (and [[nodiscard]] as needed) 2023-12-18 14:00:28 +01:00
Michele Caini
72304e6781 test: const-correctness (and [[maybe_unused] as needed) 2023-12-18 14:00:04 +01:00
Michele Caini
bdd213009c test: const-correctness 2023-12-18 13:59:34 +01:00
Michele Caini
5f576667d3 doc: cleanup 2023-12-18 12:24:00 +01:00
Michele Caini
1ec2f3eeb8 type_traits: avoid redefining type again and again 2023-12-18 10:02:48 +01:00
Michele Caini
b1a5b4b982 test: cleanup 2023-12-17 12:52:39 +01:00
Michele Caini
9e4f40fee6 test: minor changes 2023-12-15 16:46:27 +01:00
Michele Caini
fa0add3c02 test: avoid using a moved from vector 2023-12-15 16:46:00 +01:00
Michele Caini
937ed81fa2 dispatcher: [[nodiscard]] 2023-12-15 16:38:46 +01:00
Michele Caini
daa83a1885 registry: avoid shadow warnings with gcc 2023-12-15 12:13:49 +01:00
Michele Caini
eb290d8bd4 *: updated TODO 2023-12-15 11:59:12 +01:00
Michele Caini
dad6061cbb view: minor changes 2023-12-15 11:49:54 +01:00
Michele Caini
07777c78aa view: back to the plain pointer model for internal functions 2023-12-15 11:40:59 +01:00
Michele Caini
7a12dc6533 view: refine internal functions 2023-12-15 11:29:25 +01:00
Michele Caini
80c8c76fdb view: use more explicit types on internal functions 2023-12-15 09:00:09 +01:00
Michele Caini
1931ade325 view: drop check set as a whole 2023-12-15 08:54:28 +01:00
Michele Caini
2e264dffb3 view: index-based view iterator (no check set required) 2023-12-15 08:54:18 +01:00
Michele Caini
09197a91a0 view: prepare to drop check set 2023-12-15 08:42:30 +01:00
Michele Caini
70053f9301 core: minimize compilation cost of type_list_unique 2023-12-15 08:15:45 +01:00
Michele Caini
91a193141f view: proper ctors for multi type base view 2023-12-14 14:47:50 +01:00
Michele Caini
5d8a1e3ee1 view: proper ctors for single type base view 2023-12-14 14:47:39 +01:00
Michele Caini
9c85cd8d55 view: prepare to optimize further view definitions 2023-12-14 09:42:04 +01:00
Ezekiel Warren
1fb1027036 build system: use env var for bazel version in CI (#1098) 2023-12-14 09:16:11 +01:00
Michele Caini
bdea65d500 build: updated action versions 2023-12-13 12:35:54 +01:00
Michele Caini
88f65c8d5d doc: updated README file 2023-12-13 12:35:26 +01:00
Samuel Dowling
8355f12398 doc: add conan badge to README (#1096) 2023-12-13 12:22:28 +01:00
Michele Caini
f3120f7e7d view: drop duplicated function 2023-12-13 12:00:20 +01:00
Mojert
a74b1b8220 doc: include FetchContent as needed (#1097) 2023-12-13 11:57:28 +01:00
Michele Caini
b81e40e07e signal: doc review 2023-12-12 14:15:07 +01:00
Michele Caini
e4df106417 resource: doc review 2023-12-12 14:15:02 +01:00
Michele Caini
d0a16cd852 process: doc review 2023-12-12 14:14:53 +01:00
Michele Caini
b274d49b3f platform: doc review 2023-12-12 14:14:48 +01:00
Michele Caini
3d9651b783 meta: doc review 2023-12-12 14:14:42 +01:00
Michele Caini
dfd7cb90d0 graph: doc review 2023-12-12 14:14:36 +01:00
Michele Caini
c33bee892d entity: doc review 2023-12-12 14:14:30 +01:00
Michele Caini
a986f68e45 core: doc review 2023-12-12 14:14:20 +01:00
Michele Caini
c0f1a691f9 container: doc review 2023-12-12 14:14:12 +01:00
Michele Caini
711e82b688 view: suppress doc warning 2023-12-12 14:14:01 +01:00
Michele Caini
5190db0222 registry: explicit iterable and const_iterable types 2023-12-11 12:14:41 +01:00
Michele Caini
b00830d382 registry: refine ::assure (const and non-const) 2023-12-11 12:09:16 +01:00
Michele Caini
cd24c0aac5 view: use a name that doesn't drive me crazy when I search for it 2023-12-07 16:56:03 +01:00
Michele Caini
a927d6db07 view: use the right len for the check array 2023-12-06 10:01:47 +01:00
Michele Caini
294a606cf9 *: updated TODO 2023-12-06 09:13:04 +01:00
Michele Caini
735db1e6af view: shared implementation for common views 2023-12-06 09:12:57 +01:00
Michele Caini
3ec5e81d81 view: backward compat/coverage purposes 2023-12-05 17:08:16 +01:00
Michele Caini
4664d0601f view: explicit trivial return types 2023-12-05 15:02:25 +01:00
Michele Caini
43fcc94682 view: minor changes 2023-12-05 15:01:12 +01:00
Michele Caini
4cdff43781 view: shared implementation for storage views 2023-12-05 15:00:14 +01:00
Michele Caini
9d23522086 *: updated TODO 2023-12-05 14:46:19 +01:00
Michele Caini
feed10c9f6 view: avoid calculating the check set again and again 2023-12-04 08:54:37 +01:00
Michele Caini
9732092c0f view: improved validity check 2023-12-03 23:23:22 +01:00
Michele Caini
aa46b2504c *: updated TODO 2023-12-01 15:53:58 +01:00
Michele Caini
c3e36e7148 build: prepare for tools 2023-12-01 15:24:37 +01:00
Michele Caini
c912b85f6e test: reserved bits example - see #888 2023-12-01 15:24:27 +01:00
Michele Caini
4f700f90ec natvis: update snippets for storage/sparse set 2023-12-01 09:01:12 +01:00
Michele Caini
f2c417435c test: another couple of changes to make all compilers happy :) 2023-11-30 15:19:51 +01:00
Michele Caini
48c374be2a test: try to make all compilers happy again 2023-11-30 15:16:57 +01:00
Michele Caini
4cb4553796 *: updated TODO 2023-11-30 15:13:34 +01:00
Michele Caini
3db20d0aa5 entity: fully support reserved bits on identifiers 2023-11-30 15:13:15 +01:00
Michele Caini
9e62e1dab6 sparse_set: ::contains uses the right version mask 2023-11-30 15:12:47 +01:00
Michele Caini
42529d0ab2 doc: minor changes 2023-11-30 11:42:18 +01:00
Michele Caini
637d6bbabc natvis: drop unused intrinsic 2023-11-29 17:30:40 +01:00
Michele Caini
f8eba58e19 test: avoid using registry if not needed 2023-11-29 16:13:48 +01:00
Michele Caini
d58c5766bc test: use template keyword with dependent template name 2023-11-29 13:06:41 +01:00
Michele Caini
5b2c136a7c entity: avoid unnecessary operations 2023-11-29 12:59:39 +01:00
Michele Caini
8c532fb377 test: more on entity traits 2023-11-28 16:56:08 +01:00
Michele Caini
c5552d85c3 *: updated TODO 2023-11-28 08:04:28 +01:00
Michele Caini
d5c162e6a0 test: more on view 2023-11-28 08:04:08 +01:00
Michele Caini
95b73d94f7 storage: minor changes 2023-11-28 08:03:53 +01:00
Michele Caini
9f80b14f88 meta: avoid duplicating data in meta containers 2023-11-27 15:13:59 +01:00
Michele Caini
1a6565d5f9 meta: review meta containers' begin/end functions 2023-11-27 15:13:29 +01:00
Michele Caini
9c60708203 meta: drop unnecessary remove_reference_t 2023-11-27 08:25:11 +01:00
Michele Caini
e0cac58500 meta: avoid indirections due to any in meta containers 2023-11-27 08:18:33 +01:00
Michele Caini
60e2557c45 meta_range_iterator: iterator_concept 2023-11-26 10:17:42 +01:00
Michele Caini
228066c1f6 cache: iterator_concept 2023-11-26 10:17:26 +01:00
Michele Caini
0744937811 adjacency_matrix: iterator_concept 2023-11-26 10:17:18 +01:00
Michele Caini
a383c0c0a5 storage: iterator_concept 2023-11-26 10:17:01 +01:00
Michele Caini
b6da31d933 registry: iterator_concept 2023-11-26 10:16:52 +01:00
Michele Caini
5985531d9e handle: iterator_concept 2023-11-26 10:16:42 +01:00
Michele Caini
847099932d group: iterator_concept 2023-11-26 10:16:33 +01:00
Michele Caini
d43416102b dense_map: iterator_concept 2023-11-26 10:16:20 +01:00
Michele Caini
9e73c3c44a meta: iterator_concept 2023-11-26 10:16:03 +01:00
Michele Caini
d152153547 view: iterator_concept - close #1090 2023-11-23 18:01:44 +01:00
Michele Caini
5016dd8bb0 test: try to make gcc happy again 2023-11-23 15:21:34 +01:00
Michele Caini
a3fdc96b0d container: minor changes 2023-11-23 14:58:21 +01:00
Michele Caini
b5fbe50fa1 doc: updated FAQs - close #1063 2023-11-23 14:34:11 +01:00
Michele Caini
e9039ece28 meta: drop [[maybe_unused]] 2023-11-22 15:48:07 +01:00
Michele Caini
60e8763fe8 *: updated TODO 2023-11-22 10:15:47 +01:00
Michele Caini
e3475c026c mixin: make sigh mixin work with custom registry types - close #1079 2023-11-22 10:15:17 +01:00
Michele Caini
24c95e493f meta: internal changes to make things easier to maintain and evolve 2023-11-21 14:35:40 +01:00
Michele Caini
8ce80145de test: minor changes 2023-11-20 16:47:26 +01:00
Michele Caini
838d0e5f76 doc: updated README 2023-11-20 16:46:44 +01:00
Michele Caini
7fecbf7e03 doc: minor changes 2023-11-17 16:26:45 +01:00
Michele Caini
e04f2bdb24 *: coding style 2023-11-17 16:26:21 +01:00
Michele Caini
c26a24e713 test: static-less throwing allocator 2023-11-17 15:51:08 +01:00
Michele Caini
c785260783 test: continue the internal rework 2023-11-17 09:06:28 +01:00
Michele Caini
8372292841 flow: correctly forward allocator to members 2023-11-16 18:13:52 +01:00
Michele Caini
2ce6317c9a test: code coverage for scheduler class 2023-11-16 11:03:56 +01:00
Michele Caini
16d43d2975 scheduler: allocator-aware implementation 2023-11-16 11:03:36 +01:00
Michele Caini
d7d8752a84 test: minor changes 2023-11-16 11:02:04 +01:00
Michele Caini
339df18c06 dispatcher: minor changes 2023-11-15 17:29:20 +01:00
Michele Caini
757eb5f10c scheduler: prepare to support allocators 2023-11-15 17:29:03 +01:00
Michele Caini
580f1edb91 *: updated TODO 2023-11-15 10:11:13 +01:00
Michele Caini
6e03ca088e doc: updated scheduler doc 2023-11-15 10:10:16 +01:00
Michele Caini
c75a4804da test: minor changes 2023-11-15 10:06:12 +01:00
Michele Caini
3487196d0d scheduler: redesign basic scheduler 2023-11-15 10:06:02 +01:00
Michele Caini
adbcc8dca9 process: minor changes 2023-11-15 10:04:46 +01:00
Michele Caini
8b79ee586a test: suppress conversion warnings 2023-11-14 14:27:20 +01:00
Michele Caini
abfd8e035c test: minor changes to utility class 2023-11-14 14:26:36 +01:00
Michele Caini
9bad12064c scheduler: avoid handling ::next in the update function 2023-11-14 08:54:19 +01:00
Michele Caini
a5608bf7bb process: prepare to redesign scheduler 2023-11-14 08:53:27 +01:00
Michele Caini
b84a4bed29 test: minor changes 2023-11-13 10:52:26 +01:00
Michele Caini
94582777e8 test: minor changes 2023-11-13 10:52:10 +01:00
Michele Caini
c2b5be0836 test: minor changes 2023-11-13 10:51:50 +01:00
Michele Caini
8e13ede4d5 test: simplify/cleanup throwing_type 2023-11-13 09:10:00 +01:00
Michele Caini
857d70528d test: minor changes 2023-11-10 15:23:58 +01:00
Michele Caini
b16e4c1ec7 doc: minor changes 2023-11-10 12:43:46 +01:00
Michele Caini
b8b41b3ac7 meta: minor changes for code readability 2023-11-10 12:02:05 +01:00
Michele Caini
f13dcae6d3 test: make toolset v141 happy again 2023-11-10 11:53:28 +01:00
Michele Caini
1724e8cfeb test: internal rework 2023-11-10 10:28:12 +01:00
Michele Caini
e53601e860 view: avoid taking decisions on page_size 2023-11-10 10:27:36 +01:00
Michele Caini
5e8544cfc6 storage: void returned value type for storage entity 2023-11-10 10:25:47 +01:00
Michele Caini
dc184c10e2 snapshot: avoid using page_size directly 2023-11-09 15:37:22 +01:00
Michele Caini
f89e8560a3 doc: nth_argument[_t] 2023-11-09 14:50:51 +01:00
Michele Caini
f4a54cc0da type_traits: type based nth_argument[_t] 2023-11-09 14:50:00 +01:00
Michele Caini
6aa4c770e6 test: use shared custom_entity type 2023-11-08 12:38:36 +01:00
Michele Caini
afe942ef2e test: use shared custom_entity type 2023-11-08 12:38:16 +01:00
Michele Caini
9b4c920d1f test: shared custom_entity type 2023-11-08 12:37:54 +01:00
Michele Caini
00d0ff2e45 registry: use the right type within assure - close #1088 2023-11-08 10:15:07 +01:00
Michele Caini
3f5b6a1693 test: sigh mixin ::pop_all 2023-11-08 09:44:05 +01:00
Michele Caini
d98821e976 doc: add EnTT Editor to links.md :) 2023-11-07 13:53:39 +01:00
Michele Caini
223c8ab9b9 doc: add robotfindskitten to links.md :) 2023-11-07 13:53:22 +01:00
Michele Caini
76a6a22527 test: minor changes 2023-11-07 08:58:40 +01:00
Michele Caini
ee7e025680 test: increase coverage for handle class 2023-11-07 08:58:23 +01:00
Michele Caini
68125c4187 test: scoped include 2023-11-06 15:38:42 +01:00
Michele Caini
fd1abc952f test: iwyu docet 2023-11-06 14:46:24 +01:00
Michele Caini
5c04c872c5 test: iwyu docet 2023-11-06 14:46:00 +01:00
Michele Caini
8405a78bd8 test: use plain integral counter to test sigh mixin 2023-11-06 14:45:32 +01:00
Michele Caini
0e4f5a6870 test: use shared empty type 2023-11-06 08:55:32 +01:00
Michele Caini
a4f2ba1ab8 test: use shared empty type 2023-11-06 08:52:21 +01:00
Michele Caini
3c8ddbb0a5 test: use shared empty type 2023-11-06 08:52:05 +01:00
Michele Caini
1dbe025d36 test: use shared empty type 2023-11-06 08:51:56 +01:00
Michele Caini
16d55c9f59 test: use shared empty type 2023-11-06 08:51:46 +01:00
Michele Caini
f3bd033bf6 test: use shared empty type 2023-11-06 08:51:03 +01:00
Michele Caini
ba87e22af0 test: use shared empty type 2023-11-06 08:50:48 +01:00
Michele Caini
9d2bbac695 test: use shared empty type 2023-11-06 08:50:20 +01:00
Michele Caini
5e0f4e6ef0 test: use shared empty type 2023-11-06 08:50:00 +01:00
Michele Caini
9a362b39bf test: shared empty type 2023-11-06 08:49:37 +01:00
Michele Caini
fb624bab50 test: minor changes 2023-11-05 19:56:29 +01:00
Michele Caini
e0661a55f6 iwyu: analyzer docet :) 2023-11-05 17:12:53 +01:00
Michele Caini
1773cc9eb5 iwyu: comments/sections 2023-11-04 12:48:12 +01:00
Michele Caini
dd7c8f2ad6 iwyu: disambiguate . 2023-11-04 12:47:55 +01:00
Michele Caini
592ff17c5f test: cleanup 2023-11-03 14:46:04 +01:00
Michele Caini
6d6d83afdb test: use shared non_movable type 2023-11-03 12:51:48 +01:00
Michele Caini
134640411a test: use shared non_movable type 2023-11-03 12:49:11 +01:00
Michele Caini
6e1a5c0353 test: shared non_movable type 2023-11-03 12:48:40 +01:00
Michele Caini
b1a884f898 test: typo 2023-11-03 12:46:19 +01:00
Michele Caini
1717b3683c test: use shared non_comparable type 2023-11-03 12:45:51 +01:00
Michele Caini
a00f9e5f77 test: use shared non_comparable type 2023-11-03 12:44:47 +01:00
Michele Caini
e210c31bf2 test: use shared non_comparable type 2023-11-03 12:43:58 +01:00
Michele Caini
93094740b2 test: shared non_comparable type 2023-11-03 12:43:18 +01:00
Michele Caini
8ee3c333fb test: updated tests for allocate_unique 2023-11-03 12:18:59 +01:00
Michele Caini
302b246475 test: static-less shared throwing_type 2023-11-03 12:04:48 +01:00
Michele Caini
90b9186fd6 test: prepare to get rid of static variables in the shared types 2023-11-03 11:57:00 +01:00
Michele Caini
29e2f13e1e test: prepare to get rid of static variables in the shared types 2023-11-03 11:56:44 +01:00
Michele Caini
3d9da1dbce test: refine throwing_type (prepare to get rid of static variables) 2023-11-03 11:52:00 +01:00
Michele Caini
62a13526c9 registry: try_get should not create storage 2023-11-03 09:36:29 +01:00
Michele Caini
4dcc0e8666 test: reduce the tests for aggregate types to what is needed 2023-11-02 16:57:55 +01:00
Michele Caini
7e98afe6fe test: small cleanup 2023-11-02 15:58:30 +01:00
Michele Caini
e835bfbec7 test: use unique_ptr as move-only type 2023-11-02 15:06:04 +01:00
Michele Caini
577ff97980 test: use shared aggregate type 2023-11-02 15:05:36 +01:00
Michele Caini
76516e0320 test: cleanup 2023-11-02 15:05:16 +01:00
Michele Caini
385155f043 test: use shared aggregate type 2023-11-02 15:04:57 +01:00
Michele Caini
29b2552cf2 test: use shared aggregate type 2023-11-02 15:04:46 +01:00
Michele Caini
ee1e1a4cd5 test: shared aggregate type 2023-11-02 15:04:20 +01:00
Michele Caini
a0c85add5b test: use shared boxed_int type 2023-11-02 08:32:14 +01:00
Michele Caini
94472a6782 test: use shared boxed_int type 2023-11-02 08:30:45 +01:00
Michele Caini
cdc47832ef test: use shared boxed_int type 2023-11-02 08:30:04 +01:00
Michele Caini
acae98324b test: shared boxed_int type 2023-11-02 08:29:37 +01:00
Michele Caini
4786178705 test: increase code coverage (const overload) 2023-11-01 22:51:35 +01:00
Michele Caini
2e375a4482 test: drop unused header 2023-11-01 22:51:07 +01:00
Michele Caini
e58735a6fd test: use shared transparent_equal_to type 2023-11-01 22:50:56 +01:00
Michele Caini
0f1fff7ac8 test: use shared transparent_equal_to type 2023-11-01 22:50:49 +01:00
Michele Caini
0ef43f5d57 test: shared transparent_equal_to type 2023-11-01 22:50:37 +01:00
Michele Caini
7a2c5753c5 type_traits: try to please older compilers too 2023-10-31 15:41:24 +01:00
Michele Caini
2a3ae06d85 type_traits: thanks msvc for accepting invalid code, love it 2023-10-31 14:31:00 +01:00
Michele Caini
f009d99e5e type_traits: further improve and cleanup is_equality_comparable 2023-10-31 14:26:32 +01:00
Michele Caini
206d2f623d type_traits: further refine is_equality_comparable[_v] to avoid specializations 2023-10-31 14:08:38 +01:00
Michele Caini
273035ff49 type_traits: improve is_equality_comparable[_v] 2023-10-31 14:02:34 +01:00
Michele Caini
3540fbab74 test: sigh mixin test review (in progress) 2023-10-31 09:23:59 +01:00
Michele Caini
4983d40358 test: use shared non default constructible type 2023-10-31 09:18:57 +01:00
Michele Caini
fd970971af test: use shared non default constructible type 2023-10-31 09:18:51 +01:00
Michele Caini
c8a0701597 test: use shared non default constructible type 2023-10-31 09:18:36 +01:00
Michele Caini
ad99c4eb14 test: shared non default constructible type 2023-10-31 09:18:13 +01:00
Michele Caini
67d7b0080e test: use shared pointer stable type 2023-10-31 09:10:58 +01:00
Michele Caini
2bf66e5b42 test: use shared pointer stable type 2023-10-31 09:10:39 +01:00
Michele Caini
88d0b5a17d test: use shared pointer stable type 2023-10-31 09:10:15 +01:00
Michele Caini
c042e3d985 test: use shared pointer stable type 2023-10-31 09:10:01 +01:00
Michele Caini
2defe1c2c4 test: use shared pointer stable type 2023-10-31 09:09:37 +01:00
Michele Caini
0bc946b333 test: shared pointer stable type 2023-10-31 09:09:20 +01:00
Michele Caini
98dd0fbad7 test: start sigh mixin test review 2023-10-30 13:58:17 +01:00
Michele Caini
4bb37f7e25 test: drop unnecessary test 2023-10-30 13:54:27 +01:00
Michele Caini
23703f5c01 test: iwyu docet :) 2023-10-30 13:51:30 +01:00
Michele Caini
215a22f425 test: avoid shadow warnings 2023-10-30 12:24:16 +01:00
Michele Caini
e742fe7bf9 test: minor changes 2023-10-29 22:59:41 +01:00
Michele Caini
57d1754bc8 test: cleanup 2023-10-29 22:56:38 +01:00
Michele Caini
b95bb9bedb test: minor changes 2023-10-29 22:51:23 +01:00
Michele Caini
39f6349878 mixin: use if constexpr as it should be 2023-10-29 22:27:49 +01:00
Michele Caini
678131429b test: minor changes 2023-10-29 16:36:43 +01:00
Michele Caini
bb70e8b14e view: drop filter_as_tuple 2023-10-27 18:30:33 +02:00
Michele Caini
b9c86b3f99 view: further reduce instantiations due to filters 2023-10-27 18:29:57 +02:00
MiloWang
0ec50842a7 doc: update unreal.md (#1085) 2023-10-27 18:29:06 +02:00
Michele Caini
a678133e47 view: try to also please gcc 2023-10-26 14:28:36 +02:00
Michele Caini
b0a5f4f3e8 natvis: updated snippet for basic_view 2023-10-26 12:46:51 +02:00
Michele Caini
642f4e70b5 view: view_pack review to cleanup single type views 2023-10-26 12:44:48 +02:00
Michele Caini
efd03711cc config: provide coverage for user defined ENTT_ID_TYPE in the std namespace 2023-10-25 17:01:56 +02:00
Michele Caini
4c067ee13d meta: suppress warnings due to implicit conversions - see #394 2023-10-25 16:51:28 +02:00
Michele Caini
5cb7fc2610 test: missing include 2023-10-25 14:17:43 +02:00
Michele Caini
05ba05393c test: storage entity and sorting functions 2023-10-25 08:25:03 +02:00
Michele Caini
2206d3f921 test: drop extra ; 2023-10-24 11:20:54 +02:00
Michele Caini
1f7f613800 sparse_set: use scoped iterators within ::sort 2023-10-24 11:11:11 +02:00
Michele Caini
8d92440451 natvis: temporary workaround to print views correctly 2023-10-24 09:09:54 +02:00
Michele Caini
5fca9d4c94 nativs: basic_storage::length is no longer a thing in any case 2023-10-23 17:36:08 +02:00
Michele Caini
663353cbdb view: avoid multiple instantiations of fully_initialized 2023-10-23 17:05:40 +02:00
Michele Caini
8550a9f159 test: sort/sort_n/sort_as for storage no instance 2023-10-23 11:34:47 +02:00
Michele Caini
f01dbd68f3 test: cleanup 2023-10-23 11:33:50 +02:00
Michele Caini
5913829e21 test: minor changes 2023-10-23 08:55:51 +02:00
Michele Caini
c201a44f1a test: more about storage no instance 2023-10-23 08:54:31 +02:00
Michele Caini
382be6c4e1 *: updated TODO 2023-10-20 12:24:06 +02:00
Michele Caini
aad20ed035 storage: deprecate ::pack, use ::sort_as instead 2023-10-20 12:23:43 +02:00
Michele Caini
c24adea40f test: minor changes 2023-10-20 12:23:28 +02:00
Michele Caini
223e2376b6 test: suppress warnings due to shadow variables 2023-10-19 16:23:44 +02:00
Michele Caini
0ce65cec6b test: suppress warnings due to unused type aliases 2023-10-19 16:22:12 +02:00
Michele Caini
7ddef2586c group: iterator based sort_as 2023-10-19 14:34:38 +02:00
Michele Caini
4d9f137980 test: use iterator based sort_as in the storage tests 2023-10-19 14:34:14 +02:00
Michele Caini
03aabbfa07 registry: use iterator based sort_as 2023-10-18 11:49:42 +02:00
Michele Caini
fe0d412b98 sparse_set: iterator based sort_as as it should be 2023-10-18 11:13:07 +02:00
LiterallyVoid
826eb7ed65 doc: fix typo in Sorting section (#1080) 2023-10-18 10:32:24 +02:00
Michele Caini
2dc121e3df meta: correctly initialize all members of meta container wrappers 2023-10-17 15:20:56 +02:00
Michele Caini
75bff4d67f meta: expose basic_meta_sequence_container_traits::fixed_size 2023-10-17 14:20:11 +02:00
Michele Caini
9d33ae86be helper: storage-based to_entity function 2023-10-17 14:09:04 +02:00
Michele Caini
173c4e1c78 test: make toolset v141 happy again 2023-10-13 19:33:43 +02:00
Michele Caini
2d0b451ed0 test: storage 2023-10-13 18:48:59 +02:00
Michele Caini
35f2dbad09 *: updated TODO 2023-10-12 14:58:15 +02:00
Michele Caini
872fc4751b build: updated bazel files 2023-10-12 14:57:42 +02:00
Michele Caini
504959850c test: more on storage optimization 2023-10-12 14:55:56 +02:00
Michele Caini
2466c7123e test: storage entity 2023-10-11 08:15:00 +02:00
Michele Caini
0f9b4f1b6c test: more on storage no instance 2023-10-10 16:29:29 +02:00
Michele Caini
38f63135a8 test: split storage utilities 2023-10-10 08:38:02 +02:00
Michele Caini
b0ad2ddbe7 test: another attempt to get around an issue of msvc 2023-10-09 15:57:40 +02:00
Michele Caini
94b3944003 test: try to get around an issue of msvc 2023-10-09 15:26:39 +02:00
Michele Caini
aa1bfebc46 test: split storage tests for better management and to avoid issues with the CI 2023-10-09 14:45:59 +02:00
Michele Caini
62dd41f0a1 test: minor changes 2023-10-09 12:35:24 +02:00
Michele Caini
bb54e2542c test: typed test empty/void for storage optimization (to fully cover void version) 2023-10-09 12:35:05 +02:00
Michele Caini
639191af23 build: set test timeout once for all workflows 2023-10-06 15:26:22 +02:00
Michele Caini
4670a939bf workflow: increase test timeout to avoid failures when the CI is particularly slow 2023-10-06 15:18:35 +02:00
Michele Caini
391dcf9639 test: further reduce the number of ASSERT_DEATH to please GH CI :) 2023-10-06 15:04:18 +02:00
Michele Caini
7378abdedb test: further reduce ASSERT_DEATH cost on sparse set tests 2023-10-06 11:28:42 +02:00
Michele Caini
43c5c2dd0d test: suppress another warning by clang-cl 2023-10-06 11:12:33 +02:00
Michele Caini
ffa0a7f276 test: suppress a warning from clang-cl 2023-10-06 11:08:39 +02:00
Michele Caini
592147a499 test: drop unused aliases 2023-10-06 11:08:08 +02:00
Michele Caini
02e2532e17 test: drop a bunch of useless ASSERT_DEATH since they are very expensive on the CI 2023-10-06 11:00:12 +02:00
Michele Caini
4891c8fcd2 test: reduce the scope of the sparse set tests to something meaningful 2023-10-06 10:38:37 +02:00
Michele Caini
ae20db416f test: avoid testing entt::entity like types more than once with the sparse set 2023-10-06 10:30:26 +02:00
Michele Caini
283ad71283 mixin: suppress warning due to constepxr expression 2023-10-06 10:27:04 +02:00
Michele Caini
25bbc4e053 test: suppress warnings due to unused variables 2023-10-06 10:22:48 +02:00
Michele Caini
913d313098 sparse_set: cast null to the right entity type before using it 2023-10-06 10:04:36 +02:00
Michele Caini
7efbafb8b4 test: test different entity types to show off a wip-only failure in the sparse set after recent rework 2023-10-06 10:04:06 +02:00
Michele Caini
3500757c97 test: typed parametrized sparse set tests 2023-10-06 10:02:47 +02:00
Michele Caini
c4c6ba7620 meta: use is_void_v as needed 2023-10-05 14:18:21 +02:00
Michele Caini
c53eaf939d storage: use is_void_v as needed 2023-10-05 14:18:09 +02:00
Michele Caini
d5613e8c65 registry: use is_void_v as needed 2023-10-05 14:17:58 +02:00
Michele Caini
7fada722e7 type_traits: use is_void_v as needed 2023-10-05 14:17:46 +02:00
Michele Caini
66ee7bab32 any: use is_void_v as needed 2023-10-05 14:17:35 +02:00
Michele Caini
8848040203 meta: meta_sequence_container::resize support to non default constructible types (close #1072) 2023-10-05 12:38:14 +02:00
Michele Caini
33ef814a76 meta: minor changes 2023-10-05 11:08:52 +02:00
Michele Caini
06e032bc6d test: prepare for new meta container tests 2023-10-05 10:38:03 +02:00
Michele Caini
25e844fadd test: minor changes 2023-10-05 10:02:56 +02:00
Michele Caini
08474c671b test: drop unused type 2023-10-05 09:46:29 +02:00
Michele Caini
8f94041ea2 doc: typo 2023-10-05 09:43:31 +02:00
Michele Caini
08fc2b3a4a test: still reworking storage tests, empty types are done 2023-10-04 18:58:04 +02:00
Michele Caini
37aec0ab84 view: use type members as needed 2023-10-04 09:06:21 +02:00
Michele Caini
61ad570ecc storage: use type members as needed 2023-10-04 09:06:13 +02:00
Michele Caini
90e158e519 group: use type members as needed 2023-10-04 09:06:05 +02:00
Michele Caini
97d49b4bf3 test: rework storage tests (work in progress) 2023-10-03 14:49:43 +02:00
Michele Caini
4c3a35bc95 storage: make get_allocator work with void type 2023-10-03 14:49:04 +02:00
Michele Caini
244b5d56ff test: add required include 2023-10-02 12:08:26 +02:00
Michele Caini
755cb0f4e0 test: merge storage and storage entity tests, prepare for storage tests rework 2023-10-02 11:50:08 +02:00
Michele Caini
7b2b402d37 *: updated TODO 2023-10-02 11:49:31 +02:00
Michele Caini
41481548b8 test: finish to rework sparse set tests to cover all policies 2023-10-02 11:49:13 +02:00
Michele Caini
f2411a1db7 sparse_set: do not invoke compact automatically when sorting 2023-10-02 11:48:34 +02:00
Michele Caini
073cbae9d5 test: still working on sparse set to fully cover all policies 2023-09-29 17:29:57 +02:00
Michele Caini
db8e22196d test: make test local functions static 2023-09-29 10:16:28 +02:00
Michele Caini
8a68fea16e test: sparse set tests (work in progress) 2023-09-29 10:00:18 +02:00
Michele Caini
6501ca9b3c doc: more links (engine + YT series) 2023-09-29 09:59:45 +02:00
Michele Caini
0c7c6c58c0 test: suppress warning due to unused alias 2023-09-29 09:46:49 +02:00
Michele Caini
f3f0ba6955 test: TEST[_P] -> ENTT_DEBUG_TEST[_P] 2023-09-29 09:46:29 +02:00
Michele Caini
52da5cf894 test: help the coverage tool who goes crazy sometimes 2023-09-29 09:45:10 +02:00
Michele Caini
2878e096e6 test: keep working on sparse set tests (work in progress) 2023-09-28 09:37:55 +02:00
Michele Caini
4f3ef4818a test: review sparse set (work in progress) 2023-09-27 09:48:33 +02:00
Michele Caini
ffc0c5f2d3 *: updated TODO 2023-09-27 09:47:49 +02:00
Michele Caini
9d97c83909 view: use scoped iterators with ::contains 2023-09-26 14:32:02 +02:00
Michele Caini
6a21e29397 view: use scoped iterators with ::back 2023-09-26 14:31:42 +02:00
Michele Caini
8fdc24097e view: begin/end return scoped iterators as needed 2023-09-26 08:45:52 +02:00
Michele Caini
4e7368f79a storage: entity storage no longer has component traits exposed 2023-09-25 14:26:10 +02:00
Michele Caini
1e1dee88b7 registry: avoid warnings due to unused variables 2023-09-25 08:50:16 +02:00
Michele Caini
91e4ff6a08 storage: internal changes 2023-09-22 18:31:28 +02:00
Michele Caini
4be9aac7a6 storage: use scoped iterators in entity storage 2023-09-22 18:30:31 +02:00
Michele Caini
af059b5e21 mixin: rework pop_all to make it work without component traits 2023-09-22 18:27:48 +02:00
Michele Caini
fdb1978f89 component: cleanup 2023-09-22 17:35:33 +02:00
Michele Caini
8332e59c62 sparse_set: minor changes 2023-09-22 16:15:27 +02:00
Michele Caini
20cc263502 view: use scoped iterators as in the ::each function 2023-09-22 07:57:33 +02:00
Michele Caini
361d4ec4dd sparse_set: scoped iterator 2023-09-21 17:58:24 +02:00
Michele Caini
11f8ba5a8f *: updated TODO 2023-09-21 17:57:04 +02:00
Michele Caini
c3bf842f9f sparse_set: const correctness 2023-09-21 17:35:05 +02:00
Michele Caini
33b598289b meta: help the code coverage tool 2023-09-20 14:51:14 +02:00
Michele Caini
cfc7f99405 meta: avoid dead code warnings 2023-09-20 12:35:01 +02:00
Michele Caini
1d79550f3c *: updated TODO 2023-09-20 12:23:40 +02:00
Michele Caini
db5ecd2c96 meta: further reduce size of symbols 2023-09-20 12:22:18 +02:00
Michele Caini
1280367a35 test: cleanup 2023-09-19 09:27:01 +02:00
Michele Caini
14706c8f9a test: cleanup 2023-09-18 14:38:16 +02:00
Michele Caini
b821df4279 *: update TODO 2023-09-18 14:08:26 +02:00
Michele Caini
07ff28d9dc sparse_set/storage: put swap_or_move check into the base class 2023-09-18 14:07:59 +02:00
Michele Caini
d15fd2a8b7 test: ENTT_DEBUG_TEST_P 2023-09-18 14:07:14 +02:00
Michele Caini
6e4381934a storage: improve asserts 2023-09-14 23:47:50 +02:00
Michele Caini
46ccded145 *: in_use -> free_list 2023-09-14 23:34:59 +02:00
Ezekiel Warren
01453b67fc build system: add bzlmod support + tests (#1057) 2023-09-14 08:48:17 +02:00
Michele Caini
7ec739b5c7 view: drop specialization for single type views with exclusion list 2023-09-13 13:00:01 +02:00
Michele Caini
fd8279ee90 *: cleanup TODO 2023-09-12 08:54:47 +02:00
Michele Caini
a6fea6727b sparse_set/storage: improve emplace(N) with non-sequential entities 2023-09-12 08:54:38 +02:00
Michele Caini
e561e8cbdd storage: deprecate in_use 2023-09-12 08:47:34 +02:00
Michele Caini
8d39f89aff sparse_set: added free_list(N) for swap_only sets 2023-09-12 08:46:57 +02:00
Michele Caini
05644a742f *: updated TODO 2023-09-11 12:47:57 +02:00
Michele Caini
f0f7fcf5b7 test: a few more checks for the sparse set class 2023-09-11 12:47:48 +02:00
Michele Caini
16bb54c524 sparse_set/storage: move swap_only policy towards the base 2023-09-11 10:51:29 +02:00
Michele Caini
70c3eaab80 natvis: add free_list to the sparse set snippet 2023-09-08 11:17:06 +02:00
Michele Caini
104fb0f06c doc: update to reflect last changes (close #1061) 2023-09-08 11:12:33 +02:00
Michele Caini
d93f96f48f test: use the right values for the free_list to avoid regressions 2023-09-07 18:22:20 +02:00
Michele Caini
8731eb91f4 sparse_set: split swap_at for internal uses 2023-09-07 10:41:12 +02:00
Michele Caini
a987609b2a sparse_set: make ::free_list [[nodiscard]] 2023-09-06 15:13:16 +02:00
Michele Caini
40793e9485 sparse_set: minor changes 2023-09-06 14:59:56 +02:00
Michele Caini
874587a591 sparse_set: swap_only pop function 2023-09-06 08:37:31 +02:00
Michele Caini
59a401fbe2 *: updated TODO 2023-09-06 08:37:00 +02:00
Michele Caini
9c797ead28 sparse_set: extra (temporary) check 2023-09-05 15:05:15 +02:00
Michele Caini
b6bb7c1a84 sparse_set: clear policy_to_head 2023-09-05 15:05:04 +02:00
Michele Caini
995f7e8e19 sparse_set: review pop_all 2023-09-05 14:37:14 +02:00
Michele Caini
9732f0547d sparse_set/storage: drop length in favor of a temporary function to make the transition easier 2023-09-05 14:36:33 +02:00
Michele Caini
9c9a71af74 sparse_set: update/set head properly accordingly with the policy 2023-09-05 10:00:03 +02:00
Michele Caini
be09b3a40a entity: update doc 2023-09-05 09:58:52 +02:00
Michele Caini
d79d08ef11 sparse_set: use the underlying entity type for the head of the free list 2023-09-04 17:37:40 +02:00
Michele Caini
c7377cbbbf sparse_set: strip the version from the head of the free list 2023-09-04 17:33:24 +02:00
Michele Caini
bbcc88217a sparse_set: make ::pop_all work property in managed swap_only mode 2023-09-04 12:40:33 +02:00
Michele Caini
c2838b4861 sparse_set: ::free_list function (for later uses) 2023-09-01 14:26:02 +02:00
Michele Caini
19c256d9a4 sparse_set: prepare to make swap_only a proper deletion policy managed at sparse set level 2023-09-01 09:26:42 +02:00
Michele Caini
7b08533a76 *: updated TODO 2023-08-31 11:28:15 +02:00
Michele Caini
baf0057628 sparse_set: drop swap_at as it ought to be 2023-08-31 11:28:15 +02:00
Michele Caini
99bf93dce6 storage: stop using swap_at, prepare to make swap_only a proper sparse set policy 2023-08-31 11:28:15 +02:00
Michele Caini
776ec96565 sparse_set: minor changes 2023-08-31 11:28:15 +02:00
Michele Caini
d03c444eae sparse_set: prepare to offer the free_list value to the user 2023-08-31 11:28:15 +02:00
Michele Caini
de6678c47a sparse_set: refine ::sort_n to work with upcoming changes 2023-08-31 11:28:15 +02:00
Michele Caini
b780198526 sparse_set: make ::compact work only with the right deletion policies 2023-08-31 11:28:15 +02:00
Michele Caini
27f1e9e476 sparse_set: refine ::contiguous to work fine with upcoming changes 2023-08-31 11:28:15 +02:00
Michele Caini
a9de9da75b sparse_set: prepare to drop swap_at 2023-08-31 11:28:15 +02:00
Michele Caini
4f0a119279 sparse_set: swap-only mode for easy identification 2023-08-31 11:28:15 +02:00
Michele Caini
9d3b4b8d78 meta: suppress warnings due to unused variables 2023-08-31 11:28:07 +02:00
Michele Caini
9a59de939e meta: deprecate ::key_only 2023-08-31 11:27:48 +02:00
Michele Caini
c5a2d00395 meta: deprecate (again) ::owner 2023-08-31 11:27:43 +02:00
Michele Caini
36c2087269 any: deprecate (again) ::owner 2023-08-31 11:27:39 +02:00
Michele Caini
7178970524 meta: make basic_meta_[sequence|associative]_container_traits public with doc 2023-08-21 12:30:13 +02:00
Michele Caini
d76fa92be3 meta: use the right return type for meta assoc container ::erase function 2023-08-21 11:30:37 +02:00
Michele Caini
e942348211 meta: [[nodiscard]] as it ought to be 2023-08-21 09:48:17 +02:00
Michele Caini
3e12f4f55f *: updated TODO 2023-08-19 17:36:29 +02:00
Michele Caini
4ed51d3fd8 meta: back to the multi-function model because it's easier to partially customize it 2023-08-19 17:35:51 +02:00
Michele Caini
c9a5fda400 meta: allow for a smoother transition before dropping meta_associative_container::key_only 2023-08-19 16:22:59 +02:00
Michele Caini
45d4bd0cf5 meta: allow for a smoother transition before deprecating ::owner 2023-08-19 16:15:48 +02:00
Michele Caini
aafa8a9b0b any: allow for a smoother transition before deprecating ::owner 2023-08-19 16:15:38 +02:00
Michele Caini
b1e47fc7d4 snpashot: review/cleanup orphans but keep it in place 2023-08-19 16:07:26 +02:00
Michele Caini
b87b8cf272 *: updated TODO 2023-08-19 16:07:26 +02:00
Michele Caini
e797fadaff meta: further review meta container vtables 2023-08-19 16:07:26 +02:00
Michele Caini
316b5b9992 meta: split begin/cbegin and end/cend for meta assoc containers, review vtable 2023-08-19 16:07:26 +02:00
Michele Caini
5aeae3a452 meta: split begin/cbegin and end/cend for meta seq containers, review vtable 2023-08-19 16:07:26 +02:00
Michele Caini
3f4f976396 *: updated TODO 2023-08-19 16:07:26 +02:00
Michele Caini
7cd83ced17 meta: split insert/erase for meta containers to simplify the cleanup/improvement 2023-08-19 16:07:26 +02:00
Michele Caini
f832a8f97c meta: static_cast only vtable for meta associative containers 2023-08-19 16:07:26 +02:00
Michele Caini
256eb727a5 meta: const correctness 2023-08-19 16:07:26 +02:00
Michele Caini
3ae6613445 meta: further reduce vtable size for meta associative containers 2023-08-19 16:07:26 +02:00
Michele Caini
5979dd2e15 meta: further reduce the size of meta_any vtables for non-container non-pointer-like types 2023-08-19 16:07:26 +02:00
Michele Caini
b203a31039 meta: I probably went a little too far :) 2023-08-19 16:07:26 +02:00
Michele Caini
1f99b49617 meta:
* drop any from meta containers rebind
* further reduce the size of the meta any vtable for containers
2023-08-19 16:07:26 +02:00
Michele Caini
efbf5e3111 meta:
* further reduce the size of the meta_any vtable
* drop meta_any operation enum
2023-08-19 16:07:26 +02:00
Michele Caini
b52fe64628 *: updated TODO 2023-08-19 16:07:26 +02:00
Michele Caini
0d3511c05f meta: further refine meta_any vtable 2023-08-19 16:07:26 +02:00
Michele Caini
ace3da4173 meta: refine meta_any vtable, prepare to drop any from meta container rebind 2023-08-19 16:07:26 +02:00
Michele Caini
f65a21f88c meta: minor changes 2023-08-19 16:07:26 +02:00
DonutVikingChap
47aeeabc51 *: fix C++20 global module fragment inclusion (#1047) 2023-08-19 16:07:26 +02:00
Michele Caini
049860b884 meta: decouple container operations for coverage purposes and share them properly 2023-08-19 16:07:26 +02:00
Michele Caini
b7d23e1aec meta: suppress comma warnings (close #1044) 2023-08-19 16:07:26 +02:00
Michele Caini
f1f33a3ac7 build: enable -Wcomma on clang (see #1044) 2023-08-19 16:07:26 +02:00
Michele Caini
607e6d96a4 meta: share look_for function to reduce duplication 2023-08-19 16:07:26 +02:00
Michele Caini
d8551ead86 sparse_set: prepare for yet another option 2023-08-19 16:07:26 +02:00
Michele Caini
29510716bb meta: drop friendship between meta_any and meta_type 2023-08-19 16:07:26 +02:00
Michele Caini
735e0af91a meta: try_convert to replace/merge can_convert and allow_cast 2023-08-19 16:07:26 +02:00
Michele Caini
2666982645 meta: drop constexpr from meta iterators since it drives gcc crazy 2023-08-19 16:07:25 +02:00
Michele Caini
768c8de48c doc: cleanup 2023-08-19 16:07:25 +02:00
Michele Caini
ce6a04ae5a meta: avoid passing contexts to meta containers' vtables 2023-08-19 16:07:25 +02:00
Michele Caini
f8012d5055 meta: avoid shadow warnings 2023-08-19 16:07:25 +02:00
Michele Caini
966efd2397 doc: minor changes 2023-08-19 16:07:25 +02:00
Michele Caini
9344798fa1 doc: update the give me everything section (close #1043) 2023-08-19 16:07:25 +02:00
skypjack
95643b6a56 meta: drop unnecessary move 2023-08-19 16:07:25 +02:00
skypjack
6b2c88e542 meta: cleanup 2023-08-19 16:07:25 +02:00
skypjack
0df38df1c6 *: updated TODO 2023-08-19 16:07:25 +02:00
skypjack
73dd3dcc26 meta: add meta_type::can_convert (close #1028) 2023-08-19 16:07:25 +02:00
skypjack
1e65fede6c test: minor changes 2023-08-19 16:07:25 +02:00
skypjack
b3c0d91eb3 meta: drop redundant assert 2023-08-19 16:07:25 +02:00
skypjack
eaa93d287e meta: meta_type::can_cast (see #1028) 2023-08-19 16:07:25 +02:00
skypjack
4517ae22e5 test: cleanup 2023-08-19 16:07:25 +02:00
skypjack
f4537ad272 *: updated TODO 2023-08-19 16:07:25 +02:00
skypjack
c425199239 meta: make basic meta container traits publicly available 2023-08-19 16:07:25 +02:00
skypjack
e15894872d meta: slightly improved meta_any::allow_cast 2023-08-19 16:07:25 +02:00
skypjack
54a15d7b51 doc: minor changes 2023-08-19 16:07:25 +02:00
skypjack
b1b6470cc2 doc: drop unused comments 2023-08-19 16:07:25 +02:00
skypjack
3a89263e73 doc: cleanup 2023-08-19 16:07:25 +02:00
skypjack
533b755b11 doc: minor changes 2023-08-19 16:07:25 +02:00
skypjack
c5d94545b5 *: updated TODO 2023-08-19 16:07:25 +02:00
skypjack
2ae3e52f76 meta: merge meta container operation enums 2023-08-19 16:07:25 +02:00
skypjack
f20b9e3875 meta: drop useless key_only func on meta assoc containers 2023-08-19 16:07:25 +02:00
skypjack
a1ca352818 meta: exploit vtable params for meta containers 2023-08-19 16:07:25 +02:00
skypjack
6b96e2f2a9 meta: make the non-consteness of the value-or-container explicit in the meta container vtable 2023-08-19 16:07:25 +02:00
skypjack
b1b4c67b75 meta: drop meta op cbegin/cend, use unused param value to pass constness around 2023-08-19 16:07:25 +02:00
skypjack
f775d3be67 meta: drop meta op cfind, use unused param value to pass constness around 2023-08-19 16:07:25 +02:00
skypjack
09f7814503 meta: drop redundant function in the meta associative container 2023-08-19 16:07:25 +02:00
skypjack
745c5384bb *: updated TODO 2023-08-19 16:07:25 +02:00
skypjack
5ff2b97f67 meta: fake vtable for meta associative containers 2023-08-19 16:07:25 +02:00
skypjack
44f30dc01c meta: fake vtable for meta sequence containers 2023-08-19 16:07:25 +02:00
skypjack
4b307cb2b2 doc: updated meta container doc (::reserve) 2023-08-19 16:07:25 +02:00
skypjack
378d98096d meta: meta containers support to ::reserve 2023-08-19 16:07:25 +02:00
skypjack
2b12d29a2f meta: minor changes 2023-08-19 16:07:25 +02:00
skypjack
7382a68505 *: updated TODO 2023-08-19 16:07:25 +02:00
skypjack
e10d2cb37e test: avoid shadow warnings 2023-08-19 16:07:25 +02:00
skypjack
c4e7a62ac3 meta: update meta seq container ::clear 2023-08-19 16:07:25 +02:00
skypjack
f354378fec *: updated TODO 2023-08-19 16:07:25 +02:00
Michele Caini
c638cb38b9 test: avoid shadow warnings 2023-08-19 16:07:25 +02:00
Michele Caini
d255ecf0f9 *: updated TODO 2023-08-19 16:07:25 +02:00
Michele Caini
4ea691118c view: specialization for single type views with exclusion lists - close #1032 2023-08-19 16:07:25 +02:00
Michele Caini
bfd962cda2 doc: minor changes 2023-08-19 16:07:25 +02:00
Michele Caini
719c233e77 doc: avoid warnings and errors by doxygen 2023-08-19 16:07:25 +02:00
Michele Caini
bd78235b28 doc: update doxy.in to doxygen 1.9.7 2023-08-19 16:07:25 +02:00
Michele Caini
aa292ed113 doc: drop typo 2023-08-19 16:07:25 +02:00
Michele Caini
b99d789543 test: yet another failing test to fix #1032 and avoid regressions 2023-08-19 16:07:25 +02:00
Michele Caini
5ef58cce5d test: failing test to fix #1032 and avoid regressions 2023-08-19 16:07:25 +02:00
Michele Caini
31c3868bc8 test: name consistency 2023-08-19 16:07:25 +02:00
Michele Caini
071a97becb test: review benchmark file 2023-08-19 16:07:25 +02:00
Michele Caini
1df40ada5c test: view ::use and ::refresh 2023-08-19 16:07:25 +02:00
Michele Caini
a12b99ad49 test: drop useless call to view::use 2023-08-19 16:07:25 +02:00
Michele Caini
e1d84d0b92 test: drop useless call to refresh 2023-08-19 16:07:25 +02:00
Michele Caini
fb47ef66da view: internal changes 2023-08-19 16:07:24 +02:00
Michele Caini
9621348ad0 view: internal changes (prepare to additional specialization) 2023-08-19 16:07:24 +02:00
Michele Caini
560e1429f1 test: yet another non-failing test for the storage entity 2023-08-19 16:07:24 +02:00
Michele Caini
18a945e7d6 test: prepare to fix #1032 and avoid regressions 2023-08-19 16:07:24 +02:00
Michele Caini
16548fc3a1 test: a few more 2023-08-19 16:07:24 +02:00
Michele Caini
4272d954ea test: cleanup 2023-08-19 16:07:24 +02:00
Michele Caini
77b5907a9f snapshot: code coverage 2023-08-19 16:07:24 +02:00
Michele Caini
acae0533bc view: drop unnecessary data member from single type views 2023-08-19 16:07:24 +02:00
Michele Caini
a5e259c19b view: minor changes 2023-08-19 16:07:24 +02:00
Michele Caini
12865f318e links: add saurian sorcery :) 2023-08-19 16:07:24 +02:00
pokutnev
51704ee7ce container: workaround for the max() ambiguity with MAX macro (#1034) 2023-08-19 16:07:18 +02:00
Michele Caini
ad6f4a3dd8 meta: further reduce symbol size of meta assoc container iterator 2023-08-19 16:07:11 +02:00
Michele Caini
70ffa25c4c meta: further reduce symbol size of meta seq container iterator 2023-08-19 16:07:05 +02:00
Michele Caini
4aef168a50 view: minor changes 2023-08-19 16:06:12 +02:00
Michele Caini
40fe70f15f meta: further reduce symbol size of meta assoc container iterator vtable 2023-08-19 16:05:37 +02:00
Michele Caini
812351cfdb meta: further reduce symbol size of meta seq container iterator vtable 2023-08-19 16:05:30 +02:00
Michele Caini
518e1abb26 meta: revert changes to meta container iterators, I strongly prefer this version actually 2023-08-19 16:05:24 +02:00
Michele Caini
9db64d3389 meta: avoid invoking .data() more than once 2023-08-19 16:05:18 +02:00
Michele Caini
2ad1a44432 meta: reduce symbol size of meta assoc traits ::insert_or_erase function 2023-08-19 16:04:50 +02:00
Michele Caini
3047c73952 meta: reduce symbol size of meta assoc traits ::iter function 2023-08-19 16:04:44 +02:00
Michele Caini
c8f5b1afa6 meta: reduce symbol size of meta assoc traits ::clear function 2023-08-19 16:04:40 +02:00
Michele Caini
54be0c2d9c meta: reduce symbol size of meta assoc traits ::find function 2023-08-19 16:04:26 +02:00
Michele Caini
b5b4991ae6 meta: reduce symbol size of meta assoc traits ::size function 2023-08-19 16:04:15 +02:00
Michele Caini
3273c75057 *: updated TODO 2023-08-19 16:04:09 +02:00
Michele Caini
f60f0cf279 meta: reduce symbol size of meta seq traits ::insert_or_erase function 2023-08-19 16:04:01 +02:00
Michele Caini
9e7881ee08 meta: reduce symbol size of meta seq traits ::iter function 2023-08-19 16:03:52 +02:00
Michele Caini
9d45825947 meta: minor changes 2023-08-19 16:02:27 +02:00
Michele Caini
c993388e5c meta_any:
* introduce meta_any_policy
* deprecate ::owner member
* add ::policy member
2023-08-19 16:02:20 +02:00
Michele Caini
b45cf33eef any:
* introduce any_policy
* deprecate ::owner member
* add ::policy member
2023-08-19 16:02:14 +02:00
Michele Caini
de376a0b25 meta: reduce symbol size of meta seq traits ::size function 2023-08-19 16:00:05 +02:00
Michele Caini
c659fad434 meta: make meta seq ::clear forward to ::resize 2023-08-19 16:00:05 +02:00
Michele Caini
5e7e4a4b11 meta: further reduce symbol size of meta seq traits ::size function 2023-08-19 16:00:05 +02:00
Michele Caini
7efbc3f72d meta: reduce symbol size of meta seq traits ::resize function 2023-08-19 16:00:05 +02:00
Michele Caini
8c93171303 doc: cleanup 2023-08-19 16:00:05 +02:00
Michele Caini
d8f314df25 registry: use the "right" allocator 2023-08-19 16:00:05 +02:00
Michele Caini
be0872e71e meta: internal changes 2023-08-19 16:00:05 +02:00
Michele Caini
3b95cbd697 doc: cleanup 2023-08-19 16:00:05 +02:00
Michele Caini
5ef07da451 view: further reduce instantiations 2023-08-19 16:00:05 +02:00
Michele Caini
5d98fc06c2 view: let's trust the compiler and assume that it's good enough to optimize this :) 2023-08-19 16:00:05 +02:00
Michele Caini
7f531e5e2d view: further reduce instantiations 2023-08-19 16:00:05 +02:00
Michele Caini
542a700f2c view: further reduce instantiations and symbols 2023-08-19 16:00:05 +02:00
Michele Caini
f3eaa1f74a view: reduce instantiations due to none_of 2023-08-19 16:00:05 +02:00
Michele Caini
06680c64eb doc: dense set 2023-08-19 16:00:05 +02:00
Michele Caini
4b1a19677a dense_set: reverse iterators 2023-08-19 16:00:05 +02:00
Michele Caini
5792f43185 *: updated TODO 2023-08-19 16:00:05 +02:00
Michele Caini
e89298eb52 doc: add Minecraft Legends to links 2023-08-19 16:00:05 +02:00
Michele Caini
22fada19ec build: minor changes 2023-08-19 16:00:05 +02:00
Michele Caini
ef7fa8ae0f build: make the debug build suitable for SizeBench 2023-08-19 16:00:05 +02:00
Michele Caini
72fa115160 update single include file to v3.12.2 2023-08-19 16:00:05 +02:00
Michele Caini
3665cf11f3 meta: additional assert to avoid mistakes with meta containers 2023-08-19 16:00:05 +02:00
Michele Caini
9b5910547f meta: check type when assigning a meta container 2023-08-19 16:00:05 +02:00
Michele Caini
04bcb51c73 registry: make ::valid backward compatible 2023-08-19 16:00:05 +02:00
Michele Caini
774049c75e build: update iwyu version 2023-08-19 16:00:05 +02:00
Michele Caini
b8e52934ea build: update cereal version 2023-08-19 16:00:05 +02:00
Michele Caini
5047aeacb5 doc: add hellbound :) 2023-08-19 16:00:05 +02:00
Michele Caini
6594da85f1 snapshot: try to suppress a warning from older versions of gcc 2023-08-19 16:00:05 +02:00
Michele Caini
f532807d62 update single include file to v3.12.1 2023-08-19 16:00:05 +02:00
Michele Caini
ffdc7893fe registry: drop ::assign (deprecated function) 2023-08-19 16:00:05 +02:00
Michele Caini
25a401042a registry: drop ::release (deprecated function) 2023-08-19 16:00:05 +02:00
Michele Caini
22914d4687 registry: drop ::release (deprecated function) 2023-08-19 16:00:05 +02:00
Michele Caini
7970a24359 registry: drop ::release (deprecated function) 2023-08-19 16:00:04 +02:00
Michele Caini
8bbc113258 registry: drop ::released (deprecated function) 2023-08-19 16:00:04 +02:00
Michele Caini
10c13e8fbe snapshot: reintroduce support to storage listeners 2023-08-19 15:59:57 +02:00
Michele Caini
0fec57e76d registry: drop ::data (deprecated function) 2023-06-17 22:42:55 +02:00
Michele Caini
e72bf9acb1 registry: drop ::each (deprecated function) 2023-06-16 17:35:06 +02:00
Michele Caini
fc4287e9e1 doc: typo 2023-06-16 15:59:59 +02:00
Michele Caini
5f184ed655 registry: drop ::capacity (deprecated function) 2023-06-16 11:25:07 +02:00
Michele Caini
abad59f0ce registry: drop ::reserve (deprecated function) 2023-06-16 11:23:45 +02:00
Michele Caini
cd3474e5b5 registry: drop ::alive (deprecated function) 2023-06-16 11:21:59 +02:00
Michele Caini
57d8072901 registry: drop ::empty (deprecated function) 2023-06-16 11:18:10 +02:00
Michele Caini
d7d0ba498c registry: drop ::size (deprecated function) 2023-06-15 16:42:55 +02:00
Michele Caini
169fcdc323 snapshot: drop deprecated methods (and fix tests accordingly) 2023-06-15 14:16:18 +02:00
Michele Caini
a8a19b0c4d test: meta include review (thanks iwyu) 2023-06-14 15:38:58 +02:00
Michele Caini
a669b84e6a test: lib/meta include review (thanks iwyu) 2023-06-14 15:38:58 +02:00
Michele Caini
c5e764750b test: example include review (thanks iwyu) 2023-06-14 15:38:57 +02:00
Michele Caini
7d2b7974fb test: resource include review (thanks iwyu) 2023-06-14 15:38:57 +02:00
Michele Caini
ee955f7bef test: entity include review (thanks iwyu) 2023-06-14 15:38:57 +02:00
Michele Caini
ad370d35c2 test: config include review (thanks iwyu) 2023-06-14 15:38:57 +02:00
Michele Caini
6a76afc061 process: add missing include 2023-06-14 15:38:57 +02:00
Michele Caini
c67f659041 iwyu: updated imp file 2023-06-14 15:38:57 +02:00
Michele Caini
dc5eea48ae now working on version 3.13 2023-06-14 15:38:42 +02:00
Michele Caini
cb974bf567 adjacency_matrix: fix in_edges() is off by 1 in some cases (close #1019) 2023-06-13 11:56:56 +02:00
Michele Caini
7b7d82e6f6 doc: snapshot (close #984) 2023-06-12 08:22:13 +02:00
Michele Caini
05c6898fc2 test: self-fixing archive example for snapshot classes 2023-06-12 08:20:32 +02:00
Michele Caini
7ffa459a66 snapshot: drop ::get member template parameter 2023-06-12 08:19:47 +02:00
Michele Caini
93e8e94e60 test: basic continuous loader 2023-06-11 12:52:20 +02:00
Michele Caini
c4e2416621 snapshot: review basic_continuous_loader (and drop shrink) 2023-06-09 09:28:53 +02:00
Michele Caini
9c25419b9a test: more on basic_snapshot_loader 2023-06-09 09:02:42 +02:00
Michele Caini
1879830df1 snapshot: drop pointless assert 2023-06-09 09:01:12 +02:00
Michele Caini
29298c0eb1 test: guarantee code coverage, we'll update the test later on 2023-06-08 10:34:34 +02:00
Michele Caini
247abef1d7 test: rollback for code coverage purposes on the snapshot class 2023-06-08 09:47:40 +02:00
Michele Caini
6994d98d2a test: typo 2023-06-07 17:07:22 +02:00
Michele Caini
9a600ece2d test: snapshot 2023-06-07 15:32:41 +02:00
Michele Caini
f91226ef47 snapshot: share ::orphans implementation (to deprecate in future though) 2023-06-07 09:17:25 +02:00
Michele Caini
e366ffbd30 doc: snapshot 2023-06-07 09:14:14 +02:00
Michele Caini
63b300d39d snapshot: again, dense_map::contains is a thing 2023-06-06 10:18:22 +02:00
Michele Caini
afb70d1570 test: avoid warnings due to unused variables 2023-06-06 10:17:38 +02:00
Michele Caini
49534eec0d snapshot: dense_map::contains is a thing fortunately 2023-06-06 10:14:33 +02:00
Michele Caini
3f1277f7bd snapshot: use the right allocator for the remote-local mapping 2023-06-06 10:10:10 +02:00
Michele Caini
26fad4c385 test: basic snapshot loader 2023-06-06 09:26:34 +02:00
Michele Caini
25b3afacf6 test: basic snapshot 2023-06-06 09:25:56 +02:00
Michele Caini
2d25bbb090 snapshot: check registry type 2023-06-05 16:03:11 +02:00
Michele Caini
0eb834582d snapshot: small cleanup 2023-06-01 14:36:57 +02:00
Michele Caini
124a440527 test: use the new snapshot get functions in the test suite 2023-06-01 10:10:32 +02:00
Michele Caini
5c704636ef test: use the new snapshot get functions in the test suite 2023-06-01 10:10:00 +02:00
Michele Caini
31fd94cc3c snapshot: cleanup to get ready to drop an internal function 2023-05-31 15:16:13 +02:00
Michele Caini
573e43272a snapshot: reduce storage lookups 2023-05-31 15:14:05 +02:00
Michele Caini
1d89434812 snapshot: drop useless function 2023-05-31 15:03:14 +02:00
Michele Caini
e0a1ef7c1b snapshot: check on member type class 2023-05-31 14:44:59 +02:00
Michele Caini
48ac0e0eb4 snapshot: add basic_continuous_loader::get, deprecate ::entities and ::component 2023-05-31 10:04:37 +02:00
Michele Caini
bcb6234d94 snapshot: add basic_snapshot_loader::get, deprecate ::entities and ::component 2023-05-30 15:51:47 +02:00
Michele Caini
f967963264 snapshot: reject entity type in the range-get (now get instead of get_sparse) 2023-05-30 14:23:00 +02:00
Michele Caini
b22c55dd21 doc: typo 2023-05-30 14:21:43 +02:00
Michele Caini
4ff5a536ca snapshot: add basic_snapshot::get, deprecate ::entities and ::component 2023-05-29 14:11:37 +02:00
Michele Caini
fff5f578ac test: avoid using deprecated functions in an example 2023-05-26 15:11:53 +02:00
Michele Caini
0f44c8c923 doc: reflect recent changes 2023-05-26 15:06:34 +02:00
Michele Caini
0b6ad03150 snapshot:
* single element only archive functions required
* avoid iterating elements more than once
2023-05-26 15:00:25 +02:00
Michele Caini
2450b0bc69 test: minor changes (waiting for a rework) 2023-05-26 10:39:54 +02:00
Michele Caini
fc8eebf367 snapshot: use component_traits instead of is_empty_v 2023-05-26 08:49:15 +02:00
Michele Caini
e4f51f2b7f snapshot: avoid multiple lookups of the same storage 2023-05-25 12:04:24 +02:00
Michele Caini
2c2216a89e doc: typo 2023-05-25 11:51:51 +02:00
Michele Caini
cafe851809 snapshot: deprecate multi-type component loading function 2023-05-25 11:50:40 +02:00
Michele Caini
35e338cc99 snapshot: deprecate multi-type component loading function 2023-05-25 11:13:41 +02:00
Michele Caini
8feeaaef7c doc: minor changes 2023-05-25 11:12:51 +02:00
Michele Caini
e7a3c4e370 snapshot: add missing [[deprecate(...)]] 2023-05-24 10:00:38 +02:00
Michele Caini
ea5c558bd4 snapshot: cleanup (waiting for further improvements) 2023-05-24 09:59:46 +02:00
Michele Caini
94f0ed179f snapshot: deprecate multi-type component loading function 2023-05-24 09:54:49 +02:00
Michele Caini
244c359491 snapshot: deprecate multi-type component loading function 2023-05-24 09:47:20 +02:00
Michele Caini
1f24fea21a type_traits: formatting 2023-05-23 14:02:20 +02:00
Michele Caini
8deaa09b24 test: perform static checks at compile-time 2023-05-23 14:00:24 +02:00
Dominic Koepke
85bffb7143 type_traits: std::tuple traits specialization for entt::type_list and entt::value_list (#1011) 2023-05-23 13:56:29 +02:00
Michele Caini
325ca310d3 view: updated ::refresh 2023-05-22 19:10:01 +02:00
Michele Caini
d903e268f0 snapshot: minor changes 2023-05-22 19:03:11 +02:00
Michele Caini
f4b26756c5 snapshot: improved basic_snapshot::component 2023-05-22 18:51:03 +02:00
Michele Caini
fb3a34ee91 *: updated TODO 2023-05-22 18:35:22 +02:00
Michele Caini
6902bb6c41 doc: typo 2023-05-22 17:34:09 +02:00
Michele Caini
379819b2b0 test: cleanup 2023-05-22 16:25:35 +02:00
Michele Caini
59abfbfb5a meta: refine policy check on value types for non-member data 2023-05-19 12:20:21 +02:00
Michele Caini
6e2d871844 registry: avoid casting return types directly to better support empty storage 2023-05-19 12:19:27 +02:00
Michele Caini
57ec3c85cb registry: erase_if (close #977) 2023-05-19 10:44:00 +02:00
Michele Caini
4afdf287f1 doc: minor changes 2023-05-19 10:43:12 +02:00
Michele Caini
2810ac7cb9 registry: suppress a warning on msvc 2023-05-18 14:56:58 +02:00
Michele Caini
e0d27f9bf8 *: updated TODO 2023-05-18 14:06:34 +02:00
Michele Caini
de303c9990 test: reverse-each for storage entity 2023-05-18 14:06:28 +02:00
Michele Caini
1619e780f4 test: reverse each for plain storage classes 2023-05-18 14:06:16 +02:00
Michele Caini
a1e37eca6b storage: reverse-each 2023-05-18 14:06:03 +02:00
Michele Caini
c345e7456c doc: note on reverse iterations 2023-05-17 16:26:15 +02:00
Michele Caini
d166c026f7 snapshot: minor changes 2023-05-17 14:39:54 +02:00
Michele Caini
5e639996d6 doc: minor changes 2023-05-17 10:24:13 +02:00
Michele Caini
dac2ef5a9c doc: typo 2023-05-16 10:56:22 +02:00
Michele Caini
71d7888e81 snapshot: drop redundant check 2023-05-16 10:15:01 +02:00
Michele Caini
84a4df9c49 doc: exclude-only views 2023-05-16 09:55:08 +02:00
Michele Caini
95bc203196 doc: entity lifecycle 2023-05-16 09:49:35 +02:00
Michele Caini
5a9f6d211c doc: cleanup 2023-05-15 16:13:31 +02:00
Michele Caini
a29302faae test: more on entity signals 2023-05-15 08:49:07 +02:00
Michele Caini
75efa72c65 registry: cleanup ::erase 2023-05-15 08:33:26 +02:00
Michele Caini
58a84665b0 registry: cleanup ::remove 2023-05-15 08:33:06 +02:00
Michele Caini
a5263384d9 doc: drop redundant comments 2023-05-13 14:31:30 +02:00
Michele Caini
c0e6759c69 doc: cleanup a little further 2023-05-13 14:22:58 +02:00
Michele Caini
d754f74316 doc: cleanup 2023-05-13 14:06:58 +02:00
Michele Caini
1df5399431 doc: drop pointless tags 2023-05-13 13:57:48 +02:00
Michele Caini
c284e6feed doc: minor changes 2023-05-13 13:29:26 +02:00
Michele Caini
500239758a test: typo 2023-05-12 08:28:24 +02:00
DonutVikingChap
319ecd8084 organizer: fix organizer::vertex::prepare not creating component pools (#1014) 2023-05-12 08:24:23 +02:00
Michele Caini
d7891fabc0 doc: mention named pools support when registering listeners 2023-05-12 08:23:08 +02:00
Michele Caini
e287dd0419 helper: minor changes 2023-05-12 08:22:38 +02:00
Michele Caini
4dee9dde11 registry: named pools support for on_construct/on_update/on_destroy 2023-05-12 08:22:18 +02:00
Michele Caini
9bae6e67bc doc: update connection helper doc 2023-05-11 14:23:15 +02:00
Michele Caini
aa7a7ce25a doc: minor changes 2023-05-11 14:00:53 +02:00
Michele Caini
a969468c57 registry: de-deprecate :) on_construct/on_update/on_destroy 2023-05-11 13:58:52 +02:00
Michele Caini
a1e76fc638 doc: more about entity storage 2023-05-10 11:05:09 +02:00
Michele Caini
d8ed4ca354 registry: refine how entity storage is used internally 2023-05-10 10:47:11 +02:00
Michele Caini
3248e3f91e helper: make sigh_helper work with named pools 2023-05-09 11:09:16 +02:00
Michele Caini
f00687e6f9 doc: updated registry documentation 2023-05-09 10:55:08 +02:00
Michele Caini
5240c6b60a registry: deprecate on_construct/on_update/on_destroy 2023-05-09 10:54:49 +02:00
Michele Caini
67604a88e1 natvis: update registry snippet 2023-05-09 10:49:07 +02:00
Michele Caini
4242dfb8b5 registry: use entity storage directly as much as possible 2023-05-09 10:24:09 +02:00
Michele Caini
f96d8ee832 registry: prepare to split component storage and entity storage 2023-05-09 09:24:25 +02:00
Michele Caini
c147ec37c9 test: try to make gcc happy again 2023-05-09 08:48:22 +02:00
Michele Caini
094ddbba36 meta: avoid shadow warnings 2023-05-09 08:44:24 +02:00
Michele Caini
634630ca2d test: add missing template keywords (thanks msvc for ignoring them) 2023-05-09 08:34:06 +02:00
Michele Caini
d78c26f266 *: updated TODO 2023-05-08 12:45:52 +02:00
Michele Caini
fabc6c9bd7 test: full cross-registry entity-copy example with meta (not strictly required) 2023-05-08 12:45:18 +02:00
Michele Caini
b6e8ddd2ad meta: fight against the small nuances of the language :) 2023-05-08 12:24:43 +02:00
Michele Caini
cf2bbae6e1 mixin: make it simpler to modify the underlying type 2023-05-07 23:38:05 +02:00
Michele Caini
08799616d0 *: updated TODO 2023-05-05 16:19:51 +02:00
Michele Caini
58bebf78d8 meta: reduce symbols and their sizes if possible 2023-05-05 15:53:58 +02:00
Michele Caini
d534fad3ee doc: more about views 2023-05-04 11:35:27 +02:00
Michele Caini
871dc7a401 doc: drop references to storage placeholders 2023-05-04 10:12:35 +02:00
Michele Caini
1fe7c78f7e test: minor changes 2023-05-03 16:22:01 +02:00
Michele Caini
22a65f80fc test: cleanup 2023-05-03 16:11:08 +02:00
Michele Caini
756ea8a388 *: updated TODO 2023-05-03 14:49:43 +02:00
Michele Caini
12186cb401 registry: drop internal static storage variables from ::assure 2023-05-03 14:49:35 +02:00
Michele Caini
aa9ffb9eef registry: const ::storage<T>(...) returns a pointer to possibly null storage 2023-05-03 14:46:40 +02:00
Michele Caini
dcb5aed901 registry: lazily/partially initialize views in the ::view const function 2023-05-02 14:39:06 +02:00
Michele Caini
34f6a747a8 registry: add support for non-existent pools to try_get 2023-05-02 14:37:06 +02:00
Michele Caini
912cb2ad54 snapshot: constness review 2023-05-02 14:30:20 +02:00
Michele Caini
885488b3d6 registry: any_of supports non-existing pools now 2023-04-28 16:28:32 +02:00
Michele Caini
3d3d3ef2d9 registry: all_of supports non-existing pools now 2023-04-28 16:28:13 +02:00
Michele Caini
a7120b3400 registry: coding style 2023-04-28 16:26:56 +02:00
Michele Caini
51915205b0 test: cover stable multi-type model 2023-04-28 16:25:03 +02:00
Michele Caini
4a3ee042ea view: refine ::storage function 2023-04-28 16:24:38 +02:00
Michele Caini
88a1b8d0df view: stable multi-type view ::each(cb) function 2023-04-28 16:24:18 +02:00
Michele Caini
7e18a0f966 view: update ::use function 2023-04-28 16:23:33 +02:00
Michele Caini
c367082ddd view: unchecked_refresh function 2023-04-28 16:22:23 +02:00
Michele Caini
9f94b5306d view: double check on none_of 2023-04-28 16:21:17 +02:00
Michele Caini
44ed10c50b view: stable multi type view ::find/::back/::front functions 2023-04-28 15:55:33 +02:00
Michele Caini
1b22809412 view: stable multi type view ::begin/::end functions 2023-04-28 15:48:09 +02:00
Michele Caini
bdabbaa63d view: stable multi type view ::contains function 2023-04-28 15:47:16 +02:00
Michele Caini
c79c109b77 view: stable multi type view ::size_hint function 2023-04-28 15:44:00 +02:00
Michele Caini
f1a2133820 registry: prepare to remove static storage from const assure 2023-04-28 15:35:15 +02:00
Michele Caini
17dc061490 view: stable single type view ::each(cb) function 2023-04-28 14:20:45 +02:00
Michele Caini
3b8d82330d view: drop unused return 2023-04-28 12:19:45 +02:00
Michele Caini
a20829e700 view: ::handle returns a pointer rather than a reference 2023-04-28 09:42:14 +02:00
Michele Caini
5be2fdc158 view: stable single type view ::each() function 2023-04-27 09:19:57 +02:00
Michele Caini
873b107e69 -: updated TODO 2023-04-27 09:19:27 +02:00
Michele Caini
356bbbe53e view: stable single type view ::find function 2023-04-27 09:11:03 +02:00
Michele Caini
e3ce4e1567 view: stable single type view ::front/::back functions 2023-04-27 09:10:29 +02:00
Michele Caini
e02050c515 view: stable single type view ::rbegin/::rend functions 2023-04-27 09:07:37 +02:00
Michele Caini
26930633f0 view: stable single type view ::begin/::end functions 2023-04-27 08:59:50 +02:00
Michele Caini
b7a485767f view: stable single type view ::contains function 2023-04-26 15:49:21 +02:00
Michele Caini
f54cdccd4f view: stable single type view ::empty function 2023-04-26 15:48:45 +02:00
Michele Caini
41c9a32f31 view: stable single type view ::size function 2023-04-26 15:48:06 +02:00
Michele Caini
736ef35805 view: make operator bool work properly with partially initialized views 2023-04-26 13:10:31 +02:00
Michele Caini
0128cbb4f3 test: minor changes 2023-04-24 18:15:22 +02:00
Michele Caini
ff0a407151 test: prepare test suite for safe invalid views 2023-04-24 18:15:09 +02:00
Michele Caini
34f4403864 view: avoid using storage, further prepare for empty safe views 2023-04-24 17:49:49 +02:00
Michele Caini
b1c78efb6c nativs: updated signal file 2023-04-24 17:31:56 +02:00
Michele Caini
28f03ff9ce meta: add missing checks on factory<...>::data 2023-04-24 09:50:57 +02:00
Michele Caini
a5fe61adbb *: minor changes 2023-04-24 08:35:52 +02:00
Michele Caini
457f5e59ea view: rollback handle() usage and prepare to safe empty views 2023-04-24 08:35:17 +02:00
Michele Caini
422fd284e7 group: refine group ::find function 2023-04-21 15:32:14 +02:00
Michele Caini
6f32225736 view: refine single type view ::find function 2023-04-21 15:28:21 +02:00
Michele Caini
366bbceb02 doc: use doxygen-awesome-css 2023-04-21 09:03:44 +02:00
Michele Caini
7b7f81e08f doc: update reference.md 2023-04-21 08:55:48 +02:00
Michele Caini
cfe955f970 doc: update links.md 2023-04-21 08:55:38 +02:00
Michele Caini
684ddc9de9 doc: minor changes 2023-04-21 08:42:52 +02:00
Michele Caini
f5d38a9aed doc: drop redundant doxy variable 2023-04-21 08:34:23 +02:00
Michele Caini
447e3693f2 doc: updated doxy file (doxygen 1.9.6) 2023-04-21 08:29:25 +02:00
Michele Caini
909490bf63 view: try to make g++ happy again 2023-04-20 14:25:39 +02:00
Michele Caini
d90363e4a4 view: make view pack also work with empty views 2023-04-20 13:48:23 +02:00
Michele Caini
ee5de744c3 view: add missing [[nodiscard]] 2023-04-19 13:51:31 +02:00
Michele Caini
d401c88a04 view: assert on null handles 2023-04-19 11:54:45 +02:00
Michele Caini
80563b9557 view: allow swapping storage elements of a view 2023-04-19 09:29:31 +02:00
Michele Caini
c74900057c sigh_mixin: avoid shadow warnings 2023-04-18 15:22:21 +02:00
Michele Caini
78867d5c9b group: make msvc happy with constness on virtual functions 2023-04-18 15:22:04 +02:00
Michele Caini
d435fc7792 basic_entt_traits: suppress a warning by gcc 2023-04-18 15:21:26 +02:00
Michele Caini
e6f76e0f96 view: try to make VS happy again :) 2023-04-18 14:44:28 +02:00
Michele Caini
1c6b533609 test: minor changes 2023-04-18 13:28:01 +02:00
Michele Caini
5c3d8360c2 view: turn ::use into a self-contained, non-const function 2023-04-18 13:10:01 +02:00
Michele Caini
3882c7d9af view: turn ::refresh into a self contained, non-const function 2023-04-18 13:00:59 +02:00
Michele Caini
15726218bd view: doc 2023-04-18 12:58:04 +02:00
Michele Caini
869bfc82cf test: minor changes 2023-04-17 18:34:53 +02:00
Michele Caini
0eb3d54b21 group: change signature of ::storage to return a (maybe null) pointer rather than a reference 2023-04-17 11:09:10 +02:00
Michele Caini
f83290f762 view: change signature of ::storage to return a (maybe null) pointer rather than a reference 2023-04-17 11:08:55 +02:00
Michele Caini
686a3b9d79 registry: make storage_for_type available to the final user 2023-04-14 12:37:05 +02:00
Michele Caini
4d57d5c327 registry: make ::storage<T> return type explicit 2023-04-14 12:33:41 +02:00
Michele Caini
36c21cf7fa registry: drop redundant traits usage 2023-04-14 12:24:31 +02:00
Michele Caini
7ab10e1936 test: minor changes 2023-04-14 12:03:00 +02:00
Michele Caini
41467d35a4 -: updated TODO 2023-04-14 09:57:42 +02:00
Michele Caini
d351252a12 doc: entity storage 2023-04-14 09:11:42 +02:00
Michele Caini
c6cd4f701c doc: refine storage section 2023-04-13 18:19:31 +02:00
Michele Caini
65889cca44 doc: brief mention of void storage 2023-04-13 14:02:17 +02:00
Michele Caini
f1914fd946 doc: rearrange a few things 2023-04-13 13:46:08 +02:00
Michele Caini
e53af7bef7 registry: minor changes 2023-04-12 15:43:46 +02:00
Michele Caini
b910cd2615 *: updated TODO 2023-04-12 15:16:00 +02:00
Michele Caini
58d331ca0e registry: minor changes 2023-04-12 09:21:47 +02:00
Michele Caini
17f5b0a330 registry: avoid bumping version on destroy if not requested 2023-04-12 09:14:02 +02:00
Michele Caini
de386292bc registry: deprecate ::each 2023-04-12 09:05:38 +02:00
Michele Caini
88bf26a2f8 registry: deprecate ::assign 2023-04-07 18:06:12 +02:00
Michele Caini
3caad4100d mixin: common internal owner_or_assert function 2023-04-07 17:27:32 +02:00
Michele Caini
916203a240 test: stress assert on entity limit 2023-04-07 15:59:22 +02:00
Michele Caini
62f1971f7b test: minor changes 2023-04-07 15:58:48 +02:00
Michele Caini
4fde96357d natvis: updated registry snippet 2023-04-07 09:19:36 +02:00
Michele Caini
c3730b65fb group:
* unified model
* drop group handler's size function (no longer required)
2023-04-07 09:19:22 +02:00
Michele Caini
1ea072cd38 group: back to the unified model for group handlers 2023-04-07 09:17:47 +02:00
Michele Caini
bbe4582ee9 meta: minor changes 2023-04-06 12:40:50 +02:00
Michele Caini
89ab5c328a meta: operator==/!= for meta_func 2023-04-06 12:40:42 +02:00
Michele Caini
3a4672793d meta: operator==/!= for meta_prop 2023-04-06 12:40:12 +02:00
Michele Caini
0a0446f35c meta: operator==/!= for meta_data (close #1002) 2023-04-06 12:39:50 +02:00
Michele Caini
fc58ff74be meta: operator==/!= for meta_handle (see #1002) 2023-04-06 12:39:19 +02:00
Michele Caini
fed6831cdc locator: support to opaque structures (close #956) 2023-04-05 14:20:34 +02:00
Michele Caini
1605c8d9d3 natvis: updated entity file 2023-04-04 11:51:56 +02:00
Michele Caini
d6641c7d8d -: updated TODO file 2023-04-04 09:36:16 +02:00
Michele Caini
5079f38e99 storage: allow on_update signals on entity storage 2023-04-04 09:36:04 +02:00
Michele Caini
1eab2a4a80 meta: fix constness detection for static functions in meta_type::invoke 2023-04-04 09:35:10 +02:00
Michele Caini
c331107651 test: cleanup 2023-04-03 15:30:10 +02:00
Michele Caini
117b0bd675 test: more about storage<...>::patch 2023-04-03 08:56:02 +02:00
Michele Caini
9b4a6f8776 storage: use allocator_traits::destroy rather than destroy_at 2023-04-03 08:41:23 +02:00
Michele Caini
f4e6f2b375 group: suppress shadow warning 2023-03-31 15:51:03 +02:00
Michele Caini
5971fb7aa4 -: updated TODO 2023-03-31 14:00:05 +02:00
Michele Caini
10dfe7e935 sigh: allow disconnecting listeners during iterations (close #986) 2023-03-31 13:59:53 +02:00
Michele Caini
a9208a9565 doc: fixed typo 2023-03-31 11:14:54 +02:00
Michele Caini
1cc5b32cab test: cleanup 2023-03-31 11:14:39 +02:00
Michele Caini
f8a972a3c6 signal: drop sink::before 2023-03-31 09:05:02 +02:00
Michele Caini
5b7cc20027 group: rollback some (no longer required) changes to the owning_group_descriptor 2023-03-30 15:07:51 +02:00
Michele Caini
bd34e7f2c7 group: drop nested groups support, prepare to the large group review and multi storage support 2023-03-30 15:00:32 +02:00
Michele Caini
46fe29c3f6 group: make matching functions virtual for owning groups 2023-03-30 08:48:04 +02:00
Michele Caini
c50e2815c8 group: make owning_group_descriptor depend on the storage base type 2023-03-30 08:43:53 +02:00
Michele Caini
fbfee632d5 group: minor changes 2023-03-29 18:32:19 +02:00
Michele Caini
77c59aabfa group: group_handler::size function for owning groups 2023-03-29 18:26:41 +02:00
Michele Caini
ebb1e8a728 group: single check function for group handlers 2023-03-29 18:26:08 +02:00
Michele Caini
1646217f09 group: make types explicit for the next/prev functions 2023-03-29 17:36:31 +02:00
Michele Caini
645edfb2b8 group: decouple constructing and setting prev/next links 2023-03-29 17:35:53 +02:00
Michele Caini
61f28298c9 group/registry: minor changes 2023-03-29 15:11:05 +02:00
Michele Caini
d19f97bf21 group: use ::handle() if possible 2023-03-29 13:13:07 +02:00
Michele Caini
70c611a84f group: cleanup 2023-03-29 13:12:05 +02:00
Michele Caini
286428c19c group: make common_type base of non-owning group handlers 2023-03-29 13:11:59 +02:00
Michele Caini
6ec719bcfa group: reduce the footprint of non-owning group handlers 2023-03-29 12:54:39 +02:00
Michele Caini
11f9bb2d74 registry: use shared_ptr<void> for non-owning groups (prepare to drop the basic handler dependency) 2023-03-29 12:21:56 +02:00
Michele Caini
5a1ba5ad7d regisrtry: decouple container types for groups 2023-03-29 11:51:20 +02:00
Michele Caini
cf094e7ef5 registry: finally split owning and non-owning groups as it ought to be 2023-03-29 11:50:20 +02:00
Michele Caini
31808bd9a2 sigh: flip the last commit on its head and drop redundant functions rather than merging them 2023-03-27 17:01:14 +02:00
Michele Caini
61a5173a75 sigh: merge a couple of functions 2023-03-27 16:30:16 +02:00
Michele Caini
ed6fe9e657 sigh/sink: refine internal definition 2023-03-27 15:56:35 +02:00
Michele Caini
e30fa85200 doc: cleanup 2023-03-27 15:51:49 +02:00
Michele Caini
ca1069e182 snapshot: avoid allocations if possible 2023-03-27 14:41:46 +02:00
Michele Caini
70f73a0949 snapshot: drop pointless checks 2023-03-27 13:44:03 +02:00
Michele Caini
710fff0e3f entity: make get_t, exclude_t and owned_t constexpr constructible 2023-03-27 11:21:31 +02:00
Michele Caini
660bc5843e entity: turn get_t, exclude_t and owned_t into proper classes (close #998) 2023-03-27 11:15:35 +02:00
Michele Caini
13295a14ee type_traits: v141 toolset workaround for value_list_diff 2023-03-27 11:10:45 +02:00
Michele Caini
9ce07ff617 type_traits: value_list_diff[_t] 2023-03-26 12:46:27 +02:00
Michele Caini
b272e04bab type_traits: value_list_contains[_v] 2023-03-26 12:42:15 +02:00
Michele Caini
28b11912ab test: cleanup 2023-03-26 12:38:50 +02:00
Michele Caini
b9f096d125 type_traits: value_list_unique[_t] 2023-03-26 12:38:09 +02:00
Michele Caini
8c60faa1d0 type_traits: value_list_index[_v] 2023-03-26 12:33:04 +02:00
Michele Caini
1f93ea4eee snapshot: avoid unnecessary lookups 2023-03-24 17:09:09 +01:00
Michele Caini
7ca77e53f6 snapshot: avoid unnecessary lookups 2023-03-24 17:08:01 +01:00
Michele Caini
69397f3658 snapshot: avoid unnecessary lookups 2023-03-24 17:04:23 +01:00
Michele Caini
f907bc066a snapshot: drop redundant checks and avoid unnecessary lookups 2023-03-24 16:48:30 +01:00
Michele Caini
bda52701f5 snapshot: avoid unnecessary lookups 2023-03-24 16:45:41 +01:00
Michele Caini
d26f7684ce snapshot: minor changes 2023-03-24 16:42:24 +01:00
Michele Caini
63d6c2bff6 snapshot: avoid unnecessary lookups 2023-03-24 16:41:26 +01:00
Michele Caini
cc45e73414 snapshot: also avoid using views if not required 2023-03-24 16:37:20 +01:00
Michele Caini
5d092bcb18 snapshot: avoid unnecessary lookups 2023-03-24 16:35:22 +01:00
Michele Caini
295c68841c snapshot: review ::orphans functions 2023-03-24 16:33:40 +01:00
Michele Caini
2664b52559 observer: allocator support 2023-03-24 16:04:52 +01:00
Michele Caini
dd36328331 observer: configurable mask type 2023-03-24 15:56:54 +01:00
Michele Caini
c8c929e4a2 group: use type members properly 2023-03-24 12:16:36 +01:00
Michele Caini
d1ef7bf155 view: use type members properly 2023-03-24 12:16:30 +01:00
Michele Caini
1ab23f17d2 group: early exit on signal races 2023-03-24 11:24:07 +01:00
Michele Caini
a72eb4693b group: minor changes 2023-03-24 11:11:40 +01:00
Michele Caini
67579d062b -: updated TODO 2023-03-23 16:48:48 +01:00
Michele Caini
766a233f37 view: base_type -> common_type 2023-03-23 16:42:13 +01:00
Michele Caini
905671c236 runtime_view: base_type -> common_type 2023-03-23 16:42:06 +01:00
Michele Caini
27c1383e46 group: base_type -> common_type 2023-03-23 16:41:54 +01:00
Michele Caini
029ccc8f75 registry: base_type -> common_type 2023-03-23 16:41:32 +01:00
Michele Caini
cde40d586d group: drop unused using decl 2023-03-23 13:37:40 +01:00
Michele Caini
6a16a8a20b group: auto init for owning groups 2023-03-23 08:52:38 +01:00
Michele Caini
1a12dede6f group: auto init for non-owning groups 2023-03-23 08:36:23 +01:00
Michele Caini
35a78b65ea group: cleanup 2023-03-22 16:42:31 +01:00
Michele Caini
ada19432f6 group: support for index based sort 2023-03-22 13:50:08 +01:00
Michele Caini
4998e90870 doc: minor changes 2023-03-22 13:49:45 +01:00
Michele Caini
471c11c6dc sparse_set: respect -> sort_as (naming is hard, you know) 2023-03-21 14:26:35 +01:00
Michele Caini
3e13e0b59b group: sort/respect -> sort_as (also decoupled from group types) 2023-03-21 14:14:18 +01:00
Michele Caini
53cd105f2e group: reuse pools as much as possible 2023-03-20 10:07:11 +01:00
Michele Caini
24b31c3798 group: reuse pools as much as possible 2023-03-20 10:00:07 +01:00
Michele Caini
def82b534b group: index based get 2023-03-20 08:11:42 +01:00
Michele Caini
a424f4ebf6 view: review get 2023-03-19 16:02:06 +01:00
Michele Caini
b8f0a8d8ec doc: a couple of interesting articles/series (close #994) 2023-03-19 15:31:31 +01:00
Michele Caini
7941226eff group: try to reuse pools when sorting and also please all compilers out there at the same time (aka me figthing ICEs again) 2023-03-18 12:48:52 +01:00
Michele Caini
86bbb2f6bb group: reuse pools when sorting 2023-03-18 12:41:11 +01:00
Michele Caini
3c176f7258 test: suppress warnings due to unused variables 2023-03-18 12:21:13 +01:00
Michele Caini
3642c8a784 registry: drop [[nodiscard]] from ::group (close #991) 2023-03-18 12:17:39 +01:00
Michele Caini
0e80d90a76 group: use storage<idx> as much as possible 2023-03-18 12:14:55 +01:00
Michele Caini
4fdf2dccd4 group: update doc 2023-03-17 16:16:53 +01:00
Michele Caini
f8a997e6c5 group: minor changes 2023-03-17 16:14:16 +01:00
Michele Caini
40f676ed14 test: drop unused include 2023-03-17 12:20:19 +01:00
Michele Caini
5e346748ea test: code coverage for groups and registry 2023-03-17 12:19:23 +01:00
Michele Caini
3ef61fe014 meta: support meta member functions on primitive types 2023-03-17 10:43:08 +01:00
Michele Caini
3885d280d3 test: cleanup 2023-03-17 10:36:10 +01:00
Michele Caini
f41b914197 meta: allow updating values on meta properties 2023-03-17 08:45:58 +01:00
Michele Caini
e0684f6348 registry: cleanup/minor changes 2023-03-17 08:45:00 +01:00
Michele Caini
fb980a78c0 registry: further refine the group function(s) 2023-03-16 15:00:17 +01:00
Michele Caini
c2430ab48d doc: minor changes 2023-03-16 12:25:46 +01:00
Michele Caini
d36d9cb39c registry: further cleanup group functions 2023-03-16 12:25:28 +01:00
Michele Caini
0017c08bb6 group: get pools from handlers 2023-03-16 09:26:47 +01:00
Michele Caini
e737ff7470 group: get filter from handlers 2023-03-15 10:32:59 +01:00
Michele Caini
945dc40937 group: split group handler functions 2023-03-15 09:39:50 +01:00
Michele Caini
7ef0085115 registry: drop group_data 2023-03-14 15:45:13 +01:00
Michele Caini
d2fa68813f registry/group: prepare to get rid of group_data 2023-03-14 15:35:00 +01:00
Michele Caini
f22a09a9a2 group: in-between change to simplify dropping group_data 2023-03-14 15:12:02 +01:00
Michele Caini
b0aba79a56 snapshot: minor changes 2023-03-14 15:10:02 +01:00
Michele Caini
7c23e4a2f6 registry: minor changes 2023-03-14 13:56:06 +01:00
Michele Caini
7fe035ce4b group: move group size from registry group_data to basic_group_handler 2023-03-14 11:38:41 +01:00
Michele Caini
3e7160edad group: minor changes 2023-03-14 11:12:25 +01:00
Michele Caini
aaeb686ec7 group: common base class for group handlers 2023-03-14 11:11:22 +01:00
Michele Caini
3fdf4884d8 group: prepare for group handler common base class 2023-03-14 11:09:35 +01:00
Michele Caini
1b23ff4b91 registry: use common group handler types as keys for the group set 2023-03-14 10:50:02 +01:00
Michele Caini
88dac318e5 group: wrap the len of owning groups to avoid changing it by mistake 2023-03-13 14:47:26 +01:00
Michele Caini
520c2e660d group: make group handlers work with multiple storage of the same type 2023-03-12 19:54:03 +01:00
Michele Caini
f5d0d451b4 group: split pools and filter in the group handlers 2023-03-11 19:23:09 +01:00
Michele Caini
8af6fc0cc4 group: use ::handle internally if possible 2023-03-10 14:41:51 +01:00
Michele Caini
c04b97a313 group: add ::handle function to all group types 2023-03-10 12:46:08 +01:00
Michele Caini
1d85414dc2 doc: drop refs to registry::version (close #992) 2023-03-10 12:23:07 +01:00
Michele Caini
c6533827f0 group: fight with clang format from time to time :) 2023-03-10 10:20:16 +01:00
Michele Caini
b5803451b4 group: make owning groups work with their handlers 2023-03-10 10:17:56 +01:00
Michele Caini
3417d66b2b group: make non-owning groups work with their handlers 2023-03-10 10:05:10 +01:00
Michele Caini
1e61204e80 registry: deduce group handler type from group type 2023-03-10 09:57:23 +01:00
Michele Caini
19c4857ef1 group: cleanup 2023-03-03 11:31:01 +01:00
Michele Caini
66ea94898e registry/group: move group handler to group file as it ought to be 2023-03-03 09:20:10 +01:00
Michele Caini
ced6d21c3e registry: break dependency between registry and group handlers 2023-03-02 10:06:39 +01:00
Michele Caini
429c7c45c7 registry: further cleanup things 2023-03-02 10:03:23 +01:00
Michele Caini
c03b1111aa registry: small cleanup 2023-03-02 09:54:26 +01:00
Michele Caini
ebd7d3acdc registry: storage based model with pools for groups 2023-03-02 09:54:08 +01:00
Michele Caini
5aeec60cfe registry: prepare to switch to storage based group handlers 2023-03-02 09:47:48 +01:00
Michele Caini
620b4f7517 registry: pass handlers to group callbacks 2023-03-02 08:50:25 +01:00
Michele Caini
6d58004c11 registry: minor changes to simplify the implementation slightly 2023-03-02 08:47:23 +01:00
Michele Caini
df6d926dec registry: prepare for a storage based group handler 2023-03-02 08:46:56 +01:00
Michele Caini
e63af24cb5 registry: turn the non-owning group handler in a storage 2023-03-01 18:03:38 +01:00
Michele Caini
068d9f8ae8 registry: discard unused arguments from listeners if possible 2023-03-01 17:50:58 +01:00
Michele Caini
c19c848c46 test: suppress warnings due to unused variables 2023-03-01 12:17:55 +01:00
Michele Caini
0bf0a0a8fa doc: delegate 2023-03-01 12:12:28 +01:00
Michele Caini
743e8678ea delegate: also support functions that skip first elements (on second attempt only) 2023-03-01 12:12:18 +01:00
Michele Caini
a7ad1c06f4 delegate: prepare to support filtering on both sides 2023-02-28 10:46:54 +01:00
Michele Caini
b1af70e70f registry: avoid checking pools in the group handler if possible 2023-02-28 10:23:58 +01:00
Michele Caini
c87c3533ee registry: avoid checking pools in the group handler if possible 2023-02-28 10:08:54 +01:00
Michele Caini
4839a0ee6c registry: cleanup 2023-02-27 16:31:46 +01:00
Michele Caini
a0f0c44e6d registry: minor changes 2023-02-27 16:25:35 +01:00
Michele Caini
74691dc1d9 group: just use meaningful names :) 2023-02-27 16:21:04 +01:00
Michele Caini
e4957badb7 registry: split group handler to further refine group management 2023-02-27 16:17:05 +01:00
Michele Caini
46791c4c3a registry: turn group handler functions into static ones 2023-02-27 16:03:33 +01:00
Michele Caini
56c3917841 registry: prepare to rework groups 2023-02-27 15:59:54 +01:00
Michele Caini
1fb13d3e93 doc: minor changes 2023-02-27 15:32:06 +01:00
Michele Caini
535beb4e2d storage: drop unnecessary use of integral_constant 2023-02-24 16:32:36 +01:00
Michele Caini
2d318b88c9 -: updated TODO 2023-02-24 16:29:35 +01:00
Michele Caini
b7f0b76cef entity/mixin: add missing include 2023-02-24 15:12:49 +01:00
Michele Caini
d30312f516 entity/helper: add missing include, drop unnecessary traits calls 2023-02-24 15:11:19 +01:00
Michele Caini
30772848e6 meta: avoid unnecessary calls to std::move 2023-02-24 15:04:40 +01:00
Nicolas Jakob
eca01a3979 doc: add vcpkg badge and vcpkg.link (#985) 2023-02-24 14:52:06 +01:00
Michele Caini
35ef0b7ac1 core: reduces the number of instantiations a bit 2023-02-23 16:21:49 +01:00
Michele Caini
19ccba3a6c meta: reduces the number of instantiations a bit 2023-02-23 16:21:40 +01:00
Michele Caini
207b7674ae doc: fix typo 2023-02-23 16:04:41 +01:00
Michele Caini
631c55ba92 storage: minor changes/tests 2023-02-22 11:32:15 +01:00
Michele Caini
e7b30fd36d storage: return iterator to elements rather than entities and only if it makes sense 2023-02-22 11:14:30 +01:00
Michele Caini
3e959007b9 storage: ::insert returns an iterator to the range of inserted entities 2023-02-21 10:52:37 +01:00
Michele Caini
07ec4ca230 -: updated TODO 2023-02-21 10:50:42 +01:00
Michele Caini
6e4946b682 storage: uniform interface to simplify mixin implementation 2023-02-20 09:34:11 +01:00
Michele Caini
47ea16f17c test: signals on entity creation/destruction 2023-02-18 11:51:18 +01:00
Michele Caini
722857fc07 test: get rid of pointless template parameters 2023-02-18 11:30:35 +01:00
Michele Caini
2125b38381 test: minor changes 2023-02-17 11:00:50 +01:00
Michele Caini
289de7d57b test: exclude only views 2023-02-17 10:59:36 +01:00
Michele Caini
25ecd8e797 test: minor changes 2023-02-17 10:59:11 +01:00
Michele Caini
319dfdb070 test: filtered registry view 2023-02-17 10:36:07 +01:00
Michele Caini
9dbbcac01e -: updated TODO 2023-02-17 09:09:36 +01:00
Michele Caini
f545c8e058 registry: deprecate ::release 2023-02-17 09:09:28 +01:00
Michele Caini
c68fa6a654 registry: make ::destroy work without ::release (the latter to be deprecated) 2023-02-16 10:00:45 +01:00
Michele Caini
d288ecd70d registry: make ::release use ::bump return value 2023-02-16 09:59:42 +01:00
Michele Caini
312d3aba84 sparse_set: bump returns the version in use (for convenience) 2023-02-16 09:58:07 +01:00
Michele Caini
4d2b2c6de5 registry: use traits_type::next if possible 2023-02-16 09:24:03 +01:00
Michele Caini
80d55a226c test: increase code coverage 2023-02-15 10:15:55 +01:00
Michele Caini
d86a539350 test: suppress warnings due to unused variables 2023-02-15 09:45:34 +01:00
Michele Caini
0f7098d0e0 -: updated TODO 2023-02-15 09:34:25 +01:00
Michele Caini
8c96be1e92 registry: deprecate a bunch of functions because of the entity storage 2023-02-15 09:34:08 +01:00
Michele Caini
37f396bfe3 registry: make entity storage storage as any other 2023-02-14 14:24:20 +01:00
Michele Caini
75894dc401 storage: update traits_type for entity storage 2023-02-13 11:42:30 +01:00
Michele Caini
cdee000ce8 any: rollback a change that turns vs toolset v141 crazy 2023-02-10 11:26:59 +01:00
Michele Caini
54ca62600b dispatcher: refine aggregate check 2023-02-10 10:56:48 +01:00
Michele Caini
6f4280ed59 any: refine aggregate check 2023-02-10 10:56:41 +01:00
Michele Caini
ddf56b78cd storage: backward compatibility on component requirements 2023-02-10 10:56:32 +01:00
Michele Caini
53a854f54f any: just cleanup the code to make it easier to work with 2023-02-10 10:40:48 +01:00
Michele Caini
4896acac7d storage: typo 2023-02-10 10:40:22 +01:00
Michele Caini
e3defeba2a test: suppress warnings due to unused variables 2023-02-10 10:01:39 +01:00
Michele Caini
62079908ce storage: use proper value type for entity storage 2023-02-10 09:37:13 +01:00
Michele Caini
e65a8f2e5f doc: add link to koala engine :) 2023-02-10 08:33:41 +01:00
Michele Caini
9f27fb1e57 registry: further prepare to turn the entity storage into a plain pool 2023-02-09 13:40:41 +01:00
Michele Caini
04d734e76c registry: prepare to turn the entity pool in a plain storage 2023-02-09 12:44:23 +01:00
Michele Caini
df50fa1b59 natvis: cleanup 2023-02-08 16:54:50 +01:00
Michele Caini
051872b8c8 natvis: update registry definition 2023-02-08 16:47:40 +01:00
Michele Caini
57ab9e7be0 registry: avoid using assure if not required 2023-02-07 14:56:56 +01:00
Michele Caini
69d95ba759 test: more bench to stress a little an upcoming feature 2023-02-06 11:27:02 +01:00
Michele Caini
9caf66d7c8 test: cleanup 2023-02-06 11:07:22 +01:00
Michele Caini
74cb0d40c5 test: internal rework 2023-02-06 10:59:10 +01:00
Michele Caini
deac7f34b8 dispatcher: refine aggregate support 2023-02-03 18:40:22 +01:00
Michele Caini
a9883f27c6 storage: refine transparent aggregate support 2023-02-03 18:37:14 +01:00
skypjack
85b1e57d8d sparse_set: drop fast_compact, expect full clear 2023-02-02 18:27:20 +01:00
skypjack
b7d8e01867 storage: make the entity storage perform a full clear rather than a fake one (still viable via erase) 2023-02-02 17:34:11 +01:00
Michele Caini
390a56176f -: updated TODO file 2023-02-01 16:07:17 +01:00
Michele Caini
a1b888cce6 natvis: add optiona storage length item for entity storage 2023-02-01 16:06:56 +01:00
Michele Caini
2107dd6891 natvis: fix already existing errors due to renaming or design changes 2023-02-01 16:06:37 +01:00
Michele Caini
1fca56afef storage: make it easier to refine the natvis file 2023-02-01 16:05:47 +01:00
Michele Caini
c0762a6a5a storage: add get/get_as_tuple to entity storage to make it suitable for use with views 2023-01-31 17:23:42 +01:00
Michele Caini
f48de1bac9 test: stress get/get_as_tuple for empty types 2023-01-31 17:22:07 +01:00
Michele Caini
c7dfce89e5 sigh_mixin: refine pop_all 2023-01-30 19:03:43 +01:00
Michele Caini
822fafcd42 view: uniform implementation to simplify upcoming changes 2023-01-30 16:17:44 +01:00
Michele Caini
1476d4ea9b sparse_set: refine ::respect 2023-01-30 11:26:37 +01:00
Michele Caini
c1c63777e8 -: updated TODO 2023-01-30 10:52:10 +01:00
Michele Caini
2fab25ae89 registry: refine internal check 2023-01-30 10:52:03 +01:00
Michele Caini
75d4491522 -: updated TODO 2023-01-27 09:10:18 +01:00
Michele Caini
c7866fb21b storage: use entt traits next function if possible 2023-01-27 09:10:11 +01:00
Michele Caini
87987bacde entity: added basic_entt_traits::next (with tests) 2023-01-27 09:09:28 +01:00
Michele Caini
bde0219fe6 snapshot: review basic_continuous_loader::entities 2023-01-26 11:54:03 +01:00
Michele Caini
ad64c849b8 storage: suppress warnings 2023-01-26 11:42:19 +01:00
Michele Caini
b808bb83b7 test: suppress warnings 2023-01-26 11:42:11 +01:00
Michele Caini
d0090d35fb snapshot: try to make sizes an opaque value to the caller 2023-01-26 11:30:52 +01:00
Michele Caini
7a1a06a24b sigh_mixin: avoid shadow warnings 2023-01-26 11:13:39 +01:00
Michele Caini
000b17881b -: updated TODO 2023-01-26 09:41:36 +01:00
Michele Caini
068b6ed49b registry: first (almost) backward compatible version with opaque hidden entity storage 2023-01-26 09:41:25 +01:00
Michele Caini
0187fb48aa test: sigh mixin for entity storage types 2023-01-25 11:30:41 +01:00
Michele Caini
35a2b38441 sigh_mixin: also support entity storage types 2023-01-25 11:29:53 +01:00
Michele Caini
4747c9a4c8 registry: extended checks to support swap-only entity storage types 2023-01-24 17:34:46 +01:00
Michele Caini
7be8d83278 registry: make a couple of conditions opaque 2023-01-24 17:08:26 +01:00
Michele Caini
a5d6757d6f registry: prepare to get rid of the vector of entities 2023-01-24 17:06:58 +01:00
Michele Caini
3f09d47c81 storage: remove redundant typename keyword 2023-01-24 16:55:54 +01:00
Michele Caini
9c06d6ba0f registry: use type member names 2023-01-24 16:54:32 +01:00
Michele Caini
b7c819bf48 test: entity storage 2023-01-24 16:46:50 +01:00
Michele Caini
9f31803ba7 storage: swap-only entity storage 2023-01-24 16:46:37 +01:00
Michele Caini
1e7deff9c9 test: drop redundant checks 2023-01-24 10:57:37 +01:00
Michele Caini
04ac15d8d9 test: minor changes 2023-01-24 10:57:25 +01:00
Michele Caini
3762189916 sigh_mixin: make pop_all use narrow view iterators if any 2023-01-23 10:09:56 +01:00
Michele Caini
18d6e466d0 -: [[nodiscard]] as appropriate 2023-01-22 18:16:57 +01:00
Michele Caini
095ecf3142 group: extended_group_iterator::base to return the underlying iterator 2023-01-22 18:16:18 +01:00
Michele Caini
433ed863e5 view: extended_view_iterator::base to return the underlying iterator 2023-01-22 18:08:32 +01:00
Michele Caini
0dba68e754 storage: coding style/minor changes 2023-01-22 18:02:49 +01:00
Michele Caini
1ab2815823 storage: extended_storage_iterator::base to return the underlying iterator 2023-01-22 18:00:28 +01:00
Michele Caini
2af5a725e4 doc:
* updated copyright
* udpated TODO list
2023-01-20 08:50:26 +01:00
Michele Caini
a86bf1332b test: try to make lcov happy 2023-01-18 15:34:49 +01:00
Michele Caini
831054bff1 test: share as much as possible 2023-01-18 10:29:37 +01:00
Michele Caini
f94de1c069 test: rework lib stuff to share common files 2023-01-18 10:17:54 +01:00
Michele Caini
a3d9503a17 test: try to make lcov happy 2023-01-18 08:34:39 +01:00
Michele Caini
3f2b15f9f7 test: try to make lcov happy 2023-01-17 11:42:14 +01:00
Michele Caini
e48817d518 test: try to make lcov happy 2023-01-17 10:57:41 +01:00
Michele Caini
d11cebe30b view: uniform design to also help natvis without having to poke into stl internals 2023-01-17 08:19:34 +01:00
Michele Caini
77a5efb327 natvis: updated to_entity intrinsic 2023-01-17 08:18:21 +01:00
Michele Caini
851006efee -: updated TODO 2023-01-16 16:36:12 +01:00
Michele Caini
6fc6b2fb35 sigh_mixin: further improve ::pop_all 2023-01-16 16:08:11 +01:00
Michele Caini
ed17a2c48b sparse_set: ::contiguous function 2023-01-16 11:07:10 +01:00
Michele Caini
bd00e797a9 sparse_set: further refine pop_all to make it even faster 2023-01-13 12:09:42 +01:00
Michele Caini
e645c4928a -: updated TODO 2023-01-13 12:06:12 +01:00
Michele Caini
a425878e80 sparse_set/storage: clear is backward compatible now 2023-01-12 18:37:33 +01:00
Michele Caini
f3cd9d374d storage: fixed clear_all counter 2023-01-12 15:53:50 +01:00
Michele Caini
b3e93b084e registry: naming convention 2023-01-12 14:45:36 +01:00
Michele Caini
314c189c49 test: minor changes 2023-01-12 08:56:40 +01:00
Michele Caini
2bb2c55662 build: try to make lcov happy again 2023-01-11 18:36:23 +01:00
Michele Caini
d13c126e99 view: avoid name clashes 2023-01-11 18:09:33 +01:00
Michele Caini
9b54ee37a6 flow: propagate allocator to generated graph + internal rework 2023-01-11 17:55:57 +01:00
Michele Caini
e1ead9d3ee build: update coverage workflow 2023-01-11 17:20:58 +01:00
Michele Caini
cf61068dc0 mixin: suppress a warning with gcc11 2023-01-11 16:50:18 +01:00
Michele Caini
82863f8291 test: code coverage for range functionalities 2023-01-11 16:49:52 +01:00
Michele Caini
e4de59827f test: try to make lcov happy 2023-01-11 16:17:02 +01:00
Michele Caini
ccea4c920a memory: code coverage 2023-01-11 15:54:56 +01:00
Michele Caini
89166f0e47 build: refine analyzer workflow 2023-01-11 15:33:00 +01:00
Michele Caini
7a05a16c54 registry: slightly better destroy (yet not quite there though) 2023-01-11 11:54:29 +01:00
Michele Caini
d0854646c7 test: yet another test to stress the upcoming changes 2023-01-11 11:03:04 +01:00
Michele Caini
1e9c9fe5f8 registry: better, faster range-remove + refine range-erase 2023-01-11 10:15:19 +01:00
Michele Caini
80fac8d8e5 test: minor changes 2023-01-11 10:13:47 +01:00
Michele Caini
c774b98389 -: updated TODO 2023-01-11 09:55:57 +01:00
Michele Caini
3fd0403cc9 registry: faster, better range-erase 2023-01-11 09:55:47 +01:00
Michele Caini
6eb3347a3b test: a couple of extra functions to stress the upcoming changes 2023-01-11 09:34:22 +01:00
Michele Caini
89bceaff75 -: updated TODO 2023-01-10 15:51:35 +01:00
Michele Caini
dc25c9c1a2 sparse_set: invoke release_sparse_pages before clearing the sparse array 2023-01-10 15:33:48 +01:00
Michele Caini
e68ba5870c sigh_mixin: add a missing include 2023-01-10 15:01:48 +01:00
Michele Caini
c68cb33751 entity: make deletion_policy publicly available via fwd.hpp 2023-01-10 14:41:13 +01:00
Michele Caini
59f807fd02 sparse_set: suppress warnings due to unused expressions 2023-01-10 14:32:43 +01:00
Michele Caini
232ffebc1e sparse_set: internal clear_all function 2023-01-10 14:22:50 +01:00
Michele Caini
3cea845a0f sparse_set: sparse_set_iterator::data function 2023-01-10 10:53:30 +01:00
Michele Caini
295f3b32e4 registry: a couple of extra move calls here and there 2023-01-10 09:23:28 +01:00
Michele Caini
254da2c3c6 sparse_set: better, faster range remove 2023-01-10 08:43:02 +01:00
Michele Caini
ecd3b8d933 sparse_set: prevent rework errors as much as possible 2023-01-09 18:48:22 +01:00
Michele Caini
c673b9b17c sigh_mixin: slightly improved pop + review insert 2023-01-09 17:45:33 +01:00
Michele Caini
cd28de0d63 test: clear-stable bench 2023-01-09 15:20:19 +01:00
Michele Caini
672f6a7112 test: minor changes 2023-01-09 15:18:06 +01:00
Michele Caini
3b50672b70 storage: restore storage_for/storage_type duality, it turned out to be very useful in practice 2022-12-28 17:56:22 +01:00
Michele Caini
f0613b1c6c sparse_set/storage: minor changes to reuse type members 2022-12-27 12:35:37 +01:00
Michele Caini
2197e160ef -: drop file pushed by mistake :) 2022-12-23 16:58:32 +01:00
Michele Caini
2dccd90166 handle: discard entity on destruction 2022-12-23 16:53:51 +01:00
Michele Caini
2f873f2dd2 -: storage_mixin.hpp -> mixin.hpp (non-storage mixins are also a thing) 2022-12-22 15:12:03 +01:00
Michele Caini
fde1a524ea sparse_set: ::get -> ::value (to avoid hiding from derived classes) 2022-12-22 13:10:57 +01:00
Michele Caini
0558010479 doc: drop references to docsforge + minor changes 2022-12-22 11:40:03 +01:00
Michele Caini
79a054a524 sigh_mixin: scope base_type properly 2022-12-20 15:46:18 +01:00
Michele Caini
d94e443a14 doc: drop outdated section 2022-12-20 15:32:35 +01:00
Michele Caini
3862184e88 sigh_mixin: support self managed storage classes 2022-12-20 15:30:37 +01:00
Michele Caini
f40fa3c2f6 test:
* use range destroy
* avoid compiler optimizations
2022-12-19 16:28:50 +01:00
Michele Caini
01bc93459b test (bench): the new entity storage enables the fast path in all cases 2022-12-19 15:30:30 +01:00
Michele Caini
151bd07391 sparse_set: revert optmized range push, it prevents self-managed storage classes 2022-12-19 08:24:08 +01:00
Michele Caini
935393aae0 sparse_set: better, faster range push 2022-12-16 18:45:51 +01:00
Michele Caini
fbfde43477 snapshot: avoid unused variable warnings 2022-12-16 15:21:56 +01:00
Michele Caini
2ffbe115b7 component_traits: revert entity customization support 2022-12-16 15:14:31 +01:00
Michele Caini
645973eb79 sparse_set: insert -> push 2022-12-15 18:11:19 +01:00
Michele Caini
1332307972 sparse_set: emplace -> push 2022-12-15 14:16:58 +01:00
Michele Caini
b700f5eb5d doc: typo 2022-12-15 12:18:29 +01:00
Michele Caini
e60dbdc521 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
2022-12-15 11:19:44 +01:00
Michele Caini
c66623b330 sigh_mixin: avoid hiding basic_iterator type meber 2022-12-14 12:06:06 +01:00
Michele Caini
62246d8796 storage: avoid hiding basic_iterator type meber 2022-12-14 10:49:32 +01:00
Michele Caini
b35f131309 sparse_set: support swap-only mixins 2022-12-13 17:36:58 +01:00
Michele Caini
3dd82633a3 -: drop storage_mixin.cpp, I forgot to do it a couple of commits ago :) 2022-12-13 15:41:07 +01:00
Michele Caini
00231bf8a7 storage: make swap_at non-final to support checks on derived classes 2022-12-13 15:40:25 +01:00
Michele Caini
58d392e813 -: minor changes 2022-12-13 15:36:49 +01:00
Michele Caini
1d4d99d090 mixin: sigh_storage_mixin -> sigh_mixin 2022-12-13 10:53:07 +01:00
Michele Caini
fe3edf2c83 -: minor changes 2022-12-13 10:50:17 +01:00
Michele Caini
0864ba0429 -: drop useless typename 2022-12-13 10:48:34 +01:00
Michele Caini
3a96980013 build: minor changes 2022-12-12 18:43:44 +01:00
Michele Caini
423f7a555d is_equality_comparable: detect C-style arrays directly 2022-12-12 18:32:07 +01:00
Michele Caini
5db8ad53ac build: update gh workflow 2022-12-12 18:19:51 +01:00
Michele Caini
c2ab357802 view: make also VS toolset v141 happy 2022-12-12 15:42:39 +01:00
Michele Caini
4fb558f143 view: further reduce instantiations 2022-12-12 15:08:37 +01:00
Michele Caini
5762a8a086 view: reuse internal functions if possible 2022-12-12 11:49:47 +01:00
Michele Caini
ed4c675211 sparse_set/storage: drop move_element 2022-12-12 09:38:58 +01:00
Michele Caini
f157898462 config: ENTT_FAIL(msg) -> ENTT_ASSERT(false, msg) 2022-12-07 12:22:35 +01:00
Michele Caini
6d20709e07 storage: minor changes 2022-12-07 10:41:33 +01:00
Michele Caini
a9a9853c01 sigh_storage_mixin: use entity_type from Type 2022-12-06 14:34:16 +01:00
Michele Caini
af14aa4c9e doc: more about signals (sigh_storage_mixin) 2022-12-06 12:01:01 +01:00
Michele Caini
24d6b98817 test: minor changes 2022-12-05 15:29:43 +01:00
Michele Caini
899f4baa63 storage:
* drop storage_for]_t]
* make storage_type[_t] deal with constness
2022-12-05 14:47:32 +01:00
Michele Caini
c1ab7ba025 sigh_storage_mixin: make all virtual member functions final 2022-12-05 09:52:22 +01:00
Michele Caini
9d38f60207 registry: thanks MSVC for accepting invalid C++ code 2022-12-02 11:57:29 +01:00
Michele Caini
0efa25cf61 sigh: cool, I keep doing the same error again and again apparently :) 2022-12-02 11:49:21 +01:00
Michele Caini
6316b60451 registry: make it work with storage<void> also in C++17 2022-12-02 10:56:03 +01:00
Michele Caini
f268fb60a8 entity: avoid breaking changes due to type members renaming 2022-12-01 13:26:22 +01:00
Michele Caini
3520d6915c entity: add base_type 2022-12-01 12:48:26 +01:00
Michele Caini
4da7a84518 entity: make checks work with 64b identifiers :) 2022-12-01 12:26:24 +01:00
Michele Caini
382dfc3bb4 entity: strict check on entity/version masks 2022-12-01 11:17:31 +01:00
Michele Caini
b6dcdc816e entity:
* also expose entity_mask and version mask to the final user
* avoid default args with entt_traits::construct for backward compatibility
2022-12-01 09:22:46 +01:00
Michele Caini
c9d544089a doc: review/cleanup entity.md a bit (done) 2022-11-30 16:17:02 +01:00
Michele Caini
3eb5faeed5 doc: review/cleanup entity.md a bit (work in progress) 2022-11-29 14:40:27 +01:00
Michele Caini
7a328c7edf doc: updated links 2022-11-29 08:21:19 +01:00
Michele Caini
6567aa1951 doc: a note about listeners disconnection (close #958) 2022-11-28 15:40:21 +01:00
Michele Caini
92319f0111 entt_traits: split basic impl, simplify def 2022-11-28 12:48:16 +01:00
Michele Caini
782d86b6e7 entt_traits: value_type -> type (cuz it's not a value type after all) 2022-11-26 18:57:10 +01:00
Michele Caini
c2cae37c1d entity_traits: make page_size type explicit 2022-11-25 18:24:12 +01:00
Michele Caini
1026d26ecb entt_traits: drop reserved value 2022-11-25 11:13:14 +01:00
Michele Caini
7156803dbe test: local non-static constexpr variables 2022-11-25 11:11:54 +01:00
Michele Caini
f54ed54247 helper: local non-static constexpr variables 2022-11-25 11:07:29 +01:00
Michele Caini
f30b50195a algorithm: local non-static constexpr variables 2022-11-25 10:59:47 +01:00
Michele Caini
c90ab9affd sparse_set:
* break dependency on traits_type::reserved
* use a tombstone if all I need is a tombstone
2022-11-25 10:23:43 +01:00
Michele Caini
c2f6ca43f1 doc: graph (close #957) 2022-11-24 09:33:34 +01:00
Michele Caini
3e5e41d88f test: cover some corner cases of the flow class 2022-11-24 09:33:04 +01:00
Michele Caini
9eafc0431d flow: minor changes 2022-11-24 09:31:14 +01:00
Michele Caini
0a82b777b2 component_traits: support specializations based on entity type 2022-11-23 14:53:31 +01:00
Michele Caini
32bcc01a46 component:
* make component_traits treat void properly
* drop ignore_as_empty_v
2022-11-23 12:36:21 +01:00
Michele Caini
9c3fe3546b nativs: entity module 2022-11-23 11:29:48 +01:00
Michele Caini
83f8aed583 helper: use traits_type from storage class directly 2022-11-23 11:28:08 +01:00
Michele Caini
2fd6602740 snapshot: use public registry traits_type member type 2022-11-23 11:11:54 +01:00
Michele Caini
a554d406e7 registry:
* public traits_type member type
* break dependency on component_traits
* use public storage traits_type member type
2022-11-23 11:06:51 +01:00
Michele Caini
5f12f872e6 test: minor changes 2022-11-23 11:04:56 +01:00
Michele Caini
be4eb68a30 helper:
* break dependency on component_traits
* use public storage traits_type member type
2022-11-23 08:58:45 +01:00
Michele Caini
df5284d9e5 view:
* break dependency on component_traits
* use public storage traits_type member type
2022-11-23 08:56:46 +01:00
Michele Caini
0e27d33e7e storage: public traits_type member type 2022-11-23 08:54:56 +01:00
Michele Caini
fe6e6ae738 sparse_set: public traits_type member type 2022-11-23 08:52:54 +01:00
Michele Caini
9d29713eaa entity: naming convention 2022-11-22 15:33:55 +01:00
Michele Caini
270d0277db group: cleanup 2022-11-22 15:33:20 +01:00
Michele Caini
0bd06c8d5d hashed_string: naming convention 2022-11-22 15:32:37 +01:00
Michele Caini
733f215ccf storage: break dependency between component_traits and storage_iterator 2022-11-22 14:53:20 +01:00
Michele Caini
ad01a69fe5 *: renaming/coding style 2022-11-22 14:45:26 +01:00
Michele Caini
dd9c1dade8 sparse_set: no need to differentiate template args for sparse_set_iterator 2022-11-22 14:44:59 +01:00
Michele Caini
b8f70519f6 doc: fixed typo 2022-11-22 14:26:46 +01:00
Michele Caini
9b9d212dde *: coding style 2022-11-22 12:44:31 +01:00
Michele Caini
3fe15969db doc: cleanup 2022-11-22 12:30:51 +01:00
Michele Caini
ec4bf222c6 meta: avoid the +1u trick for 0-sized arrays 2022-11-21 12:48:40 +01:00
Michele Caini
1173908ee4 meta: avoid rebinding when forwarding requests 2022-11-21 12:43:26 +01:00
Michele Caini
2595b8a925 doc: sigh_helper 2022-11-21 10:52:26 +01:00
Michele Caini
f4e2a8c76c sigh_builder: add all missing .template that msvc kindly accepted anyway 2022-11-21 10:38:44 +01:00
Michele Caini
66e1a05652 entity: sigh_helper utility with tests (close #928) 2022-11-21 10:38:42 +01:00
Michele Caini
87283dc41f storage: simplified impl in order to introduce multi-type storage more easily 2022-11-18 14:40:55 +01:00
Michele Caini
a802ebffed storage:
* move storage_type[_t] and storage_for[_t] to fwd.hpp
* no need to include storage.hpp when forward defining views
2022-11-17 17:48:11 +01:00
genar
b84b09421e doc: add Arch ECS to references.md (#954) 2022-11-17 08:45:25 +01:00
Michele Caini
940fd09396 todo: add a note for a (soon to be released) change 2022-11-17 08:42:11 +01:00
Michele Caini
920338be59 doc: add ecsact to links.md (thanks @zaucy for pointing this out) 2022-11-17 08:24:48 +01:00
Michele Caini
bcd1155b77 gh: add more gcc and clang versions 2022-11-16 11:10:13 +01:00
Michele Caini
1dc88109e8 gh: update workflows 2022-11-16 11:03:38 +01:00
Michele Caini
262c1f53c1 cmake: only enable -Wdocumentation for clang-cl 2022-11-16 10:47:03 +01:00
Michele Caini
4af0a3a0d1 doc: cleanup 2022-11-16 10:30:40 +01:00
Michele Caini
be16418289 doc: cleanup 2022-11-16 10:30:35 +01:00
Michele Caini
b54a52fbfe doc: fixed typo 2022-11-16 10:30:18 +01:00
Michele Caini
ae88159952 doc: fixed typo 2022-11-16 10:30:12 +01:00
Michele Caini
62c764f681 doc: fixed typo 2022-11-16 10:29:59 +01:00
Michele Caini
2c48cc10ae cmake: enable documentation diagnostic for clang 2022-11-16 10:23:00 +01:00
Michele Caini
82f2866789 sigh: drop redundant function 2022-11-15 15:40:10 +01:00
Michele Caini
d56e5a269c registry: propagate allocator to context 2022-11-15 14:48:10 +01:00
Michele Caini
1517b29513 doc: document delegate raw access 2022-11-15 08:27:00 +01:00
Michele Caini
bea7b43a1a delegate: target member function 2022-11-15 08:26:44 +01:00
Michele Caini
2f878f8b5a sigh: refine ::collect 2022-11-14 17:47:48 +01:00
Michele Caini
fc68c1b290 view/group: cleanup 2022-11-14 17:13:38 +01:00
Michele Caini
9081c185da meta: minor changes 2022-11-14 14:57:37 +01:00
Michele Caini
7c4493f237 group: make filter storage available 2022-11-14 11:19:22 +01:00
Michele Caini
da4e73ab85 view: make filter storage available 2022-11-14 11:00:57 +01:00
Michele Caini
f3e7f98b48 registry: extra check when moving a registry 2022-11-13 12:36:19 +01:00
Michele Caini
3925fc6124 emitter: extra allocator check when moving 2022-11-13 12:23:32 +01:00
Michele Caini
c639130c1e dispatcher: extra allocator check when moving 2022-11-13 12:21:21 +01:00
Michele Caini
75c3116008 registry: cleanup 2022-11-13 12:01:36 +01:00
Michele Caini
e9e14eb49c meta: [[nodiscard]] 2022-11-13 11:15:50 +01:00
Michele Caini
d1558304f8 any: [[nodiscard]] 2022-11-13 11:15:42 +01:00
Michele Caini
0531b530b1 snapshot: minor changes 2022-11-11 16:37:39 +01:00
Michele Caini
f9d0178dd7 workflow: bump iwyu version 2022-11-11 11:01:04 +01:00
Michele Caini
b66b8d37eb test: suppress warning 2022-11-11 10:59:10 +01:00
Michele Caini
05ef4c29d8 storage: minor changes 2022-11-10 18:47:02 +01:00
Michele Caini
9c3d756692 test: cleanup include directives 2022-11-10 15:56:17 +01:00
Michele Caini
93651e46f5 registry: drop [[deprecated]] functions 2022-11-10 15:05:48 +01:00
Michele Caini
ea901cbfa0 test: code coverage 2022-11-09 12:15:03 +01:00
Michele Caini
d5dc4f43ee doc: meta.md 2022-11-09 12:15:02 +01:00
Michele Caini
498e02f154 doc: core.md 2022-11-09 12:15:02 +01:00
Michele Caini
d0ea8f4f96 cmake: suppress some warnings for clang-cl, it goes a little wrong otherwise 2022-11-09 12:15:02 +01:00
Michele Caini
dec3b7bb39 test: suppress warnings 2022-11-09 12:15:02 +01:00
Michele Caini
10bc8b05ad test: use /W1 with VS (but for toolset v141, too bugged for that) 2022-11-09 12:15:02 +01:00
Michele Caini
ad77b54dce cmake: bump version to get some cool feature/update 2022-11-09 12:15:02 +01:00
Michele Caini
b6724b0283 group: pass filter storage to groups (in-between change for full storage access) 2022-11-09 12:15:02 +01:00
Michele Caini
54270b1038 group: make them easily copyable/movable 2022-11-09 12:15:02 +01:00
Michele Caini
31dc732a74 doc: graph.md 2022-11-09 12:15:02 +01:00
Michele Caini
f0e02d6d39 doc: container.md 2022-11-09 12:15:02 +01:00
Michele Caini
156d6e4ead doc: poly.md 2022-11-09 12:15:02 +01:00
Michele Caini
4375c1c3d6 doc: lib.md 2022-11-09 12:15:02 +01:00
Michele Caini
24a9cd67ee scheduler: forgot to add the fwd file to the previous commit :) 2022-11-09 12:15:02 +01:00
Michele Caini
ba8d522c19 doc: add the worst engine (love the name) to the list of links 2022-11-09 12:15:01 +01:00
Michele Caini
3ae46214a4 doc: review process.md 2022-11-09 12:15:01 +01:00
Michele Caini
5119fe8d7b scheduler: basic type model with default for common cases 2022-11-09 12:15:01 +01:00
Michele Caini
ed0319cdd8 view: avoid shadow warnings 2022-11-09 12:15:01 +01:00
Michele Caini
bc50da6a7c storage: suppress warnings with non copyable nor default constructible types 2022-11-09 12:15:01 +01:00
Michele Caini
52b3b4c249 group: suppress warnings for unused variables in case of empty types 2022-11-09 12:15:01 +01:00
Michele Caini
74bab529d2 test: minor changes 2022-11-09 12:15:01 +01:00
Michele Caini
b1b143917b meta: [[maybe_unused]] variable to avoid warnings with corner cases 2022-11-09 12:15:01 +01:00
Michele Caini
7beb4c85c4 test: suppress a few warnings (entity) 2022-11-09 12:15:01 +01:00
Michele Caini
f3beb5670a test: suppress a few warnings (container) 2022-11-09 12:15:01 +01:00
Michele Caini
446c67b69a test: suppress a few warnings (resource) 2022-11-09 12:15:01 +01:00
Michele Caini
c4507bd172 test: suppress a few warnings (poly) 2022-11-09 12:15:01 +01:00
Michele Caini
61e872bb4e test: suppress a few warnings (meta) 2022-11-09 12:15:01 +01:00
Michele Caini
9f22a3e23a test: suppress a few warnings (memory) 2022-11-09 12:15:00 +01:00
Michele Caini
653dd5cd42 test: suppress a few warnings (tuple) 2022-11-09 12:15:00 +01:00
Michele Caini
bc53ed3be9 test: suppress a few warnings (flow) 2022-11-09 12:15:00 +01:00
Michele Caini
f935bbccee dense_set: suppress warnings due to possible narrowing conversions 2022-11-09 12:15:00 +01:00
Michele Caini
c7d5053536 dense_map: suppress warnings due to possible narrowing conversions 2022-11-09 12:15:00 +01:00
Michele Caini
ea78f1d970 now working on version 3.12 2022-11-09 12:14:10 +01:00
225 changed files with 38539 additions and 25735 deletions

1
.bazelignore Normal file
View File

@@ -0,0 +1 @@
test

1
.bazeliskrc Normal file
View File

@@ -0,0 +1 @@
USE_BAZEL_VERSION=6.x

17
.bazelrc Normal file
View File

@@ -0,0 +1,17 @@
common --enable_bzlmod
build --enable_platform_specific_config
build --incompatible_use_platforms_repo_for_constraints
build --incompatible_enable_cc_toolchain_resolution
build --enable_runfiles
build --incompatible_strict_action_env
# required for googletest
build:linux --cxxopt=-std=c++17
build:macos --cxxopt=-std=c++17
common:ci --announce_rc
common:ci --verbose_failures
common:ci --keep_going
test:ci --test_output=errors
try-import %workspace%/user.bazelrc

26
.clang-tidy Normal file
View File

@@ -0,0 +1,26 @@
Checks: >
bugprone-*,
concurrency-*,
cppcoreguidelines-*,
misc-*,
-misc-no-recursion,
modernize-*,
-modernize-use-trailing-return-type,
performance-*,
portability-*,
readibility-*
CheckOptions:
- key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor
value: true
- key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions
value: true
- key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctionsWhenCopyIsDeleted
value: true
- key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic
value: true
- key: misc-non-private-member-variables-in-classes.IgnorePublicMemberVariables
value: true
- key: cppcoreguidelines-avoid-magic-numbers.IgnorePowersOf2IntegerValues
value: true
- key: cppcoreguidelines-avoid-magic-numbers.IgnoreAllFloatingPointValues
value: true

View File

@@ -12,14 +12,14 @@ jobs:
timeout-minutes: 30
env:
IWYU: 0.18
LLVM: 14
IWYU: "0.20"
LLVM: "16"
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Install llvm/clang
# see: https://apt.llvm.org/
run: |
@@ -39,17 +39,23 @@ jobs:
git clone https://github.com/include-what-you-use/include-what-you-use.git --branch $IWYU --depth 1
mkdir include-what-you-use/build
cd include-what-you-use/build
cmake -DCMAKE_C_COMPILER=clang-$LLVM -DCMAKE_CXX_COMPILER=clang++-$LLVM -DCMAKE_INSTALL_PREFIX=./ ..
cmake -DCMAKE_C_COMPILER=clang-$LLVM \
-DCMAKE_CXX_COMPILER=clang++-$LLVM \
-DCMAKE_INSTALL_PREFIX=./ \
..
make -j4
bin/include-what-you-use --version
- name: Compile tests
working-directory: build
run: |
export PATH=$PATH:${GITHUB_WORKSPACE}/build/include-what-you-use/build/bin
cmake -DENTT_BUILD_TESTING=ON \
cmake -DCMAKE_C_COMPILER=clang-$LLVM \
-DCMAKE_CXX_COMPILER=clang++-$LLVM \
-DENTT_BUILD_TESTING=ON \
-DENTT_BUILD_BENCHMARK=ON \
-DENTT_BUILD_EXAMPLE=ON \
-DENTT_BUILD_LIB=ON \
-DENTT_BUILD_SNAPSHOT=ON \
-DCMAKE_CXX_INCLUDE_WHAT_YOU_USE="include-what-you-use;-Xiwyu;--mapping_file=${GITHUB_WORKSPACE}/entt.imp;-Xiwyu;--no_fwd_decls;-Xiwyu;--verbose=1" ..
-DCMAKE_CXX_INCLUDE_WHAT_YOU_USE="include-what-you-use;-Xiwyu;--mapping_file=${GITHUB_WORKSPACE}/entt.imp;-Xiwyu;--no_fwd_decls;-Xiwyu;--verbose=1" \
..
make -j4

View File

@@ -0,0 +1,20 @@
name: Bazel Release
on:
release:
types: [published]
jobs:
# A release archive is required for bzlmod
# See: https://blog.bazel.build/2023/02/15/github-archive-checksum.html
bazel-release-archive:
runs-on: ubuntu-latest
continue-on-error: true
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- run: git archive $GITHUB_REF -o "entt-${GITHUB_REF:10}.tar.gz"
- run: gh release upload ${GITHUB_REF:10} "entt-${GITHUB_REF:10}.tar.gz"
env:
GH_TOKEN: ${{ github.token }}

20
.github/workflows/bazel.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
name: bazel
on: [push, pull_request]
jobs:
test:
strategy:
matrix:
os:
- ubuntu-latest
- windows-latest
- macos-latest
runs-on: ${{ matrix.os }}
continue-on-error: true
steps:
- uses: actions/checkout@v4
- run: bazelisk test --config=ci ...
working-directory: test
env:
USE_BAZEL_VERSION: 6.x

View File

@@ -9,38 +9,61 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, ubuntu-20.04]
compiler:
- pkg: g++-7
exe: g++-7
- pkg: g++-8
exe: g++-8
- pkg: g++-9
exe: g++-9
- pkg: g++-10
exe: g++-10
- pkg: clang-8
exe: clang++-8
- pkg: clang-9
exe: clang++-9
- pkg: clang-10
exe: clang++-10
- pkg: clang-11
exe: clang++-11
- pkg: clang-12
exe: clang++-12
- { pkg: g++, exe: 'g++', version: 7 }
- { pkg: g++, exe: 'g++', version: 8 }
- { pkg: g++, exe: 'g++', version: 9 }
- { pkg: g++, exe: 'g++', version: 10 }
- { pkg: g++, exe: 'g++', version: 11 }
- { pkg: g++, exe: 'g++', version: 12 }
- { pkg: clang, exe: 'clang++', version: 8 }
- { pkg: clang, exe: 'clang++', version: 9 }
- { pkg: clang, exe: 'clang++', version: 10 }
- { pkg: clang, exe: 'clang++', version: 11 }
- { pkg: clang, exe: 'clang++', version: 12 }
- { pkg: clang, exe: 'clang++', version: 13 }
- { pkg: clang, exe: 'clang++', version: 14 }
exclude:
- os: ubuntu-latest
compiler: { pkg: g++, exe: 'g++', version: 7 }
- os: ubuntu-latest
compiler: { pkg: g++, exe: 'g++', version: 8 }
- os: ubuntu-latest
compiler: { pkg: g++, exe: 'g++', version: 9 }
- os: ubuntu-latest
compiler: { pkg: clang, exe: 'clang++', version: 8 }
- os: ubuntu-latest
compiler: { pkg: clang, exe: 'clang++', version: 9 }
- os: ubuntu-latest
compiler: { pkg: clang, exe: 'clang++', version: 10 }
- os: ubuntu-latest
compiler: { pkg: clang, exe: 'clang++', version: 11 }
- os: ubuntu-20.04
compiler: { pkg: g++, exe: 'g++', version: 10 }
- os: ubuntu-20.04
compiler: { pkg: g++, exe: 'g++', version: 11 }
- os: ubuntu-20.04
compiler: { pkg: g++, exe: 'g++', version: 12 }
- os: ubuntu-20.04
compiler: { pkg: clang, exe: 'clang++', version: 12 }
- os: ubuntu-20.04
compiler: { pkg: clang, exe: 'clang++', version: 13 }
- os: ubuntu-20.04
compiler: { pkg: clang, exe: 'clang++', version: 14 }
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Install compiler
run: |
sudo apt update
sudo apt install -y ${{ matrix.compiler.pkg }}
sudo apt install -y ${{ matrix.compiler.pkg }}-${{ matrix.compiler.version }}
- name: Compile tests
working-directory: build
env:
CXX: ${{ matrix.compiler.exe }}
CXX: ${{ matrix.compiler.exe }}-${{ matrix.compiler.version }}
run: |
cmake -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON ..
make -j4
@@ -48,33 +71,7 @@ jobs:
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 30 -C Debug -j4
linux-extra:
timeout-minutes: 15
strategy:
matrix:
compiler: [g++, clang++]
id_type: ["std::uint32_t", "std::uint64_t"]
cxx_std: [cxx_std_17, cxx_std_20]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Compile tests
working-directory: build
env:
CXX: ${{ matrix.compiler }}
run: |
cmake -DENTT_BUILD_TESTING=ON -DENTT_CXX_STD=${{ matrix.cxx_std }} -DENTT_ID_TYPE=${{ matrix.id_type }} ..
make -j4
- name: Run tests
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 30 -C Debug -j4
run: ctest -C Debug -j4
windows:
timeout-minutes: 15
@@ -93,7 +90,7 @@ jobs:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Compile tests
working-directory: build
run: |
@@ -103,37 +100,14 @@ jobs:
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 30 -C Debug -j4
windows-extra:
timeout-minutes: 15
strategy:
matrix:
id_type: ["std::uint32_t", "std::uint64_t"]
cxx_std: [cxx_std_17, cxx_std_20]
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- name: Compile tests
working-directory: build
run: |
cmake -DENTT_BUILD_TESTING=ON -DENTT_CXX_STD=${{ matrix.cxx_std }} -DENTT_ID_TYPE=${{ matrix.id_type }} ..
cmake --build . -j 4
- name: Run tests
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 30 -C Debug -j4
run: ctest -C Debug -j4
macos:
timeout-minutes: 15
runs-on: macOS-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Compile tests
working-directory: build
run: |
@@ -143,27 +117,28 @@ jobs:
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 30 -C Debug -j4
run: ctest -C Debug -j4
macos-extra:
extra:
timeout-minutes: 15
strategy:
matrix:
os: [windows-latest, macOS-latest, ubuntu-latest]
id_type: ["std::uint32_t", "std::uint64_t"]
cxx_std: [cxx_std_17, cxx_std_20]
runs-on: macOS-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Compile tests
working-directory: build
run: |
cmake -DENTT_BUILD_TESTING=ON -DENTT_CXX_STD=${{ matrix.cxx_std }} -DENTT_ID_TYPE=${{ matrix.id_type }} ..
make -j4
cmake --build . -j 4
- name: Run tests
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 30 -C Debug -j4
run: ctest -C Debug -j4

View File

@@ -9,11 +9,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Compile tests
working-directory: build
env:
CXXFLAGS: "--coverage -fno-inline"
CXXFLAGS: "--coverage -fno-elide-constructors -fno-inline -fno-default-inline"
CXX: g++
run: |
cmake -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON ..
@@ -22,7 +22,7 @@ jobs:
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 30 -C Debug -j4
run: ctest -C Debug -j4
- name: Collect data
working-directory: build
run: |
@@ -30,7 +30,7 @@ jobs:
lcov -c -d . -o coverage.info
lcov -l coverage.info
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v2
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: build/coverage.info

View File

@@ -15,7 +15,7 @@ jobs:
FORMULA: entt.rb
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Clone repository
working-directory: build
env:

View File

@@ -16,7 +16,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
# temporary workaround for https://github.com/actions/runner-images/issues/8659
- uses: mjp41/workaround8649@c8550b715ccdc17f89c8d5c28d7a48eeff9c94a8
- name: Compile tests
working-directory: build
env:
@@ -28,4 +30,4 @@ jobs:
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 30 -C Debug -j4
run: ctest -C Debug -j4

2
.gitignore vendored
View File

@@ -11,3 +11,5 @@ cpp.hint
# Bazel
/bazel-*
/test/bazel-*
/user.bazelrc

View File

@@ -1,14 +1,6 @@
_msvc_copts = ["/std:c++17"]
_gcc_copts = ["-std=c++17"]
package(default_visibility = ["//visibility:public"])
cc_library(
alias(
name = "entt",
visibility = ["//visibility:public"],
strip_include_prefix = "src",
hdrs = glob(["src/**/*.h", "src/**/*.hpp"]),
copts = select({
"@bazel_tools//src/conditions:windows": _msvc_copts,
"@bazel_tools//src/conditions:windows_msvc": _msvc_copts,
"//conditions:default": _gcc_copts,
}),
actual = "//src:entt",
)

View File

@@ -1,21 +1,15 @@
#
# EnTT
#
cmake_minimum_required(VERSION 3.12.4)
cmake_minimum_required(VERSION 3.15.7)
#
# Read project version
#
set(ENTT_VERSION_REGEX "#define ENTT_VERSION_.*[ \t]+(.+)")
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/src/entt/config/version.h" ENTT_VERSION REGEX ${ENTT_VERSION_REGEX})
list(TRANSFORM ENTT_VERSION REPLACE ${ENTT_VERSION_REGEX} "\\1")
string(JOIN "." ENTT_VERSION ${ENTT_VERSION})
#
# Project configuration
#
project(
EnTT
@@ -31,21 +25,18 @@ endif()
message(VERBOSE "*")
message(VERBOSE "* ${PROJECT_NAME} v${PROJECT_VERSION} (${CMAKE_BUILD_TYPE})")
message(VERBOSE "* Copyright (c) 2017-2022 Michele Caini <michele.caini@gmail.com>")
message(VERBOSE "* Copyright (c) 2017-2023 Michele Caini <michele.caini@gmail.com>")
message(VERBOSE "*")
#
# CMake stuff
#
list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
#
# Compiler stuff
#
option(ENTT_USE_LIBCPP "Use libc++ by adding -stdlib=libc++ flag if available." OFF)
option(ENTT_USE_SANITIZER "Enable sanitizers by adding -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined flags if available." OFF)
option(ENTT_USE_CLANG_TIDY "Enable static analysis with clang-tidy" OFF)
if(ENTT_USE_LIBCPP)
if(NOT WIN32)
@@ -65,7 +56,7 @@ if(ENTT_USE_LIBCPP)
endif()
if(NOT ENTT_HAS_LIBCPP)
message(VERBOSE "The option ENTT_USE_LIBCPP is set but libc++ is not available. The flag will not be added to the target.")
message(VERBOSE "The option ENTT_USE_LIBCPP is set but libc++ is not available.")
endif()
endif()
@@ -76,13 +67,19 @@ if(ENTT_USE_SANITIZER)
endif()
if(NOT ENTT_HAS_SANITIZER)
message(VERBOSE "The option ENTT_USE_SANITIZER is set but sanitizer support is not available. The flags will not be added to the target.")
message(VERBOSE "The option ENTT_USE_SANITIZER is set but sanitizer support is not available.")
endif()
endif()
if(ENTT_USE_CLANG_TIDY)
find_program(ENTT_CLANG_TIDY_EXECUTABLE "clang-tidy")
if(NOT ENTT_CLANG_TIDY_EXECUTABLE)
message(VERBOSE "The option ENTT_USE_CLANG_TIDY is set but clang-tidy executable is not available.")
endif()
endif()
#
# Add EnTT target
#
option(ENTT_INCLUDE_HEADERS "Add all EnTT headers to the EnTT target." OFF)
option(ENTT_INCLUDE_NATVIS "Add EnTT natvis files to the EnTT target." OFF)
@@ -143,6 +140,7 @@ if(ENTT_INCLUDE_HEADERS)
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/fwd.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/group.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/handle.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/mixin.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/helper.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/observer.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/organizer.hpp>
@@ -151,7 +149,6 @@ if(ENTT_INCLUDE_HEADERS)
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/snapshot.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/sparse_set.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/storage.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/storage_mixin.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/view.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/graph/adjacency_matrix.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/graph/dot.hpp>
@@ -216,13 +213,15 @@ if(ENTT_HAS_SANITIZER)
target_link_libraries(EnTT INTERFACE $<$<CONFIG:Debug>:-fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined>)
endif()
if(ENTT_CLANG_TIDY_EXECUTABLE)
set(CMAKE_CXX_CLANG_TIDY "${ENTT_CLANG_TIDY_EXECUTABLE};--config-file=${EnTT_SOURCE_DIR}/.clang-tidy;--header-filter=${EnTT_SOURCE_DIR}/src/entt/.*;--extra-arg=/EHsc")
endif()
if(ENTT_HAS_LIBCPP)
target_compile_options(EnTT BEFORE INTERFACE -stdlib=libc++)
endif()
#
# Install pkg-config file
#
include(JoinPaths)
@@ -241,9 +240,7 @@ install(
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
)
#
# Install EnTT
#
include(CMakePackageConfigHelpers)
@@ -285,13 +282,17 @@ install(
DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake
)
install(DIRECTORY src/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(
DIRECTORY src/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
FILES_MATCHING
PATTERN "*.h"
PATTERN "*.hpp"
)
export(PACKAGE EnTT)
#
# Tests
#
option(ENTT_BUILD_TESTING "Enable building tests." OFF)
@@ -310,9 +311,15 @@ if(ENTT_BUILD_TESTING)
add_subdirectory(test)
endif()
#
# Tools
option(ENTT_BUILD_TOOLS "Enable building tools." OFF)
if(ENTT_BUILD_TOOLS)
add_subdirectory(tools)
endif()
# Documentation
#
option(ENTT_BUILD_DOCS "Enable building with documentation." OFF)

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2017-2022 Michele Caini, author of EnTT
Copyright (c) 2017-2023 Michele Caini, author of EnTT
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

8
MODULE.bazel Normal file
View File

@@ -0,0 +1,8 @@
module(
name = "entt",
version = "3.12.2",
compatibility_level = 3012,
)
bazel_dep(name = "rules_cc", version = "0.0.8")
bazel_dep(name = "bazel_skylib", version = "1.4.2")

View File

@@ -6,10 +6,11 @@
[![Build Status](https://github.com/skypjack/entt/workflows/build/badge.svg)](https://github.com/skypjack/entt/actions)
[![Coverage](https://codecov.io/gh/skypjack/entt/branch/master/graph/badge.svg)](https://codecov.io/gh/skypjack/entt)
[![Try online](https://img.shields.io/badge/try-online-brightgreen)](https://godbolt.org/z/zxW73f)
[![Documentation](https://img.shields.io/badge/docs-docsforge-blue)](http://entt.docsforge.com/)
[![Documentation](https://img.shields.io/badge/docs-doxygen-blue)](https://skypjack.github.io/entt/)
[![Vcpkg port](https://img.shields.io/vcpkg/v/entt)](https://vcpkg.link/ports/entt)
[![Conan Center](https://img.shields.io/conan/v/entt)](https://conan.io/center/recipes/entt)
[![Gitter chat](https://badges.gitter.im/skypjack/entt.png)](https://gitter.im/skypjack/entt)
[![Discord channel](https://img.shields.io/discord/707607951396962417?logo=discord)](https://discord.gg/5BjPWBd)
[![Donate](https://img.shields.io/badge/donate-paypal-blue.svg)](https://www.paypal.me/skypjack)
> `EnTT` has been a dream so far, we haven't found a single bug to date and it's
> super easy to work with
@@ -38,7 +39,8 @@ Don't forget to check the
there.
Do you want to support `EnTT`? Consider becoming a
[**sponsor**](https://github.com/users/skypjack/sponsorship).
[**sponsor**](https://github.com/users/skypjack/sponsorship) or making a
donation via [**PayPal**](https://www.paypal.me/skypjack).<br/>
Many thanks to [these people](https://skypjack.github.io/sponsorship/) and
**special** thanks to:
@@ -50,7 +52,7 @@ Many thanks to [these people](https://skypjack.github.io/sponsorship/) and
* [Introduction](#introduction)
* [Code Example](#code-example)
* [Motivation](#motivation)
* [Performance](#performance)
* [Benchmark](#benchmark)
* [Integration](#integration)
* [Requirements](#requirements)
* [CMake](#cmake)
@@ -174,37 +176,28 @@ Nowadays, `EnTT` is finally what I was looking for: still faster than its
_competitors_, lower memory usage in the average case, a really good API and an
amazing set of features. And even more, of course.
## Performance
## Benchmark
The proposed entity-component system is incredibly fast to iterate entities and
components, this is a fact. Some compilers make a lot of optimizations because
of how `EnTT` works, some others aren't that good. In general, if we consider
real world cases, `EnTT` is somewhere between a bit and much faster than many of
the other solutions around, although I couldn't check them all for obvious
reasons.
For what it's worth, you'll **never** see me trying to make other projects look
bad or offer dubious comparisons just to make this library seem cooler.<br/>
I leave this activity to others, if they enjoy it (and it seems that some people
actually like it). I prefer to make better use of my time.
If you are interested, you can compile the `benchmark` test in release mode (to
enable compiler optimizations, otherwise it would make little sense) by setting
the `ENTT_BUILD_BENCHMARK` option of `CMake` to `ON`, then evaluate yourself
whether you're satisfied with the results or not.
Honestly I got tired of updating the README file whenever there is an
improvement.<br/>
There are already a lot of projects out there that use `EnTT` as a basis for
There are also a lot of projects out there that use `EnTT` as a basis for
comparison (this should already tell you a lot). Many of these benchmarks are
completely wrong, many others are simply incomplete, good at omitting some
information and using the wrong function to compare a given feature. Certainly
there are also good ones but they age quickly if nobody updates them, especially
when the library they are dealing with is actively developed.
The choice to use `EnTT` should be based on its carefully designed API, its
set of features and the general performance, **not** because some single
benchmark shows it to be the fastest tool available.
In the future I'll likely try to get even better performance while still adding
new features, mainly for fun.<br/>
If you want to contribute and/or have suggestions, feel free to make a PR or
open an issue to discuss your idea.
when the library they are dealing with is actively developed.<br/>
Out of all of them, [this](https://github.com/abeimler/ecs_benchmark) seems like
the most up-to-date project and also covers a certain number of libraries. I
can't say exactly whether `EnTT` is used correctly or not. However, even if used
poorly, it should still give the reader an idea of where it's going to operate.
# Integration
@@ -326,6 +319,18 @@ If you spot errors or have suggestions, any contribution is welcome!
[documentation](https://build2.org/build2-toolchain/doc/build2-toolchain-intro.xhtml#guide-repositories)
for more details.
* [`bzlmod`](https://bazel.build/external/overview#bzlmod), Bazel's external
dependency management system.<br/>
To use the [`entt`](https://registry.bazel.build/modules/entt) module in a
`bazel` project, add the following to your `MODULE.bazel` file:
```starlark
bazel_dep(name = "entt", version = "3.12.2")
```
EnTT will now be available as `@entt` (short for `@entt//:entt`) to be used
in your `cc_*` rule `deps`.
Consider this list a work in progress and help me to make it longer if you like.
## pkg-config
@@ -343,8 +348,8 @@ The documentation is based on [doxygen](http://www.doxygen.nl/). To build it:
$ cmake .. -DENTT_BUILD_DOCS=ON
$ make
The API reference will be created in HTML format within the directory
`build/docs/html`. To navigate it with your favorite browser:
The API reference is created in HTML format in the `build/docs/html` directory.
To navigate it with your favorite browser:
$ cd build
$ your_favorite_browser docs/html/index.html
@@ -353,10 +358,7 @@ The API reference will be created in HTML format within the directory
@cond TURN_OFF_DOXYGEN
-->
The same version is also available [online](https://skypjack.github.io/entt/)
for the latest release, that is the last stable tag. If you are looking for
something more pleasing to the eye, consider reading the nice-looking version
available on [docsforge](https://entt.docsforge.com/): same documentation, much
more pleasant to read.<br/>
for the latest release, that is the last stable tag.<br/>
Moreover, there exists a [wiki](https://github.com/skypjack/entt/wiki) dedicated
to the project where users can find all related documentation pages.
<!--
@@ -366,9 +368,8 @@ to the project where users can find all related documentation pages.
# Tests
To compile and run the tests, `EnTT` requires *googletest*.<br/>
`cmake` will download and compile the library before compiling anything else.
In order to build the tests, set the `CMake` option `ENTT_BUILD_TESTING` to
`ON`.
`cmake` downloads and compiles the library before compiling anything else. In
order to build the tests, set the `CMake` option `ENTT_BUILD_TESTING` to `ON`.
To build the most basic set of tests:
@@ -420,7 +421,7 @@ know who has participated so far.
# License
Code and documentation Copyright (c) 2017-2022 Michele Caini.<br/>
Code and documentation Copyright (c) 2017-2023 Michele Caini.<br/>
Colorful logo Copyright (c) 2018-2021 Richard Caseres.
Code released under

38
TODO
View File

@@ -1,27 +1,31 @@
* debugging tools (#60): the issue online already contains interesting tips on this, look at it
* work stealing job system (see #100) + mt scheduler based on const awareness for types
EXAMPLES
* filter on runtime values/variables (not only types)
* support to polymorphic types (see #859)
DOC:
* storage<void>
* custom storage/view
* examples (and credits) from @alanjfs :)
* update entity doc when the storage based model is in place
* in-place O(1) release/destroy for non-orphaned entities, out-of-sync model
* view: single vs multi type views are no longer a thing actually
* bump entities, reserved bits on identifiers
TODO (high prio):
* remove the static storage from the const assure in the registry
WIP:
TODO:
* resource cache: avoid using shared ptr with loader and the others
* further optimize exclusion lists in multi type views (no existence check)
* further improve meta resolve function by id (bimap)
* get rid of observers, storage based views made them pointless - document alternatives
* add storage getter for filters to views and groups
* exploit the tombstone mechanism to allow enabling/disabling entities (see bump, compact and clear for further details)
* basic_storage::bind for cross-registry setups (see and remove todo from entity_copy.cpp)
* process scheduler: reviews, use free lists internally
* dedicated entity storage, in-place O(1) release/destroy for non-orphaned entities, out-of-sync model
* entity-only and exclude-only views (both solved with entity storage and storage<void>)
* custom allocators all over (registry, ...)
* add test for maximum number of entities reached
* deprecate non-owning groups in favor of owning views and view packs, introduce lazy owning views
* bring nested groups back in place (see bd34e7f)
* work stealing job system (see #100) + mt scheduler based on const awareness for types
* view: reduce inst due to/improve perf with index-based approach in dispatch_get/pick_and_each/each (single type too, define storage ::at and ::at_as_tuple)
* view: update natvis as needed after the last rework, merge pools/filter in the same array, drop check (?) and turn view into a position
* view: type-only view_iterator (dyn get/excl sizes), type-only basic_common_view (dyn get/excl sizes with pointer to array from derived)
* combine version-mask-vs-version-bits tricks with reserved bits to allow things like enabling/disabling
* basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) uses moved-from packed in assert (same for storage)
* maybe drop begin(v)/end(v) from sparse set and only use scoped begin/end at call sites
* view unchecked_refresh loop is never executed if Get is 1u
* review all // NOLINT + linter workflow + review clang-tidy file
* self contained entity traits to avoid explicit specializations (ie enum constants)
* review assure conflicts check, hash doesn't fit the purpose maybe
* allow to zero-sized versions (with non-regression tests)
* auto type info data from types if present

View File

@@ -1 +0,0 @@
workspace(name = "com_github_skypjack_entt")

1
WORKSPACE.bazel Normal file
View File

@@ -0,0 +1 @@
# SEE MODULE.bazel

0
bazel/BUILD.bazel Normal file
View File

13
bazel/copts.bzl Normal file
View File

@@ -0,0 +1,13 @@
load("@bazel_skylib//lib:selects.bzl", "selects")
COPTS = selects.with_or({
("//conditions:default", "@rules_cc//cc/compiler:clang", "@rules_cc//cc/compiler:gcc", "@rules_cc//cc/compiler:mingw-gcc"): [
"-std=c++17",
"-w",
],
("@rules_cc//cc/compiler:msvc-cl", "@rules_cc//cc/compiler:clang-cl"): [
"/std:c++17",
"/permissive-",
"/w",
],
})

View File

@@ -1,9 +1,23 @@
#
# Doxygen configuration (documentation)
#
set(DOXY_DEPS_DIRECTORY ${EnTT_SOURCE_DIR}/deps)
include(FetchContent)
FetchContent_Declare(
doxygen-awesome-css
GIT_REPOSITORY https://github.com/jothepro/doxygen-awesome-css
GIT_TAG main
GIT_SHALLOW 1
)
FetchContent_GetProperties(doxygen-awesome-css)
if(NOT doxygen-awesome-css_POPULATED)
FetchContent_Populate(doxygen-awesome-css)
set(doxygen-awesome-css_INCLUDE_DIR ${doxygen-awesome-css_SOURCE_DIR})
endif()
set(DOXY_SOURCE_DIRECTORY ${EnTT_SOURCE_DIR}/src)
set(DOXY_CSS_DIRECTORY ${doxygen-awesome-css_INCLUDE_DIR})
set(DOXY_DOCS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
set(DOXY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})

View File

@@ -1,4 +1,4 @@
# Doxyfile 1.9.4
# Doxyfile 1.9.7
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
@@ -19,7 +19,8 @@
# configuration file:
# doxygen -x [configFile]
# Use doxygen to compare the used configuration file with the template
# configuration file without replacing the environment variables:
# configuration file without replacing the environment variables or CMake type
# replacement variables:
# doxygen -x_noenv [configFile]
#---------------------------------------------------------------------------
@@ -85,7 +86,7 @@ CREATE_SUBDIRS = NO
# level increment doubles the number of directories, resulting in 4096
# directories at level 8 which is the default and also the maximum value. The
# sub-directories are organized in 2 levels, the first level always has a fixed
# numer of 16 directories.
# number of 16 directories.
# Minimum value: 0, maximum value: 8, default value: 8.
# This tag requires that the tag CREATE_SUBDIRS is set to YES.
@@ -352,6 +353,17 @@ MARKDOWN_SUPPORT = YES
TOC_INCLUDE_HEADINGS = 5
# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to
# generate identifiers for the Markdown headings. Note: Every identifier is
# unique.
# Possible values are: DOXYGEN Use a fixed 'autotoc_md' string followed by a
# sequence number starting at 0. and GITHUB Use the lower case version of title
# with any whitespace replaced by '-' and punctations characters removed..
# The default value is: DOXYGEN.
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
MARKDOWN_ID_STYLE = DOXYGEN
# When enabled doxygen tries to link words that correspond to documented
# classes, or namespaces to their corresponding documentation. Such a link can
# be prevented in individual cases by putting a % sign in front of the word or
@@ -476,6 +488,14 @@ LOOKUP_CACHE_SIZE = 0
NUM_PROC_THREADS = 1
# If the TIMESTAMP tag is set different from NO then each generated page will
# contain the date or date and time when the page was generated. Setting this to
# NO can help when comparing the output of multiple runs.
# Possible values are: YES, NO, DATETIME and DATE.
# The default value is: NO.
TIMESTAMP = NO
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
@@ -557,7 +577,8 @@ HIDE_UNDOC_MEMBERS = NO
# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
# undocumented classes that are normally visible in the class hierarchy. If set
# to NO, these classes will be included in the various overviews. This option
# has no effect if EXTRACT_ALL is enabled.
# will also hide undocumented C++ concepts if enabled. This option has no effect
# if EXTRACT_ALL is enabled.
# The default value is: NO.
HIDE_UNDOC_CLASSES = NO
@@ -595,7 +616,8 @@ INTERNAL_DOCS = NO
# Windows (including Cygwin) and MacOS, users should typically set this option
# to NO, whereas on Linux or other Unix flavors it should typically be set to
# YES.
# The default value is: system dependent.
# Possible values are: SYSTEM, NO and YES.
# The default value is: SYSTEM.
CASE_SENSE_NAMES = YES
@@ -847,11 +869,26 @@ WARN_IF_INCOMPLETE_DOC = YES
WARN_NO_PARAMDOC = YES
# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about
# undocumented enumeration values. If set to NO, doxygen will accept
# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag
# will automatically be disabled.
# The default value is: NO.
WARN_IF_UNDOC_ENUM_VAL = NO
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
# at the end of the doxygen process doxygen will return with a non-zero status.
# Possible values are: NO, YES and FAIL_ON_WARNINGS.
# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves
# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not
# write the warning messages in between other messages but write them at the end
# of a run, in case a WARN_LOGFILE is defined the warning messages will be
# besides being in the defined file also be shown at the end of a run, unless
# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case
# the behavior will remain as with the setting FAIL_ON_WARNINGS.
# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT.
# The default value is: NO.
WARN_AS_ERROR = NO
@@ -905,10 +942,21 @@ INPUT = @DOXY_SOURCE_DIRECTORY@ \
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
# documentation (see:
# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
# See also: INPUT_FILE_ENCODING
# The default value is: UTF-8.
INPUT_ENCODING = UTF-8
# This tag can be used to specify the character encoding of the source files
# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify
# character encoding on a per file pattern basis. Doxygen will compare the file
# name with each pattern and apply the encoding instead of the default
# INPUT_ENCODING) if there is a match. The character encodings are a list of the
# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding
# "INPUT_ENCODING" for further information on supported encodings.
INPUT_FILE_ENCODING =
# If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
# *.h) to filter out the source-files in the directories.
@@ -945,7 +993,7 @@ RECURSIVE = YES
# Note that relative paths are relative to the directory from which doxygen is
# run.
EXCLUDE = @DOXY_DEPS_DIRECTORY@
EXCLUDE =
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded
@@ -968,9 +1016,6 @@ EXCLUDE_PATTERNS =
# output. The symbol name can be a fully qualified name, a word, or if the
# wildcard * is used, a substring. Examples: ANamespace, AClass,
# ANamespace::AClass, ANamespace::*Test
#
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories use the pattern */test/*
EXCLUDE_SYMBOLS =
@@ -1015,6 +1060,11 @@ IMAGE_PATH =
# code is scanned, but not when the output code is generated. If lines are added
# or removed, the anchors will not be placed correctly.
#
# Note that doxygen will use the data processed and written to standard output
# for further processing, therefore nothing else, like debug statements or used
# commands (so in case of a Windows batch file always use @echo OFF), should be
# written to standard output.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# properly processed by doxygen.
@@ -1056,6 +1106,15 @@ FILTER_SOURCE_PATTERNS =
USE_MDFILE_AS_MAINPAGE = @PROJECT_SOURCE_DIR@/README.md
# The Fortran standard specifies that for fixed formatted Fortran code all
# characters from position 72 are to be considered as comment. A common
# extension is to allow longer lines before the automatic comment starts. The
# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can
# be processed before the automatic comment starts.
# Minimum value: 7, maximum value: 10000, default value: 72.
FORTRAN_COMMENT_AFTER = 72
#---------------------------------------------------------------------------
# Configuration options related to source browsing
#---------------------------------------------------------------------------
@@ -1193,10 +1252,11 @@ CLANG_DATABASE_PATH =
ALPHABETICAL_INDEX = YES
# In case all classes in a project start with a common prefix, all classes will
# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
# can be used to specify a prefix (or a list of prefixes) that should be ignored
# while generating the index headers.
# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes)
# that should be ignored while generating the index headers. The IGNORE_PREFIX
# tag works for classes, function and member names. The entity will be placed in
# the alphabetical list under the first letter of the entity name that remains
# after removing the prefix.
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
IGNORE_PREFIX =
@@ -1265,7 +1325,7 @@ HTML_FOOTER =
# obsolete.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_STYLESHEET =
HTML_STYLESHEET = @DOXY_CSS_DIRECTORY@/doxygen-awesome.css
# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
# cascading style sheets that are included after the standard style sheets
@@ -1275,7 +1335,12 @@ HTML_STYLESHEET =
# Doxygen will copy the style sheet files to the output directory.
# Note: The order of the extra style sheet files is of importance (e.g. the last
# style sheet in the list overrules the setting of the previous ones in the
# list). For an example see the documentation.
# list).
# Note: Since the styling of scrollbars can currently not be overruled in
# Webkit/Chromium, the styling will be left out of the default doxygen.css if
# one or more extra stylesheets have been specified. So if scrollbar
# customization is desired it has to be added explicitly. For an example see the
# documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_STYLESHEET =
@@ -1290,6 +1355,19 @@ HTML_EXTRA_STYLESHEET =
HTML_EXTRA_FILES =
# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output
# should be rendered with a dark or light theme.
# Possible values are: LIGHT always generate light mode output, DARK always
# generate dark mode output, AUTO_LIGHT automatically set the mode according to
# the user preference, use light mode if no preference is set (the default),
# AUTO_DARK automatically set the mode according to the user preference, use
# dark mode if no preference is set and TOGGLE allow to user to switch between
# light and dark mode via a button.
# The default value is: AUTO_LIGHT.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE = LIGHT
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
# will adjust the colors in the style sheet and background images according to
# this color. Hue is specified as an angle on a color-wheel, see
@@ -1320,15 +1398,6 @@ HTML_COLORSTYLE_SAT = 100
HTML_COLORSTYLE_GAMMA = 80
# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
# page will contain the date and time when the page was generated. Setting this
# to YES can help to show when doxygen was last run and thus if the
# documentation is up to date.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_TIMESTAMP = NO
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
# documentation will contain a main index with vertical navigation menus that
# are dynamically created via JavaScript. If disabled, the navigation index will
@@ -1478,6 +1547,16 @@ BINARY_TOC = NO
TOC_EXPAND = NO
# The SITEMAP_URL tag is used to specify the full URL of the place where the
# generated documentation will be placed on the server by the user during the
# deployment of the documentation. The generated sitemap is called sitemap.xml
# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL
# is specified no sitemap is generated. For information about the sitemap
# protocol see https://www.sitemaps.org
# This tag requires that the tag GENERATE_HTML is set to YES.
SITEMAP_URL =
# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
@@ -1653,17 +1732,6 @@ HTML_FORMULA_FORMAT = png
FORMULA_FONTSIZE = 10
# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
# generated for formulas are transparent PNGs. Transparent PNGs are not
# supported properly for IE 6.0, but are supported on all modern browsers.
#
# Note that when changing this option you need to delete any form_*.png files in
# the HTML output directory before the changes have effect.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
FORMULA_TRANSPARENT = YES
# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
# to create new LaTeX commands to be used in formulas as building blocks. See
# the section "Including formulas" for details.
@@ -1977,9 +2045,16 @@ PDF_HYPERLINKS = YES
USE_PDFLATEX = YES
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
# command to the generated LaTeX files. This will instruct LaTeX to keep running
# if errors occur, instead of asking the user for help.
# The LATEX_BATCHMODE tag ignals the behavior of LaTeX in case of an error.
# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch
# mode nothing is printed on the terminal, errors are scrolled as if <return> is
# hit at every error; missing files that TeX tries to input or request from
# keyboard input (\read on a not open input stream) cause the job to abort,
# NON_STOP In nonstop mode the diagnostic message will appear on the terminal,
# but there is no possibility of user interaction just like in batch mode,
# SCROLL In scroll mode, TeX will stop only for missing files to input or if
# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at
# each error, asking for user intervention.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
@@ -2000,14 +2075,6 @@ LATEX_HIDE_INDICES = NO
LATEX_BIB_STYLE = plain
# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
# page will contain the date and time when the page was generated. Setting this
# to NO can help when comparing the output of multiple runs.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_TIMESTAMP = NO
# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
# path from which the emoji images will be read. If a relative path is entered,
# it will be relative to the LATEX_OUTPUT directory. If left blank the
@@ -2173,7 +2240,7 @@ DOCBOOK_OUTPUT = docbook
#---------------------------------------------------------------------------
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures
# the structure of the code including all documentation. Note that this feature
# is still experimental and incomplete at the moment.
# The default value is: NO.
@@ -2344,16 +2411,9 @@ EXTERNAL_GROUPS = YES
EXTERNAL_PAGES = YES
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
# Configuration options related to diagram generator tools
#---------------------------------------------------------------------------
# You can include diagrams made with dia in doxygen documentation. Doxygen will
# then run dia to produce the diagram and insert it in the documentation. The
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
# If left empty dia is assumed to be found in the default search path.
DIA_PATH =
# If set to YES the inheritance and collaboration graphs will hide inheritance
# and usage relations if the target is undocumented or is not a class.
# The default value is: YES.
@@ -2362,7 +2422,7 @@ HIDE_UNDOC_RELATIONS = YES
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
# available from the path. This tool is part of Graphviz (see:
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
# Bell Labs. The other options in this section have no effect if this option is
# set to NO
# The default value is: NO.
@@ -2379,37 +2439,51 @@ HAVE_DOT = YES
DOT_NUM_THREADS = 0
# When you want a differently looking font in the dot files that doxygen
# generates you can specify the font name using DOT_FONTNAME. You need to make
# sure dot is able to find the font, which can be done by putting it in a
# standard location or by setting the DOTFONTPATH environment variable or by
# setting DOT_FONTPATH to the directory containing the font.
# The default value is: Helvetica.
# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of
# subgraphs. When you want a differently looking font in the dot files that
# doxygen generates you can specify fontname, fontcolor and fontsize attributes.
# For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node,
# Edge and Graph Attributes specification</a> You need to make sure dot is able
# to find the font, which can be done by putting it in a standard location or by
# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
# directory containing the font. Default graphviz fontsize is 14.
# The default value is: fontname=Helvetica,fontsize=10.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTNAME = Helvetica
DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10"
# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
# dot graphs.
# Minimum value: 4, maximum value: 24, default value: 10.
# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can
# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a
# href=https://graphviz.org/doc/info/arrows.html>Complete documentation about
# arrows shapes.</a>
# The default value is: labelfontname=Helvetica,labelfontsize=10.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTSIZE = 10
DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10"
# By default doxygen will tell dot to use the default font as specified with
# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
# the path where dot can find it using this tag.
# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes
# around nodes set 'shape=plain' or 'shape=plaintext' <a
# href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a>
# The default value is: shape=box,height=0.2,width=0.4.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4"
# You can set the path where dot can find font specified with fontname in
# DOT_COMMON_ATTR and others dot attributes.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTPATH =
# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a
# graph for each documented class showing the direct and indirect inheritance
# relations. In case HAVE_DOT is set as well dot will be used to draw the graph,
# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set
# to TEXT the direct and indirect inheritance relations will be shown as texts /
# links.
# Possible values are: NO, YES, TEXT and GRAPH.
# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will
# generate a graph for each documented class showing the direct and indirect
# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and
# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case
# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the
# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used.
# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance
# relations will be shown as texts / links.
# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN.
# The default value is: YES.
CLASS_GRAPH = YES
@@ -2550,7 +2624,7 @@ DIR_GRAPH_MAX_DEPTH = 1
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
# generated by dot. For an explanation of the image formats see the section
# output formats in the documentation of the dot tool (Graphviz (see:
# http://www.graphviz.org/)).
# https://www.graphviz.org/)).
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
# to make the SVG files visible in IE 9+ (other browsers do not have this
# requirement).
@@ -2587,11 +2661,12 @@ DOT_PATH =
DOTFILE_DIRS =
# The MSCFILE_DIRS tag can be used to specify one or more directories that
# contain msc files that are included in the documentation (see the \mscfile
# command).
# You can include diagrams made with dia in doxygen documentation. Doxygen will
# then run dia to produce the diagram and insert it in the documentation. The
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
# If left empty dia is assumed to be found in the default search path.
MSCFILE_DIRS =
DIA_PATH =
# The DIAFILE_DIRS tag can be used to specify one or more directories that
# contain dia files that are included in the documentation (see the \diafile
@@ -2641,18 +2716,6 @@ DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 0
# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
# background. This is disabled by default, because dot on Windows does not seem
# to support this out of the box.
#
# Warning: Depending on the platform used, enabling this option may lead to
# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
# read).
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_TRANSPARENT = NO
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
# files in one run (i.e. multiple -o and -T options on the command line). This
# makes dot run faster, but since only newer versions of dot (>1.8.10) support
@@ -2680,3 +2743,19 @@ GENERATE_LEGEND = YES
# The default value is: YES.
DOT_CLEANUP = YES
# You can define message sequence charts within doxygen comments using the \msc
# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will
# use a built-in version of mscgen tool to produce the charts. Alternatively,
# the MSCGEN_TOOL tag can also specify the name an external tool. For instance,
# specifying prog as the value, doxygen will call the tool as prog -T
# <outfile_format> -o <outputfile> <inputfile>. The external tool should support
# output file formats "png", "eps", "svg", and "ismap".
MSCGEN_TOOL =
# The MSCFILE_DIRS tag can be used to specify one or more directories that
# contain msc files that are included in the documentation (see the \mscfile
# command).
MSCFILE_DIRS =

View File

@@ -17,7 +17,6 @@
* [ENTT_DISABLE_ASSERT](#entt_disable_assert)
* [ENTT_NO_ETO](#entt_no_eto)
* [ENTT_STANDARD_CPP](#entt_standard_cpp)
<!--
@endcond TURN_OFF_DOXYGEN
-->

View File

@@ -9,7 +9,6 @@
* [Containers](#containers)
* [Dense map](#dense-map)
* [Dense set](#dense-set)
<!--
@endcond TURN_OFF_DOXYGEN
-->
@@ -21,7 +20,7 @@ difficult to do better (although it's very easy to do worse, as many examples
available online demonstrate).<br/>
`EnTT` doesn't try in any way to replace what is offered by the standard. Quite
the opposite, given the widespread use that is made of standard containers.<br/>
However, the library also tries to fill a gap in features and functionality by
However, the library also tries to fill a gap in features and functionalities by
making available some containers initially developed for internal use.
This section of the library is likely to grow larger over time. However, for the
@@ -40,7 +39,7 @@ The implementation is based on _sparse sets_ and each bucket is identified by an
implicit list within the packed array itself.
The interface is very close to its counterpart in the standard library, that is,
`std::unordered_map`.<br/>
the `std::unordered_map` class.<br/>
However, both local and non-local iterators returned by a dense map belong to
the input iterator category although they respectively model the concepts of a
_forward iterator_ type and a _random access iterator_ type.<br/>
@@ -63,5 +62,6 @@ The implementation is based on _sparse sets_ and each bucket is identified by an
implicit list within the packed array itself.
The interface is in all respects similar to its counterpart in the standard
library, that is, `std::unordered_set`.<br/>
Therefore, there is no need to go into the API description.
library, that is, the `std::unordered_set` class.<br/>
However, this type of set also supports reverse iteration and therefore offers
all the functions necessary for the purpose (such as `rbegin` and `rend`).

View File

@@ -46,25 +46,24 @@
# Introduction
`EnTT` comes with a bunch of core functionalities mostly used by the other parts
of the library itself.<br/>
Hardly users will include these features in their code, but it's worth
describing what `EnTT` offers so as not to reinvent the wheel in case of need.
of the library.<br/>
Many of these tools are also useful in everyday work. Therefore, it's worth
describing them so as not to reinvent the wheel in case of need.
# Any as in any type
`EnTT` comes with its own `any` type. It may seem redundant considering that
C++17 introduced `std::any`, but it is not (hopefully).<br/>
`EnTT` offers its own `any` type. It may seem redundant considering that C++17
introduced `std::any`, but it is not (hopefully).<br/>
First of all, the _type_ returned by an `std::any` is a const reference to an
`std::type_info`, an implementation defined class that's not something everyone
wants to see in a software. Furthermore, there is no way to connect it with the
type system of the library and therefore with its integrated RTTI support.<br/>
Note that this class is largely used internally by the library itself.
wants to see in a software. Furthermore, there is no way to bind it to the type
system of the library and therefore with its integrated RTTI support.
The API is very similar to that of its most famous counterpart, mainly because
this class serves the same purpose of being an opaque container for any type of
value.<br/>
Instances of `any` also minimize the number of allocations by relying on a well
known technique called _small buffer optimization_ and a fake vtable.
The `any` API is very similar to that of its most famous counterpart, mainly
because this class serves the same purpose of being an opaque container for any
type of value.<br/>
Instances also minimize the number of allocations by relying on a well known
technique called _small buffer optimization_ and a fake vtable.
Creating an object of the `any` type, whether empty or not, is trivial:
@@ -93,8 +92,8 @@ Furthermore, an instance of `any` isn't tied to an actual type. Therefore, the
wrapper is reconfigured when it's assigned a new object of a type other than
the one it contains.
There exists also a way to directly assign a value to the variable contained by
an `entt::any`, without necessarily replacing it. This is especially useful when
There is also a way to directly assign a value to the variable contained by an
`entt::any`, without necessarily replacing it. This is especially useful when
the object is used in _aliasing mode_, as described below:
```cpp
@@ -108,15 +107,15 @@ any.assign(value);
any.assign(std::move(value));
```
The `any` class will also perform a check on the type information and whether or
not the original type was copy or move assignable, as appropriate.<br/>
In all cases, the `assign` function returns a boolean value to indicate the
success or failure of the operation.
The `any` class performs a check on the type information and whether or not the
original type was copy or move assignable, as appropriate.<br/>
In all cases, the `assign` function returns a boolean value that is true in case
of success and false otherwise.
When in doubt about the type of object contained, the `type` member function of
`any` returns a const reference to the `type_info` associated with its element,
or `type_id<void>()` if the container is empty. The type is also used internally
when comparing two `any` objects:
When in doubt about the type of object contained, the `type` member function
returns a const reference to the `type_info` associated with its element, or
`type_id<void>()` if the container is empty.<br/>
The type is also used internally when comparing two `any` objects:
```cpp
if(any == empty) { /* ... */ }
@@ -125,7 +124,7 @@ if(any == empty) { /* ... */ }
In this case, before proceeding with a comparison, it's verified that the _type_
of the two objects is actually the same.<br/>
Refer to the `EnTT` type system documentation for more details about how
`type_info` works and on possible risks of a comparison.
`type_info` works and the possible risks of a comparison.
A particularly interesting feature of this class is that it can also be used as
an opaque container for const and non-const references:
@@ -153,22 +152,19 @@ entt::any ref = other.as_ref();
```
In this case, it doesn't matter if the original container actually holds an
object or acts already as a reference for unmanaged elements, the new instance
thus created won't create copies and will only serve as a reference for the
original item.<br/>
This means that, starting from the example above, both `ref` and `other` will
point to the same object, whether it's initially contained in `other` or already
an unmanaged element.
object or is as a reference for unmanaged elements already. The new instance
thus created doesn't create copies and only serves as a reference for the
original item.
As a side note, it's worth mentioning that, while everything works transparently
when it comes to non-const references, there are some exceptions when it comes
to const references.<br/>
It's worth mentioning that, while everything works transparently when it comes
to non-const references, there are some exceptions when it comes to const
references.<br/>
In particular, the `data` member function invoked on a non-const instance of
`any` that wraps a const reference will return a null pointer in all cases.
`any` that wraps a const reference returns a null pointer in all cases.
To cast an instance of `any` to a type, the library offers a set of `any_cast`
functions in all respects similar to their most famous counterparts.<br/>
The only difference is that, in the case of `EnTT`, these won't raise exceptions
The only difference is that, in the case of `EnTT`, they won't raise exceptions
but will only trigger an assert in debug mode, otherwise resulting in undefined
behavior in case of misuse in release mode.
@@ -188,31 +184,23 @@ using my_any = entt::basic_any<sizeof(double[4])>;
This feature, in addition to allowing the choice of a size that best suits the
needs of an application, also offers the possibility of forcing dynamic creation
of objects during construction.<br/>
In other terms, if the size is 0, `any` avoids the use of any optimization and
always dynamically allocates objects (except for aliasing cases).
Note that the size of the internal storage as well as the alignment requirements
are directly part of the type and therefore contribute to define different types
that won't be able to interoperate with each other.
In other terms, if the size is 0, `any` suppresses the small buffer optimization
and always dynamically allocates objects (except for aliasing cases).
## Alignment requirement
The alignment requirement is optional and by default the most stringent (the
largest) for any object whose size is at most equal to the one provided.<br/>
The `basic_any` class template inspects the alignment requirements in each case,
even when not provided and may decide not to use the small buffer optimization
in order to meet them.
The alignment requirement is provided as an optional second parameter following
the desired size for the internal storage:
It's provided as an optional second parameter following the desired size for the
internal storage:
```cpp
using my_any = entt::basic_any<sizeof(double[4]), alignof(double[4])>;
```
Note that the alignment requirements as well as the size of the internal storage
are directly part of the type and therefore contribute to define different types
that won't be able to interoperate with each other.
The `basic_any` class template inspects the alignment requirements in each case,
even when not provided and may decide not to use the small buffer optimization
in order to meet them.
# Compressed pair
@@ -225,8 +213,8 @@ is more important than having some cool and probably useless feature.
Although the API is very close to that of `std::pair` (apart from the fact that
the template parameters are inferred from the constructor and therefore there is
no` entt::make_compressed_pair`), the major difference is that `first` and
`second` are functions for implementation needs:
no `entt::make_compressed_pair`), the major difference is that `first` and
`second` are functions for implementation requirements:
```cpp
entt::compressed_pair pair{0, 3.};
@@ -239,16 +227,15 @@ intuition. At the end of the day, it's just a pair and nothing more.
# Enum as bitmask
Sometimes it's useful to be able to use enums as bitmasks. However, enum classes
aren't really suitable for the purpose out of the box. Main problem is that they
don't convert implicitly to their underlying type.<br/>
All that remains is to make a choice between using old-fashioned enums (with all
their problems that I don't want to discuss here) or writing _ugly_ code.
aren't really suitable for the purpose. Main problem is that they don't convert
implicitly to their underlying type.<br/>
The choice is then between using old-fashioned enums (with all their problems
that I don't want to discuss here) or writing _ugly_ code.
Fortunately, there is also a third way: adding enough operators in the global
scope to treat enum classes as bitmask transparently.<br/>
The ultimate goal is to be able to write code like the following (or maybe
something more meaningful, but this should give a grasp and remain simple at the
same time):
scope to treat enum classes as bitmasks transparently.<br/>
The ultimate goal is to write code like the following (or maybe something more
meaningful, but this should give a grasp and remain simple at the same time):
```cpp
enum class my_flag {
@@ -261,11 +248,11 @@ const my_flag flags = my_flag::enabled;
const bool is_enabled = !!(flags & my_flag::enabled);
```
The problem with adding all operators to the global scope is that these will
come into play even when not required, with the risk of introducing errors that
are difficult to deal with.<br/>
The problem with adding all operators to the global scope is that these come
into play even when not required, with the risk of introducing errors that are
difficult to deal with.<br/>
However, C++ offers enough tools to get around this problem. In particular, the
library requires users to register all enum classes for which bitmask support
library requires users to register the enum classes for which bitmask support
should be enabled:
```cpp
@@ -276,7 +263,7 @@ struct entt::enum_as_bitmask<my_flag>
```
This is handy when dealing with enum classes defined by third party libraries
and over which the users have no control. However, it's also verbose and can be
and over which the user has no control. However, it's also verbose and can be
avoided by adding a specific value to the enum class itself:
```cpp
@@ -289,23 +276,21 @@ enum class my_flag {
```
In this case, there is no need to specialize the `enum_as_bitmask` traits, since
`EnTT` will automatically detect the flag and enable the bitmask support.<br/>
Once the enum class has been registered (in one way or the other) all the most
common operators will be available, such as `&`, `|` but also `&=` and `|=`.
`EnTT` automatically detects the flag and enables the bitmask support.<br/>
Once the enum class is registered (in one way or the other), the most common
operators such as `&`, `|` but also `&=` and `|=` are available for use.
Refer to the official documentation for the full list of operators.
# Hashed strings
A hashed string is a zero overhead unique identifier. Users can use
human-readable identifiers in the codebase while using their numeric
counterparts at runtime, thus without affecting performance.<br/>
Hashed strings are human-readable identifiers in the codebase that turn into
numeric values at runtime, thus without affecting performance.<br/>
The class has an implicit `constexpr` constructor that chews a bunch of
characters. Once created, all what one can do with it is getting back the
original string through the `data` member function or converting the instance
into a number.<br/>
The good part is that a hashed string can be used wherever a constant expression
is required and no _string-to-number_ conversion will take place at runtime if
used carefully.
characters. Once created, one can get the original string by means of the `data`
member function or convert the instance into a number.<br/>
A hashed string is well suited wherever a constant expression is required. No
_string-to-number_ conversion will take place at runtime if used carefully.
Example of use:
@@ -318,19 +303,18 @@ auto resource = load(entt::hashed_string{"gui/background"});
```
There is also a _user defined literal_ dedicated to hashed strings to make them
more user-friendly:
more _user-friendly_:
```cpp
using namespace entt::literals;
constexpr auto str = "text"_hs;
```
To use it, remember that all user defined literals in `EnTT` are enclosed in the
`entt::literals` namespace. Therefore, the entire namespace or selectively the
literal of interest must be explicitly included before each use, a bit like
`std::literals`.<br/>
Finally, in case users need to create hashed strings at runtime, this class also
offers the necessary functionalities:
User defined literals in `EnTT` are enclosed in the `entt::literals` namespace.
Therefore, the entire namespace or selectively the literal of interest must be
explicitly included before each use, a bit like `std::literals`.<br/>
The class also offers the necessary functionalities to create hashed strings at
runtime:
```cpp
std::string orig{"text"};
@@ -343,16 +327,14 @@ const auto hash = entt::hashed_string::value(orig.c_str());
```
This possibility shouldn't be exploited in tight loops, since the computation
takes place at runtime and no longer at compile-time and could therefore impact
takes place at runtime and no longer at compile-time. It could therefore affect
performance to some degrees.
## Wide characters
The hashed string has a design that is close to that of an `std::basic_string`.
It means that `hashed_string` is nothing more than an alias for
`basic_hashed_string<char>`. For those who want to use the C++ type for wide
character representation, there exists also the alias `hashed_wstring` for
`basic_hashed_string<wchar_t>`.<br/>
The `hashed_string` class is an alias for `basic_hashed_string<char>`. To use
the C++ type for wide character representations, there exists also the alias
`hashed_wstring` for `basic_hashed_string<wchar_t>`.<br/>
In this case, the user defined literal to use to create hashed strings on the
fly is `_hws`:
@@ -360,16 +342,15 @@ fly is `_hws`:
constexpr auto str = L"text"_hws;
```
Note that the hash type of the `hashed_wstring` is the same of its counterpart.
The hash type of `hashed_wstring` is the same as its counterpart.
## Conflicts
The hashed string class uses internally FNV-1a to compute the numeric
counterpart of a string. Because of the _pigeonhole principle_, conflicts are
possible. This is a fact.<br/>
The hashed string class uses FNV-1a internally to hash strings. Because of the
_pigeonhole principle_, conflicts are possible. This is a fact.<br/>
There is no silver bullet to solve the problem of conflicts when dealing with
hashing functions. In this case, the best solution seemed to be to give up.
That's all.<br/>
hashing functions. In this case, the best solution is likely to give up. That's
all.<br/>
After all, human-readable unique identifiers aren't something strictly defined
and over which users have not the control. Choosing a slightly different
identifier is probably the best solution to make the conflict disappear in this
@@ -377,8 +358,8 @@ case.
# Iterators
Writing and working with iterators isn't always easy and more often than not
leads to duplicated code.<br/>
Writing and working with iterators isn't always easy. More often than not it
also leads to duplicated code.<br/>
`EnTT` tries to overcome this problem by offering some utilities designed to
make this hard work easier.
@@ -431,7 +412,7 @@ user.
## Iterable adaptor
Typically, a container class provides `begin` and `end` member functions (with
their const counterparts) to be iterated by the user.<br/>
their const counterparts) for iteration.<br/>
However, it can happen that a class offers multiple iteration methods or allows
users to iterate different sets of _elements_.
@@ -452,8 +433,8 @@ by returning an iterable object for the purpose.
There are a handful of tools within `EnTT` to interact with memory in one way or
another.<br/>
Some are geared towards simplifying the implementation of (internal or external)
allocator aware containers. Others, on the other hand, are designed to help the
developer with everyday problems.
allocator aware containers. Others are designed to help the developer with
everyday problems.
The former are very specific and for niche problems. These are tools designed to
unwrap fancy or plain pointers (`to_address`) or to help forget the meaning of
@@ -481,7 +462,7 @@ modulus and for this reason preferred in many areas.
A nasty thing in C++ (at least up to C++20) is the fact that shared pointers
support allocators while unique pointers don't.<br/>
There is a proposal at the moment that also shows among the other things how
There is a proposal at the moment that also shows (among the other things) how
this can be implemented without any compiler support.
The `allocate_unique` function follows this proposal, making a virtue out of
@@ -498,13 +479,16 @@ the same feature.
# Monostate
The monostate pattern is often presented as an alternative to a singleton based
configuration system. This is exactly its purpose in `EnTT`. Moreover, this
implementation is thread safe by design (hopefully).<br/>
Keys are represented by hashed strings, values are basic types like `int`s or
`bool`s. Values of different types can be associated to each key, even more than
one at a time. Because of this, users must pay attention to use the same type
both during an assignment and when they try to read back their data. Otherwise,
they will probably incur in unexpected results.
configuration system.<br/>
This is exactly its purpose in `EnTT`. Moreover, this implementation is thread
safe by design (hopefully).
Keys are integral values (easily obtained by hashed strings), values are basic
types like `int`s or `bool`s. Values of different types can be associated with
each key, even more than one at a time.<br/>
Because of this, one should pay attention to use the same type both during an
assignment and when trying to read back the data. Otherwise, there is the risk
to incur in unexpected results.
Example of use:
@@ -542,17 +526,10 @@ Basically, the whole system relies on a handful of classes. In particular:
auto index = entt::type_index<a_type>::value();
```
The returned value isn't guaranteed to be stable across different runs.
The returned value isn't guaranteed to be stable across different runs.<br/>
However, it can be very useful as index in associative and unordered
associative containers or for positional accesses in a vector or an array.
So as not to conflict with the other tools available, the `family` class isn't
used to generate these indexes. Therefore, the numeric identifiers returned by
the two tools may differ.<br/>
On the other hand, this leaves users with full powers over the `family` class
and therefore the generation of custom runtime sequences of indices for their
own purposes, if necessary.
An external generator can also be used if needed. In fact, `type_index` can be
specialized by type and is also _sfinae-friendly_ in order to allow more
refined specializations such as:
@@ -566,7 +543,7 @@ Basically, the whole system relies on a handful of classes. In particular:
};
```
Note that indexes **must** still be generated sequentially in this case.<br/>
Indexes **must** be sequentially generated in this case.<br/>
The tool is widely used within `EnTT`. Generating indices not sequentially
would break an assumption and would likely lead to undesired behaviors.
@@ -583,14 +560,14 @@ Basically, the whole system relies on a handful of classes. In particular:
This function **can** use non-standard features of the language for its own
purposes. This makes it possible to provide compile-time identifiers that
remain stable across different runs.<br/>
In all cases, users can prevent the library from using these features by means
of the `ENTT_STANDARD_CPP` definition. In this case, there is no guarantee
that identifiers remain stable across executions. Moreover, they are generated
Users can prevent the library from using these features by means of the
`ENTT_STANDARD_CPP` definition. In this case, there is no guarantee that
identifiers remain stable across executions. Moreover, they are generated
at runtime and are no longer a compile-time thing.
As for `type_index`, also `type_hash` is a _sfinae-friendly_ class that can be
specialized in order to customize its behavior globally or on a per-type or
per-traits basis.
As it happens with `type_index`, also `type_hash` is a _sfinae-friendly_ class
that can be specialized in order to customize its behavior globally or on a
per-type or per-traits basis.
* The name associated with a given type:
@@ -598,10 +575,9 @@ Basically, the whole system relies on a handful of classes. In particular:
auto name = entt::type_name<a_type>::value();
```
The name associated with a type is extracted from some information generally
made available by the compiler in use. Therefore, it may differ depending on
the compiler and may be empty in the event that this information isn't
available.<br/>
This value is extracted from some information generally made available by the
compiler in use. Therefore, it may differ depending on the compiler and may be
empty in the event that this information isn't available.<br/>
For example, given the following class:
```cpp
@@ -612,21 +588,20 @@ Basically, the whole system relies on a handful of classes. In particular:
when MSVC is in use.<br/>
Most of the time the name is also retrieved at compile-time and is therefore
always returned through an `std::string_view`. Users can easily access it and
modify it as needed, for example by removing the word `struct` to standardize
the result. `EnTT` won't do this for obvious reasons, since it requires
copying and creating a new string potentially at runtime.
modify it as needed, for example by removing the word `struct` to normalize
the result. `EnTT` doesn't do this for obvious reasons, since it would be
creating a new string at runtime otherwise.
This function **can** use non-standard features of the language for its own
purposes. Users can prevent the library from using non-standard features by
means of the `ENTT_STANDARD_CPP` definition. In this case, the name will be
empty by default.
purposes. Users can prevent the library from using these features by means of
the `ENTT_STANDARD_CPP` definition. In this case, the name is just empty.
As for `type_index`, also `type_name` is a _sfinae-friendly_ class that can be
specialized in order to customize its behavior globally or on a per-type or
per-traits basis.
As it happens with `type_index`, also `type_name` is a _sfinae-friendly_ class
that can be specialized in order to customize its behavior globally or on a
per-type or per-traits basis.
These are then combined into utilities that aim to offer an API that is somewhat
similar to that offered by the language.
similar to that made available by the standard library.
### Type info
@@ -708,8 +683,8 @@ In fact, although this is quite rare, it's not entirely excluded.
Another case where two types are assigned the same identifier is when classes
from different contexts (for example two or more libraries loaded at runtime)
have the same fully qualified name. In this case, also `type_name` will return
the same value for the two types.<br/>
have the same fully qualified name. In this case, `type_name` returns the same
value for the two types.<br/>
Fortunately, there are several easy ways to deal with this:
* The most trivial one is to define the `ENTT_STANDARD_CPP` macro. Runtime
@@ -739,9 +714,9 @@ offered by this module.
### Size of
The standard operator `sizeof` complains when users provide it for example with
function or incomplete types. On the other hand, it's guaranteed that its result
is always nonzero, even if applied to an empty class type.<br/>
The standard operator `sizeof` complains if users provide it with functions or
incomplete types. On the other hand, it's guaranteed that its result is always
non-zero, even if applied to an empty class type.<br/>
This small class combines the two and offers an alternative to `sizeof` that
works under all circumstances, returning zero if the type isn't supported:
@@ -777,8 +752,8 @@ A utility to easily transfer the constness of a type to another type:
using type = entt::constness_as_t<dst_type, const src_type>;
```
The trait is subject to the rules of the language. Therefore, for example,
transferring constness between references won't give the desired effect.
The trait is subject to the rules of the language. For example, _transferring_
constness between references won't give the desired effect.
### Member class type
@@ -798,7 +773,7 @@ A utility to quickly find the n-th argument of a function, member function or
data member (for blind operations on opaque types):
```cpp
using type = entt::nt_argument_t<1u, &clazz::member>;
using type = entt::nth_argument_t<1u, decltype(&clazz::member)>;
```
Disambiguation of overloaded functions is the responsibility of the user, should
@@ -825,8 +800,8 @@ registry.emplace<enemy_tag>(entity);
### Tag
Since `id_type` is very important and widely used in `EnTT`, there is a more
user-friendly shortcut for the creation of integral constants based on it.<br/>
Type `id_type` is very important and widely used in `EnTT`. Therefore, there is
a more user-friendly shortcut for the creation of constants based on it.<br/>
This shortcut is the alias template `entt::tag`.
If used in combination with hashed strings, it helps to use human-readable names
@@ -918,8 +893,8 @@ Perhaps a bit ugly to see in a codebase but it gets the job done at least.
## Runtime generator
To generate sequential numeric identifiers at runtime, `EnTT` offers the
`family` class template:
The `family` class template helps to generate sequential numeric identifiers for
types at runtime:
```cpp
// defines a custom generator
@@ -936,8 +911,8 @@ numeric identifier for the given type.<br/>
The generator is customizable, so as to get different _sequences_ for different
purposes if needed.
Please, note that identifiers aren't guaranteed to be stable across different
runs. Indeed it mostly depends on the flow of execution.
Identifiers aren't guaranteed to be stable across different runs. Indeed it
mostly depends on the flow of execution.
# Utilities

File diff suppressed because it is too large Load Diff

View File

@@ -14,6 +14,7 @@
* [Warning C4003: the min, the max and the macro](#warning-C4003-the-min-the-max-and-the-macro)
* [The standard and the non-copyable types](#the-standard-and-the-non-copyable-types)
* [Which functions trigger which signals](#which-functions-trigger-which-signals)
* [Duplicate storage for the same component](#duplicate-storage-for-the-same-component)
<!--
@endcond TURN_OFF_DOXYGEN
-->
@@ -193,7 +194,7 @@ to mitigate the problem makes it manageable.
## Which functions trigger which signals
The `registry` class offers three signals that are emitted following specific
Storage classes offer three _signals_ that are emitted following specific
operations. Maybe not everyone knows what these operations are, though.<br/>
If this isn't clear, below you can find a _vademecum_ for this purpose:
@@ -213,3 +214,28 @@ otherwise the latter is replaced and therefore `on_update` is triggered. As for
the second case, components are removed from their entities and thus freed when
they are recycled. It means that `on_destroyed` is triggered for every component
owned by the entity that is destroyed.
## Duplicate storage for the same component
It's rare but you can see double sometimes, especially when it comes to storage.
This can be caused by a conflict in the hash assigned to the various component
types (one of a kind) or by bugs in your compiler
([more common](https://github.com/skypjack/entt/issues/1063) apparently).<br/>
Regardless of the cause, `EnTT` offers a customization point that also serves as
a solution in this case:
```cpp
template<>
struct entt::type_hash<Type> final {
[[nodiscard]] static constexpr id_type value() noexcept {
return hashed_string::value("Type");
}
[[nodiscard]] constexpr operator id_type() const noexcept {
return value();
}
};
```
Specializing `type_hash` directly bypasses the default implementation offered by
`EnTT`, thus avoiding any possible conflicts or compiler bugs.

View File

@@ -14,7 +14,6 @@
* [Fake resources and order of execution](#fake-resources-and-order-of-execution)
* [Sync points](#sync-points)
* [Execution graph](#execution-graph)
<!--
@endcond TURN_OFF_DOXYGEN
-->
@@ -23,15 +22,15 @@
`EnTT` doesn't aim to offer everything one needs to work with graphs. Therefore,
anyone looking for this in the _graph_ submodule will be disappointed.<br/>
Quite the opposite is true. This submodule is minimal and contains only the data
structures and algorithms strictly necessary for the development of some tools
such as the _flow builder_.
Quite the opposite is true though. This submodule is minimal and contains only
the data structures and algorithms strictly necessary for the development of
some tools such as the _flow builder_.
# Data structures
As anticipated in the introduction, the aim isn't to offer all possible data
structures suitable for representing and working with graphs. Many will likely
be added or refined over time, however I want to discourage anyone expecting
be added or refined over time. However I want to discourage anyone expecting
tight scheduling on the subject.<br/>
The data structures presented in this section are mainly useful for the
development and support of some tools which are also part of the same submodule.
@@ -49,7 +48,7 @@ The `directed_tag` type _creates_ the graph as directed. There is also an
`undirected_tag` counterpart which creates it as undirected.<br/>
The interface deviates slightly from the typical double indexing of C and offers
an API that is perhaps more familiar to a C++ programmer. Therefore, the access
and modification of an element will take place via the `contains`, `insert` and
and modification of an element takes place via the `contains`, `insert` and
`erase` functions rather than a double call to an `operator[]`:
```cpp
@@ -60,14 +59,14 @@ if(adjacency_matrix.contains(0u, 1u)) {
}
```
Both `insert` and` erase` are idempotent functions which have no effect if the
Both `insert` and` erase` are _idempotent_ functions which have no effect if the
element already exists or has already been deleted.<br/>
The first one returns an `std::pair` containing the iterator to the element and
a boolean value indicating whether the element has been inserted or was already
present. The second one instead returns the number of deleted elements (0 or 1).
a boolean value indicating whether the element was newly inserted or not. The
second one returns the number of deleted elements (0 or 1).
An adjacency matrix must be initialized with the number of elements (vertices)
when constructing it but can also be resized later using the `resize` function:
An adjacency matrix is initialized with the number of elements (vertices) when
constructing it but can also be resized later using the `resize` function:
```cpp
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
@@ -82,8 +81,8 @@ for(auto &&vertex: adjacency_matrix.vertices()) {
}
```
Note that the same result can be obtained with the following snippet, since the
vertices are unsigned integral values:
The same result is obtained with the following snippet, since the vertices are
plain unsigned integral values:
```cpp
for(auto last = adjacency_matrix.size(), pos = {}; pos < last; ++pos) {
@@ -93,8 +92,8 @@ for(auto last = adjacency_matrix.size(), pos = {}; pos < last; ++pos) {
As for visiting the edges, a few functions are available.<br/>
When the purpose is to visit all the edges of a given adjacency matrix, the
`edges` function returns an iterable object that can be used to get them as
pairs of vertices:
`edges` function returns an iterable object that is used to get them as pairs of
vertices:
```cpp
for(auto [lhs, rhs]: adjacency_matrix.edges()) {
@@ -102,8 +101,8 @@ for(auto [lhs, rhs]: adjacency_matrix.edges()) {
}
```
On the other hand, if the goal is to visit all the in- or out-edges of a given
vertex, the `in_edges` and `out_edges` functions are meant for that:
If the goal is to visit all the in- or out-edges of a given vertex instead, the
`in_edges` and `out_edges` functions are meant for that:
```cpp
for(auto [lhs, rhs]: adjacency_matrix.out_edges(3u)) {
@@ -111,11 +110,11 @@ for(auto [lhs, rhs]: adjacency_matrix.out_edges(3u)) {
}
```
As might be expected, these functions expect the vertex to visit (that is, to
return the in- or out-edges for) as an argument.<br/>
Both the functions expect the vertex to visit (that is, to return the in- or
out-edges for) as an argument.<br/>
Finally, the adjacency matrix is an allocator-aware container and offers most of
the functionality one would expect from this type of containers, such as `clear`
or 'get_allocator` and so on.
the functionalities one would expect from this type of containers, such as
`clear` or 'get_allocator` and so on.
## Graphviz dot language
@@ -129,19 +128,19 @@ std::ostringstream output{};
entt::dot(output, adjacency_matrix);
```
However, there is also the option of providing a callback to which the vertices
are passed and which can be used to add (`dot`) properties to the output from
time to time:
It's also possible to provide a callback to which the vertices are passed and
which can be used to add (`dot`) properties to the output as needed:
```cpp
std::ostringstream output{};
entt::dot(output, adjacency_matrix, [](auto &output, auto vertex) {
out << "label=\"v\"" << vertex << ",shape=\"box\"";
});
```
This second mode is particularly convenient when the user wants to associate
data managed externally to the graph being converted.
externally managed data to the graph being converted.
# Flow builder
@@ -155,42 +154,42 @@ specified.<br/>
Most of the functions in the API also return the flow builder itself, according
to what is the common sense API when it comes to builder classes.
Once all tasks have been registered and resources assigned to them, an execution
graph in the form of an adjacency matrix is returned to the user.<br/>
Once all tasks are registered and resources assigned to them, an execution graph
in the form of an adjacency matrix is returned to the user.<br/>
This graph contains all the tasks assigned to the flow builder in the form of
_vertices_. The _vertex_ itself can be used as an index to get the identifier
passed during registration.
_vertices_. The _vertex_ itself is used as an index to get the identifier passed
during registration.
## Tasks and resources
Although these terms are used extensively in the documentation, the flow builder
has no real concept of tasks and resources.<br/>
This class works mainly with _identifiers_, that is, values of type `id_type`.
That is, both tasks and resources are identified by integral values.<br/>
In other terms, both tasks and resources are identified by integral values.<br/>
This allows not to couple the class itself to the rest of the library or to any
particular data structure. On the other hand, it requires the user to keep track
of the association between identifiers and operations or actual data.
Once a flow builder has been created (which requires no constructor arguments),
the first thing to do is to bind a task. This will indicate to the builder who
intends to consume the resources that will be specified immediately after:
Once a flow builder is created (which requires no constructor arguments), the
first thing to do is to bind a task. This tells to the builder _who_ intends to
consume the resources that are specified immediately after:
```cpp
entt::flow builder{};
builder.bind("task_1"_hs);
```
Note that the example uses the `EnTT` hashed string to generate an identifier
for the task.<br/>
Indeed, the use of `id_type` as an identifier type is not by accident. In fact,
The example uses the `EnTT` hashed string to generate an identifier for the
task.<br/>
Indeed, the use of `id_type` as an identifier type isn't by accident. In fact,
it matches well with the internal hashed string class. Moreover, it's also the
same type returned by the hash function of the internal RTTI system, in case the
user wants to rely on that.<br/>
However, being an integral value, it leaves the user full freedom to rely on his
own tools if he deems it necessary.
own tools if necessary.
Once a task has been associated with the flow builder, it can be assigned
read-only or read-write resources, as appropriate:
Once a task is associated with the flow builder, it's also assigned read-only or
read-write resources as appropriate:
```cpp
builder
@@ -203,13 +202,87 @@ builder
As mentioned, many functions return the builder itself and it's therefore easy
to concatenate the different calls.<br/>
Also in the case of resources, these are identified by numeric values of type
Also in the case of resources, they are identified by numeric values of type
`id_type`. As above, the choice is not entirely random. This goes well with the
tools offered by the library while leaving room for maximum flexibility.
Finally, both the `ro` and` rw` functions also offer an overload that accepts a
pair of iterators, so that one can pass a range of resources in one go.
### Rebinding
The `flow` class is resource based rather than task based. This means that graph
generation is driven by resources and not by the order of _appearance_ of tasks
during flow definition.<br/>
Although this concept is particularly important, it's almost irrelevant for the
vast majority of cases. However, it becomes relevant when _rebinding_ resources
or tasks.
In fact, nothing prevents rebinding elements to a flow.<br/>
However, the behavior changes slightly from case to case and has some nuances
that it's worth knowing about.
Directly rebinding a resource without the task being replaced trivially results
in the task's access mode for that resource being updated:
```cpp
builder.bind("task"_hs).rw("resource"_hs).ro("resource"_hs)
```
In this case, the resource is accessed in read-only mode, regardless of the
first call to `rw`.<br/>
Behind the scenes, the call doesn't actually _replace_ the previous one but is
queued after it instead, overwriting it when generating the graph. Thus, a large
number of resource rebindings may even impact processing times (very difficult
to observe but theoretically possible).
Rebinding resources and also combining it with changes to tasks has far more
implications instead.<br/>
As mentioned, graph generation takes place starting from resources and not from
tasks. Therefore, the result may not be as expected:
```cpp
builder
.bind("task_1"_hs)
.ro("resource"_hs)
.bind("task_2"_hs)
.ro("resource"_hs)
.bind("task_1"_hs)
.rw("resource"_hs);
```
What happens here is that the resource first _sees_ a read-only access request
from the first task, then a read-write request from the second task and finally
a new read-only request from the first task.<br/>
Although this definition would probably be counted as an error, the resulting
graph may be unexpected. This in fact consists of a single edge outgoing from
the second task and directed to the first task.<br/>
To intuitively understand what happens, it's enough to think of the fact that a
task never has an edge pointing to itself.
While not obvious, this approach has its pros and cons like any other solution.
For example, creating loops is actually simple in the context of resource-based
graph generations:
```cpp
builder
.bind("task_1"_hs)
.rw("resource"_hs)
.bind("task_2"_hs)
.rw("resource"_hs)
.bind("task_1"_hs)
.rw("resource"_hs);
```
As expected, this definition leads to the creation of two edges that define a
loop between the two tasks.
As a general rule, rebinding resources and tasks is highly discouraged because
it could lead to subtle bugs if users don't know what they're doing.<br/>
However, once the mechanisms of resource-based graph generation are understood,
it can offer to the expert user a flexibility and a range of possibilities
otherwise inaccessible.
## Fake resources and order of execution
The flow builder doesn't offer the ability to specify when a task should execute
@@ -217,10 +290,10 @@ before or after another task.<br/>
In fact, the order of _registration_ on the resources also determines the order
in which the tasks are processed during the generation of the execution graph.
However, there is a way to force the execution order of two processes.<br/>
However, there is a way to _force_ the execution order of two processes.<br/>
Briefly, since accessing a resource in opposite modes requires sequential rather
than parallel scheduling, it's possible to make use of fake resources to force
the order execution:
than parallel scheduling, it's possible to make use of fake resources to rule on
the execution order:
```cpp
builder
@@ -235,10 +308,10 @@ builder
.ro("fake"_hs)
```
This snippet forces the execution of `task_2` and `task_3` **after** `task_1`.
This is due to the fact that the latter sets a read-write requirement on a fake
This snippet forces the execution of `task_1` **before** `task_2` and `task_3`.
This is due to the fact that the former sets a read-write requirement on a fake
resource that the other tasks also want to access in read-only mode.<br/>
Similarly, it's possible to force a task to run after a certain group:
Similarly, it's possible to force a task to run **after** a certain group:
```cpp
builder
@@ -261,7 +334,7 @@ others tasks.
Sometimes it's useful to assign the role of _sync point_ to a node.<br/>
Whether it accesses new resources or is simply a watershed, the procedure for
assigning this role to a vertex is always the same: first it's tied to the flow
assigning this role to a vertex is always the same. First it's tied to the flow
builder, then the `sync` function is invoked:
```cpp
@@ -283,7 +356,7 @@ all specified constraints to return the best scheduling for the vertices:
entt::adjacency_matrix<entt::directed_tag> graph = builder.graph();
```
The search for the main vertices, that is those without in-edges, is usually the
Searching for the main vertices (that is, those without in-edges) is usually the
first thing required:
```cpp
@@ -294,6 +367,6 @@ for(auto &&vertex: graph) {
}
```
Starting from them, using the other functions appropriately (such as `out_edges`
to retrieve the children of a given task or `edges` to access their identifiers)
it will be possible to instantiate an execution graph.
Then it's possible to instantiate an execution graph by means of other functions
such as `out_edges` to retrieve the children of a given task or `edges` to get
the identifiers.

View File

@@ -19,14 +19,12 @@
general and on GNU/Linux when default visibility was set to hidden. The
limitation was mainly due to a custom utility used to assign unique, sequential
identifiers with different types.<br/>
Fortunately, nowadays using `EnTT` across boundaries is much easier.
Fortunately, nowadays `EnTT` works smoothly across boundaries.
## Smooth until proven otherwise
Many classes in `EnTT` make extensive use of type erasure for their purposes.
This isn't a problem on itself (in fact, it's the basis of an API so convenient
to use). However, a way is needed to recognize the objects whose type has been
erased on the other side of a boundary.<br/>
This raises the need to identify objects whose type has been erased.<br/>
The `type_hash` class template is how identifiers are generated and thus made
available to the rest of the library. In general, this class doesn't arouse much
interest. The only exception is when a conflict between identifiers occurs
@@ -36,13 +34,13 @@ The section dedicated to `type_info` contains all the details to get around the
issue in a concise and elegant way. Please refer to the specific documentation.
When working with linked libraries, compile definitions `ENTT_API_EXPORT` and
`ENTT_API_IMPORT` can be used where there is a need to import or export symbols,
so as to make everything work nicely across boundaries.<br/>
`ENTT_API_IMPORT` are to import or export symbols, so as to make everything work
nicely across boundaries.<br/>
On the other hand, everything should run smoothly when working with plugins or
shared libraries that don't export any symbols.
For anyone who needs more details, the test suite contains multiple examples
covering the most common cases (see the `lib` directory for all details).<br/>
For those who need more details, the test suite contains many examples covering
the most common cases (see the `lib` directory for all details).<br/>
It goes without saying that it's impossible to cover **all** possible cases.
However, what is offered should hopefully serve as a basis for all of them.
@@ -70,8 +68,8 @@ entt::locator<entt::meta_ctx>::reset(handle);
From now on, both spaces refer to the same context and on it are attached all
new meta types, no matter where they are created.<br/>
Note that resetting the main context doesn't also propagate changes across
boundaries. In other words, resetting a context results in the decoupling of the
Note that _replacing_ the main context doesn't also propagate changes across
boundaries. In other words, replacing a context results in the decoupling of the
two sides and therefore a divergence in the contents.
## Memory Management

View File

@@ -1,5 +1,22 @@
# EnTT in Action
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [EnTT in Action](#entt-in-action)
* [Games](#games)
* [Engines and the like](#engines-and-the-like)
* [Articles, videos and blog posts](#articles-videos-and-blog-posts)
* [Any Other Business](#any-other-business)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
`EnTT` is widely used in private and commercial applications. I cannot even
mention most of them because of some signatures I put on some documents time
ago. Fortunately, there are also people who took the time to implement open
@@ -7,16 +24,24 @@ source projects based on `EnTT` and didn't hold back when it came to documenting
them.
Below an incomplete list of games, applications and articles that can be used as
a reference. Where I put the word _apparently_ means that the use of `EnTT` is
documented but the authors didn't make explicit announcements or contacted me
directly.
a reference.<br/>
Where I put the word _apparently_ means that the use of `EnTT` is documented but
the authors didn't make explicit announcements or contacted me directly.
I hope this list can grow much more in the future:
If you know of other resources out there that are about `EnTT`, feel free to
open an issue or a PR and I'll be glad to add them to this page.<br/>
I hope the following lists can grow much more in the future.
# EnTT in Action
## Games
* Games:
* [Minecraft](https://minecraft.net/en-us/attribution/) by
[Mojang](https://mojang.com/): of course, **that** Minecraft, see the
open source attributions page for more details.
* [Minecraft Legends](https://www.minecraft.net/it-it/about-legends) by
[Mojang](https://mojang.com/): an action strategy game where users have to
fight to defend the Overworld.
* [Minecraft Earth](https://www.minecraft.net/en-us/about-earth) by
[Mojang](https://mojang.com/): an augmented reality game for mobile, that
lets users bring Minecraft into the real world.
@@ -102,8 +127,19 @@ I hope this list can grow much more in the future:
multi-player arcade shooter game prototype.
* [Confetti Party](https://github.com/hexerei/entt-confetti): C++ sample
application as a starting point using `EnTT` and `SDL2`.
* [Hellbound](https://buas.itch.io/hellbound): a top-down action rogue-like
where to fight colossal demons in procedurally generated levels of hell.
* [Saurian Sorcery](https://github.com/cajallen/spellbook): a tower defense
game where to assemble a tribe of lizards to defend against robot invaders.
* [robotfindskitten](https://github.com/autogalkin/robotfindskitten): a clone
of `robotfindskitten` inside `Notepad.exe`, powered by `EnTT`.
* [Orion](https://github.com/alekskoloch/Orion): Outer-space Research and
Interstellar Observation Network (a space shooter game).
* [EnTT Boids](https://github.com/DanielEliasib/entt_boids): a simple boids
implementation using `EnTT` and `Raylib`.
## Engines and the like:
* Engines and the like:
* [Aether Engine](https://hadean.com/spatial-simulation/)
[v1.1+](https://docs.hadean.com/v1.1/Licenses/) by
[Hadean](https://hadean.com/): a library designed for spatially partitioning
@@ -168,8 +204,26 @@ I hope this list can grow much more in the future:
engine based on `SDL2` and `EnTT`.
* [Ducktape](https://github.com/DucktapeEngine/Ducktape): an open source C++
2D & 3D game engine that focuses on being fast and powerful.
* [The Worst Engine](https://github.com/Parasik72/TWE): a game engine based on
OpenGL.
* [Ecsact](https://ecsact.dev/): a language aimed at describing ECS, with a
[runtime implementation](https://github.com/ecsact-dev/ecsact_rt_entt) based
on `EnTT`.
* [AGE (Arc Game Engine)](https://github.com/MohitSethi99/ArcGameEngine): an
open-source engine for building 2D & 3D real-time rendering and interactive
contents.
* [Kengine](https://github.com/phisko/kengine): the _Koala engine_ is a game
engine entirely implemented as an entity-component-system.
* [Scion2D](https://github.com/dwjclark11/Scion2D): 2D game engine with
[YouTube series](https://www.youtube.com/playlist?list=PL3HUvSWOJR7XRDwVVQqqWO-zyyscb8L-v)
included.
* [EnTT Editor](https://github.com/TheDimin/EnttEditor): an editor for `EnTT`
libary that combines its built-in reflection system with `ImGui`.
* [Era Game Engine](https://github.com/EldarMuradov/EraGameEngine): a modern
ECS-based game engine.
## Articles, videos and blog posts:
* Articles, videos and blog posts:
* [Some posts](https://skypjack.github.io/tags/#entt) on my personal
[blog](https://skypjack.github.io/) are about `EnTT`, for those who want to
know **more** on this project.
@@ -193,6 +247,23 @@ I hope this list can grow much more in the future:
- ... And so on.
[Check out](https://www.youtube.com/channel/UCQ-W1KE9EYfdxhL6S4twUNw) the
_Game Engine Series_ by The Cherno for more videos.
* [Game Engine series](https://www.youtube.com/@JADE-iteGames/videos) by
[dwjclark11](https://github.com/dwjclark11) (not just `EnTT` but a lot of
it):
- [Getting into ECS](https://youtu.be/k9CbonLopJU?si=za3Tisyc96_92DWM)
- [Creating ECS Wrapper Classes](https://youtu.be/yetyuMJRdbo?si=PJTkmap4Ysqbzb_M)
- [Runtime Reflection using EnTT meta](https://youtu.be/GrXV5A07GTY?si=fKdWTj9AOhnhtiXq)
- [Adding entt::meta and Sol2 bindings](https://youtu.be/IM55JgxOqFA?si=rsbb4AG_NVh4IUmD)
(with [part two](https://youtu.be/-PTt-b1tzRw?si=zPJ4vEluyheMcNgO) too)
- ... And so on.
[Check it out](https://www.youtube.com/playlist?list=PL3HUvSWOJR7XRDwVVQqqWO-zyyscb8L-v)
for more videos.
* [Warmonger Dynasty devlog series](https://david-delassus.medium.com/list/warmonger-dynasty-devlogs-f64b71f556de)
by [linkdd](https://github.com/linkdd): an interesting walkthrough of
developing a game (also) with EnTT.
* [Use EnTT When You Need An ECS](https://www.codingwiththomas.com/blog/use-entt-when-you-need-an-ecs)
by [Thomas](https://www.codingwiththomas.com/): I couldn't have said it
better.
* [Space Battle: Huge edition](http://victor.madtriangles.com/code%20experiment/2018/06/11/post-ecs-battle-huge.html):
huge space battle built entirely from scratch.
* [Space Battle](https://github.com/vblanco20-1/ECS_SpaceBattle): huge space
@@ -219,7 +290,8 @@ I hope this list can grow much more in the future:
MMO(RPG)s and its [follow-up](https://youtu.be/yGlZeopx2hU) episode about
player bots and full external ECS: a series definitely worth looking at.
* Any Other Business:
## Any Other Business:
* [ArcGIS Runtime SDKs](https://developers.arcgis.com/arcgis-runtime/) by
[Esri](https://www.esri.com/): they use `EnTT` for the internal ECS and the
cross platform C++ rendering engine. The SDKs are utilized by a lot of
@@ -261,6 +333,3 @@ I hope this list can grow much more in the future:
* GitHub contains also
[many other examples](https://github.com/search?o=desc&q=%22skypjack%2Fentt%22&s=indexed&type=Code)
of use of `EnTT` from which to take inspiration if interested.
If you know of other resources out there that are about `EnTT`, feel free to
open an issue or a PR and I'll be glad to add them to this page.

View File

@@ -67,17 +67,15 @@ recommended.
# Reflection in a nutshell
Reflection always starts from real types (users cannot reflect imaginary types
and it would not make much sense, we wouldn't be talking about reflection
anymore).<br/>
To create a meta node, the library provides the `meta` function that accepts a
type to reflect as a template parameter:
Reflection always starts from actual C++ types. Users cannot reflect _imaginary_
types.<br/>
The `meta` function is where it all starts:
```cpp
auto factory = entt::meta<my_type>();
```
The returned value is a factory object to use to continue building the meta
The returned value is a _factory object_ to use to continue building the meta
type.
By default, a meta type is associated with the identifier returned by the
@@ -88,45 +86,42 @@ However, it's also possible to assign custom identifiers to meta types:
auto factory = entt::meta<my_type>().type("reflected_type"_hs);
```
Identifiers are important because users can retrieve meta types at runtime by
searching for them by _name_ other than by type.<br/>
On the other hand, there are cases in which users can be interested in adding
features to a reflected type so that the reflection system can use it correctly
under the hood, but they don't want to also make the type _searchable_. In this
case, it's sufficient not to invoke `type`.
Identifiers are used to _retrieve_ meta types at runtime by _name_ other than by
type.<br/>
However, users can be interested in adding features to a reflected type so that
the reflection system can use it correctly under the hood, while they don't want
to also make the type _searchable_. In this case, it's sufficient not to invoke
`type`.
A factory is such that all its member functions return the factory itself or a
decorated version of it. This object can be used to add the following:
A factory is such that all its member functions return the factory itself. It's
generally used to create the following:
* _Constructors_. Actual constructors can be assigned to a reflected type by
specifying their list of arguments. Free functions (namely, factories) can be
used as well, as long as the return type is the expected one. From a client's
point of view, nothing changes if a constructor is a free function or an
actual constructor.<br/>
Use the `ctor` member function for this purpose:
* _Constructors_. A constructors is assigned to a reflected type by specifying
its _list of arguments_. Free functions are also accepted if the return type
is the expected one. From a client perspective, nothing changes between a free
function or an actual constructor:
```cpp
entt::meta<my_type>().ctor<int, char>().ctor<&factory>();
```
* _Destructors_. Free functions and member functions can be used as destructors
of reflected types. The purpose is to give users the ability to free up
resources that require special treatment before an object is actually
destroyed.<br/>
Use the `dtor` member function for this purpose:
Meta default constructors are implicitly generated, if possible.
* _Destructors_. Both free functions and member functions are valid destructors:
```cpp
entt::meta<my_type>().dtor<&destroy>();
```
The purpose is to offer the possibility to free up resources that require
_special treatment_ before an object is actually destroyed.<br/>
A function should neither delete nor explicitly invoke the destructor of a
given instance.
* _Data members_. Both real data members of the underlying type and static and
global variables, as well as constants of any kind, can be attached to a meta
type. From the point of view of the client, all the variables associated with
the reflected type will appear as if they were part of the type itself.<br/>
Use the `data` member function for this purpose:
* _Data members_. Meta data members are actual data members of the underlying
type but also static and global variables or constants of any kind. From the
point of view of the client, all the variables associated with the reflected
type appear as if they were part of the type itself:
```cpp
entt::meta<my_type>()
@@ -135,13 +130,11 @@ decorated version of it. This object can be used to add the following:
.data<&global_variable>("global"_hs);
```
The function requires as an argument the identifier to give to the meta data
once created. Users can then access meta data at runtime by searching for them
by _name_.<br/>
Data members can also be defined by means of a setter and getter pair. Setters
and getters can be either free functions, class members or a mix of them, as
long as they respect the required signatures. This approach is also convenient
to create a read-only variable from a non-const data member:
The `data` function requires the identifier to use for the meta data member.
Users can then access it by _name_ at runtime.<br/>
Data members are also defined by means of a setter and getter pair. These are
either free functions, class members or a mix of them. This approach is also
convenient to create read-only properties from a non-const data member:
```cpp
entt::meta<my_type>().data<nullptr, &my_type::data_member>("member"_hs);
@@ -153,13 +146,10 @@ decorated version of it. This object can be used to add the following:
entt::meta<my_type>().data<entt::value_list<&from_int, &from_string>, &my_type::data_member>("member"_hs);
```
Refer to the inline documentation for all the details.
* _Member functions_. Both real member functions of the underlying type and free
functions can be attached to a meta type. From the point of view of the
client, all the functions associated with the reflected type will appear as if
they were part of the type itself.<br/>
Use the `func` member function for this purpose:
* _Member functions_. Meta member functions are actual member functions of the
underlying type but also plain free functions. From the point of view of the
client, all the functions associated with the reflected type appear as if they
were part of the type itself:
```cpp
entt::meta<my_type>()
@@ -168,40 +158,31 @@ decorated version of it. This object can be used to add the following:
.func<&free_function>("free"_hs);
```
The function requires as an argument the identifier to give to the meta
function once created. Users can then access meta functions at runtime by
searching for them by _name_.<br/>
The `func` function requires the identifier to use for the meta data function.
Users can then access it by _name_ at runtime.<br/>
Overloading of meta functions is supported. Overloaded functions are resolved
at runtime by the reflection system according to the types of the arguments.
* _Base classes_. A base class is such that the underlying type is actually
derived from it. In this case, the reflection system tracks the relationship
and allows for implicit casts at runtime when required.<br/>
Use the `base` member function for this purpose:
derived from it:
```cpp
entt::meta<derived_type>().base<base_type>();
```
From now on, wherever a `base_type` is required, an instance of `derived_type`
will also be accepted.
The reflection system tracks the relationship and allows for implicit casts at
runtime when required. In other terms, wherever a `base_type` is required, an
instance of `derived_type` is also accepted.
* _Conversion functions_. Actual types can be converted, this is a fact. Just
think of the relationship between a `double` and an `int` to see it. Similar
to bases, conversion functions allow users to define conversions that will be
implicitly performed by the reflection system when required.<br/>
Use the `conv` member function for this purpose:
* _Conversion functions_. Conversion functions allow users to define conversions
that are implicitly performed by the reflection system when required:
```cpp
entt::meta<double>().conv<int>();
```
That's all, everything users need to create meta types and enjoy the reflection
system. At first glance it may not seem that much, but users usually learn to
appreciate it over time.<br/>
Also, do not forget what these few lines hide under the hood: a built-in,
non-intrusive and macro-free system for reflection in C++. Features that are
definitely worth the price, at least for me.
This is everything users need to create meta types. Refer to the inline
documentation for further details.
## Any to the rescue
@@ -214,13 +195,13 @@ The API is very similar to that of the `any` type. The class `meta_any` _wraps_
many of the feature to infer a meta node, before forwarding some or all of the
arguments to the underlying storage.<br/>
Among the few relevant differences, `meta_any` adds support for containers and
pointer-like types (see the following sections for more details), while `any`
does not.<br/>
Similar to `any`, this class can also be used to create _aliases_ for unmanaged
pointer-like types, while `any` doesn't.<br/>
Similar to `any`, this class is also used to create _aliases_ for unmanaged
objects either with `forward_as_meta` or using the `std::in_place_type<T &>`
disambiguation tag, as well as from an existing object by means of the `as_ref`
member function. However, unlike `any`, `meta_any` treats an empty instance and
one initialized with `void` differently:
member function.<br/>
Unlike `any` instead, `meta_any` treats an empty instance and one initialized
with `void` differently:
```cpp
entt::meta_any empty{};
@@ -229,21 +210,19 @@ entt::meta_any other{std::in_place_type<void>};
While `any` considers both as empty, `meta_any` treats objects initialized with
`void` as if they were _valid_ ones. This allows to differentiate between failed
function calls and function calls that are successful but return nothing.<br/>
function calls and function calls that are successful but return nothing.
Finally, the member functions `try_cast`, `cast` and `allow_cast` are used to
cast the underlying object to a given type (either a reference or a value type)
or to _convert_ a `meta_any` in such a way that a cast becomes viable for the
resulting object. There is in fact no `any_cast` equivalent for `meta_any`.
resulting object.<br/>
There is in fact no `any_cast` equivalent for `meta_any`.
## Enjoy the runtime
Once the web of reflected types has been constructed, it's a matter of using it
at runtime where required.<br/>
All this has the great merit that the reflection system stands in fact as a
non-intrusive tool for the runtime, unlike the vast majority of the things
offered by this library and closely linked to the compile-time.
To search for a reflected type there are a few options:
Once the web of reflected types is constructed, it's a matter of using it at
runtime where required.<br/>
There are a few options to search for a reflected type:
```cpp
// direct access to a reflected type
@@ -257,8 +236,8 @@ auto by_type_id = entt::resolve(entt::type_id<my_type>());
```
There exists also an overload of the `resolve` function to use to iterate all
the reflected types at once. It returns an iterable object that can be used in a
range-for loop:
reflected types at once. It returns an iterable object to be used in a range-for
loop:
```cpp
for(auto &&[id, type]: entt::resolve()) {
@@ -270,9 +249,7 @@ In all cases, the returned value is an instance of `meta_type` (possibly with
its id). This kind of objects offer an API to know their _runtime identifiers_,
to iterate all the meta objects associated with them and even to build instances
of the underlying type.<br/>
Refer to the inline documentation for all the details.
Meta data members and functions are accessed by name among the other things:
Meta data members and functions are accessed by name:
* Meta data members:
@@ -297,11 +274,11 @@ Meta data members and functions are accessed by name among the other things:
A meta function object offers an API to query the underlying type (for
example, to know if it's a const or a static function), to know the number of
arguments, the meta return type and the meta types of the parameters. In
addition, a meta function object can be used to invoke the underlying function
and then get the return value in the form of a `meta_any` object.
addition, a meta function object is used to invoke the underlying function and
then get the return value in the form of a `meta_any` object.
All the meta objects thus obtained as well as the meta types can be explicitly
converted to a boolean value to check if they are valid:
All the meta objects thus obtained as well as the meta types explicitly convert
to a boolean value to check for validity:
```cpp
if(auto func = entt::resolve<my_type>().func("member"_hs); func) {
@@ -319,26 +296,23 @@ for(auto &&[id, type]: entt::resolve<my_type>().base()) {
}
```
A meta type can also be used to `construct` actual instances of the underlying
Meta type are also used to `construct` actual instances of the underlying
type.<br/>
In particular, the `construct` member function accepts a variable number of
arguments and searches for a match. It then returns a `meta_any` object that may
or may not be initialized, depending on whether a suitable constructor has been
found or not.
or may not be initialized, depending on whether a suitable constructor was found
or not.
There is no object that wraps the destructor of a meta type nor a `destroy`
member function in its API. Destructors are invoked implicitly by `meta_any`
behind the scenes and users have not to deal with them explicitly. Furthermore,
they have no name, cannot be searched and wouldn't have member functions to
expose anyway.<br/>
Similarly, conversion functions aren't directly accessible. They are used
they've no name, cannot be searched and wouldn't have member functions to expose
anyway.<br/>
Similarly, conversion functions aren't directly accessible. They're used
internally by `meta_any` and the meta objects when needed.
Meta types and meta objects in general contain much more than what is said: a
plethora of functions in addition to those listed whose purposes and uses go
unfortunately beyond the scope of this document.<br/>
I invite anyone interested in the subject to look at the code, experiment and
read the inline documentation to get the best out of this powerful tool.
Meta types and meta objects in general contain much more than what was said.
Refer to the inline documentation for further details.
## Container support
@@ -349,7 +323,7 @@ meta system in many cases.
To make a container be recognized as such by the meta system, users are required
to provide specializations for either the `meta_sequence_container_traits` class
or the `meta_associative_container_traits` class, according to the actual type
or the `meta_associative_container_traits` class, according to the actual _type_
of the container.<br/>
`EnTT` already exports the specializations for some common classes. In
particular:
@@ -386,11 +360,10 @@ if(any.type().is_sequence_container()) {
The method to use to get a proxy object for associative containers is
`as_associative_container` instead.<br/>
It goes without saying that it's not necessary to perform a double check.
Instead, it's sufficient to query the meta type or verify that the proxy object
is valid. In fact, proxies are contextually convertible to bool to know if they
are valid. For example, invalid proxies are returned when the wrapped object
isn't a container.<br/>
It's not necessary to perform a double check actually. Instead, it's enough to
query the meta type or verify that the proxy object is valid. In fact, proxies
are contextually convertible to bool to check for validity. For example, invalid
proxies are returned when the wrapped object isn't a container.<br/>
In all cases, users aren't expected to _reflect_ containers explicitly. It's
sufficient to assign a container for which a specialization of the traits
classes exists to a `meta_any` object to be able to get its proxy object.
@@ -402,32 +375,22 @@ to case. In particular:
* The `value_type` member function returns the meta type of the elements.
* The `size` member function returns the number of elements in the container as
an unsigned integer value:
```cpp
const auto size = view.size();
```
an unsigned integer value.
* The `resize` member function allows to resize the wrapped container and
returns true in case of success:
```cpp
const bool ok = view.resize(3u);
```
returns true in case of success.<br/>
For example, it's not possible to resize fixed size containers.
* The `clear` member function allows to clear the wrapped container and returns
true in case of success:
```cpp
const bool ok = view.clear();
```
true in case of success.<br/>
For example, it's not possible to clear fixed size containers.
* The `begin` and `end` member functions return opaque iterators that can be
used to iterate the container directly:
* The `reserve` member function allows to increase the capacity of the wrapped
container and returns true in case of success.<br/>
For example, it's not possible to increase capacity of fixed size containers.
* The `begin` and `end` member functions return opaque iterators that is used to
iterate the container directly:
```cpp
for(entt::meta_any element: view) {
@@ -441,7 +404,7 @@ to case. In particular:
All meta iterators are input iterators and don't offer an indirection operator
on purpose.
* The `insert` member function can be used to add elements to the container. It
* The `insert` member function is used to add elements to the container. It
accepts a meta iterator and the element to insert:
```cpp
@@ -451,15 +414,15 @@ to case. In particular:
```
This function returns a meta iterator pointing to the inserted element and a
boolean value to indicate whether the operation was successful or not. Note
that a call to `insert` may silently fail in case of fixed size containers or
whether the arguments aren't at least convertible to the required types.<br/>
Since the meta iterators are contextually convertible to bool, users can rely
on them to know if the operation has failed on the actual container or
upstream, for example for an argument conversion problem.
boolean value to indicate whether the operation was successful or not. A call
to `insert` may silently fail in case of fixed size containers or whether the
arguments aren't at least convertible to the required types.<br/>
Since meta iterators are contextually convertible to bool, users can rely on
them to know if the operation failed on the actual container or upstream, for
example due to an argument conversion problem.
* The `erase` member function can be used to remove elements from the container.
It accepts a meta iterator to the element to remove:
* The `erase` member function is used to remove elements from the container. It
accepts a meta iterator to the element to remove:
```cpp
auto first = view.begin();
@@ -468,11 +431,11 @@ to case. In particular:
```
This function returns a meta iterator following the last removed element and a
boolean value to indicate whether the operation was successful or not. Note
that a call to `erase` may silently fail in case of fixed size containers.
boolean value to indicate whether the operation was successful or not. A call
to `erase` may silently fail in case of fixed size containers.
* The `operator[]` can be used to access elements in a container. It accepts a
single argument, that is the position of the element to return:
* The `operator[]` is used to access container elements. It accepts a single
argument, the position of the element to return:
```cpp
for(std::size_t pos{}, last = view.size(); pos < last; ++pos) {
@@ -482,8 +445,8 @@ to case. In particular:
```
The function returns instances of `meta_any` that directly refer to the actual
elements. Modifying the returned object will then directly modify the element
inside the container.<br/>
elements. Modifying the returned object directly modifies the element inside
the container.<br/>
Depending on the underlying sequence container, this operation may not be as
efficient. For example, in the case of an `std::list`, a positional access
translates to a linear visit of the list itself (probably not what the user
@@ -508,21 +471,17 @@ differences in behavior in the case of key-only containers. In particular:
`std::map<int, char>`.
* The `size` member function returns the number of elements in the container as
an unsigned integer value:
```cpp
const auto size = view.size();
```
an unsigned integer value.
* The `clear` member function allows to clear the wrapped container and returns
true in case of success:
true in case of success.
```cpp
const bool ok = view.clear();
```
* The `reserve` member function allows to increase the capacity of the wrapped
container and returns true in case of success.<br/>
For example, it's not possible to increase capacity of standard maps.
* The `begin` and `end` member functions return opaque iterators that can be
used to iterate the container directly:
* The `begin` and `end` member functions return opaque iterators that are used
to iterate the container directly:
```cpp
for(std::pair<entt::meta_any, entt::meta_any> element: view) {
@@ -539,11 +498,11 @@ differences in behavior in the case of key-only containers. In particular:
While the accessed key is usually constant in the associative containers and
is therefore returned by copy, the value (if any) is wrapped by an instance of
`meta_any` that directly refers to the actual element. Modifying it will then
directly modify the element inside the container.
`meta_any` that directly refers to the actual element. Modifying it directly
modifies the element inside the container.
* The `insert` member function can be used to add elements to the container. It
accepts two arguments, respectively the key and the value to be inserted:
* The `insert` member function is used to add elements to a container. It gets
two arguments, respectively the key and the value to insert:
```cpp
auto last = view.end();
@@ -552,39 +511,39 @@ differences in behavior in the case of key-only containers. In particular:
```
This function returns a boolean value to indicate whether the operation was
successful or not. Note that a call to `insert` may fail when the arguments
aren't at least convertible to the required types.
successful or not. A call to `insert` may fail when the arguments aren't at
least convertible to the required types.
* The `erase` member function can be used to remove elements from the container.
It accepts a single argument, that is the key to be removed:
* The `erase` member function is used to remove elements from a container. It
gets a single argument, the key to remove:
```cpp
view.erase(42);
```
This function returns a boolean value to indicate whether the operation was
successful or not. Note that a call to `erase` may fail when the argument
isn't at least convertible to the required type.
successful or not. A call to `erase` may fail when the argument isn't at least
convertible to the required type.
* The `operator[]` can be used to access elements in a container. It accepts a
single argument, that is the key of the element to return:
* The `operator[]` is used to access elements in a container. It gets a single
argument, the key of the element to return:
```cpp
entt::meta_any value = view[42];
```
The function returns instances of `meta_any` that directly refer to the actual
elements. Modifying the returned object will then directly modify the element
inside the container.
elements. Modifying the returned object directly modifies the element inside
the container.
Container support is minimal but likely sufficient to satisfy all needs.
## Pointer-like types
As with containers, it's also possible to communicate to the meta system which
types to consider _pointers_. This will allow to dereference instances of
`meta_any`, thus obtaining light _references_ to the pointed objects that are
also correctly associated with their meta types.<br/>
As with containers, it's also possible to _tell_ to the meta system which types
are _pointers_. This makes it possible to dereference instances of `meta_any`,
thus obtaining light _references_ to pointed objects that are also correctly
associated with their meta types.<br/>
To make the meta system recognize a type as _pointer-like_, users can specialize
the `is_meta_pointer_like` class. `EnTT` already exports the specializations for
some common classes. In particular:
@@ -614,13 +573,12 @@ if(any.type().is_pointer_like()) {
}
```
Of course, it's not necessary to perform a double check. Instead, it's enough to
query the meta type or verify that the returned object is valid. For example,
invalid instances are returned when the wrapped object isn't a pointer-like
type.<br/>
Note that dereferencing a pointer-like object returns an instance of `meta_any`
which refers to the pointed object and allows users to modify it directly
(unless the returned element is const, of course).
It's not necessary to perform a double check. Instead, it's enough to query the
meta type or verify that the returned object is valid. For example, invalid
instances are returned when the wrapped object isn't a pointer-like type.<br/>
Dereferencing a pointer-like object returns an instance of `meta_any` which
_refers_ to the pointed object. Modifying it means modifying the pointed object
directly (unless the returned element is const).
In general, _dereferencing_ a pointer-like type boils down to a `*ptr`. However,
`EnTT` also supports classes that don't offer an `operator*`. In particular:
@@ -648,12 +606,12 @@ In general, _dereferencing_ a pointer-like type boils down to a `*ptr`. However,
};
```
In all other cases, that is, when dereferencing a pointer works as expected and
regardless of the pointed type, no user intervention is required.
In all other cases and when dereferencing a pointer works as expected regardless
of the pointed type, no user intervention is required.
## Template information
Meta types also provide a minimal set of information about the nature of the
Meta types also provide a minimal set of information about the _nature_ of the
original type in case it's a class template.<br/>
By default, this works out of the box and requires no user action. However, it's
important to include the header file `template.hpp` to make this information
@@ -688,9 +646,9 @@ template<typename Ret, typename... Args>
struct function_type<Ret(Args...)> {};
```
In this case, rather than the function type, the user might want the return type
and unpacked arguments as if they were different template parameters for the
original class template.<br/>
In this case, rather than the function type, it might be useful to provide the
return type and unpacked arguments as if they were different template parameters
for the original class template.<br/>
To achieve this, users must enter the library internals and provide their own
specialization for the class template `entt::meta_template_traits`, such as:
@@ -704,8 +662,8 @@ struct entt::meta_template_traits<function_type<Ret(Args...)>> {
The reflection system doesn't verify the accuracy of the information nor infer a
correspondence between real types and meta types.<br/>
Therefore, the specialization will be used as is and the information it contains
will be associated with the appropriate type when required.
Therefore, the specialization is used as is and the information it contains is
associated with the appropriate type when required.
## Automatic conversions
@@ -752,29 +710,29 @@ any.allow_cast(type);
int value = any.cast<int>();
```
This should make working with arithmetic types and scoped or unscoped enums as
easy as it is in C++.<br/>
It's also worth noting that it's still possible to set up conversion functions
manually and these will always be preferred over the automatic ones.
This makes working with arithmetic types and scoped or unscoped enums as easy as
it is in C++.<br/>
It's still possible to set up conversion functions manually and these are always
preferred over the automatic ones.
## Implicitly generated default constructor
In many cases, it's useful to be able to create objects of default constructible
types through the reflection system, while not having to explicitly register the
meta type or the default constructor.<br/>
Creating objects of default constructible types through the reflection system
while not having to explicitly register the meta type or its default constructor
is also possible.<br/>
For example, in the case of primitive types like `int` or `char`, but not just
them.
For this reason and only for default constructible types, default constructors
are automatically defined and associated with their meta types, whether they are
explicitly or implicitly generated.<br/>
For default constructible types only, default constructors are automatically
defined and associated with their meta types, whether they are explicitly or
implicitly generated.<br/>
Therefore, this is all is needed to construct an integer from its meta type:
```cpp
entt::resolve<int>().construct();
```
Where the meta type can be for example the one returned from a meta container,
Where the meta type is for example the one returned from a meta container,
useful for building keys without knowing or having to register the actual types.
In all cases, when users register default constructors, they are preferred both
@@ -783,8 +741,8 @@ during searches and when the `construct` member function is invoked.
## From void to any
Sometimes all a user has is an opaque pointer to an object of a known meta type.
It would be handy in this case to be able to construct a `meta_any` object from
them.<br/>
It would be handy in this case to be able to construct a `meta_any` element from
it.<br/>
For this purpose, the `meta_type` class offers a `from_void` member function
designed to convert an opaque pointer into a `meta_any`:
@@ -792,9 +750,8 @@ designed to convert an opaque pointer into a `meta_any`:
entt::meta_any any = entt::resolve(id).from_void(element);
```
It goes without saying that it's not possible to do a check on the actual type.
Therefore, this call can be considered as a _static cast_ with all the problems
and undefined behaviors of the case following errors.<br/>
Unfortunately, it's not possible to do a check on the actual type. Therefore,
this call can be considered as a _static cast_ with all its _problems_.<br/>
On the other hand, the ability to construct a `meta_any` from an opaque pointer
opens the door to some pretty interesting uses that are worth exploring.
@@ -826,17 +783,17 @@ There are a few alternatives available at the moment:
entt::meta<my_type>().func<&my_type::member_function, entt::as_void_t>("member"_hs);
```
If the use with functions is obvious, it must be said that it's also possible
to use this policy with constructors and data members. In the first case, the
constructor will be invoked but the returned wrapper will actually be empty.
In the second case, instead, the property will not be accessible for reading.
If the use with functions is obvious, perhaps less so is use with constructors
and data members. In the first case, the returned wrapper is always empty even
though the constructor is still invoked. In the second case, the property
isn't accessible for reading instead.
* The _as-ref_ and _as-cref_ policies, associated with the types
`entt::as_ref_t` and `entt::as_cref_t`.<br/>
They allow to build wrappers that act as references to unmanaged objects.
Accessing the object contained in the wrapper for which the _reference_ was
requested will make it possible to directly access the instance used to
initialize the wrapper itself:
requested makes it possible to directly access the instance used to initialize
the wrapper itself:
```cpp
entt::meta<my_type>().data<&my_type::data_member, entt::as_ref_t>("member"_hs);
@@ -854,21 +811,16 @@ obvious corner cases that can in turn be solved with the use of policies.
## Named constants and enums
A special mention should be made for constant values and enums. It wouldn't be
necessary, but it will help distracted readers.
As mentioned, the `data` member function can be used to reflect constants of any
type among the other things.<br/>
This allows users to create meta types for enums that will work exactly like any
other meta type built from a class. Similarly, arithmetic types can be enriched
As mentioned, the `data` member function is used to reflect constants of any
type.<br/>
This allows users to create meta types for enums that work exactly like any
other meta type built from a class. Similarly, arithmetic types are _enriched_
with constants of special meaning where required.<br/>
Personally, I find it very useful not to export what is the difference between
enums and classes in C++ directly in the space of the reflected types.
All values thus exported appear to users as if they were constant data members
of the reflected types. This avoids the need to _export_ what is the difference
between enums and classes in C++ directly in the space of the reflected types.
All the values thus exported will appear to users as if they were constant data
members of the reflected types.
Exporting constant values or elements from an enum is as simple as ever:
Exposing constant values or elements from an enum is quite simple:
```cpp
entt::meta<my_enum>()
@@ -878,28 +830,22 @@ entt::meta<my_enum>()
entt::meta<int>().data<2048>("max_int"_hs);
```
It goes without saying that accessing them is trivial as well. It's a matter of
doing the following, as with any other data member of a meta type:
Accessing them is trivial as well. It's a matter of doing the following, as with
any other data member of a meta type:
```cpp
auto value = entt::resolve<my_enum>().data("a_value"_hs).get({}).cast<my_enum>();
auto max = entt::resolve<int>().data("max_int"_hs).get({}).cast<int>();
```
As a side note, remember that all this happens behind the scenes without any
allocation because of the small object optimization performed by the `meta_any`
class.
All this happens behind the scenes without any allocation because of the small
object optimization performed by the `meta_any` class.
## Properties and meta objects
Sometimes (for example, when it comes to creating an editor) it might be useful
to attach properties to the meta objects created. Fortunately, this is possible
for most of them.<br/>
For the meta objects that support properties, the member functions of the
factory used for registering them will return an extended version of the factory
itself. The latter can be used to attach properties to the last created meta
object.<br/>
Apparently, it's more difficult to say than to do:
for most of them:
```cpp
entt::meta<my_type>().type("reflected_type"_hs).prop("tooltip"_hs, "message");
@@ -914,10 +860,10 @@ Key only properties are also supported out of the box:
entt::meta<my_type>().type("reflected_type"_hs).prop(my_enum::key_only);
```
To attach multiple properties to a meta object, it's possible to invoke `prop`
more than once.<br/>
It's also possible to invoke `prop` at different times, as long as the factory
is reset to the meta object of interest.
To attach multiple properties to a meta object, just invoke `prop` more than
once.<br/>
It's also possible to call `prop` at different times, as long as the factory is
reset to the meta object of interest.
The meta objects for which properties are supported are currently meta types,
meta data and meta functions.<br/>
@@ -940,7 +886,7 @@ form of a `meta_any` object.
## Unregister types
A type registered with the reflection system can also be unregistered. This
A type registered with the reflection system can also be _unregistered_. This
means unregistering all its data members, member functions, conversion functions
and so on. However, base classes aren't unregistered as well, since they don't
necessarily depend on it.<br/>
@@ -969,7 +915,7 @@ A type can be re-registered later with a completely different name and form.
## Meta context
All meta types and their parts are created at runtime and stored in a default
_context_. This can be reached via a service locator as:
_context_. This is obtained via a service locator as:
```cpp
auto &&context = entt::locator<entt::meta_context>::value_or();
@@ -984,8 +930,8 @@ auto &&context = entt::locator<entt::meta_context>::value_or();
std::swap(context, other);
```
This can be useful for testing purposes or to define multiple contexts with
different meta objects to be used as appropriate.
This is useful for testing purposes or to define multiple context objects with
different meta type to use as appropriate.
If _replacing_ the default context isn't enough, `EnTT` also offers the ability
to use multiple and externally managed contexts with the runtime reflection
@@ -998,16 +944,16 @@ entt::meta_ctx context{};
auto factory = entt::meta<my_type>(context).type("reflected_type"_hs);
```
By doing so, the new meta type won't be available in the default context but
will be usable by passing around the new context when needed, such as when
creating a new `meta_any` object:
By doing so, the new meta type isn't available in the default context but is
usable by passing around the new context when needed, such as when creating a
new `meta_any` object:
```cpp
entt::meta_any any{context, std::in_place_type<my_type>};
```
Similarly, to search for meta types in a context other than the default one, it
will be necessary to pass it to the `resolve` function:
Similarly, to search for meta types in a context other than the default one,
it's necessary to pass it to the `resolve` function:
```cpp
entt::meta_type type = entt::resolve(context, "reflected_type"_hs)

View File

@@ -26,17 +26,16 @@ This module aims to make it simple and easy to use.
The library allows to define _concepts_ as interfaces to fulfill with concrete
classes without having to inherit from a common base.<br/>
This is, among others, one of the advantages of static polymorphism in general
Among others, this is one of the advantages of static polymorphism in general
and of a generic wrapper like that offered by the `poly` class template in
particular.<br/>
What users get is an object that can be passed around as such and not through a
reference or a pointer, as happens when it comes to working with dynamic
polymorphism.
The result is an object to pass around as such and not through a reference or a
pointer, as it happens when it comes to working with dynamic polymorphism.
Since the `poly` class template makes use of `entt::any` internally, it also
supports most of its feature. Among the most important, the possibility to
create aliases to existing and thus unmanaged objects. This allows users to
exploit the static polymorphism while maintaining ownership of objects.<br/>
supports most of its feature. For example, the possibility to create aliases to
existing and thus unmanaged objects. This allows users to exploit the static
polymorphism while maintaining ownership of objects.<br/>
Likewise, the `poly` class template also benefits from the small buffer
optimization offered by the `entt::any` class and therefore minimizes the number
of allocations, avoiding them altogether where possible.
@@ -44,7 +43,7 @@ of allocations, avoiding them altogether where possible.
## Other libraries
There are some very interesting libraries regarding static polymorphism.<br/>
Among all, the two that I prefer are:
The ones that I like more are:
* [`dyno`](https://github.com/ldionne/dyno): runtime polymorphism done right.
* [`Poly`](https://github.com/facebook/folly/blob/master/folly/docs/Poly.md):
@@ -69,18 +68,18 @@ use the terminology introduced by Eric Niebler) is to define a _concept_ that
types will have to adhere to.<br/>
For this purpose, the library offers a single class that supports both deduced
and fully defined interfaces. Although having interfaces deduced automatically
is convenient and allows users to write less code in most cases, this has some
is convenient and allows users to write less code in most cases, it has some
limitations and it's therefore useful to be able to get around the deduction by
providing a custom definition for the static virtual table.
Once the interface is defined, it will be sufficient to provide a generic
implementation to fulfill the concept.<br/>
Once the interface is defined, a generic implementation is needed to fulfill the
concept itself.<br/>
Also in this case, the library allows customizations based on types or families
of types, so as to be able to go beyond the generic case where necessary.
## Deduced interface
This is how a concept with a deduced interface is introduced:
This is how a concept with a deduced interface is defined:
```cpp
struct Drawable: entt::type_list<> {
@@ -108,12 +107,12 @@ struct Drawable: entt::type_list<> {
};
```
In this case, all parameters must be passed to `invoke` after the reference to
In this case, all parameters are passed to `invoke` after the reference to
`this` and the return value is whatever the internal call returns.<br/>
As for `invoke`, this is a name that is injected into the _concept_ through
`Base`, from which one must necessarily inherit. Since it's also a dependent
name, the `this-> template` form is unfortunately necessary due to the rules of
the language. However, there exists also an alternative that goes through an
the language. However, there also exists an alternative that goes through an
external call:
```cpp
@@ -165,12 +164,12 @@ struct Drawable: entt::type_list<bool(int) const> {
Why should a user fully define a concept if the function types are the same as
the deduced ones?<br>
Because, in fact, this is exactly the limitation that can be worked around by
manually defining the static virtual table.
In fact, this is the limitation that can be worked around by manually defining
the static virtual table.
When things are deduced, there is an implicit constraint.<br/>
If the concept exposes a member function called `draw` with function type
`void()`, a concept can be satisfied:
`void()`, a concept is satisfied:
* Either by a class that exposes a member function with the same name and the
same signature.
@@ -179,7 +178,7 @@ If the concept exposes a member function called `draw` with function type
interface itself.
In other words, it's not possible to make use of functions not belonging to the
interface, even if they are present in the types that fulfill the concept.<br/>
interface, even if they're part of the types that fulfill the concept.<br/>
Similarly, it's not possible to deduce a function in the static virtual table
with a function type different from that of the associated member function in
the interface itself.
@@ -200,8 +199,8 @@ struct Drawable: entt::type_list<> {
};
```
In this case, it's stated that the `draw` method of a generic type will be
enough to satisfy the requirements of the `Drawable` concept.<br/>
In this case, it's stated that the `draw` method of a generic type is enough to
satisfy the requirements of the `Drawable` concept.<br/>
Both member functions and free functions are supported to fulfill concepts:
```cpp
@@ -251,15 +250,15 @@ struct DrawableAndErasable: entt::type_list<> {
```
The static virtual table is empty and must remain so.<br/>
On the other hand, `type` no longer inherits from `Base` and instead forwards
On the other hand, `type` no longer inherits from `Base`. Instead, it forwards
its template parameter to the type exposed by the _base class_. Internally, the
size of the static virtual table of the base class is used as an offset for the
local indexes.<br/>
_size_ of the static virtual table of the base class is used as an offset for
the local indexes.<br/>
Finally, by means of the `value_list_cat_t` utility, the implementation consists
in appending the new functions to the previous list.
As for a defined concept instead, also the list of types must be extended, in a
similar way to what is shown for the implementation of the above concept.<br/>
As for a defined concept instead, the list of types is _extended_ in a similar
way to what is shown for the implementation of the above concept.<br/>
To do this, it's useful to declare a function that allows to convert a _concept_
into its underlying `type_list` object:
@@ -268,8 +267,8 @@ template<typename... Type>
entt::type_list<Type...> as_type_list(const entt::type_list<Type...> &);
```
The definition isn't strictly required, since the function will only be used
through a `decltype` as it follows:
The definition isn't strictly required, since the function is only used through
a `decltype` as it follows:
```cpp
struct DrawableAndErasable: entt::type_list_cat_t<
@@ -286,9 +285,8 @@ Everything else is the same as already shown instead.
# Static polymorphism in the wild
Once the _concept_ and implementation have been introduced, it will be possible
to use the `poly` class template to contain instances that meet the
requirements:
Once the _concept_ and implementation are defined, it's possible to use the
`poly` class template to _wrap_ instances that meet the requirements:
```cpp
using drawable = entt::poly<Drawable>;
@@ -310,9 +308,9 @@ instance = square{};
instance->draw();
```
The `poly` class template offers a wide range of constructors, from the default
one (which will return an uninitialized `poly` object) to the copy and move
constructors, as well as the ability to create objects in-place.<br/>
This class offers a wide range of constructors, from the default one (which
returns an uninitialized `poly` object) to the copy and move constructors, as
well as the ability to create objects in-place.<br/>
Among others, there is also a constructor that allows users to wrap unmanaged
objects in a `poly` instance (either const or non-const ones):
@@ -329,14 +327,14 @@ drawable other = instance.as_ref();
```
In both cases, although the interface of the `poly` object doesn't change, it
won't construct any element or take care of destroying the referenced objects.
doesn't construct any element or take care of destroying the referenced objects.
Note also how the underlying concept is accessed via a call to `operator->` and
not directly as `instance.draw()`.<br/>
This allows users to decouple the API of the wrapper from that of the concept.
Therefore, where `instance.data()` will invoke the `data` member function of the
poly object, `instance->data()` will map directly to the functionality exposed
by the underlying concept.
Therefore, where `instance.data()` invokes the `data` member function of the
poly object, `instance->data()` maps directly to the functionality exposed by
the underlying concept.
# Storage size and alignment requirement
@@ -351,9 +349,9 @@ entt::basic_poly<Drawable, sizeof(double[4]), alignof(double[4])>
The default size is `sizeof(double[2])`, which seems like a good compromise
between a buffer that is too large and one unable to hold anything larger than
an integer. The alignment requirement is optional instead and by default such
that it's the most stringent (the largest) for any object whose size is at most
equal to the one provided.<br/>
an integer. The alignment requirement is optional and by default such that it's
the most stringent (the largest) for any object whose size is at most equal to
the one provided.<br/>
It's worth noting that providing a size of 0 (which is an accepted value in all
respects) will force the system to dynamically allocate the contained objects in
all cases.

View File

@@ -15,18 +15,17 @@
# Introduction
Sometimes processes are a useful tool to work around the strict definition of a
system and introduce logic in a different way, usually without resorting to the
introduction of other components.
`EnTT` offers a minimal support to this paradigm by introducing a few classes
that users can use to define and execute cooperative processes.
Processes are a useful tool to work around the strict definition of a system and
introduce logic in a different way, usually without resorting to other component
types.<br/>
`EnTT` offers minimal support to this paradigm by introducing a few classes used
to define and execute cooperative processes.
# The process
A typical process must inherit from the `process` class template that stays true
to the CRTP idiom. Moreover, derived classes must specify what's the intended
type for elapsed times.
A typical task inherits from the `process` class template that stays true to the
CRTP idiom. Moreover, derived classes specify what the intended type for elapsed
times is.
A process should expose publicly the following member functions whether needed
(note that it isn't required to define a function unless the derived class wants
@@ -34,39 +33,38 @@ to _override_ the default behavior):
* `void update(Delta, void *);`
It's invoked once per tick until a process is explicitly aborted or it
terminates either with or without errors. Even though it's not mandatory to
declare this member function, as a rule of thumb each process should at
least define it to work properly. The `void *` parameter is an opaque pointer
to user data (if any) forwarded directly to the process during an update.
This is invoked once per tick until a process is explicitly aborted or ends
either with or without errors. Even though it's not mandatory to declare this
member function, as a rule of thumb each process should at least define it to
work _properly_. The `void *` parameter is an opaque pointer to user data (if
any) forwarded directly to the process during an update.
* `void init();`
It's invoked when the process joins the running queue of a scheduler. This
happens as soon as it's attached to the scheduler if the process is a top
level one, otherwise when it replaces its parent if the process is a
continuation.
This is invoked when the process joins the running queue of a scheduler. It
happens usually as soon as the process is attached to the scheduler if it's a
top level one, otherwise when it replaces its parent if it's a _continuation_.
* `void succeeded();`
It's invoked in case of success, immediately after an update and during the
This is invoked in case of success, immediately after an update and during the
same tick.
* `void failed();`
It's invoked in case of errors, immediately after an update and during the
This is invoked in case of errors, immediately after an update and during the
same tick.
* `void aborted();`
It's invoked only if a process is explicitly aborted. There is no guarantee
that it executes in the same tick, this depends solely on whether the
process is aborted immediately or not.
This is invoked only if a process is explicitly aborted. There is no guarantee
that it executes in the same tick, it depends solely on whether the process is
aborted immediately or not.
Derived classes can also change the internal state of a process by invoking
`succeed` and `fail`, as well as `pause` and `unpause` the process itself. All
these are protected member functions made available to be able to manage the
life cycle of a process from a derived class.
`succeed` and `fail`, as well as `pause` and `unpause` the process itself.<br/>
All these are protected member functions made available to manage the life cycle
of a process from a derived class.
Here is a minimal example for the sake of curiosity:
@@ -95,14 +93,14 @@ private:
## Adaptor
Lambdas and functors can't be used directly with a scheduler for they are not
Lambdas and functors can't be used directly with a scheduler because they aren't
properly defined processes with managed life cycles.<br/>
This class helps in filling the gap and turning lambdas and functors into
full-featured processes usable by a scheduler.
The function call operator has a signature similar to the one of the `update`
function of a process but for the fact that it receives two extra arguments to
call whenever a process is terminated with success or with an error:
function of a process but for the fact that it receives two extra callbacks to
invoke whenever a process terminates with success or with an error:
```cpp
void(Delta delta, void *data, auto succeed, auto fail);
@@ -127,9 +125,9 @@ A cooperative scheduler runs different processes and helps managing their life
cycles.
Each process is invoked once per tick. If it terminates, it's removed
automatically from the scheduler and it's never invoked again. Otherwise it's
automatically from the scheduler and it's never invoked again. Otherwise, it's
a good candidate to run one more time the next tick.<br/>
A process can also have a child. In this case, the parent process is replaced
A process can also have a _child_. In this case, the parent process is replaced
with its child when it terminates and only if it returns with success. In case
of errors, both the parent process and its child are discarded. This way, it's
easy to create chain of processes to run sequentially.
@@ -138,18 +136,25 @@ Using a scheduler is straightforward. To create it, users must provide only the
type for the elapsed times and no arguments at all:
```cpp
entt::scheduler<std::uint32_t> scheduler;
entt::basic_scheduler<std::uint64_t> scheduler;
```
It has member functions to query its internal data structures, like `empty` or
`size`, as well as a `clear` utility to reset it to a clean state:
Otherwise, the `scheduler` alias is also available for the most common cases. It
uses `std::uint32_t` as a default type:
```cpp
entt::scheduler scheduler;
```
The class has member functions to query its internal data structures, like
`empty` or `size`, as well as a `clear` utility to reset it to a clean state:
```cpp
// checks if there are processes still running
const auto empty = scheduler.empty();
// gets the number of processes still running
entt::scheduler<std::uint32_t>::size_type size = scheduler.size();
entt::scheduler::size_type size = scheduler.size();
// resets the scheduler to its initial state and discards all the processes
scheduler.clear();
@@ -172,8 +177,8 @@ To attach a process to a scheduler there are mainly two ways:
scheduler.attach([](auto...){ /* ... */ });
```
In both cases, the return value is an opaque object that offers a `then` member
function to use to create chains of processes to run sequentially.<br/>
In both cases, the scheduler is returned and its `then` member function can be
used to create chains of processes to run sequentially.<br/>
As a minimal example of use:
```cpp
@@ -201,7 +206,7 @@ scheduler.update(delta, &data);
```
In addition to these functions, the scheduler offers an `abort` member function
that can be used to discard all the running processes at once:
that is used to discard all the running processes at once:
```cpp
// aborts all the processes abruptly ...

View File

@@ -1,18 +1,35 @@
# Similar projects
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [Similar projects](#similar-projects)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
There are many projects similar to `EnTT`, both open source and not.<br/>
Some even borrowed some ideas from this library and expressed them in different
languages.<br/>
Others developed different architectures from scratch and therefore offer
alternative solutions with their pros and cons.
Below an incomplete list of those that I've come across so far.<br/>
If you know of other similar projects out there, feel free to open an issue or a
PR and I'll be glad to add them to this page.<br/>
I hope the following lists can grow much more in the future.
# Similar projects
Below an incomplete list of similar projects that I've come across so far.<br/>
If some terms or designs aren't clear, I recommend referring to the
[_ECS Back and Forth_](https://skypjack.github.io/tags/#ecs) series for all the
details.
I hope this list can grow much more in the future:
* C:
* [destral_ecs](https://github.com/roig/destral_ecs): a single-file ECS based
on sparse sets.
@@ -34,6 +51,8 @@ I hope this list can grow much more in the future:
solution between an ECS and dynamic mixins.
* C#
* [Arch](https://github.com/genaray/Arch): a simple, fast and _unity entities_
inspired archetype ECS with optional multithreading.
* [Entitas](https://github.com/sschmid/Entitas-CSharp): the ECS framework for
C# and Unity, where _reactive systems_ were invented.
* [LeoECS](https://github.com/Leopotam/ecs): simple lightweight C# Entity
@@ -70,6 +89,3 @@ I hope this list can grow much more in the future:
* Zig
* [zig-ecs](https://github.com/prime31/zig-ecs): a _zig-ification_ of `EnTT`.
If you know of other resources out there that can be of interest for the reader,
feel free to open an issue or a PR and I'll be glad to add them to this page.

View File

@@ -9,6 +9,7 @@
* [Delegate](#delegate)
* [Runtime arguments](#runtime-arguments)
* [Lambda support](#lambda-support)
* [Raw access](#raw-access)
* [Signals](#signals)
* [Event dispatcher](#event-dispatcher)
* [Named queues](#named-queues)
@@ -38,7 +39,7 @@ lightweight classes to solve the same and many other problems.
# Delegate
A delegate can be used as a general purpose invoker with no memory overhead for
free functions and member functions provided along with an instance on which to
free functions, lambdas and members provided along with an instance on which to
invoke them.<br/>
It doesn't claim to be a drop-in replacement for an `std::function`, so don't
expect to use it whenever an `std::function` fits well. That said, it's most
@@ -92,9 +93,9 @@ delegate.connect<&g>(c);
delegate(42);
```
The function `g` is invoked with a reference to `c` and `42`. However, the
function type of the delegate is still `void(int)`. This is also the signature
of its function call operator.<br/>
Function `g` is invoked with a reference to `c` and `42`. However, the function
type of the delegate is still `void(int)`. This is also the signature of its
function call operator.<br/>
Another interesting aspect of the delegate class is that it accepts functions
with a list of parameters that is shorter than that of its function type:
@@ -105,9 +106,15 @@ delegate(42);
```
Where the function type of the delegate is `void(int)` as above. It goes without
saying that the extra arguments are silently discarded internally.<br/>
This is a nice-to-have feature in a lot of cases, as an example when the
`delegate` class is used as a building block of a signal-slot system.
saying that the extra arguments are silently discarded internally. This is a
nice-to-have feature in a lot of cases, as an example when the `delegate` class
is used as a building block of a signal-slot system.<br/>
In fact, this filtering works both ways. The class tries to pass its first
_count_ arguments **first**, then the last _count_. Watch out for conversion
rules if in doubt when connecting a listener!<br/>
Arbitrary functions that pull random arguments from the delegate list aren't
supported instead. Other feature were preferred, such as support for functions
with compatible argument lists although not equal to those of the delegate.
To create and initialize a delegate at once, there are a few specialized
constructors. Because of the rules of the language, the listener is provided by
@@ -231,6 +238,24 @@ As above, the first parameter (`const void *`) isn't part of the function type
of the delegate and is used to dispatch arbitrary user data back and forth. In
other terms, the function type of the delegate above is `int(int)`.
## Raw access
While not recommended, a delegate also allows direct access to the stored
callable function target and underlying data, if any.<br/>
This makes it possible to bypass the behavior of the delegate itself and force
calls on different instances:
```cpp
my_struct other;
delegate.target(&other, 42);
```
It goes without saying that this type of approach is **very** risky, especially
since there is no way of knowing whether the contained function was originally a
member function of some class, a free function or a lambda.<br/>
Another possible (and meaningful) use of this feature is that of identifying a
particular delegate through its descriptive _traits_ instead.
# Signals
Signal handlers work with references to classes, function pointers and pointers
@@ -290,7 +315,7 @@ sink.disconnect<&foo>();
sink.disconnect<&listener::bar>(instance);
// disconnect all member functions of an instance, if any
sink.disconnect(instance);
sink.disconnect(&instance);
// discards all listeners at once
sink.disconnect();
@@ -300,15 +325,6 @@ As shown above, listeners don't have to strictly follow the signature of the
signal. As long as a listener can be invoked with the given arguments to yield a
result that is convertible to the given return type, everything works just
fine.<br/>
It's also possible to connect a listener before other elements already contained
by the signal. The `before` function returns a `sink` object that is correctly
initialized for the purpose and can be used to connect one or more listeners in
order and in the desired position:
```cpp
sink.before<&foo>().connect<&listener::bar>(instance);
```
In all cases, the `connect` member function returns by default a `connection`
object to be used as an alternative to break a connection by means of its
`release` member function.<br/>
@@ -409,7 +425,7 @@ of them at once:
```cpp
dispatcher.sink<an_event>().disconnect<&listener::receive>(listener);
dispatcher.sink<another_event>().disconnect(listener);
dispatcher.sink<another_event>().disconnect(&listener);
```
The `trigger` member function serves the purpose of sending an immediate event

View File

@@ -14,6 +14,8 @@
## Enable Cpp17
> Skip this part if you are working with UE5, Since UE5 uses cpp17 by default.
As of writing (Unreal Engine v4.25), the default C++ standard of Unreal Engine
is C++14.<br/>
On the other hand, note that `EnTT` requires C++17 to compile. To enable it, in

View File

@@ -1,34 +1,42 @@
[
# gtest only
{ "include": [ "@<gtest/internal/.*>", "private", "<gtest/gtest.h>", "public" ] },
{ "include": [ "@<gtest/gtest-.*>", "private", "<gtest/gtest.h>", "public" ] },
{ "include": [ "@[\"<].*/container/fwd.hpp[\">]", "private", "<entt/container/dense_map.hpp>", "public" ] },
{ "include": [ "@[\"<].*/container/fwd.hpp[\">]", "private", "<entt/container/dense_set.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd.hpp[\">]", "private", "<entt/core/any.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd.hpp[\">]", "private", "<entt/core/family.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd.hpp[\">]", "private", "<entt/core/hashed_string.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd.hpp[\">]", "private", "<entt/core/ident.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd.hpp[\">]", "private", "<entt/core/monostate.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd.hpp[\">]", "private", "<entt/core/type_info.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd.hpp[\">]", "private", "<entt/core/type_traits.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/entity.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/group.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/handle.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/helper.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/observer.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/organizer.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/registry.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/runtime_view.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/snapshot.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/sparse_set.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/storage.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/view.hpp>", "public" ] },
{ "include": [ "@[\"<].*/meta/fwd.hpp[\">]", "private", "<entt/meta/meta.hpp>", "public" ] },
{ "include": [ "@[\"<].*/poly/fwd.hpp[\">]", "private", "<entt/poly/poly.hpp>", "public" ] },
{ "include": [ "@[\"<].*/resource/fwd.hpp[\">]", "private", "<entt/resource/cache.hpp>", "public" ] },
{ "include": [ "@[\"<].*/resource/fwd.hpp[\">]", "private", "<entt/resource/loader.hpp>", "public" ] },
{ "include": [ "@[\"<].*/resource/fwd.hpp[\">]", "private", "<entt/resource/resource.hpp>", "public" ] },
{ "include": [ "@[\"<].*/signal/fwd.hpp[\">]", "private", "<entt/signal/delegate.hpp>", "public" ] },
{ "include": [ "@[\"<].*/signal/fwd.hpp[\">]", "private", "<entt/signal/dispatcher.hpp>", "public" ] },
{ "include": [ "@[\"<].*/signal/fwd.hpp[\">]", "private", "<entt/signal/emitter.hpp>", "public" ] },
{ "include": [ "@[\"<].*/signal/fwd.hpp[\">]", "private", "<entt/signal/sigh.hpp>", "public" ] }
# forward files
{ "include": [ "@[\"<].*/container/fwd\\.hpp[\">]", "private", "<entt/container/dense_map.hpp>", "public" ] },
{ "include": [ "@[\"<].*/container/fwd\\.hpp[\">]", "private", "<entt/container/dense_set.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/any.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/family.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/hashed_string.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/ident.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/monostate.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/type_info.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/type_traits.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/component.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/entity.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/group.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/handle.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/helper.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/observer.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/organizer.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/registry.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/runtime_view.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/snapshot.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/sparse_set.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/storage.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/view.hpp>", "public" ] },
{ "include": [ "@[\"<].*/graph/fwd\\.hpp[\">]", "private", "<entt/graph/adjacency_matrix.hpp>", "public" ] },
{ "include": [ "@[\"<].*/graph/fwd\\.hpp[\">]", "private", "<entt/graph/dot.hpp>", "public" ] },
{ "include": [ "@[\"<].*/graph/fwd\\.hpp[\">]", "private", "<entt/graph/flow.hpp>", "public" ] },
{ "include": [ "@[\"<].*/meta/fwd\\.hpp[\">]", "private", "<entt/meta/meta.hpp>", "public" ] },
{ "include": [ "@[\"<].*/poly/fwd\\.hpp[\">]", "private", "<entt/poly/poly.hpp>", "public" ] },
{ "include": [ "@[\"<].*/process/fwd\\.hpp[\">]", "private", "<entt/process/process.hpp>", "public" ] },
{ "include": [ "@[\"<].*/process/fwd\\.hpp[\">]", "private", "<entt/process/scheduler.hpp>", "public" ] },
{ "include": [ "@[\"<].*/resource/fwd\\.hpp[\">]", "private", "<entt/resource/cache.hpp>", "public" ] },
{ "include": [ "@[\"<].*/resource/fwd\\.hpp[\">]", "private", "<entt/resource/loader.hpp>", "public" ] },
{ "include": [ "@[\"<].*/resource/fwd\\.hpp[\">]", "private", "<entt/resource/resource.hpp>", "public" ] },
{ "include": [ "@[\"<].*/signal/fwd\\.hpp[\">]", "private", "<entt/signal/delegate.hpp>", "public" ] },
{ "include": [ "@[\"<].*/signal/fwd\\.hpp[\">]", "private", "<entt/signal/dispatcher.hpp>", "public" ] },
{ "include": [ "@[\"<].*/signal/fwd\\.hpp[\">]", "private", "<entt/signal/emitter.hpp>", "public" ] },
{ "include": [ "@[\"<].*/signal/fwd\\.hpp[\">]", "private", "<entt/signal/sigh.hpp>", "public" ] }
]

View File

@@ -1,62 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="entt::basic_registry&lt;*&gt;">
<Intrinsic Name="pools_size" Expression="pools.packed.first_base::value.size()"/>
<Intrinsic Name="vars_size" Expression="vars.ctx.packed.first_base::value.size()"/>
<Intrinsic Name="to_entity" Expression="*((entity_traits::entity_type *)&amp;entity) &amp; entity_traits::entity_mask">
<Parameter Name="entity" Type="entity_traits::value_type &amp;"/>
</Intrinsic>
<DisplayString>{{ size={ epool.size() } }}</DisplayString>
<DisplayString>{{ pools={ pools.size() } }}</DisplayString>
<Expand>
<Item IncludeView="simple" Name="[epool]">epool,view(simple)nr</Item>
<Synthetic Name="[epool]" ExcludeView="simple">
<DisplayString>{ epool.size() }</DisplayString>
<Expand>
<CustomListItems>
<Variable Name="pos" InitialValue="0" />
<Variable Name="last" InitialValue="epool.size()"/>
<Loop>
<Break Condition="pos == last"/>
<If Condition="to_entity(epool[pos]) == pos">
<Item Name="[{ pos }]">epool[pos]</Item>
</If>
<Exec>++pos</Exec>
</Loop>
</CustomListItems>
</Expand>
</Synthetic>
<Synthetic Name="[destroyed]" ExcludeView="simple">
<DisplayString>{ to_entity(free_list) != entity_traits::entity_mask }</DisplayString>
<Expand>
<CustomListItems>
<Variable Name="it" InitialValue="to_entity(free_list)" />
<Loop>
<Break Condition="it == entity_traits::entity_mask"/>
<Item Name="[{ it }]">epool[it]</Item>
<Exec>it = to_entity(epool[it])</Exec>
</Loop>
</CustomListItems>
</Expand>
</Synthetic>
<Item Name="[entities]">entities</Item>
<Synthetic Name="[pools]">
<DisplayString>{ pools_size() }</DisplayString>
<DisplayString>{ pools.size() }</DisplayString>
<Expand>
<IndexListItems ExcludeView="simple">
<Size>pools_size()</Size>
<Size>pools.size()</Size>
<ValueNode>*pools.packed.first_base::value[$i].element.second</ValueNode>
</IndexListItems>
<IndexListItems IncludeView="simple">
<Size>pools_size()</Size>
<Size>pools.size()</Size>
<ValueNode>*pools.packed.first_base::value[$i].element.second,view(simple)</ValueNode>
</IndexListItems>
</Expand>
</Synthetic>
<Item Name="[groups]" ExcludeView="simple">groups.size()</Item>
<Synthetic Name="[vars]">
<DisplayString>{ vars_size() }</DisplayString>
<DisplayString>{ vars.ctx.size() }</DisplayString>
<Expand>
<IndexListItems>
<Size>vars_size()</Size>
<Size>vars.ctx.size()</Size>
<ValueNode>vars.ctx.packed.first_base::value[$i].element.second</ValueNode>
</IndexListItems>
</Expand>
@@ -64,25 +30,27 @@
</Expand>
</Type>
<Type Name="entt::basic_sparse_set&lt;*&gt;">
<Intrinsic Name="cap" Expression="(traits_type::version_mask &lt;&lt; traits_type::length)"/>
<DisplayString>{{ size={ packed.size() }, type={ info->alias,na } }}</DisplayString>
<Expand>
<Item Name="[capacity]" ExcludeView="simple">packed.capacity()</Item>
<Item Name="[policy]">mode,en</Item>
<Item Name="[free_list]">head</Item>
<Synthetic Name="[sparse]">
<DisplayString>{ sparse.size() * entity_traits::page_size }</DisplayString>
<DisplayString>{ sparse.size() * traits_type::page_size }</DisplayString>
<Expand>
<ExpandedItem IncludeView="simple">sparse,view(simple)</ExpandedItem>
<CustomListItems ExcludeView="simple">
<Variable Name="pos" InitialValue="0"/>
<Variable Name="page" InitialValue="0"/>
<Variable Name="offset" InitialValue="0"/>
<Variable Name="last" InitialValue="sparse.size() * entity_traits::page_size"/>
<Variable Name="last" InitialValue="sparse.size() * traits_type::page_size"/>
<Loop>
<Break Condition="pos == last"/>
<Exec>page = pos / entity_traits::page_size</Exec>
<Exec>offset = pos &amp; (entity_traits::page_size - 1)</Exec>
<If Condition="sparse[page] &amp;&amp; (*((entity_traits::entity_type *)&amp;sparse[page][offset]) &lt; ~entity_traits::entity_mask)">
<Item Name="[{ pos }]">*((entity_traits::entity_type *)&amp;sparse[page][offset]) &amp; entity_traits::entity_mask</Item>
<Exec>page = pos / traits_type::page_size</Exec>
<Exec>offset = pos &amp; (traits_type::page_size - 1)</Exec>
<If Condition="sparse[page] &amp;&amp; (*((traits_type::entity_type *)&amp;sparse[page][offset]) &lt; cap())">
<Item Name="[{ pos }]">*((traits_type::entity_type *)&amp;sparse[page][offset]) &amp; traits_type::entity_mask</Item>
</If>
<Exec>++pos</Exec>
</Loop>
@@ -98,7 +66,7 @@
<Variable Name="last" InitialValue="packed.size()"/>
<Loop>
<Break Condition="pos == last"/>
<If Condition="*((entity_traits::entity_type *)&amp;packed[pos]) &lt; ~entity_traits::entity_mask">
<If Condition="*((traits_type::entity_type *)&amp;packed[pos]) &lt; cap()">
<Item Name="[{ pos }]">packed[pos]</Item>
</If>
<Exec>++pos</Exec>
@@ -109,20 +77,21 @@
</Expand>
</Type>
<Type Name="entt::basic_storage&lt;*&gt;">
<Intrinsic Name="cap" Expression="(base_type::traits_type::version_mask &lt;&lt; base_type::traits_type::length)"/>
<DisplayString>{{ size={ base_type::packed.size() }, type={ base_type::info->alias,na } }}</DisplayString>
<Expand>
<Item Name="[capacity]" Optional="true" ExcludeView="simple">packed.first_base::value.capacity() * comp_traits::page_size</Item>
<Item Name="[page size]" Optional="true" ExcludeView="simple">comp_traits::page_size</Item>
<Item Name="[capacity]" Optional="true" ExcludeView="simple">payload.capacity() * traits_type::page_size</Item>
<Item Name="[page size]" Optional="true" ExcludeView="simple">traits_type::page_size</Item>
<Item Name="[base]" ExcludeView="simple">(base_type*)this,nand</Item>
<Item Name="[base]" IncludeView="simple">(base_type*)this,view(simple)nand</Item>
<!-- having SFINAE-like techniques in natvis is priceless :) -->
<CustomListItems Condition="packed.first_base::value.size() != 0" Optional="true">
<CustomListItems Condition="payload.size() != 0" Optional="true">
<Variable Name="pos" InitialValue="0" />
<Variable Name="last" InitialValue="base_type::packed.size()"/>
<Loop>
<Break Condition="pos == last"/>
<If Condition="*((base_type::entity_traits::entity_type *)&amp;base_type::packed[pos]) &lt; ~base_type::entity_traits::entity_mask">
<Item Name="[{ pos }:{ base_type::packed[pos] }]">packed.first_base::value[pos / comp_traits::page_size][pos &amp; (comp_traits::page_size - 1)]</Item>
<If Condition="*((base_type::traits_type::entity_type *)&amp;base_type::packed[pos]) &lt; cap()">
<Item Name="[{ pos }:{ base_type::packed[pos] }]">payload[pos / traits_type::page_size][pos &amp; (traits_type::page_size - 1)]</Item>
</If>
<Exec>++pos</Exec>
</Loop>
@@ -130,10 +99,12 @@
</Expand>
</Type>
<Type Name="entt::basic_view&lt;*&gt;">
<DisplayString>{{ size_hint={ view->packed.size() } }}</DisplayString>
<DisplayString Condition="leading != nullptr">{{ size_hint={ leading->packed.size() } }}</DisplayString>
<DisplayString>{{ size_hint=0 }}</DisplayString>
<Expand>
<Item Name="[pools]">pools,na</Item>
<Item Name="[filter]">filter,na</Item>
<Item Name="[pools]" Optional="true">pools,na</Item>
<Item Name="[filter]" Optional="true">filter,na</Item>
<Item Name="[handle]" Condition="leading != nullptr">leading,na</Item>
</Expand>
</Type>
<Type Name="entt::basic_runtime_view&lt;*&gt;">

View File

@@ -8,7 +8,7 @@
</Expand>
</Type>
<Type Name="entt::basic_dispatcher&lt;*&gt;">
<Intrinsic Name="size" Expression="pools.first_base::value.packed.first_base::value.size()"/>
<Intrinsic Name="size" Expression="pools.first_base::value.size()"/>
<DisplayString>{{ size={ size() } }}</DisplayString>
<Expand>
<Synthetic Name="[pools]">
@@ -50,7 +50,6 @@
<DisplayString>{{ type={ "$T1" } }}</DisplayString>
<Expand>
<Item Name="[signal]">signal,na</Item>
<Item Name="[offset]">offset</Item>
</Expand>
</Type>
</AutoVisualizer>

File diff suppressed because it is too large Load Diff

11
src/BUILD.bazel Normal file
View File

@@ -0,0 +1,11 @@
load("@bazel_skylib//lib:selects.bzl", "selects")
load("//bazel:copts.bzl", "COPTS")
package(default_visibility = ["//:__subpackages__"])
cc_library(
name = "entt",
includes = ["."],
hdrs = glob(["**/*.h", "**/*.hpp"]),
copts = COPTS,
)

View File

@@ -25,6 +25,8 @@
#ifndef ENTT_ID_TYPE
# include <cstdint>
# define ENTT_ID_TYPE std::uint32_t
#else
# include <cstdint> // provides coverage for types in the std namespace
#endif
#ifndef ENTT_SPARSE_PAGE
@@ -50,6 +52,8 @@
# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg)
#endif
#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg);
#ifdef ENTT_NO_ETO
# define ENTT_ETO_TYPE(Type) void
#else

View File

@@ -4,8 +4,8 @@
#include "macro.h"
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 11
#define ENTT_VERSION_PATCH 1
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \

View File

@@ -20,11 +20,7 @@
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename Key, typename Type>
@@ -69,6 +65,7 @@ public:
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
using iterator_concept = std::random_access_iterator_tag;
constexpr dense_map_iterator() noexcept
: it{} {}
@@ -128,51 +125,51 @@ public:
return {it->element.first, it->element.second};
}
template<typename ILhs, typename IRhs>
friend constexpr std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) noexcept;
template<typename Lhs, typename Rhs>
friend constexpr std::ptrdiff_t operator-(const dense_map_iterator<Lhs> &, const dense_map_iterator<Rhs> &) noexcept;
template<typename ILhs, typename IRhs>
friend constexpr bool operator==(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) noexcept;
template<typename Lhs, typename Rhs>
friend constexpr bool operator==(const dense_map_iterator<Lhs> &, const dense_map_iterator<Rhs> &) noexcept;
template<typename ILhs, typename IRhs>
friend constexpr bool operator<(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) noexcept;
template<typename Lhs, typename Rhs>
friend constexpr bool operator<(const dense_map_iterator<Lhs> &, const dense_map_iterator<Rhs> &) noexcept;
private:
It it;
};
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
return lhs.it - rhs.it;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator==(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator==(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
return lhs.it == rhs.it;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator!=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator!=(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
return !(lhs == rhs);
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator<(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator<(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
return lhs.it < rhs.it;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator>(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator>(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
return rhs < lhs;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator<=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator<=(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
return !(lhs > rhs);
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator>=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator>=(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
return !(lhs < rhs);
}
@@ -190,6 +187,7 @@ public:
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
using iterator_concept = std::forward_iterator_tag;
constexpr dense_map_local_iterator() noexcept
: it{},
@@ -230,22 +228,18 @@ private:
std::size_t offset;
};
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator<Lhs> &lhs, const dense_map_local_iterator<Rhs> &rhs) noexcept {
return lhs.index() == rhs.index();
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator<Lhs> &lhs, const dense_map_local_iterator<Rhs> &rhs) noexcept {
return !(lhs == rhs);
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
/**
* @brief Associative container for key-value pairs with unique keys.
@@ -266,7 +260,7 @@ class dense_map {
static constexpr std::size_t minimum_capacity = 8u;
using node_type = internal::dense_map_node<Key, Type>;
using alloc_traits = typename std::allocator_traits<Allocator>;
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, std::pair<const Key, Type>>, "Invalid value type");
using sparse_container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
using packed_container_type = std::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
@@ -464,7 +458,6 @@ public:
/**
* @brief Returns an iterator to the beginning.
*
* The returned iterator points to the first instance of the internal array.
* If the array is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first instance of the internal array.
@@ -485,11 +478,6 @@ public:
/**
* @brief Returns an iterator to the end.
*
* The returned iterator points to the element following the last instance
* of the internal array. Attempting to dereference the returned iterator
* results in undefined behavior.
*
* @return An iterator to the element following the last instance of the
* internal array.
*/
@@ -838,7 +826,7 @@ public:
}
/*! @copydoc equal_range */
template<class Other>
template<typename Other>
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, std::pair<const_iterator, const_iterator>>>
equal_range(const Other &key) const {
const auto it = find(key);
@@ -995,7 +983,7 @@ public:
sparse.first().resize(sz);
for(auto &&elem: sparse.first()) {
elem = std::numeric_limits<size_type>::max();
elem = (std::numeric_limits<size_type>::max)();
}
for(size_type pos{}, last = size(); pos < last; ++pos) {
@@ -1039,11 +1027,7 @@ private:
} // namespace entt
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace std {
template<typename Key, typename Value, typename Allocator>
@@ -1051,10 +1035,6 @@ struct uses_allocator<entt::internal::dense_map_node<Key, Value>, Allocator>
: std::true_type {};
} // namespace std
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
#endif

View File

@@ -19,11 +19,7 @@
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename It>
@@ -96,51 +92,51 @@ public:
return *operator->();
}
template<typename ILhs, typename IRhs>
friend constexpr std::ptrdiff_t operator-(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) noexcept;
template<typename Lhs, typename Rhs>
friend constexpr std::ptrdiff_t operator-(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept;
template<typename ILhs, typename IRhs>
friend constexpr bool operator==(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) noexcept;
template<typename Lhs, typename Rhs>
friend constexpr bool operator==(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept;
template<typename ILhs, typename IRhs>
friend constexpr bool operator<(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) noexcept;
template<typename Lhs, typename Rhs>
friend constexpr bool operator<(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept;
private:
It it;
};
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
return lhs.it - rhs.it;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator==(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator==(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
return lhs.it == rhs.it;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator!=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator!=(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
return !(lhs == rhs);
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator<(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator<(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
return lhs.it < rhs.it;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator>(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator>(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
return rhs < lhs;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator<=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator<=(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
return !(lhs > rhs);
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator>=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator>=(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
return !(lhs < rhs);
}
@@ -195,22 +191,18 @@ private:
std::size_t offset;
};
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator<ILhs> &lhs, const dense_set_local_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator<Lhs> &lhs, const dense_set_local_iterator<Rhs> &rhs) noexcept {
return lhs.index() == rhs.index();
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator<ILhs> &lhs, const dense_set_local_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator<Lhs> &lhs, const dense_set_local_iterator<Rhs> &rhs) noexcept {
return !(lhs == rhs);
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
/**
* @brief Associative container for unique objects of a given type.
@@ -311,6 +303,10 @@ public:
using iterator = internal::dense_set_iterator<typename packed_container_type::iterator>;
/*! @brief Constant random access iterator type. */
using const_iterator = internal::dense_set_iterator<typename packed_container_type::const_iterator>;
/*! @brief Reverse iterator type. */
using reverse_iterator = std::reverse_iterator<iterator>;
/*! @brief Constant reverse iterator type. */
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
/*! @brief Forward iterator type. */
using local_iterator = internal::dense_set_local_iterator<typename packed_container_type::iterator>;
/*! @brief Constant forward iterator type. */
@@ -410,7 +406,6 @@ public:
/**
* @brief Returns an iterator to the beginning.
*
* The returned iterator points to the first instance of the internal array.
* If the array is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first instance of the internal array.
@@ -431,11 +426,6 @@ public:
/**
* @brief Returns an iterator to the end.
*
* The returned iterator points to the element following the last instance
* of the internal array. Attempting to dereference the returned iterator
* results in undefined behavior.
*
* @return An iterator to the element following the last instance of the
* internal array.
*/
@@ -453,6 +443,46 @@ public:
return packed.first().end();
}
/**
* @brief Returns a reverse iterator to the beginning.
*
* If the array is empty, the returned iterator will be equal to `rend()`.
*
* @return An iterator to the first instance of the reversed internal array.
*/
[[nodiscard]] const_reverse_iterator crbegin() const noexcept {
return std::make_reverse_iterator(cend());
}
/*! @copydoc crbegin */
[[nodiscard]] const_reverse_iterator rbegin() const noexcept {
return crbegin();
}
/*! @copydoc rbegin */
[[nodiscard]] reverse_iterator rbegin() noexcept {
return std::make_reverse_iterator(end());
}
/**
* @brief Returns a reverse iterator to the end.
* @return An iterator to the element following the last instance of the
* reversed internal array.
*/
[[nodiscard]] const_reverse_iterator crend() const noexcept {
return std::make_reverse_iterator(cbegin());
}
/*! @copydoc crend */
[[nodiscard]] const_reverse_iterator rend() const noexcept {
return crend();
}
/*! @copydoc rend */
[[nodiscard]] reverse_iterator rend() noexcept {
return std::make_reverse_iterator(begin());
}
/**
* @brief Checks whether a container is empty.
* @return True if the container is empty, false otherwise.
@@ -691,7 +721,7 @@ public:
}
/*! @copydoc equal_range */
template<class Other>
template<typename Other>
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, std::pair<const_iterator, const_iterator>>>
equal_range(const Other &value) const {
const auto it = find(value);
@@ -848,7 +878,7 @@ public:
sparse.first().resize(sz);
for(auto &&elem: sparse.first()) {
elem = std::numeric_limits<size_type>::max();
elem = (std::numeric_limits<size_type>::max)();
}
for(size_type pos{}, last = size(); pos < last; ++pos) {

View File

@@ -3,6 +3,7 @@
#include <functional>
#include <memory>
#include <utility>
namespace entt {

View File

@@ -95,14 +95,15 @@ struct radix_sort {
template<typename It, typename Getter = identity>
void operator()(It first, It last, Getter getter = Getter{}) const {
if(first < last) {
static constexpr auto mask = (1 << Bit) - 1;
static constexpr auto buckets = 1 << Bit;
static constexpr auto passes = N / Bit;
constexpr auto passes = N / Bit;
using value_type = typename std::iterator_traits<It>::value_type;
std::vector<value_type> aux(std::distance(first, last));
auto part = [getter = std::move(getter)](auto from, auto to, auto out, auto start) {
constexpr auto mask = (1 << Bit) - 1;
constexpr auto buckets = 1 << Bit;
std::size_t index[buckets]{};
std::size_t count[buckets]{};

View File

@@ -13,11 +13,7 @@
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
enum class any_operation : std::uint8_t {
@@ -30,19 +26,19 @@ enum class any_operation : std::uint8_t {
get
};
} // namespace internal
/*! @endcond */
/*! @brief Possible modes of an any object. */
enum class any_policy : std::uint8_t {
/*! @brief Default mode, the object owns the contained element. */
owner,
/*! @brief Aliasing mode, the object _points_ to a non-const element. */
ref,
/*! @brief Const aliasing mode, the object _points_ to a const element. */
cref
};
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief A SBO friendly, type-safe container for single values of any type.
* @tparam Len Size of the storage reserved for the small buffer optimization.
@@ -51,7 +47,6 @@ enum class any_policy : std::uint8_t {
template<std::size_t Len, std::size_t Align>
class basic_any {
using operation = internal::any_operation;
using policy = internal::any_policy;
using vtable_type = const void *(const operation, const basic_any &, const void *);
struct storage_type {
@@ -59,15 +54,15 @@ class basic_any {
};
template<typename Type>
static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len &&std::is_nothrow_move_constructible_v<Type>;
static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v<Type>;
template<typename Type>
static const void *basic_vtable(const operation op, const basic_any &value, const void *other) {
static_assert(!std::is_same_v<Type, void> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
static_assert(!std::is_void_v<Type> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
const Type *element = nullptr;
if constexpr(in_situ<Type>) {
element = value.owner() ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance);
element = (value.mode == any_policy::owner) ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance);
} else {
element = static_cast<const Type *>(value.instance);
}
@@ -80,7 +75,7 @@ class basic_any {
break;
case operation::move:
if constexpr(in_situ<Type>) {
if(value.owner()) {
if(value.mode == any_policy::owner) {
return new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(element))};
}
}
@@ -128,17 +123,17 @@ class basic_any {
vtable = basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>;
if constexpr(std::is_lvalue_reference_v<Type>) {
static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v<Args> && ...), "Invalid arguments");
mode = std::is_const_v<std::remove_reference_t<Type>> ? policy::cref : policy::ref;
static_assert((std::is_lvalue_reference_v<Args> && ...) && (sizeof...(Args) == 1u), "Invalid arguments");
mode = std::is_const_v<std::remove_reference_t<Type>> ? any_policy::cref : any_policy::ref;
instance = (std::addressof(args), ...);
} else if constexpr(in_situ<std::remove_cv_t<std::remove_reference_t<Type>>>) {
if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
if constexpr(std::is_aggregate_v<std::remove_cv_t<std::remove_reference_t<Type>>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>)) {
new(&storage) std::remove_cv_t<std::remove_reference_t<Type>>{std::forward<Args>(args)...};
} else {
new(&storage) std::remove_cv_t<std::remove_reference_t<Type>>(std::forward<Args>(args)...);
}
} else {
if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
if constexpr(std::is_aggregate_v<std::remove_cv_t<std::remove_reference_t<Type>>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>)) {
instance = new std::remove_cv_t<std::remove_reference_t<Type>>{std::forward<Args>(args)...};
} else {
instance = new std::remove_cv_t<std::remove_reference_t<Type>>(std::forward<Args>(args)...);
@@ -147,7 +142,7 @@ class basic_any {
}
}
basic_any(const basic_any &other, const policy pol) noexcept
basic_any(const basic_any &other, const any_policy pol) noexcept
: instance{other.data()},
info{other.info},
vtable{other.vtable},
@@ -174,7 +169,7 @@ public:
: instance{},
info{},
vtable{},
mode{policy::owner} {
mode{any_policy::owner} {
initialize<Type>(std::forward<Args>(args)...);
}
@@ -214,7 +209,7 @@ public:
/*! @brief Frees the internal storage, whatever it means. */
~basic_any() {
if(vtable && owner()) {
if(vtable && (mode == any_policy::owner)) {
vtable(operation::destroy, *this, nullptr);
}
}
@@ -295,7 +290,7 @@ public:
* @return An opaque pointer the contained instance, if any.
*/
[[nodiscard]] void *data() noexcept {
return mode == policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data());
return mode == any_policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data());
}
/**
@@ -304,7 +299,7 @@ public:
* @return An opaque pointer the contained instance, if any.
*/
[[nodiscard]] void *data(const type_info &req) noexcept {
return mode == policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data(req));
return mode == any_policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data(req));
}
/**
@@ -325,7 +320,7 @@ public:
* @return True in case of success, false otherwise.
*/
bool assign(const basic_any &other) {
if(vtable && mode != policy::cref && *info == *other.info) {
if(vtable && mode != any_policy::cref && *info == *other.info) {
return (vtable(operation::assign, *this, other.data()) != nullptr);
}
@@ -334,7 +329,7 @@ public:
/*! @copydoc assign */
bool assign(basic_any &&other) {
if(vtable && mode != policy::cref && *info == *other.info) {
if(vtable && mode != any_policy::cref && *info == *other.info) {
if(auto *val = other.data(); val) {
return (vtable(operation::transfer, *this, val) != nullptr);
} else {
@@ -347,7 +342,7 @@ public:
/*! @brief Destroys contained object */
void reset() {
if(vtable && owner()) {
if(vtable && (mode == any_policy::owner)) {
vtable(operation::destroy, *this, nullptr);
}
@@ -355,7 +350,7 @@ public:
ENTT_ASSERT((instance = nullptr) == nullptr, "");
info = &type_id<void>();
vtable = nullptr;
mode = policy::owner;
mode = any_policy::owner;
}
/**
@@ -393,20 +388,28 @@ public:
* @return A wrapper that shares a reference to an unmanaged object.
*/
[[nodiscard]] basic_any as_ref() noexcept {
return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)};
return basic_any{*this, (mode == any_policy::cref ? any_policy::cref : any_policy::ref)};
}
/*! @copydoc as_ref */
[[nodiscard]] basic_any as_ref() const noexcept {
return basic_any{*this, policy::cref};
return basic_any{*this, any_policy::cref};
}
/**
* @brief Returns true if a wrapper owns its object, false otherwise.
* @return True if the wrapper owns its object, false otherwise.
*/
[[nodiscard]] bool owner() const noexcept {
return (mode == policy::owner);
[[deprecated("use policy() and any_policy instead")]] [[nodiscard]] bool owner() const noexcept {
return (mode == any_policy::owner);
}
/**
* @brief Returns the current mode of an any object.
* @return The current mode of the any object.
*/
[[nodiscard]] any_policy policy() const noexcept {
return mode;
}
private:
@@ -416,7 +419,7 @@ private:
};
const type_info *info;
vtable_type *vtable;
policy mode;
any_policy mode;
};
/**
@@ -428,7 +431,7 @@ private:
* @return The element converted to the requested type.
*/
template<typename Type, std::size_t Len, std::size_t Align>
Type any_cast(const basic_any<Len, Align> &data) noexcept {
[[nodiscard]] Type any_cast(const basic_any<Len, Align> &data) noexcept {
const auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
ENTT_ASSERT(instance, "Invalid instance");
return static_cast<Type>(*instance);
@@ -436,7 +439,7 @@ Type any_cast(const basic_any<Len, Align> &data) noexcept {
/*! @copydoc any_cast */
template<typename Type, std::size_t Len, std::size_t Align>
Type any_cast(basic_any<Len, Align> &data) noexcept {
[[nodiscard]] Type any_cast(basic_any<Len, Align> &data) noexcept {
// forces const on non-reference types to make them work also with wrappers for const references
auto *const instance = any_cast<std::remove_reference_t<const Type>>(&data);
ENTT_ASSERT(instance, "Invalid instance");
@@ -445,7 +448,7 @@ Type any_cast(basic_any<Len, Align> &data) noexcept {
/*! @copydoc any_cast */
template<typename Type, std::size_t Len, std::size_t Align>
Type any_cast(basic_any<Len, Align> &&data) noexcept {
[[nodiscard]] Type any_cast(basic_any<Len, Align> &&data) noexcept {
if constexpr(std::is_copy_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
if(auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); instance) {
return static_cast<Type>(std::move(*instance));
@@ -461,14 +464,14 @@ Type any_cast(basic_any<Len, Align> &&data) noexcept {
/*! @copydoc any_cast */
template<typename Type, std::size_t Len, std::size_t Align>
const Type *any_cast(const basic_any<Len, Align> *data) noexcept {
[[nodiscard]] const Type *any_cast(const basic_any<Len, Align> *data) noexcept {
const auto &info = type_id<std::remove_cv_t<Type>>();
return static_cast<const Type *>(data->data(info));
}
/*! @copydoc any_cast */
template<typename Type, std::size_t Len, std::size_t Align>
Type *any_cast(basic_any<Len, Align> *data) noexcept {
[[nodiscard]] Type *any_cast(basic_any<Len, Align> *data) noexcept {
if constexpr(std::is_const_v<Type>) {
// last attempt to make wrappers for const references return their values
return any_cast<Type>(&std::as_const(*data));
@@ -488,7 +491,7 @@ Type *any_cast(basic_any<Len, Align> *data) noexcept {
* @return A properly initialized wrapper for an object of the given type.
*/
template<typename Type, std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename... Args>
basic_any<Len, Align> make_any(Args &&...args) {
[[nodiscard]] basic_any<Len, Align> make_any(Args &&...args) {
return basic_any<Len, Align>{std::in_place_type<Type>, std::forward<Args>(args)...};
}
@@ -501,7 +504,7 @@ basic_any<Len, Align> make_any(Args &&...args) {
* @return A properly initialized and not necessarily owning wrapper.
*/
template<std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename Type>
basic_any<Len, Align> forward_as_any(Type &&value) {
[[nodiscard]] basic_any<Len, Align> forward_as_any(Type &&value) {
return basic_any<Len, Align>{std::in_place_type<Type &&>, std::forward<Type>(value)};
}

View File

@@ -9,11 +9,7 @@
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename Type, std::size_t, typename = void>
@@ -73,11 +69,7 @@ struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Ty
};
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
/**
* @brief A compressed pair.

View File

@@ -7,11 +7,7 @@
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename>
@@ -43,11 +39,7 @@ struct basic_hashed_string {
};
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
/**
* @brief Zero overhead unique identifier.
@@ -67,7 +59,7 @@ struct basic_hashed_string {
template<typename Char>
class basic_hashed_string: internal::basic_hashed_string<Char> {
using base_type = internal::basic_hashed_string<Char>;
using hs_traits = internal::fnv1a_traits<id_type>;
using traits_type = internal::fnv1a_traits<id_type>;
struct const_wrapper {
// non-explicit constructor on purpose
@@ -79,10 +71,10 @@ class basic_hashed_string: internal::basic_hashed_string<Char> {
// FowlerNollVo hash function v. 1a - the good
[[nodiscard]] static constexpr auto helper(const Char *str) noexcept {
base_type base{str, 0u, hs_traits::offset};
base_type base{str, 0u, traits_type::offset};
for(; str[base.length]; ++base.length) {
base.hash = (base.hash ^ static_cast<hs_traits::type>(str[base.length])) * hs_traits::prime;
base.hash = (base.hash ^ static_cast<traits_type::type>(str[base.length])) * traits_type::prime;
}
return base;
@@ -90,10 +82,10 @@ class basic_hashed_string: internal::basic_hashed_string<Char> {
// FowlerNollVo hash function v. 1a - the good
[[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept {
base_type base{str, len, hs_traits::offset};
base_type base{str, len, traits_type::offset};
for(size_type pos{}; pos < len; ++pos) {
base.hash = (base.hash ^ static_cast<hs_traits::type>(str[pos])) * hs_traits::prime;
base.hash = (base.hash ^ static_cast<traits_type::type>(str[pos])) * traits_type::prime;
}
return base;
@@ -175,7 +167,7 @@ public:
* @return The size of the hashed string.
*/
[[nodiscard]] constexpr size_type size() const noexcept {
return base_type::length;
return base_type::length; // NOLINT
}
/**

View File

@@ -12,7 +12,8 @@
namespace entt {
/**
* @brief Checks whether a value is a power of two or not.
* @brief Checks whether a value is a power of two or not (waiting for C++20 and
* `std::has_single_bit`).
* @param value A value that may or may not be a power of two.
* @return True if the value is a power of two, false otherwise.
*/
@@ -21,7 +22,8 @@ namespace entt {
}
/**
* @brief Computes the smallest power of two greater than or equal to a value.
* @brief Computes the smallest power of two greater than or equal to a value
* (waiting for C++20 and `std::bit_ceil`).
* @param value The value to use.
* @return The smallest power of two greater than or equal to the given value.
*/
@@ -106,7 +108,7 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma
/**
* @brief Deleter for allocator-aware unique pointers (waiting for C++20).
* @tparam Args Types of arguments to use to construct the object.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Allocator>
struct allocation_deleter: private Allocator {
@@ -127,7 +129,7 @@ struct allocation_deleter: private Allocator {
* @param ptr A valid pointer to an object of the given type.
*/
constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v<typename allocator_type::value_type>) {
using alloc_traits = typename std::allocator_traits<Allocator>;
using alloc_traits = std::allocator_traits<Allocator>;
alloc_traits::destroy(*this, to_address(ptr));
alloc_traits::deallocate(*this, ptr, 1u);
}
@@ -163,11 +165,7 @@ ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) {
return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc};
}
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename Type>
@@ -223,11 +221,7 @@ struct uses_allocator_construction<std::pair<Type, Other>> {
};
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
/**
* @brief Uses-allocator construction utility (waiting for C++20).

View File

@@ -7,11 +7,7 @@
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename>
@@ -21,11 +17,7 @@ template<typename... Args>
struct is_tuple_impl<std::tuple<Args...>>: std::true_type {};
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
/**
* @brief Provides the member constant `value` to true if a given type is a
@@ -69,7 +61,7 @@ struct forward_apply: private Func {
* @tparam Args Types of arguments to use to construct the new instance.
* @param args Parameters to use to construct the instance.
*/
template<class... Args>
template<typename... Args>
constexpr forward_apply(Args &&...args) noexcept(std::is_nothrow_constructible_v<Func, Args...>)
: Func{std::forward<Args>(args)...} {}
@@ -79,13 +71,13 @@ struct forward_apply: private Func {
* @param args Parameters to forward to the underlying function.
* @return Return value of the underlying function, if any.
*/
template<class Type>
template<typename Type>
constexpr decltype(auto) operator()(Type &&args) noexcept(noexcept(std::apply(std::declval<Func &>(), args))) {
return std::apply(static_cast<Func &>(*this), std::forward<Type>(args));
}
/*! @copydoc operator()() */
template<class Type>
template<typename Type>
constexpr decltype(auto) operator()(Type &&args) const noexcept(noexcept(std::apply(std::declval<const Func &>(), args))) {
return std::apply(static_cast<const Func &>(*this), std::forward<Type>(args));
}

View File

@@ -11,11 +11,7 @@
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
struct ENTT_API type_index final {
@@ -38,26 +34,26 @@ template<typename Type>
}
template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
[[nodiscard]] static constexpr std::string_view type_name(int) noexcept {
[[nodiscard]] constexpr std::string_view type_name(int) noexcept {
constexpr auto value = stripped_type_name<Type>();
return value;
}
template<typename Type>
[[nodiscard]] static std::string_view type_name(char) noexcept {
[[nodiscard]] std::string_view type_name(char) noexcept {
static const auto value = stripped_type_name<Type>();
return value;
}
template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
[[nodiscard]] static constexpr id_type type_hash(int) noexcept {
[[nodiscard]] constexpr id_type type_hash(int) noexcept {
constexpr auto stripped = stripped_type_name<Type>();
constexpr auto value = hashed_string::value(stripped.data(), stripped.size());
return value;
}
template<typename Type>
[[nodiscard]] static id_type type_hash(char) noexcept {
[[nodiscard]] id_type type_hash(char) noexcept {
static const auto value = [](const auto stripped) {
return hashed_string::value(stripped.data(), stripped.size());
}(stripped_type_name<Type>());
@@ -65,11 +61,7 @@ template<typename Type>
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
/**
* @brief Type sequential identifier.

View File

@@ -3,6 +3,7 @@
#include <cstddef>
#include <iterator>
#include <tuple>
#include <type_traits>
#include <utility>
#include "../config/config.h"
@@ -16,7 +17,7 @@ namespace entt {
*/
template<std::size_t N>
struct choice_t
// Unfortunately, doxygen cannot parse such a construct.
// unfortunately, doxygen cannot parse such a construct
: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
{};
@@ -55,7 +56,6 @@ using type_identity_t = typename type_identity<Type>::type;
/**
* @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
* @tparam Type The type of which to return the size.
* @tparam The size of the type if `sizeof` accepts it, 0 otherwise.
*/
template<typename Type, typename = void>
struct size_of: std::integral_constant<std::size_t, 0u> {};
@@ -250,37 +250,40 @@ struct type_list_cat<type_list<Type...>> {
template<typename... List>
using type_list_cat_t = typename type_list_cat<List...>::type;
/*! @brief Primary template isn't defined on purpose. */
template<typename>
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename...>
struct type_list_unique;
template<typename First, typename... Other, typename... Type>
struct type_list_unique<type_list<First, Other...>, Type...>
: std::conditional_t<(std::is_same_v<First, Type> || ...), type_list_unique<type_list<Other...>, Type...>, type_list_unique<type_list<Other...>, Type..., First>> {};
template<typename... Type>
struct type_list_unique<type_list<>, Type...> {
using type = type_list<Type...>;
};
} // namespace internal
/*! @endcond */
/**
* @brief Removes duplicates types from a type list.
* @tparam Type One of the types provided by the given type list.
* @tparam Other The other types provided by the given type list.
* @tparam List Type list.
*/
template<typename Type, typename... Other>
struct type_list_unique<type_list<Type, Other...>> {
template<typename List>
struct type_list_unique {
/*! @brief A type list without duplicate types. */
using type = std::conditional_t<
(std::is_same_v<Type, Other> || ...),
typename type_list_unique<type_list<Other...>>::type,
type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>;
};
/*! @brief Removes duplicates types from a type list. */
template<>
struct type_list_unique<type_list<>> {
/*! @brief A type list without duplicate types. */
using type = type_list<>;
using type = typename internal::type_list_unique<List>::type;
};
/**
* @brief Helper type.
* @tparam Type A type list.
* @tparam List Type list.
*/
template<typename Type>
using type_list_unique_t = typename type_list_unique<Type>::type;
template<typename List>
using type_list_unique_t = typename type_list_unique<List>::type;
/**
* @brief Provides the member constant `value` to true if a type list contains a
@@ -297,7 +300,8 @@ struct type_list_contains;
* @tparam Other Type to look for.
*/
template<typename... Type, typename Other>
struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
struct type_list_contains<type_list<Type...>, Other>
: std::bool_constant<(std::is_same_v<Type, Other> || ...)> {};
/**
* @brief Helper variable template.
@@ -385,10 +389,20 @@ struct value_list_element<Index, value_list<Value, Other...>>
*/
template<auto Value, auto... Other>
struct value_list_element<0u, value_list<Value, Other...>> {
/*! @brief Searched type. */
using type = decltype(Value);
/*! @brief Searched value. */
static constexpr auto value = Value;
};
/**
* @brief Helper type.
* @tparam Index Index of the type to return.
* @tparam List Value list to search into.
*/
template<std::size_t Index, typename List>
using value_list_element_t = typename value_list_element<Index, List>::type;
/**
* @brief Helper type.
* @tparam Index Index of the value to return.
@@ -397,6 +411,58 @@ struct value_list_element<0u, value_list<Value, Other...>> {
template<std::size_t Index, typename List>
inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
/*! @brief Primary template isn't defined on purpose. */
template<auto, typename>
struct value_list_index;
/**
* @brief Provides compile-time type access to the values of a value list.
* @tparam Value Value to look for and for which to return the index.
* @tparam First First value provided by the value list.
* @tparam Other Other values provided by the value list.
*/
template<auto Value, auto First, auto... Other>
struct value_list_index<Value, value_list<First, Other...>> {
/*! @brief Unsigned integer type. */
using value_type = std::size_t;
/*! @brief Compile-time position of the given value in the sublist. */
static constexpr value_type value = 1u + value_list_index<Value, value_list<Other...>>::value;
};
/**
* @brief Provides compile-time type access to the values of a value list.
* @tparam Value Value to look for and for which to return the index.
* @tparam Other Other values provided by the value list.
*/
template<auto Value, auto... Other>
struct value_list_index<Value, value_list<Value, Other...>> {
static_assert(value_list_index<Value, value_list<Other...>>::value == sizeof...(Other), "Non-unique type");
/*! @brief Unsigned integer type. */
using value_type = std::size_t;
/*! @brief Compile-time position of the given value in the sublist. */
static constexpr value_type value = 0u;
};
/**
* @brief Provides compile-time type access to the values of a value list.
* @tparam Value Value to look for and for which to return the index.
*/
template<auto Value>
struct value_list_index<Value, value_list<>> {
/*! @brief Unsigned integer type. */
using value_type = std::size_t;
/*! @brief Compile-time position of the given type in the sublist. */
static constexpr value_type value = 0u;
};
/**
* @brief Helper variable template.
* @tparam List Value list.
* @tparam Value Value to look for and for which to return the index.
*/
template<auto Value, typename List>
inline constexpr std::size_t value_list_index_v = value_list_index<Value, List>::value;
/**
* @brief Concatenates multiple value lists.
* @tparam Value Values provided by the first value list.
@@ -448,6 +514,89 @@ struct value_list_cat<value_list<Value...>> {
template<typename... List>
using value_list_cat_t = typename value_list_cat<List...>::type;
/*! @brief Primary template isn't defined on purpose. */
template<typename>
struct value_list_unique;
/**
* @brief Removes duplicates values from a value list.
* @tparam Value One of the values provided by the given value list.
* @tparam Other The other values provided by the given value list.
*/
template<auto Value, auto... Other>
struct value_list_unique<value_list<Value, Other...>> {
/*! @brief A value list without duplicate types. */
using type = std::conditional_t<
((Value == Other) || ...),
typename value_list_unique<value_list<Other...>>::type,
value_list_cat_t<value_list<Value>, typename value_list_unique<value_list<Other...>>::type>>;
};
/*! @brief Removes duplicates values from a value list. */
template<>
struct value_list_unique<value_list<>> {
/*! @brief A value list without duplicate types. */
using type = value_list<>;
};
/**
* @brief Helper type.
* @tparam Type A value list.
*/
template<typename Type>
using value_list_unique_t = typename value_list_unique<Type>::type;
/**
* @brief Provides the member constant `value` to true if a value list contains
* a given value, false otherwise.
* @tparam List Value list.
* @tparam Value Value to look for.
*/
template<typename List, auto Value>
struct value_list_contains;
/**
* @copybrief value_list_contains
* @tparam Value Values provided by the value list.
* @tparam Other Value to look for.
*/
template<auto... Value, auto Other>
struct value_list_contains<value_list<Value...>, Other>
: std::bool_constant<((Value == Other) || ...)> {};
/**
* @brief Helper variable template.
* @tparam List Value list.
* @tparam Value Value to look for.
*/
template<typename List, auto Value>
inline constexpr bool value_list_contains_v = value_list_contains<List, Value>::value;
/*! @brief Primary template isn't defined on purpose. */
template<typename...>
class value_list_diff;
/**
* @brief Computes the difference between two value lists.
* @tparam Value Values provided by the first value list.
* @tparam Other Values provided by the second value list.
*/
template<auto... Value, auto... Other>
class value_list_diff<value_list<Value...>, value_list<Other...>> {
using v141_toolset_workaround = value_list<Other...>;
public:
/*! @brief A value list that is the difference between the two value lists. */
using type = value_list_cat_t<std::conditional_t<value_list_contains_v<v141_toolset_workaround, Value>, value_list<>, value_list<Value>>...>;
};
/**
* @brief Helper type.
* @tparam List Value lists between which to compute the difference.
*/
template<typename... List>
using value_list_diff_t = typename value_list_diff<List...>::type;
/*! @brief Same as std::is_invocable, but with tuples. */
template<typename, typename>
struct is_applicable: std::false_type {};
@@ -529,11 +678,7 @@ inline constexpr bool is_complete_v = is_complete<Type>::value;
template<typename Type, typename = void>
struct is_iterator: std::false_type {};
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename, typename = void>
@@ -543,15 +688,11 @@ template<typename Type>
struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
/*! @copydoc is_iterator */
template<typename Type>
struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_pointer_t<Type>>, void>>>
struct is_iterator<Type, std::enable_if_t<!std::is_void_v<std::remove_cv_t<std::remove_pointer_t<Type>>>>>
: internal::has_iterator_category<Type> {};
/**
@@ -568,7 +709,7 @@ inline constexpr bool is_iterator_v = is_iterator<Type>::value;
*/
template<typename Type>
struct is_ebco_eligible
: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
: std::bool_constant<std::is_empty_v<Type> && !std::is_final_v<Type>> {};
/**
* @brief Helper variable template.
@@ -596,19 +737,7 @@ struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::tr
template<typename Type>
inline constexpr bool is_transparent_v = is_transparent<Type>::value;
/**
* @brief Provides the member constant `value` to true if a given type is
* equality comparable, false otherwise.
* @tparam Type The type to test.
*/
template<typename Type, typename = void>
struct is_equality_comparable: std::false_type {};
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename, typename = void>
@@ -617,47 +746,69 @@ struct has_tuple_size_value: std::false_type {};
template<typename Type>
struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
template<typename, typename = void>
struct has_value_type: std::false_type {};
template<typename Type>
struct has_value_type<Type, std::void_t<typename Type::value_type>>: std::true_type {};
template<typename>
[[nodiscard]] constexpr bool dispatch_is_equality_comparable();
template<typename Type, std::size_t... Index>
[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
return (dispatch_is_equality_comparable<std::tuple_element_t<Index, Type>>() && ...);
}
template<typename>
[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
[[nodiscard]] constexpr bool maybe_equality_comparable(char) {
return false;
}
template<typename Type>
[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval<Type>() == std::declval<Type>()) {
return true;
}
template<typename Type>
[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
if constexpr(is_iterator_v<Type>) {
return true;
} else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
return maybe_equality_comparable<Type>(choice<0>);
[[nodiscard]] constexpr bool dispatch_is_equality_comparable() {
if constexpr(std::is_array_v<Type>) {
return false;
} else if constexpr(is_iterator_v<Type>) {
return maybe_equality_comparable<Type>(0);
} else if constexpr(has_value_type<Type>::value) {
if constexpr(std::is_same_v<typename Type::value_type, Type>) {
return maybe_equality_comparable<Type>(0);
} else if constexpr(dispatch_is_equality_comparable<typename Type::value_type>()) {
return maybe_equality_comparable<Type>(0);
} else {
return false;
}
} else if constexpr(is_complete_v<std::tuple_size<std::remove_cv_t<Type>>>) {
if constexpr(has_tuple_size_value<Type>::value) {
return maybe_equality_comparable<Type>(0) && unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
} else {
return maybe_equality_comparable<Type>(0);
}
} else {
return is_equality_comparable<typename Type::value_type>::value;
}
}
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_cv_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
if constexpr(has_tuple_size_value<Type>::value) {
return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
} else {
return maybe_equality_comparable<Type>(choice<1>);
return maybe_equality_comparable<Type>(0);
}
}
} // namespace internal
/*! @endcond */
/**
* Internal details not to be documented.
* @endcond
* @brief Provides the member constant `value` to true if a given type is
* equality comparable, false otherwise.
* @tparam Type The type to test.
*/
template<typename Type>
struct is_equality_comparable: std::bool_constant<internal::dispatch_is_equality_comparable<Type>()> {};
/*! @copydoc is_equality_comparable */
template<typename Type>
struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
struct is_equality_comparable<const Type>: is_equality_comparable<Type> {};
/**
* @brief Helper variable template.
@@ -724,9 +875,9 @@ using member_class_t = typename member_class<Member>::type;
/**
* @brief Extracts the n-th argument of a given function or member function.
* @tparam Index The index of the argument to extract.
* @tparam Candidate A valid function, member function or data member.
* @tparam Candidate A valid function, member function or data member type.
*/
template<std::size_t Index, auto Candidate>
template<std::size_t Index, typename Candidate>
class nth_argument {
template<typename Ret, typename... Args>
static constexpr type_list<Args...> pick_up(Ret (*)(Args...));
@@ -742,17 +893,29 @@ class nth_argument {
public:
/*! @brief N-th argument of the given function or member function. */
using type = type_list_element_t<Index, decltype(pick_up(Candidate))>;
using type = type_list_element_t<Index, decltype(pick_up(std::declval<Candidate>()))>;
};
/**
* @brief Helper type.
* @tparam Index The index of the argument to extract.
* @tparam Candidate A valid function, member function or data member.
* @tparam Candidate A valid function, member function or data member type.
*/
template<std::size_t Index, auto Candidate>
template<std::size_t Index, typename Candidate>
using nth_argument_t = typename nth_argument<Index, Candidate>::type;
} // namespace entt
template<typename... Type>
struct std::tuple_size<entt::type_list<Type...>>: std::integral_constant<std::size_t, entt::type_list<Type...>::size> {};
template<std::size_t Index, typename... Type>
struct std::tuple_element<Index, entt::type_list<Type...>>: entt::type_list_element<Index, entt::type_list<Type...>> {};
template<auto... Value>
struct std::tuple_size<entt::value_list<Value...>>: std::integral_constant<std::size_t, entt::value_list<Value...>::size> {};
template<std::size_t Index, auto... Value>
struct std::tuple_element<Index, entt::value_list<Value...>>: entt::value_list_element<Index, entt::value_list<Value...>> {};
#endif

View File

@@ -17,7 +17,7 @@ struct identity {
* @param value The actual argument.
* @return The submitted value as-is.
*/
template<class Type>
template<typename Type>
[[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept {
return std::forward<Type>(value);
}
@@ -50,7 +50,7 @@ template<typename Func>
* @brief Helper type for visitors.
* @tparam Func Types of function objects.
*/
template<class... Func>
template<typename... Func>
struct overloaded: Func... {
using Func::operator()...;
};
@@ -59,14 +59,14 @@ struct overloaded: Func... {
* @brief Deduction guide.
* @tparam Func Types of function objects.
*/
template<class... Func>
template<typename... Func>
overloaded(Func...) -> overloaded<Func...>;
/**
* @brief Basic implementation of a y-combinator.
* @tparam Func Type of a potentially recursive function.
*/
template<class Func>
template<typename Func>
struct y_combinator {
/**
* @brief Constructs a y-combinator from a given function.
@@ -81,13 +81,13 @@ struct y_combinator {
* @param args Parameters to use to invoke the underlying function.
* @return Return value of the underlying function, if any.
*/
template<class... Args>
template<typename... Args>
constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v<Func, const y_combinator &, Args...>) {
return func(*this, std::forward<Args>(args)...);
}
/*! @copydoc operator()() */
template<class... Args>
template<typename... Args>
constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v<Func, y_combinator &, Args...>) {
return func(*this, std::forward<Args>(args)...);
}

View File

@@ -4,19 +4,19 @@
#include <cstddef>
#include <type_traits>
#include "../config/config.h"
#include "fwd.hpp"
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename Type, typename = void>
struct in_place_delete: std::bool_constant<!(std::is_move_constructible_v<Type> && std::is_move_assignable_v<Type>)> {};
template<>
struct in_place_delete<void>: std::false_type {};
template<typename Type>
struct in_place_delete<Type, std::enable_if_t<Type::in_place_delete>>
: std::true_type {};
@@ -24,16 +24,15 @@ struct in_place_delete<Type, std::enable_if_t<Type::in_place_delete>>
template<typename Type, typename = void>
struct page_size: std::integral_constant<std::size_t, !std::is_empty_v<ENTT_ETO_TYPE(Type)> * ENTT_PACKED_PAGE> {};
template<>
struct page_size<void>: std::integral_constant<std::size_t, 0u> {};
template<typename Type>
struct page_size<Type, std::enable_if_t<std::is_convertible_v<decltype(Type::page_size), std::size_t>>>
struct page_size<Type, std::void_t<decltype(Type::page_size)>>
: std::integral_constant<std::size_t, Type::page_size> {};
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
/**
* @brief Common way to access various properties of components.
@@ -52,13 +51,6 @@ struct component_traits {
static constexpr std::size_t page_size = internal::page_size<Type>::value;
};
/**
* @brief Helper variable template.
* @tparam Type Type of component.
*/
template<class Type>
inline constexpr bool ignore_as_empty_v = (std::is_void_v<Type> || component_traits<Type>::page_size == 0u);
} // namespace entt
#endif

View File

@@ -9,70 +9,78 @@
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
// waiting for C++20 and std::popcount
template<typename Type>
constexpr int popcount(Type value) noexcept {
return value ? (int(value & 1) + popcount(value >> 1)) : 0;
}
template<typename, typename = void>
struct entt_traits;
template<typename Type>
struct entt_traits<Type, std::enable_if_t<std::is_enum_v<Type>>>
: entt_traits<std::underlying_type_t<Type>> {};
: entt_traits<std::underlying_type_t<Type>> {
using value_type = Type;
};
template<typename Type>
struct entt_traits<Type, std::enable_if_t<std::is_class_v<Type>>>
: entt_traits<typename Type::entity_type> {};
: entt_traits<typename Type::entity_type> {
using value_type = Type;
};
template<>
struct entt_traits<std::uint32_t> {
using value_type = std::uint32_t;
using entity_type = std::uint32_t;
using version_type = std::uint16_t;
static constexpr entity_type entity_mask = 0xFFFFF;
static constexpr entity_type version_mask = 0xFFF;
static constexpr std::size_t entity_shift = 20u;
};
template<>
struct entt_traits<std::uint64_t> {
using value_type = std::uint64_t;
using entity_type = std::uint64_t;
using version_type = std::uint32_t;
static constexpr entity_type entity_mask = 0xFFFFFFFF;
static constexpr entity_type version_mask = 0xFFFFFFFF;
static constexpr std::size_t entity_shift = 32u;
};
} // namespace internal
/*! @endcond */
/**
* Internal details not to be documented.
* @endcond
* @brief Common basic entity traits implementation.
* @tparam Traits Actual entity traits to use.
*/
template<typename Traits>
class basic_entt_traits {
static constexpr auto length = internal::popcount(Traits::entity_mask);
/**
* @brief Entity traits.
* @tparam Type Type of identifier.
*/
template<typename Type>
class entt_traits: internal::entt_traits<Type> {
using base_type = internal::entt_traits<Type>;
static_assert(Traits::entity_mask && ((typename Traits::entity_type{1} << length) == (Traits::entity_mask + 1)), "Invalid entity mask");
static_assert((typename Traits::entity_type{1} << internal::popcount(Traits::version_mask)) == (Traits::version_mask + 1), "Invalid version mask");
public:
/*! @brief Value type. */
using value_type = Type;
using value_type = typename Traits::value_type;
/*! @brief Underlying entity type. */
using entity_type = typename base_type::entity_type;
using entity_type = typename Traits::entity_type;
/*! @brief Underlying version type. */
using version_type = typename base_type::version_type;
/*! @brief Reserved identifier. */
static constexpr entity_type reserved = base_type::entity_mask | (base_type::version_mask << base_type::entity_shift);
/*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */
static constexpr auto page_size = ENTT_SPARSE_PAGE;
using version_type = typename Traits::version_type;
/*! @brief Entity mask size. */
static constexpr entity_type entity_mask = Traits::entity_mask;
/*! @brief Version mask size */
static constexpr entity_type version_mask = Traits::version_mask;
/**
* @brief Converts an entity to its underlying type.
@@ -89,7 +97,7 @@ public:
* @return The integral representation of the entity part.
*/
[[nodiscard]] static constexpr entity_type to_entity(const value_type value) noexcept {
return (to_integral(value) & base_type::entity_mask);
return (to_integral(value) & entity_mask);
}
/**
@@ -98,7 +106,17 @@ public:
* @return The integral representation of the version part.
*/
[[nodiscard]] static constexpr version_type to_version(const value_type value) noexcept {
return (to_integral(value) >> base_type::entity_shift);
return (static_cast<version_type>(to_integral(value) >> length) & version_mask);
}
/**
* @brief Returns the successor of a given identifier.
* @param value The identifier of which to return the successor.
* @return The successor of the given identifier.
*/
[[nodiscard]] static constexpr value_type next(const value_type value) noexcept {
const auto vers = to_version(value) + 1;
return construct(to_integral(value), static_cast<version_type>(vers + (vers == version_mask)));
}
/**
@@ -112,7 +130,7 @@ public:
* @return A properly constructed identifier.
*/
[[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) noexcept {
return value_type{(entity & base_type::entity_mask) | (static_cast<entity_type>(version) << base_type::entity_shift)};
return value_type{(entity & entity_mask) | (static_cast<entity_type>(version & version_mask) << length)};
}
/**
@@ -126,14 +144,27 @@ public:
* @return A properly constructed identifier.
*/
[[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept {
constexpr auto mask = (base_type::version_mask << base_type::entity_shift);
return value_type{(lhs & base_type::entity_mask) | (rhs & mask)};
return value_type{(lhs & entity_mask) | (rhs & (version_mask << length))};
}
};
/**
* @copydoc entt_traits<Entity>::to_integral
* @brief Entity traits.
* @tparam Type Type of identifier.
*/
template<typename Type>
struct entt_traits: basic_entt_traits<internal::entt_traits<Type>> {
/*! @brief Base type. */
using base_type = basic_entt_traits<internal::entt_traits<Type>>;
/*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */
static constexpr std::size_t page_size = ENTT_SPARSE_PAGE;
};
/**
* @brief Converts an entity to its underlying type.
* @tparam Entity The value type.
* @param value The value to convert.
* @return The integral representation of the given value.
*/
template<typename Entity>
[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_integral(const Entity value) noexcept {
@@ -141,8 +172,10 @@ template<typename Entity>
}
/**
* @copydoc entt_traits<Entity>::to_entity
* @brief Returns the entity part once converted to the underlying type.
* @tparam Entity The value type.
* @param value The value to convert.
* @return The integral representation of the entity part.
*/
template<typename Entity>
[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_entity(const Entity value) noexcept {
@@ -150,8 +183,10 @@ template<typename Entity>
}
/**
* @copydoc entt_traits<Entity>::to_version
* @brief Returns the version part once converted to the underlying type.
* @tparam Entity The value type.
* @param value The value to convert.
* @return The integral representation of the version part.
*/
template<typename Entity>
[[nodiscard]] constexpr typename entt_traits<Entity>::version_type to_version(const Entity value) noexcept {
@@ -167,8 +202,9 @@ struct null_t {
*/
template<typename Entity>
[[nodiscard]] constexpr operator Entity() const noexcept {
using entity_traits = entt_traits<Entity>;
return entity_traits::combine(entity_traits::reserved, entity_traits::reserved);
using traits_type = entt_traits<Entity>;
constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask);
return value;
}
/**
@@ -197,8 +233,8 @@ struct null_t {
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept {
using entity_traits = entt_traits<Entity>;
return entity_traits::to_entity(entity) == entity_traits::to_entity(*this);
using traits_type = entt_traits<Entity>;
return traits_type::to_entity(entity) == traits_type::to_entity(*this);
}
/**
@@ -246,8 +282,9 @@ struct tombstone_t {
*/
template<typename Entity>
[[nodiscard]] constexpr operator Entity() const noexcept {
using entity_traits = entt_traits<Entity>;
return entity_traits::combine(entity_traits::reserved, entity_traits::reserved);
using traits_type = entt_traits<Entity>;
constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask);
return value;
}
/**
@@ -276,8 +313,8 @@ struct tombstone_t {
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept {
using entity_traits = entt_traits<Entity>;
return entity_traits::to_version(entity) == entity_traits::to_version(*this);
using traits_type = entt_traits<Entity>;
return traits_type::to_version(entity) == traits_type::to_version(*this);
}
/**

View File

@@ -1,6 +1,7 @@
#ifndef ENTT_ENTITY_FWD_HPP
#define ENTT_ENTITY_FWD_HPP
#include <cstdint>
#include <memory>
#include <type_traits>
#include "../core/fwd.hpp"
@@ -11,52 +12,24 @@ namespace entt {
/*! @brief Default entity identifier. */
enum class entity : id_type {};
/*! @brief Storage deletion policy. */
enum class deletion_policy : std::uint8_t {
/*! @brief Swap-and-pop deletion policy. */
swap_and_pop = 0u,
/*! @brief In-place deletion policy. */
in_place = 1u,
/*! @brief Swap-only deletion policy. */
swap_only = 2u
};
template<typename Entity = entity, typename = std::allocator<Entity>>
class basic_sparse_set;
template<typename Type, typename = entity, typename = std::allocator<Type>, typename = void>
class basic_storage;
template<typename Type>
class sigh_storage_mixin;
/**
* @brief Provides a common way to define storage types.
* @tparam Type Storage value type.
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Type, typename Entity = entity, typename Allocator = std::allocator<Type>, typename = void>
struct storage_type {
/*! @brief Type-to-storage conversion result. */
using type = sigh_storage_mixin<basic_storage<Type, Entity, Allocator>>;
};
/**
* @brief Helper type.
* @tparam Args Arguments to forward.
*/
template<typename... Args>
using storage_type_t = typename storage_type<Args...>::type;
/**
* Type-to-storage conversion utility that preserves constness.
* @tparam Type Storage value type, eventually const.
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Type, typename Entity = entity, typename Allocator = std::allocator<std::remove_const_t<Type>>>
struct storage_for {
/*! @brief Type-to-storage conversion result. */
using type = constness_as_t<storage_type_t<std::remove_const_t<Type>, Entity, Allocator>, Type>;
};
/**
* @brief Helper type.
* @tparam Args Arguments to forward.
*/
template<typename... Args>
using storage_for_t = typename storage_for<Args...>::type;
template<typename, typename>
class basic_sigh_mixin;
template<typename Entity = entity, typename = std::allocator<Entity>>
class basic_registry;
@@ -70,7 +43,7 @@ class basic_runtime_view;
template<typename, typename, typename>
class basic_group;
template<typename>
template<typename, typename Mask = std::uint32_t, typename = std::allocator<Mask>>
class basic_observer;
template<typename>
@@ -88,48 +61,6 @@ class basic_snapshot_loader;
template<typename>
class basic_continuous_loader;
/**
* @brief Alias for exclusion lists.
* @tparam Type List of types.
*/
template<typename... Type>
using exclude_t = type_list<Type...>;
/**
* @brief Variable template for exclusion lists.
* @tparam Type List of types.
*/
template<typename... Type>
inline constexpr exclude_t<Type...> exclude{};
/**
* @brief Alias for lists of observed components.
* @tparam Type List of types.
*/
template<typename... Type>
using get_t = type_list<Type...>;
/**
* @brief Variable template for lists of observed components.
* @tparam Type List of types.
*/
template<typename... Type>
inline constexpr get_t<Type...> get{};
/**
* @brief Alias for lists of owned components.
* @tparam Type List of types.
*/
template<typename... Type>
using owned_t = type_list<Type...>;
/**
* @brief Variable template for lists of owned components.
* @tparam Type List of types.
*/
template<typename... Type>
inline constexpr owned_t<Type...> owned{};
/*! @brief Alias declaration for the most common use case. */
using sparse_set = basic_sparse_set<>;
@@ -140,6 +71,13 @@ using sparse_set = basic_sparse_set<>;
template<typename Type>
using storage = basic_storage<Type>;
/**
* @brief Alias declaration for the most common use case.
* @tparam Type Underlying storage type.
*/
template<typename Type>
using sigh_mixin = basic_sigh_mixin<Type, basic_registry<typename Type::entity_type, typename Type::base_type::allocator_type>>;
/*! @brief Alias declaration for the most common use case. */
using registry = basic_registry<>;
@@ -178,6 +116,134 @@ using snapshot_loader = basic_snapshot_loader<registry>;
/*! @brief Alias declaration for the most common use case. */
using continuous_loader = basic_continuous_loader<registry>;
/*! @brief Alias declaration for the most common use case. */
using runtime_view = basic_runtime_view<sparse_set>;
/*! @brief Alias declaration for the most common use case. */
using const_runtime_view = basic_runtime_view<const sparse_set>;
/**
* @brief Alias for exclusion lists.
* @tparam Type List of types.
*/
template<typename... Type>
struct exclude_t final: type_list<Type...> {
/*! @brief Default constructor. */
explicit constexpr exclude_t() {}
};
/**
* @brief Variable template for exclusion lists.
* @tparam Type List of types.
*/
template<typename... Type>
inline constexpr exclude_t<Type...> exclude{};
/**
* @brief Alias for lists of observed components.
* @tparam Type List of types.
*/
template<typename... Type>
struct get_t final: type_list<Type...> {
/*! @brief Default constructor. */
explicit constexpr get_t() {}
};
/**
* @brief Variable template for lists of observed components.
* @tparam Type List of types.
*/
template<typename... Type>
inline constexpr get_t<Type...> get{};
/**
* @brief Alias for lists of owned components.
* @tparam Type List of types.
*/
template<typename... Type>
struct owned_t final: type_list<Type...> {
/*! @brief Default constructor. */
explicit constexpr owned_t() {}
};
/**
* @brief Variable template for lists of owned components.
* @tparam Type List of types.
*/
template<typename... Type>
inline constexpr owned_t<Type...> owned{};
/**
* @brief Applies a given _function_ to a get list and generate a new list.
* @tparam Type Types provided by the get list.
* @tparam Op Unary operation as template class with a type member named `type`.
*/
template<typename... Type, template<typename...> class Op>
struct type_list_transform<get_t<Type...>, Op> {
/*! @brief Resulting get list after applying the transform function. */
using type = get_t<typename Op<Type>::type...>;
};
/**
* @brief Applies a given _function_ to an exclude list and generate a new list.
* @tparam Type Types provided by the exclude list.
* @tparam Op Unary operation as template class with a type member named `type`.
*/
template<typename... Type, template<typename...> class Op>
struct type_list_transform<exclude_t<Type...>, Op> {
/*! @brief Resulting exclude list after applying the transform function. */
using type = exclude_t<typename Op<Type>::type...>;
};
/**
* @brief Applies a given _function_ to an owned list and generate a new list.
* @tparam Type Types provided by the owned list.
* @tparam Op Unary operation as template class with a type member named `type`.
*/
template<typename... Type, template<typename...> class Op>
struct type_list_transform<owned_t<Type...>, Op> {
/*! @brief Resulting owned list after applying the transform function. */
using type = owned_t<typename Op<Type>::type...>;
};
/**
* @brief Provides a common way to define storage types.
* @tparam Type Storage value type.
* @tparam Entity A valid entity type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Type, typename Entity = entity, typename Allocator = std::allocator<Type>, typename = void>
struct storage_type {
/*! @brief Type-to-storage conversion result. */
using type = sigh_mixin<basic_storage<Type, Entity, Allocator>>;
};
/**
* @brief Helper type.
* @tparam Args Arguments to forward.
*/
template<typename... Args>
using storage_type_t = typename storage_type<Args...>::type;
/**
* Type-to-storage conversion utility that preserves constness.
* @tparam Type Storage value type, eventually const.
* @tparam Entity A valid entity type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Type, typename Entity = entity, typename Allocator = std::allocator<std::remove_const_t<Type>>>
struct storage_for {
/*! @brief Type-to-storage conversion result. */
using type = constness_as_t<storage_type_t<std::remove_const_t<Type>, Entity, Allocator>, Type>;
};
/**
* @brief Helper type.
* @tparam Args Arguments to forward.
*/
template<typename... Args>
using storage_for_t = typename storage_for<Args...>::type;
/**
* @brief Alias declaration for the most common use case.
* @tparam Get Types of storage iterated by the view.
@@ -186,12 +252,6 @@ using continuous_loader = basic_continuous_loader<registry>;
template<typename Get, typename Exclude = exclude_t<>>
using view = basic_view<type_list_transform_t<Get, storage_for>, type_list_transform_t<Exclude, storage_for>>;
/*! @brief Alias declaration for the most common use case. */
using runtime_view = basic_runtime_view<sparse_set>;
/*! @brief Alias declaration for the most common use case. */
using const_runtime_view = basic_runtime_view<const sparse_set>;
/**
* @brief Alias declaration for the most common use case.
* @tparam Owned Types of storage _owned_ by the group.

View File

@@ -5,9 +5,10 @@
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/fwd.hpp"
#include "../core/iterator.hpp"
#include "../core/type_info.hpp"
#include "../core/type_traits.hpp"
#include "component.hpp"
#include "entity.hpp"
#include "fwd.hpp"
#include "sparse_set.hpp"
@@ -15,11 +16,7 @@
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename, typename, typename>
@@ -29,7 +26,7 @@ template<typename It, typename... Owned, typename... Get>
class extended_group_iterator<It, owned_t<Owned...>, get_t<Get...>> {
template<typename Type>
auto index_to_element([[maybe_unused]] Type &cpool) const {
if constexpr(ignore_as_empty_v<typename Type::value_type>) {
if constexpr(Type::traits_type::page_size == 0u) {
return std::make_tuple();
} else {
return std::forward_as_tuple(cpool.rbegin()[it.index()]);
@@ -37,17 +34,19 @@ class extended_group_iterator<It, owned_t<Owned...>, get_t<Get...>> {
}
public:
using iterator_type = It;
using difference_type = std::ptrdiff_t;
using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::declval<Owned>().get_as_tuple({})..., std::declval<Get>().get_as_tuple({})...));
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
using iterator_concept = std::forward_iterator_tag;
constexpr extended_group_iterator()
: it{},
pools{} {}
extended_group_iterator(It from, const std::tuple<Owned *..., Get *...> &cpools)
extended_group_iterator(iterator_type from, const std::tuple<Owned *..., Get *...> &cpools)
: it{from},
pools{cpools} {}
@@ -68,6 +67,10 @@ public:
return operator*();
}
[[nodiscard]] constexpr iterator_type base() const noexcept {
return it;
}
template<typename... Lhs, typename... Rhs>
friend constexpr bool operator==(const extended_group_iterator<Lhs...> &, const extended_group_iterator<Rhs...> &) noexcept;
@@ -86,12 +89,163 @@ template<typename... Lhs, typename... Rhs>
return !(lhs == rhs);
}
} // namespace internal
struct group_descriptor {
using size_type = std::size_t;
virtual ~group_descriptor() = default;
virtual size_type owned(const id_type *, const size_type) const noexcept {
return 0u;
}
};
/**
* Internal details not to be documented.
* @endcond
*/
template<typename, typename, typename>
class group_handler;
template<typename... Owned, typename... Get, typename... Exclude>
class group_handler<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> final: public group_descriptor {
// nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here
static_assert(!std::disjunction_v<std::bool_constant<Owned::traits_type::in_place_delete>...>, "Groups do not support in-place delete");
static_assert(!std::disjunction_v<std::is_const<Owned>..., std::is_const<Get>..., std::is_const<Exclude>...>, "Const storage type not allowed");
using base_type = std::common_type_t<typename Owned::base_type..., typename Get::base_type..., typename Exclude::base_type...>;
using entity_type = typename base_type::entity_type;
template<std::size_t... Index>
void swap_elements(const std::size_t pos, const entity_type entt, std::index_sequence<Index...>) {
(std::get<Index>(pools)->swap_elements(std::get<Index>(pools)->data()[pos], entt), ...);
}
void push_on_construct(const entity_type entt) {
if(std::apply([entt, len = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < len) && (other->contains(entt) && ...); }, pools)
&& std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) {
swap_elements(len++, entt, std::index_sequence_for<Owned...>{});
}
}
void push_on_destroy(const entity_type entt) {
if(std::apply([entt, len = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < len) && (other->contains(entt) && ...); }, pools)
&& std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) {
swap_elements(len++, entt, std::index_sequence_for<Owned...>{});
}
}
void remove_if(const entity_type entt) {
if(std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < len)) {
swap_elements(--len, entt, std::index_sequence_for<Owned...>{});
}
}
public:
using size_type = typename base_type::size_type;
group_handler(Owned &...opool, Get &...gpool, Exclude &...epool)
: pools{&opool..., &gpool...},
filter{&epool...},
len{} {
std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::push_on_construct>(*this), cpool->on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, pools);
std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::remove_if>(*this), cpool->on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, filter);
// we cannot iterate backwards because we want to leave behind valid entities in case of owned types
for(auto *first = std::get<0>(pools)->data(), *last = first + std::get<0>(pools)->size(); first != last; ++first) {
push_on_construct(*first);
}
}
size_type owned(const id_type *elem, const size_type length) const noexcept final {
size_type cnt = 0u;
for(auto pos = 0u; pos < length; ++pos) {
cnt += ((elem[pos] == entt::type_hash<typename Owned::value_type>::value()) || ...);
}
return cnt;
}
[[nodiscard]] size_type length() const noexcept {
return len;
}
auto pools_as_tuple() const noexcept {
return pools;
}
auto filter_as_tuple() const noexcept {
return filter;
}
private:
std::tuple<Owned *..., Get *...> pools;
std::tuple<Exclude *...> filter;
std::size_t len;
};
template<typename... Get, typename... Exclude>
class group_handler<owned_t<>, get_t<Get...>, exclude_t<Exclude...>> final: public group_descriptor {
// nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here
static_assert(!std::disjunction_v<std::is_const<Get>..., std::is_const<Exclude>...>, "Const storage type not allowed");
using base_type = std::common_type_t<typename Get::base_type..., typename Exclude::base_type...>;
using entity_type = typename base_type::entity_type;
void push_on_construct(const entity_type entt) {
if(!elem.contains(entt)
&& std::apply([entt](auto *...cpool) { return (cpool->contains(entt) && ...); }, pools)
&& std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) {
elem.push(entt);
}
}
void push_on_destroy(const entity_type entt) {
if(!elem.contains(entt)
&& std::apply([entt](auto *...cpool) { return (cpool->contains(entt) && ...); }, pools)
&& std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) {
elem.push(entt);
}
}
void remove_if(const entity_type entt) {
elem.remove(entt);
}
public:
using common_type = base_type;
template<typename Alloc>
group_handler(const Alloc &alloc, Get &...gpool, Exclude &...epool)
: pools{&gpool...},
filter{&epool...},
elem{alloc} {
std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::push_on_construct>(*this), cpool->on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, pools);
std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::remove_if>(*this), cpool->on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, filter);
for(const auto entity: static_cast<base_type &>(*std::get<0>(pools))) {
push_on_construct(entity);
}
}
common_type &handle() noexcept {
return elem;
}
const common_type &handle() const noexcept {
return elem;
}
auto pools_as_tuple() const noexcept {
return pools;
}
auto filter_as_tuple() const noexcept {
return filter;
}
private:
std::tuple<Get *...> pools;
std::tuple<Exclude *...> filter;
base_type elem;
};
} // namespace internal
/*! @endcond */
/**
* @brief Group.
@@ -119,18 +273,28 @@ class basic_group;
* * The entity currently pointed is destroyed.
*
* In all other cases, modifying the pools iterated by the group in any way
* invalidates all the iterators and using them results in undefined behavior.
* invalidates all the iterators.
*
* @tparam Get Types of storage _observed_ by the group.
* @tparam Exclude Types of storage used to filter the group.
*/
template<typename... Get, typename... Exclude>
class basic_group<owned_t<>, get_t<Get...>, exclude_t<Exclude...>> {
using underlying_type = std::common_type_t<typename Get::entity_type..., typename Exclude::entity_type...>;
using basic_common_type = std::common_type_t<typename Get::base_type..., typename Exclude::base_type...>;
using base_type = std::common_type_t<typename Get::base_type..., typename Exclude::base_type...>;
using underlying_type = typename base_type::entity_type;
template<typename Type>
static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Get::value_type...>>;
static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Get::value_type..., typename Exclude::value_type...>>;
auto pools() const noexcept {
using return_type = std::tuple<Get *...>;
return descriptor ? descriptor->pools_as_tuple() : return_type{};
}
auto filter() const noexcept {
using return_type = std::tuple<Exclude *...>;
return descriptor ? descriptor->filter_as_tuple() : return_type{};
}
public:
/*! @brief Underlying entity identifier. */
@@ -138,53 +302,59 @@ public:
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Common type among all storage types. */
using base_type = basic_common_type;
using common_type = base_type;
/*! @brief Random access iterator type. */
using iterator = typename base_type::iterator;
using iterator = typename common_type::iterator;
/*! @brief Reversed iterator type. */
using reverse_iterator = typename base_type::reverse_iterator;
using reverse_iterator = typename common_type::reverse_iterator;
/*! @brief Iterable group type. */
using iterable = iterable_adaptor<internal::extended_group_iterator<iterator, owned_t<>, get_t<Get...>>>;
/*! @brief Group handler type. */
using handler = internal::group_handler<owned_t<>, get_t<std::remove_const_t<Get>...>, exclude_t<std::remove_const_t<Exclude>...>>;
/*! @brief Default constructor to use to create empty, invalid groups. */
basic_group() noexcept
: handler{} {}
: descriptor{} {}
/**
* @brief Constructs a group from a set of storage classes.
* @param ref The actual entities to iterate.
* @param gpool Storage types to iterate _observed_ by the group.
* @param ref A reference to a group handler.
*/
basic_group(basic_common_type &ref, Get &...gpool) noexcept
: handler{&ref},
pools{&gpool...} {}
basic_group(handler &ref) noexcept
: descriptor{&ref} {}
/**
* @brief Returns a const reference to the underlying handler.
* @return A const reference to the underlying handler.
* @brief Returns the leading storage of a group.
* @return The leading storage of the group.
*/
[[nodiscard]] const base_type &handle() const noexcept {
return *handler;
[[nodiscard]] const common_type &handle() const noexcept {
return descriptor->handle();
}
/**
* @brief Returns the storage for a given component type.
* @brief Returns the storage for a given component type, if any.
* @tparam Type Type of component of which to return the storage.
* @return The storage for the given component type.
*/
template<typename Type>
[[nodiscard]] decltype(auto) storage() const noexcept {
[[nodiscard]] auto *storage() const noexcept {
return storage<index_of<Type>>();
}
/**
* @brief Returns the storage for a given index.
* @brief Returns the storage for a given index, if any.
* @tparam Index Index of the storage to return.
* @return The storage for the given index.
*/
template<std::size_t Index>
[[nodiscard]] decltype(auto) storage() const noexcept {
return *std::get<Index>(pools);
[[nodiscard]] auto *storage() const noexcept {
constexpr auto offset = sizeof...(Get);
if constexpr(Index < offset) {
return std::get<Index>(pools());
} else {
return std::get<Index - offset>(filter());
}
}
/**
@@ -192,7 +362,7 @@ public:
* @return Number of entities that are part of the group.
*/
[[nodiscard]] size_type size() const noexcept {
return *this ? handler->size() : size_type{};
return *this ? handle().size() : size_type{};
}
/**
@@ -201,13 +371,13 @@ public:
* @return Capacity of the group.
*/
[[nodiscard]] size_type capacity() const noexcept {
return *this ? handler->capacity() : size_type{};
return *this ? handle().capacity() : size_type{};
}
/*! @brief Requests the removal of unused capacity. */
void shrink_to_fit() {
if(*this) {
handler->shrink_to_fit();
descriptor->handle().shrink_to_fit();
}
}
@@ -216,60 +386,48 @@ public:
* @return True if the group is empty, false otherwise.
*/
[[nodiscard]] bool empty() const noexcept {
return !*this || handler->empty();
return !*this || handle().empty();
}
/**
* @brief Returns an iterator to the first entity of the group.
*
* The returned iterator points to the first entity of the group. If the
* group is empty, the returned iterator will be equal to `end()`.
* If the group is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first entity of the group.
*/
[[nodiscard]] iterator begin() const noexcept {
return *this ? handler->begin() : iterator{};
return *this ? handle().begin() : iterator{};
}
/**
* @brief Returns an iterator that is past the last entity of the group.
*
* The returned iterator points to the entity following the last entity of
* the group. Attempting to dereference the returned iterator results in
* undefined behavior.
*
* @return An iterator to the entity following the last entity of the
* group.
*/
[[nodiscard]] iterator end() const noexcept {
return *this ? handler->end() : iterator{};
return *this ? handle().end() : iterator{};
}
/**
* @brief Returns an iterator to the first entity of the reversed group.
*
* The returned iterator points to the first entity of the reversed group.
* If the group is empty, the returned iterator will be equal to `rend()`.
*
* @return An iterator to the first entity of the reversed group.
*/
[[nodiscard]] reverse_iterator rbegin() const noexcept {
return *this ? handler->rbegin() : reverse_iterator{};
return *this ? handle().rbegin() : reverse_iterator{};
}
/**
* @brief Returns an iterator that is past the last entity of the reversed
* group.
*
* The returned iterator points to the entity following the last entity of
* the reversed group. Attempting to dereference the returned iterator
* results in undefined behavior.
*
* @return An iterator to the entity following the last entity of the
* reversed group.
*/
[[nodiscard]] reverse_iterator rend() const noexcept {
return *this ? handler->rend() : reverse_iterator{};
return *this ? handle().rend() : reverse_iterator{};
}
/**
@@ -299,8 +457,7 @@ public:
* iterator otherwise.
*/
[[nodiscard]] iterator find(const entity_type entt) const noexcept {
const auto it = *this ? handler->find(entt) : iterator{};
return it != end() && *it == entt ? it : end();
return *this ? handle().find(entt) : iterator{};
}
/**
@@ -317,7 +474,7 @@ public:
* @return True if the group is properly initialized, false otherwise.
*/
[[nodiscard]] explicit operator bool() const noexcept {
return handler != nullptr;
return descriptor != nullptr;
}
/**
@@ -326,32 +483,37 @@ public:
* @return True if the group contains the given entity, false otherwise.
*/
[[nodiscard]] bool contains(const entity_type entt) const noexcept {
return *this && handler->contains(entt);
return *this && handle().contains(entt);
}
/**
* @brief Returns the components assigned to the given entity.
*
* Prefer this function instead of `registry::get` during iterations. It has
* far better performance than its counterpart.
*
* @warning
* Attempting to use an invalid component type results in a compilation
* error. Attempting to use an entity that doesn't belong to the group
* results in undefined behavior.
*
* @tparam Type Types of components to get.
* @tparam Type Type of the component to get.
* @tparam Other Other types of components to get.
* @param entt A valid identifier.
* @return The components assigned to the entity.
*/
template<typename... Type>
template<typename Type, typename... Other>
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
if constexpr(sizeof...(Type) == 0) {
return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools);
} else if constexpr(sizeof...(Type) == 1) {
return (std::get<index_of<Type>>(pools)->get(entt), ...);
return get<index_of<Type>, index_of<Other>...>(entt);
}
/**
* @brief Returns the components assigned to the given entity.
* @tparam Index Indexes of the components to get.
* @param entt A valid identifier.
* @return The components assigned to the entity.
*/
template<std::size_t... Index>
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
const auto cpools = pools();
if constexpr(sizeof...(Index) == 0) {
return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools);
} else if constexpr(sizeof...(Index) == 1) {
return (std::get<Index>(cpools)->get(entt), ...);
} else {
return std::tuple_cat(std::get<index_of<Type>>(pools)->get_as_tuple(entt)...);
return std::tuple_cat(std::get<Index>(cpools)->get_as_tuple(entt)...);
}
}
@@ -402,16 +564,13 @@ public:
* @return An iterable object to use to _visit_ the group.
*/
[[nodiscard]] iterable each() const noexcept {
return iterable{{begin(), pools}, {end(), pools}};
const auto cpools = pools();
return iterable{{begin(), cpools}, {end(), cpools}};
}
/**
* @brief Sort a group according to the given comparison function.
*
* Sort the group so that iterating it with a couple of iterators returns
* entities and components in the expected order. See `begin` and `end` for
* more details.
*
* The comparison function object must return `true` if the first element
* is _less_ than the second one, `false` otherwise. The signature of the
* comparison function should be equivalent to one of the following:
@@ -433,7 +592,8 @@ public:
* * An iterator past the last element of the range to sort.
* * A comparison function to use to compare the elements.
*
* @tparam Type Optional types of components to compare.
* @tparam Type Optional type of component to compare.
* @tparam Other Other optional types of components to compare.
* @tparam Compare Type of comparison function object.
* @tparam Sort Type of sort function object.
* @tparam Args Types of arguments to forward to the sort function object.
@@ -441,52 +601,71 @@ public:
* @param algo A valid sort function object.
* @param args Arguments to forward to the sort function object, if any.
*/
template<typename... Type, typename Compare, typename Sort = std_sort, typename... Args>
template<typename Type, typename... Other, typename Compare, typename Sort = std_sort, typename... Args>
void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
sort<index_of<Type>, index_of<Other>...>(std::move(compare), std::move(algo), std::forward<Args>(args)...);
}
/**
* @brief Sort a group according to the given comparison function.
*
* @sa sort
*
* @tparam Index Optional indexes of components to compare.
* @tparam Compare Type of comparison function object.
* @tparam Sort Type of sort function object.
* @tparam Args Types of arguments to forward to the sort function object.
* @param compare A valid comparison function object.
* @param algo A valid sort function object.
* @param args Arguments to forward to the sort function object, if any.
*/
template<std::size_t... Index, typename Compare, typename Sort = std_sort, typename... Args>
void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
if(*this) {
if constexpr(sizeof...(Type) == 0) {
if constexpr(sizeof...(Index) == 0) {
static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function");
handler->sort(std::move(compare), std::move(algo), std::forward<Args>(args)...);
descriptor->handle().sort(std::move(compare), std::move(algo), std::forward<Args>(args)...);
} else {
auto comp = [this, &compare](const entity_type lhs, const entity_type rhs) {
if constexpr(sizeof...(Type) == 1) {
return compare((std::get<index_of<Type>>(pools)->get(lhs), ...), (std::get<index_of<Type>>(pools)->get(rhs), ...));
auto comp = [&compare, cpools = pools()](const entity_type lhs, const entity_type rhs) {
if constexpr(sizeof...(Index) == 1) {
return compare((std::get<Index>(cpools)->get(lhs), ...), (std::get<Index>(cpools)->get(rhs), ...));
} else {
return compare(std::forward_as_tuple(std::get<index_of<Type>>(pools)->get(lhs)...), std::forward_as_tuple(std::get<index_of<Type>>(pools)->get(rhs)...));
return compare(std::forward_as_tuple(std::get<Index>(cpools)->get(lhs)...), std::forward_as_tuple(std::get<Index>(cpools)->get(rhs)...));
}
};
handler->sort(std::move(comp), std::move(algo), std::forward<Args>(args)...);
descriptor->handle().sort(std::move(comp), std::move(algo), std::forward<Args>(args)...);
}
}
}
/**
* @brief Sort the shared pool of entities according to the given component.
* @brief Sort entities according to their order in a range.
*
* Non-owning groups of the same type share with the registry a pool of
* entities with its own order that doesn't depend on the order of any pool
* of components. Users can order the underlying data structure so that it
* respects the order of the pool of the given component.
*
* @note
* The shared pool of entities and thus its order is affected by the changes
* to each and every pool that it tracks. Therefore changes to those pools
* can quickly ruin the order imposed to the pool of entities shared between
* the non-owning groups.
* to each and every pool that it tracks.
*
* @tparam Type Type of component to use to impose the order.
* @tparam It Type of input iterator.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
*/
template<typename Type>
void sort() const {
template<typename It>
void sort_as(It first, It last) const {
if(*this) {
handler->respect(*std::get<index_of<Type>>(pools));
descriptor->handle().sort_as(first, last);
}
}
/**
* @brief Sort entities according to their order in a range.
* @param other The storage to use to impose the order.
*/
[[deprecated("use iterator based sort_as instead")]] void sort_as(const common_type &other) const {
sort_as(other.begin(), other.end());
}
private:
base_type *const handler;
const std::tuple<Get *...> pools;
handler *descriptor;
};
/**
@@ -514,7 +693,7 @@ private:
* * The entity currently pointed is destroyed.
*
* In all other cases, modifying the pools iterated by the group in any way
* invalidates all the iterators and using them results in undefined behavior.
* invalidates all the iterators.
*
* @tparam Owned Types of storage _owned_ by the group.
* @tparam Get Types of storage _observed_ by the group.
@@ -522,11 +701,21 @@ private:
*/
template<typename... Owned, typename... Get, typename... Exclude>
class basic_group<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> {
using underlying_type = std::common_type_t<typename Owned::entity_type..., typename Get::entity_type..., typename Exclude::entity_type...>;
using basic_common_type = std::common_type_t<typename Owned::base_type..., typename Get::base_type..., typename Exclude::base_type...>;
using base_type = std::common_type_t<typename Owned::base_type..., typename Get::base_type..., typename Exclude::base_type...>;
using underlying_type = typename base_type::entity_type;
template<typename Type>
static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Owned::value_type..., typename Get::value_type...>>;
static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Owned::value_type..., typename Get::value_type..., typename Exclude::value_type...>>;
auto pools() const noexcept {
using return_type = std::tuple<Owned *..., Get *...>;
return descriptor ? descriptor->pools_as_tuple() : return_type{};
}
auto filter() const noexcept {
using return_type = std::tuple<Exclude *...>;
return descriptor ? descriptor->filter_as_tuple() : return_type{};
}
public:
/*! @brief Underlying entity identifier. */
@@ -534,46 +723,59 @@ public:
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Common type among all storage types. */
using base_type = basic_common_type;
using common_type = base_type;
/*! @brief Random access iterator type. */
using iterator = typename base_type::iterator;
using iterator = typename common_type::iterator;
/*! @brief Reversed iterator type. */
using reverse_iterator = typename base_type::reverse_iterator;
using reverse_iterator = typename common_type::reverse_iterator;
/*! @brief Iterable group type. */
using iterable = iterable_adaptor<internal::extended_group_iterator<iterator, owned_t<Owned...>, get_t<Get...>>>;
/*! @brief Group handler type. */
using handler = internal::group_handler<owned_t<std::remove_const_t<Owned>...>, get_t<std::remove_const_t<Get>...>, exclude_t<std::remove_const_t<Exclude>...>>;
/*! @brief Default constructor to use to create empty, invalid groups. */
basic_group() noexcept
: length{} {}
: descriptor{} {}
/**
* @brief Constructs a group from a set of storage classes.
* @param extent The actual number of entities to iterate.
* @param opool Storage types to iterate _owned_ by the group.
* @param gpool Storage types to iterate _observed_ by the group.
* @param ref A reference to a group handler.
*/
basic_group(const std::size_t &extent, Owned &...opool, Get &...gpool) noexcept
: pools{&opool..., &gpool...},
length{&extent} {}
basic_group(handler &ref) noexcept
: descriptor{&ref} {}
/**
* @brief Returns the storage for a given component type.
* @brief Returns the leading storage of a group.
* @return The leading storage of the group.
*/
[[nodiscard]] const common_type &handle() const noexcept {
return *storage<0>();
}
/**
* @brief Returns the storage for a given component type, if any.
* @tparam Type Type of component of which to return the storage.
* @return The storage for the given component type.
*/
template<typename Type>
[[nodiscard]] decltype(auto) storage() const noexcept {
[[nodiscard]] auto *storage() const noexcept {
return storage<index_of<Type>>();
}
/**
* @brief Returns the storage for a given index.
* @brief Returns the storage for a given index, if any.
* @tparam Index Index of the storage to return.
* @return The storage for the given index.
*/
template<std::size_t Index>
[[nodiscard]] decltype(auto) storage() const noexcept {
return *std::get<Index>(pools);
[[nodiscard]] auto *storage() const noexcept {
constexpr auto offset = sizeof...(Owned) + sizeof...(Get);
if constexpr(Index < offset) {
return std::get<Index>(pools());
} else {
return std::get<Index - offset>(filter());
}
}
/**
@@ -581,7 +783,7 @@ public:
* @return Number of entities that that are part of the group.
*/
[[nodiscard]] size_type size() const noexcept {
return *this ? *length : size_type{};
return *this ? descriptor->length() : size_type{};
}
/**
@@ -589,60 +791,48 @@ public:
* @return True if the group is empty, false otherwise.
*/
[[nodiscard]] bool empty() const noexcept {
return !*this || !*length;
return !*this || !descriptor->length();
}
/**
* @brief Returns an iterator to the first entity of the group.
*
* The returned iterator points to the first entity of the group. If the
* group is empty, the returned iterator will be equal to `end()`.
* If the group is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first entity of the group.
*/
[[nodiscard]] iterator begin() const noexcept {
return *this ? (std::get<0>(pools)->base_type::end() - *length) : iterator{};
return *this ? (handle().end() - descriptor->length()) : iterator{};
}
/**
* @brief Returns an iterator that is past the last entity of the group.
*
* The returned iterator points to the entity following the last entity of
* the group. Attempting to dereference the returned iterator results in
* undefined behavior.
*
* @return An iterator to the entity following the last entity of the
* group.
*/
[[nodiscard]] iterator end() const noexcept {
return *this ? std::get<0>(pools)->base_type::end() : iterator{};
return *this ? handle().end() : iterator{};
}
/**
* @brief Returns an iterator to the first entity of the reversed group.
*
* The returned iterator points to the first entity of the reversed group.
* If the group is empty, the returned iterator will be equal to `rend()`.
*
* @return An iterator to the first entity of the reversed group.
*/
[[nodiscard]] reverse_iterator rbegin() const noexcept {
return *this ? std::get<0>(pools)->base_type::rbegin() : reverse_iterator{};
return *this ? handle().rbegin() : reverse_iterator{};
}
/**
* @brief Returns an iterator that is past the last entity of the reversed
* group.
*
* The returned iterator points to the entity following the last entity of
* the reversed group. Attempting to dereference the returned iterator
* results in undefined behavior.
*
* @return An iterator to the entity following the last entity of the
* reversed group.
*/
[[nodiscard]] reverse_iterator rend() const noexcept {
return *this ? (std::get<0>(pools)->base_type::rbegin() + *length) : reverse_iterator{};
return *this ? (handle().rbegin() + descriptor->length()) : reverse_iterator{};
}
/**
@@ -672,8 +862,8 @@ public:
* iterator otherwise.
*/
[[nodiscard]] iterator find(const entity_type entt) const noexcept {
const auto it = *this ? std::get<0>(pools)->find(entt) : iterator{};
return it != end() && it >= begin() && *it == entt ? it : end();
const auto it = *this ? handle().find(entt) : iterator{};
return it >= begin() ? it : iterator{};
}
/**
@@ -690,7 +880,7 @@ public:
* @return True if the group is properly initialized, false otherwise.
*/
[[nodiscard]] explicit operator bool() const noexcept {
return length != nullptr;
return descriptor != nullptr;
}
/**
@@ -699,32 +889,37 @@ public:
* @return True if the group contains the given entity, false otherwise.
*/
[[nodiscard]] bool contains(const entity_type entt) const noexcept {
return *this && std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < (*length));
return *this && handle().contains(entt) && (handle().index(entt) < (descriptor->length()));
}
/**
* @brief Returns the components assigned to the given entity.
*
* Prefer this function instead of `registry::get` during iterations. It has
* far better performance than its counterpart.
*
* @warning
* Attempting to use an invalid component type results in a compilation
* error. Attempting to use an entity that doesn't belong to the group
* results in undefined behavior.
*
* @tparam Type Types of components to get.
* @tparam Type Type of the component to get.
* @tparam Other Other types of components to get.
* @param entt A valid identifier.
* @return The components assigned to the entity.
*/
template<typename... Type>
template<typename Type, typename... Other>
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
if constexpr(sizeof...(Type) == 0) {
return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools);
} else if constexpr(sizeof...(Type) == 1) {
return (std::get<index_of<Type>>(pools)->get(entt), ...);
return get<index_of<Type>, index_of<Other>...>(entt);
}
/**
* @brief Returns the components assigned to the given entity.
* @tparam Index Indexes of the components to get.
* @param entt A valid identifier.
* @return The components assigned to the entity.
*/
template<std::size_t... Index>
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
const auto cpools = pools();
if constexpr(sizeof...(Index) == 0) {
return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools);
} else if constexpr(sizeof...(Index) == 1) {
return (std::get<Index>(cpools)->get(entt), ...);
} else {
return std::tuple_cat(std::get<index_of<Type>>(pools)->get_as_tuple(entt)...);
return std::tuple_cat(std::get<Index>(cpools)->get_as_tuple(entt)...);
}
}
@@ -775,16 +970,13 @@ public:
* @return An iterable object to use to _visit_ the group.
*/
[[nodiscard]] iterable each() const noexcept {
return {{begin(), pools}, {end(), pools}};
const auto cpools = pools();
return {{begin(), cpools}, {end(), cpools}};
}
/**
* @brief Sort a group according to the given comparison function.
*
* Sort the group so that iterating it with a couple of iterators returns
* entities and components in the expected order. See `begin` and `end` for
* more details.
*
* The comparison function object must return `true` if the first element
* is _less_ than the second one, `false` otherwise. The signature of the
* comparison function should be equivalent to one of the following:
@@ -807,7 +999,8 @@ public:
* * An iterator past the last element of the range to sort.
* * A comparison function to use to compare the elements.
*
* @tparam Type Optional types of components to compare.
* @tparam Type Optional type of component to compare.
* @tparam Other Other optional types of components to compare.
* @tparam Compare Type of comparison function object.
* @tparam Sort Type of sort function object.
* @tparam Args Types of arguments to forward to the sort function object.
@@ -815,36 +1008,56 @@ public:
* @param algo A valid sort function object.
* @param args Arguments to forward to the sort function object, if any.
*/
template<typename... Type, typename Compare, typename Sort = std_sort, typename... Args>
template<typename Type, typename... Other, typename Compare, typename Sort = std_sort, typename... Args>
void sort(Compare compare, Sort algo = Sort{}, Args &&...args) const {
if constexpr(sizeof...(Type) == 0) {
sort<index_of<Type>, index_of<Other>...>(std::move(compare), std::move(algo), std::forward<Args>(args)...);
}
/**
* @brief Sort a group according to the given comparison function.
*
* @sa sort
*
* @tparam Index Optional indexes of components to compare.
* @tparam Compare Type of comparison function object.
* @tparam Sort Type of sort function object.
* @tparam Args Types of arguments to forward to the sort function object.
* @param compare A valid comparison function object.
* @param algo A valid sort function object.
* @param args Arguments to forward to the sort function object, if any.
*/
template<std::size_t... Index, typename Compare, typename Sort = std_sort, typename... Args>
void sort(Compare compare, Sort algo = Sort{}, Args &&...args) const {
const auto cpools = pools();
if constexpr(sizeof...(Index) == 0) {
static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function");
std::get<0>(pools)->sort_n(*length, std::move(compare), std::move(algo), std::forward<Args>(args)...);
storage<0>()->sort_n(descriptor->length(), std::move(compare), std::move(algo), std::forward<Args>(args)...);
} else {
auto comp = [this, &compare](const entity_type lhs, const entity_type rhs) {
if constexpr(sizeof...(Type) == 1) {
return compare((std::get<index_of<Type>>(pools)->get(lhs), ...), (std::get<index_of<Type>>(pools)->get(rhs), ...));
auto comp = [&compare, &cpools](const entity_type lhs, const entity_type rhs) {
if constexpr(sizeof...(Index) == 1) {
return compare((std::get<Index>(cpools)->get(lhs), ...), (std::get<Index>(cpools)->get(rhs), ...));
} else {
return compare(std::forward_as_tuple(std::get<index_of<Type>>(pools)->get(lhs)...), std::forward_as_tuple(std::get<index_of<Type>>(pools)->get(rhs)...));
return compare(std::forward_as_tuple(std::get<Index>(cpools)->get(lhs)...), std::forward_as_tuple(std::get<Index>(cpools)->get(rhs)...));
}
};
std::get<0>(pools)->sort_n(*length, std::move(comp), std::move(algo), std::forward<Args>(args)...);
storage<0>()->sort_n(descriptor->length(), std::move(comp), std::move(algo), std::forward<Args>(args)...);
}
std::apply([this](auto *head, auto *...other) {
for(auto next = *length; next; --next) {
auto cb = [this](auto *head, auto *...other) {
for(auto next = descriptor->length(); next; --next) {
const auto pos = next - 1;
[[maybe_unused]] const auto entt = head->data()[pos];
(other->swap_elements(other->data()[pos], entt), ...);
}
},
pools);
};
std::apply(cb, cpools);
}
private:
const std::tuple<Owned *..., Get *...> pools;
const size_type *const length;
handler *descriptor;
};
} // namespace entt

View File

@@ -12,11 +12,7 @@
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename It>
@@ -33,6 +29,7 @@ public:
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
using iterator_concept = std::forward_iterator_tag;
constexpr handle_storage_iterator() noexcept
: entt{null},
@@ -43,7 +40,9 @@ public:
: entt{value},
it{from},
last{to} {
while(it != last && !it->second.contains(entt)) { ++it; }
while(it != last && !it->second.contains(entt)) {
++it;
}
}
constexpr handle_storage_iterator &operator++() noexcept {
@@ -84,11 +83,7 @@ template<typename ILhs, typename IRhs>
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
/**
* @brief Non-owning handle to an entity.
@@ -141,7 +136,7 @@ struct basic_handle {
/**
* @brief Constructs a const handle from a non-const one.
* @tparam Other A valid entity type (see entt_traits for more details).
* @tparam Other A valid entity type.
* @tparam Args Scope of the handle to construct.
* @return A const handle referring to the same registry and the same
* entity.
@@ -196,7 +191,7 @@ struct basic_handle {
/*! @brief Destroys the entity associated with a handle. */
void destroy() {
reg->destroy(entt);
reg->destroy(std::exchange(entt, null));
}
/**
@@ -204,7 +199,7 @@ struct basic_handle {
* @param version A desired version upon destruction.
*/
void destroy(const version_type version) {
reg->destroy(entt, version);
reg->destroy(std::exchange(entt, null), version);
}
/**

View File

@@ -3,12 +3,13 @@
#include <memory>
#include <type_traits>
#include <utility>
#include "../core/fwd.hpp"
#include "../core/type_traits.hpp"
#include "../signal/delegate.hpp"
#include "component.hpp"
#include "fwd.hpp"
#include "group.hpp"
#include "storage.hpp"
#include "view.hpp"
namespace entt {
@@ -28,7 +29,7 @@ public:
/*! @brief Type of registry to convert. */
using registry_type = Registry;
/*! @brief Underlying entity identifier. */
using entity_type = std::remove_const_t<typename registry_type::entity_type>;
using entity_type = typename registry_type::entity_type;
/**
* @brief Constructs a converter for a given registry.
@@ -71,7 +72,7 @@ public:
/*! @brief Type of registry to convert. */
using registry_type = Registry;
/*! @brief Underlying entity identifier. */
using entity_type = std::remove_const_t<typename registry_type::entity_type>;
using entity_type = typename registry_type::entity_type;
/**
* @brief Constructs a converter for a given registry.
@@ -103,7 +104,7 @@ private:
* @param reg A registry that contains the given entity and its components.
* @param entt Entity from which to get the component.
*/
template<auto Member, typename Registry = std::decay_t<nth_argument_t<0u, Member>>>
template<auto Member, typename Registry = std::decay_t<nth_argument_t<0u, decltype(Member)>>>
void invoke(Registry &reg, const typename Registry::entity_type entt) {
static_assert(std::is_member_function_pointer_v<decltype(Member)>, "Invalid pointer to non-static member function");
delegate<void(Registry &, const typename Registry::entity_type)> func;
@@ -115,23 +116,22 @@ void invoke(Registry &reg, const typename Registry::entity_type entt) {
* @brief Returns the entity associated with a given component.
*
* @warning
* Currently, this function only works correctly with the default pool as it
* Currently, this function only works correctly with the default storage as it
* makes assumptions about how the components are laid out.
*
* @tparam Registry Basic registry type.
* @tparam Component Type of component.
* @param reg A registry that contains the given entity and its components.
* @tparam Args Storage type template parameters.
* @param storage A storage that contains the given component.
* @param instance A valid component instance.
* @return The entity associated with the given component.
*/
template<typename Registry, typename Component>
typename Registry::entity_type to_entity(const Registry &reg, const Component &instance) {
const auto &storage = reg.template storage<Component>();
const typename Registry::base_type &base = storage;
template<typename... Args>
auto to_entity(const basic_storage<Args...> &storage, const typename basic_storage<Args...>::value_type &instance) -> typename basic_storage<Args...>::entity_type {
constexpr auto page_size = basic_storage<Args...>::traits_type::page_size;
const typename basic_storage<Args...>::base_type &base = storage;
const auto *addr = std::addressof(instance);
for(auto it = base.rbegin(), last = base.rend(); it < last; it += component_traits<Component>::page_size) {
if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < static_cast<decltype(dist)>(component_traits<Component>::page_size)) {
for(auto it = base.rbegin(), last = base.rend(); it < last; it += page_size) {
if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < static_cast<decltype(dist)>(page_size)) {
return *(it + dist);
}
}
@@ -139,6 +139,135 @@ typename Registry::entity_type to_entity(const Registry &reg, const Component &i
return null;
}
/**
* @copybrief to_entity
* @tparam Args Registry type template parameters.
* @tparam Component Type of component.
* @param reg A registry that contains the given entity and its components.
* @param instance A valid component instance.
* @return The entity associated with the given component.
*/
template<typename... Args, typename Component>
[[deprecated("use storage based to_entity instead")]] typename basic_registry<Args...>::entity_type to_entity(const basic_registry<Args...> &reg, const Component &instance) {
if(const auto *storage = reg.template storage<Component>(); storage) {
return to_entity(*storage, instance);
}
return null;
}
/*! @brief Primary template isn't defined on purpose. */
template<typename...>
struct sigh_helper;
/**
* @brief Signal connection helper for registries.
* @tparam Registry Basic registry type.
*/
template<typename Registry>
struct sigh_helper<Registry> {
/*! @brief Registry type. */
using registry_type = Registry;
/**
* @brief Constructs a helper for a given registry.
* @param ref A valid reference to a registry.
*/
sigh_helper(registry_type &ref)
: bucket{&ref} {}
/**
* @brief Binds a properly initialized helper to a given signal type.
* @tparam Type Type of signal to bind the helper to.
* @param id Optional name for the underlying storage to use.
* @return A helper for a given registry and signal type.
*/
template<typename Type>
auto with(const id_type id = type_hash<Type>::value()) noexcept {
return sigh_helper<registry_type, Type>{*bucket, id};
}
/**
* @brief Returns a reference to the underlying registry.
* @return A reference to the underlying registry.
*/
[[nodiscard]] registry_type &registry() noexcept {
return *bucket;
}
private:
registry_type *bucket;
};
/**
* @brief Signal connection helper for registries.
* @tparam Registry Basic registry type.
* @tparam Type Type of signal to connect listeners to.
*/
template<typename Registry, typename Type>
struct sigh_helper<Registry, Type> final: sigh_helper<Registry> {
/*! @brief Registry type. */
using registry_type = Registry;
/**
* @brief Constructs a helper for a given registry.
* @param ref A valid reference to a registry.
* @param id Optional name for the underlying storage to use.
*/
sigh_helper(registry_type &ref, const id_type id = type_hash<Type>::value())
: sigh_helper<Registry>{ref},
name{id} {}
/**
* @brief Forwards the call to `on_construct` on the underlying storage.
* @tparam Candidate Function or member to connect.
* @tparam Args Type of class or type of payload, if any.
* @param args A valid object that fits the purpose, if any.
* @return This helper.
*/
template<auto Candidate, typename... Args>
auto on_construct(Args &&...args) {
this->registry().template on_construct<Type>(name).template connect<Candidate>(std::forward<Args>(args)...);
return *this;
}
/**
* @brief Forwards the call to `on_update` on the underlying storage.
* @tparam Candidate Function or member to connect.
* @tparam Args Type of class or type of payload, if any.
* @param args A valid object that fits the purpose, if any.
* @return This helper.
*/
template<auto Candidate, typename... Args>
auto on_update(Args &&...args) {
this->registry().template on_update<Type>(name).template connect<Candidate>(std::forward<Args>(args)...);
return *this;
}
/**
* @brief Forwards the call to `on_destroy` on the underlying storage.
* @tparam Candidate Function or member to connect.
* @tparam Args Type of class or type of payload, if any.
* @param args A valid object that fits the purpose, if any.
* @return This helper.
*/
template<auto Candidate, typename... Args>
auto on_destroy(Args &&...args) {
this->registry().template on_destroy<Type>(name).template connect<Candidate>(std::forward<Args>(args)...);
return *this;
}
private:
id_type name;
};
/**
* @brief Deduction guide.
* @tparam Registry Basic registry type.
*/
template<typename Registry>
sigh_helper(Registry &) -> sigh_helper<Registry>;
} // namespace entt
#endif

303
src/entt/entity/mixin.hpp Normal file
View File

@@ -0,0 +1,303 @@
#ifndef ENTT_ENTITY_MIXIN_HPP
#define ENTT_ENTITY_MIXIN_HPP
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/any.hpp"
#include "../signal/sigh.hpp"
#include "entity.hpp"
#include "fwd.hpp"
namespace entt {
/**
* @brief Mixin type used to add signal support to storage types.
*
* The function type of a listener is equivalent to:
*
* @code{.cpp}
* void(basic_registry<entity_type> &, entity_type);
* @endcode
*
* This applies to all signals made available.
*
* @tparam Type Underlying storage type.
* @tparam Registry Basic registry type.
*/
template<typename Type, typename Registry>
class basic_sigh_mixin final: public Type {
using underlying_type = Type;
using owner_type = Registry;
using basic_registry_type = basic_registry<typename underlying_type::entity_type, typename underlying_type::base_type::allocator_type>;
using sigh_type = sigh<void(owner_type &, const typename underlying_type::entity_type), typename underlying_type::allocator_type>;
using underlying_iterator = typename underlying_type::base_type::basic_iterator;
static_assert(std::is_base_of_v<basic_registry_type, owner_type>, "Invalid registry type");
owner_type &owner_or_assert() const noexcept {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
return static_cast<owner_type &>(*owner);
}
void pop(underlying_iterator first, underlying_iterator last) final {
if(auto &reg = owner_or_assert(); destruction.empty()) {
underlying_type::pop(first, last);
} else {
for(; first != last; ++first) {
const auto entt = *first;
destruction.publish(reg, entt);
const auto it = underlying_type::find(entt);
underlying_type::pop(it, it + 1u);
}
}
}
void pop_all() final {
if(auto &reg = owner_or_assert(); !destruction.empty()) {
for(auto it = underlying_type::base_type::begin(0), last = underlying_type::base_type::end(0); it != last; ++it) {
if constexpr(std::is_same_v<typename underlying_type::value_type, typename underlying_type::entity_type>) {
destruction.publish(reg, *it);
} else {
if constexpr(underlying_type::traits_type::in_place_delete) {
if(const auto entt = *it; entt != tombstone) {
destruction.publish(reg, entt);
}
} else {
destruction.publish(reg, *it);
}
}
}
}
underlying_type::pop_all();
}
underlying_iterator try_emplace(const typename underlying_type::entity_type entt, const bool force_back, const void *value) final {
const auto it = underlying_type::try_emplace(entt, force_back, value);
if(auto &reg = owner_or_assert(); it != underlying_type::base_type::end()) {
construction.publish(reg, *it);
}
return it;
}
public:
/*! @brief Allocator type. */
using allocator_type = typename underlying_type::allocator_type;
/*! @brief Underlying entity identifier. */
using entity_type = typename underlying_type::entity_type;
/*! @brief Expected registry type. */
using registry_type = owner_type;
/*! @brief Default constructor. */
basic_sigh_mixin()
: basic_sigh_mixin{allocator_type{}} {}
/**
* @brief Constructs an empty storage with a given allocator.
* @param allocator The allocator to use.
*/
explicit basic_sigh_mixin(const allocator_type &allocator)
: underlying_type{allocator},
owner{},
construction{allocator},
destruction{allocator},
update{allocator} {}
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
basic_sigh_mixin(basic_sigh_mixin &&other) noexcept
: underlying_type{std::move(other)},
owner{other.owner},
construction{std::move(other.construction)},
destruction{std::move(other.destruction)},
update{std::move(other.update)} {}
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
basic_sigh_mixin(basic_sigh_mixin &&other, const allocator_type &allocator) noexcept
: underlying_type{std::move(other), allocator},
owner{other.owner},
construction{std::move(other.construction), allocator},
destruction{std::move(other.destruction), allocator},
update{std::move(other.update), allocator} {}
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This storage.
*/
basic_sigh_mixin &operator=(basic_sigh_mixin &&other) noexcept {
underlying_type::operator=(std::move(other));
owner = other.owner;
construction = std::move(other.construction);
destruction = std::move(other.destruction);
update = std::move(other.update);
return *this;
}
/**
* @brief Exchanges the contents with those of a given storage.
* @param other Storage to exchange the content with.
*/
void swap(basic_sigh_mixin &other) {
using std::swap;
underlying_type::swap(other);
swap(owner, other.owner);
swap(construction, other.construction);
swap(destruction, other.destruction);
swap(update, other.update);
}
/**
* @brief Returns a sink object.
*
* The sink returned by this function can be used to receive notifications
* whenever a new instance is created and assigned to an entity.<br/>
* Listeners are invoked after the object has been assigned to the entity.
*
* @sa sink
*
* @return A temporary sink object.
*/
[[nodiscard]] auto on_construct() noexcept {
return sink{construction};
}
/**
* @brief Returns a sink object.
*
* The sink returned by this function can be used to receive notifications
* whenever an instance is explicitly updated.<br/>
* Listeners are invoked after the object has been updated.
*
* @sa sink
*
* @return A temporary sink object.
*/
[[nodiscard]] auto on_update() noexcept {
return sink{update};
}
/**
* @brief Returns a sink object.
*
* The sink returned by this function can be used to receive notifications
* whenever an instance is removed from an entity and thus destroyed.<br/>
* Listeners are invoked before the object has been removed from the entity.
*
* @sa sink
*
* @return A temporary sink object.
*/
[[nodiscard]] auto on_destroy() noexcept {
return sink{destruction};
}
/**
* @brief Emplace elements into a storage.
*
* The behavior of this operation depends on the underlying storage type
* (for example, components vs entities).<br/>
* Refer to the specific documentation for more details.
*
* @return A return value as returned by the underlying storage.
*/
auto emplace() {
const auto entt = underlying_type::emplace();
construction.publish(owner_or_assert(), entt);
return entt;
}
/**
* @brief Emplace elements into a storage.
*
* The behavior of this operation depends on the underlying storage type
* (for example, components vs entities).<br/>
* Refer to the specific documentation for more details.
*
* @tparam Args Types of arguments to forward to the underlying storage.
* @param hint A valid identifier.
* @param args Parameters to forward to the underlying storage.
* @return A return value as returned by the underlying storage.
*/
template<typename... Args>
decltype(auto) emplace(const entity_type hint, Args &&...args) {
if constexpr(std::is_same_v<typename underlying_type::value_type, typename underlying_type::entity_type>) {
const auto entt = underlying_type::emplace(hint, std::forward<Args>(args)...);
construction.publish(owner_or_assert(), entt);
return entt;
} else {
underlying_type::emplace(hint, std::forward<Args>(args)...);
construction.publish(owner_or_assert(), hint);
return this->get(hint);
}
}
/**
* @brief Patches the given instance for an entity.
* @tparam Func Types of the function objects to invoke.
* @param entt A valid identifier.
* @param func Valid function objects.
* @return A reference to the patched instance.
*/
template<typename... Func>
decltype(auto) patch(const entity_type entt, Func &&...func) {
underlying_type::patch(entt, std::forward<Func>(func)...);
update.publish(owner_or_assert(), entt);
return this->get(entt);
}
/**
* @brief Emplace elements into a storage.
*
* The behavior of this operation depends on the underlying storage type
* (for example, components vs entities).<br/>
* Refer to the specific documentation for more details.
*
* @tparam It Iterator type (as required by the underlying storage type).
* @tparam Args Types of arguments to forward to the underlying storage.
* @param first An iterator to the first element of the range.
* @param last An iterator past the last element of the range.
* @param args Parameters to use to forward to the underlying storage.
*/
template<typename It, typename... Args>
void insert(It first, It last, Args &&...args) {
auto from = underlying_type::size();
underlying_type::insert(first, last, std::forward<Args>(args)...);
if(auto &reg = owner_or_assert(); !construction.empty()) {
for(const auto to = underlying_type::size(); from != to; ++from) {
construction.publish(reg, underlying_type::operator[](from));
}
}
}
/**
* @brief Forwards variables to derived classes, if any.
* @param value A variable wrapped in an opaque container.
*/
void bind(any value) noexcept final {
auto *reg = any_cast<basic_registry_type>(&value);
owner = reg ? reg : owner;
underlying_type::bind(std::move(value));
}
private:
basic_registry_type *owner;
sigh_type construction;
sigh_type destruction;
sigh_type update;
};
} // namespace entt
#endif

View File

@@ -43,7 +43,7 @@ struct basic_collector<> {
* @return The updated collector.
*/
template<typename... AllOf, typename... NoneOf>
static constexpr auto group(exclude_t<NoneOf...> = {}) noexcept {
static constexpr auto group(exclude_t<NoneOf...> = exclude_t{}) noexcept {
return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>>{};
}
@@ -78,7 +78,7 @@ struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule
* @return The updated collector.
*/
template<typename... AllOf, typename... NoneOf>
static constexpr auto group(exclude_t<NoneOf...> = {}) noexcept {
static constexpr auto group(exclude_t<NoneOf...> = exclude_t{}) noexcept {
return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>, current_type, Other...>{};
}
@@ -99,7 +99,7 @@ struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule
* @return The updated collector.
*/
template<typename... AllOf, typename... NoneOf>
static constexpr auto where(exclude_t<NoneOf...> = {}) noexcept {
static constexpr auto where(exclude_t<NoneOf...> = exclude_t{}) noexcept {
using extended_type = matcher<type_list<Reject..., NoneOf...>, type_list<Require..., AllOf...>, Rule...>;
return basic_collector<extended_type, Other...>{};
}
@@ -146,8 +146,7 @@ inline constexpr basic_collector<> collector{};
* * The entity currently pointed is destroyed.
*
* In all the other cases, modifying the pools of the given components in any
* way invalidates all the iterators and using them results in undefined
* behavior.
* way invalidates all the iterators.
*
* @warning
* Lifetime of an observer doesn't necessarily have to overcome that of the
@@ -156,10 +155,12 @@ inline constexpr basic_collector<> collector{};
* pointers.
*
* @tparam Registry Basic registry type.
* @tparam Mask Mask type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Registry>
class basic_observer: private basic_storage<std::uint32_t, typename Registry::entity_type> {
using base_type = basic_storage<std::uint32_t, typename Registry::entity_type>;
template<typename Registry, typename Mask, typename Allocator>
class basic_observer: private basic_storage<Mask, typename Registry::entity_type, Allocator> {
using base_type = basic_storage<Mask, typename Registry::entity_type, Allocator>;
template<typename>
struct matcher_handler;
@@ -193,10 +194,10 @@ class basic_observer: private basic_storage<std::uint32_t, typename Registry::en
}
static void disconnect(basic_observer &obs, Registry &reg) {
(reg.template on_destroy<Require>().disconnect(obs), ...);
(reg.template on_construct<Reject>().disconnect(obs), ...);
reg.template on_update<AnyOf>().disconnect(obs);
reg.template on_destroy<AnyOf>().disconnect(obs);
(reg.template on_destroy<Require>().disconnect(&obs), ...);
(reg.template on_construct<Reject>().disconnect(&obs), ...);
reg.template on_update<AnyOf>().disconnect(&obs);
reg.template on_destroy<AnyOf>().disconnect(&obs);
}
};
@@ -239,12 +240,12 @@ class basic_observer: private basic_storage<std::uint32_t, typename Registry::en
}
static void disconnect(basic_observer &obs, Registry &reg) {
(reg.template on_destroy<Require>().disconnect(obs), ...);
(reg.template on_construct<Reject>().disconnect(obs), ...);
(reg.template on_construct<AllOf>().disconnect(obs), ...);
(reg.template on_destroy<NoneOf>().disconnect(obs), ...);
(reg.template on_destroy<AllOf>().disconnect(obs), ...);
(reg.template on_construct<NoneOf>().disconnect(obs), ...);
(reg.template on_destroy<Require>().disconnect(&obs), ...);
(reg.template on_construct<Reject>().disconnect(&obs), ...);
(reg.template on_construct<AllOf>().disconnect(&obs), ...);
(reg.template on_destroy<NoneOf>().disconnect(&obs), ...);
(reg.template on_destroy<AllOf>().disconnect(&obs), ...);
(reg.template on_construct<NoneOf>().disconnect(&obs), ...);
}
};
@@ -267,15 +268,26 @@ public:
using entity_type = typename registry_type::entity_type;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Random access iterator type. */
using iterator = typename registry_type::base_type::iterator;
using iterator = typename registry_type::common_type::iterator;
/*! @brief Default constructor. */
basic_observer()
: release{} {}
: basic_observer{allocator_type{}} {}
/**
* @brief Constructs an empty storage with a given allocator.
* @param allocator The allocator to use.
*/
explicit basic_observer(const allocator_type &allocator)
: base_type{allocator},
release{} {}
/*! @brief Default copy constructor, deleted on purpose. */
basic_observer(const basic_observer &) = delete;
/*! @brief Default move constructor, deleted on purpose. */
basic_observer(basic_observer &&) = delete;
@@ -283,16 +295,14 @@ public:
* @brief Creates an observer and connects it to a given registry.
* @tparam Matcher Types of matchers to use to initialize the observer.
* @param reg A valid reference to a registry.
* @param allocator The allocator to use.
*/
template<typename... Matcher>
basic_observer(registry_type &reg, basic_collector<Matcher...>)
: basic_observer{} {
basic_observer(registry_type &reg, basic_collector<Matcher...>, const allocator_type &allocator = allocator_type{})
: basic_observer{allocator} {
connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{});
}
/*! @brief Default destructor. */
~basic_observer() = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This observer.
@@ -360,8 +370,7 @@ public:
/**
* @brief Returns an iterator to the first entity of the observer.
*
* The returned iterator points to the first entity of the observer. If the
* container is empty, the returned iterator will be equal to `end()`.
* If the observer is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first entity of the observer.
*/
@@ -371,11 +380,6 @@ public:
/**
* @brief Returns an iterator that is past the last entity of the observer.
*
* The returned iterator points to the entity following the last entity of
* the observer. Attempting to dereference the returned iterator results in
* undefined behavior.
*
* @return An iterator to the entity following the last entity of the
* observer.
*/

View File

@@ -15,11 +15,7 @@
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename>
@@ -87,11 +83,7 @@ template<typename... Req, typename Ret, typename Class, typename... Args>
resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...) const);
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
/**
* @brief Utility class for creating a static task graph.
@@ -126,7 +118,7 @@ class basic_organizer final {
if constexpr(std::is_same_v<Type, Registry>) {
return reg;
} else if constexpr(internal::is_view_v<Type>) {
return as_view{reg};
return static_cast<Type>(as_view{reg});
} else {
return reg.ctx().template emplace<std::remove_reference_t<Type>>();
}

File diff suppressed because it is too large Load Diff

View File

@@ -11,11 +11,7 @@
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename Set>
@@ -95,47 +91,27 @@ private:
};
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
/**
* @brief Generic runtime view.
*
* Runtime views iterate over those entities that have at least all the given
* components in their bags. During initialization, a runtime view looks at the
* number of entities available for each component and picks up a reference to
* the smallest set of candidate entities in order to get a performance boost
* when iterate.<br/>
* Order of elements during iterations are highly dependent on the order of the
* underlying data structures. See sparse_set and its specializations for more
* details.
* Runtime views iterate over those entities that are at least in the given
* storage. During initialization, a runtime view looks at the number of
* entities available for each component and uses the smallest set in order to
* get a performance boost when iterating.
*
* @b Important
*
* Iterators aren't invalidated if:
*
* * New instances of the given components are created and assigned to entities.
* * The entity currently pointed is modified (as an example, if one of the
* given components is removed from the entity to which the iterator points).
* * New elements are added to the storage.
* * The entity currently pointed is modified (for example, components are added
* or removed from it).
* * The entity currently pointed is destroyed.
*
* In all the other cases, modifying the pools of the given components in any
* way invalidates all the iterators and using them results in undefined
* behavior.
*
* @note
* Views share references to the underlying data structures of the registry that
* generated them. Therefore any change to the entities and to the components
* made by means of the registry are immediately reflected by the views, unless
* a pool was missing when the view was built (in this case, the view won't
* have a valid reference and won't be updated accordingly).
*
* @warning
* Lifetime of a view must not overcome that of the registry that generated it.
* In any other case, attempting to use a view results in undefined behavior.
* In all other cases, modifying the storage iterated by the view in any way
* invalidates all the iterators.
*
* @tparam Type Common base type.
* @tparam Allocator Type of allocator used to manage memory and elements.
@@ -154,9 +130,9 @@ public:
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Common type among all storage types. */
using base_type = Type;
using common_type = Type;
/*! @brief Bidirectional iterator type. */
using iterator = internal::runtime_view_iterator<base_type>;
using iterator = internal::runtime_view_iterator<common_type>;
/*! @brief Default constructor to use to create empty, invalid views. */
basic_runtime_view() noexcept
@@ -235,7 +211,7 @@ public:
* @param base An opaque reference to a storage object.
* @return This runtime view.
*/
basic_runtime_view &iterate(base_type &base) {
basic_runtime_view &iterate(common_type &base) {
if(pools.empty() || !(base.size() < pools[0u]->size())) {
pools.push_back(&base);
} else {
@@ -250,7 +226,7 @@ public:
* @param base An opaque reference to a storage object.
* @return This runtime view.
*/
basic_runtime_view &exclude(base_type &base) {
basic_runtime_view &exclude(common_type &base) {
filter.push_back(&base);
return *this;
}
@@ -267,9 +243,7 @@ public:
* @brief Returns an iterator to the first entity that has the given
* components.
*
* The returned iterator points to the first entity that has the given
* components. If the view is empty, the returned iterator will be equal to
* `end()`.
* If the view is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first entity that has the given components.
*/
@@ -280,11 +254,6 @@ public:
/**
* @brief Returns an iterator that is past the last entity that has the
* given components.
*
* The returned iterator points to the entity following the last entity that
* has the given components. Attempting to dereference the returned iterator
* results in undefined behavior.
*
* @return An iterator to the entity following the last entity that has the
* given components.
*/
@@ -307,8 +276,7 @@ public:
* @brief Iterates entities and applies the given function object to them.
*
* The function object is invoked for each entity. It is provided only with
* the entity itself. To get the components, users can use the registry with
* which the view was built.<br/>
* the entity itself.<br/>
* The signature of the function should be equivalent to the following:
*
* @code{.cpp}

View File

@@ -1,7 +1,6 @@
#ifndef ENTT_ENTITY_SNAPSHOT_HPP
#define ENTT_ENTITY_SNAPSHOT_HPP
#include <array>
#include <cstddef>
#include <iterator>
#include <tuple>
@@ -11,13 +10,29 @@
#include "../config/config.h"
#include "../container/dense_map.hpp"
#include "../core/type_traits.hpp"
#include "component.hpp"
#include "entity.hpp"
#include "fwd.hpp"
#include "view.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename Registry>
void orphans(Registry &registry) {
auto &storage = registry.template storage<typename Registry::entity_type>();
for(auto entt: storage) {
if(registry.orphan(entt)) {
storage.erase(entt);
}
}
}
} // namespace internal
/*! @endcond */
/**
* @brief Utility class to create snapshots from a registry.
*
@@ -30,34 +45,8 @@ namespace entt {
*/
template<typename Registry>
class basic_snapshot {
using entity_traits = entt_traits<typename Registry::entity_type>;
template<typename Component, typename Archive, typename It>
void get(Archive &archive, std::size_t sz, It first, It last) const {
const auto view = reg->template view<const Component>();
archive(typename entity_traits::entity_type(sz));
while(first != last) {
const auto entt = *(first++);
if(reg->template all_of<Component>(entt)) {
std::apply(archive, std::tuple_cat(std::make_tuple(entt), view.get(entt)));
}
}
}
template<typename... Component, typename Archive, typename It, std::size_t... Index>
void component(Archive &archive, It first, It last, std::index_sequence<Index...>) const {
std::array<std::size_t, sizeof...(Index)> size{};
auto begin = first;
while(begin != last) {
const auto entt = *(begin++);
((reg->template all_of<Component>(entt) ? ++size[Index] : 0u), ...);
}
(get<Component>(archive, size[Index], first, last), ...);
}
static_assert(!std::is_const_v<Registry>, "Non-const registry type required");
using traits_type = typename Registry::traits_type;
public:
/*! Basic registry type. */
@@ -79,69 +68,78 @@ public:
basic_snapshot &operator=(basic_snapshot &&) noexcept = default;
/**
* @brief Puts aside all the entities from the underlying registry.
*
* Entities are serialized along with their versions. Destroyed entities are
* taken in consideration as well by this function.
*
* @brief Serializes all elements of a type with associated identifiers.
* @tparam Type Type of elements to serialize.
* @tparam Archive Type of output archive.
* @param archive A valid reference to an output archive.
* @param id Optional name used to map the storage within the registry.
* @return An object of this type to continue creating the snapshot.
*/
template<typename Archive>
const basic_snapshot &entities(Archive &archive) const {
const auto sz = reg->size();
template<typename Type, typename Archive>
const basic_snapshot &get(Archive &archive, const id_type id = type_hash<Type>::value()) const {
if(const auto *storage = reg->template storage<Type>(id); storage) {
archive(static_cast<typename traits_type::entity_type>(storage->size()));
archive(typename entity_traits::entity_type(sz + 1u));
archive(reg->released());
if constexpr(std::is_same_v<Type, entity_type>) {
archive(static_cast<typename traits_type::entity_type>(storage->free_list()));
for(auto first = reg->data(), last = first + sz; first != last; ++first) {
archive(*first);
for(auto first = storage->data(), last = first + storage->size(); first != last; ++first) {
archive(*first);
}
} else if constexpr(component_traits<Type>::in_place_delete) {
const typename registry_type::common_type &base = *storage;
for(auto it = base.rbegin(), last = base.rend(); it != last; ++it) {
if(const auto entt = *it; entt == tombstone) {
archive(static_cast<entity_type>(null));
} else {
archive(entt);
std::apply([&archive](auto &&...args) { (archive(std::forward<decltype(args)>(args)), ...); }, storage->get_as_tuple(entt));
}
}
} else {
for(auto elem: storage->reach()) {
std::apply([&archive](auto &&...args) { (archive(std::forward<decltype(args)>(args)), ...); }, elem);
}
}
} else {
archive(typename traits_type::entity_type{});
}
return *this;
}
/**
* @brief Puts aside the given components.
*
* Each instance is serialized together with the entity to which it belongs.
* Entities are serialized along with their versions.
*
* @tparam Component Types of components to serialize.
* @tparam Archive Type of output archive.
* @param archive A valid reference to an output archive.
* @return An object of this type to continue creating the snapshot.
*/
template<typename... Component, typename Archive>
const basic_snapshot &component(Archive &archive) const {
if constexpr(sizeof...(Component) == 1u) {
const auto view = reg->template view<const Component...>();
(component<Component>(archive, view.rbegin(), view.rend()), ...);
return *this;
} else {
(component<Component>(archive), ...);
return *this;
}
}
/**
* @brief Puts aside the given components for the entities in a range.
*
* Each instance is serialized together with the entity to which it belongs.
* Entities are serialized along with their versions.
*
* @tparam Component Types of components to serialize.
* @brief Serializes all elements of a type with associated identifiers for
* the entities in a range.
* @tparam Type Type of elements to serialize.
* @tparam Archive Type of output archive.
* @tparam It Type of input iterator.
* @param archive A valid reference to an output archive.
* @param first An iterator to the first element of the range to serialize.
* @param last An iterator past the last element of the range to serialize.
* @param id Optional name used to map the storage within the registry.
* @return An object of this type to continue creating the snapshot.
*/
template<typename... Component, typename Archive, typename It>
const basic_snapshot &component(Archive &archive, It first, It last) const {
component<Component...>(archive, first, last, std::index_sequence_for<Component...>{});
template<typename Type, typename Archive, typename It>
const basic_snapshot &get(Archive &archive, It first, It last, const id_type id = type_hash<Type>::value()) const {
static_assert(!std::is_same_v<Type, entity_type>, "Entity types not supported");
if(const auto *storage = reg->template storage<Type>(id); storage && !storage->empty()) {
archive(static_cast<typename traits_type::entity_type>(std::distance(first, last)));
for(; first != last; ++first) {
if(const auto entt = *first; storage->contains(entt)) {
archive(entt);
std::apply([&archive](auto &&...args) { (archive(std::forward<decltype(args)>(args)), ...); }, storage->get_as_tuple(entt));
} else {
archive(static_cast<entity_type>(null));
}
}
} else {
archive(typename traits_type::entity_type{});
}
return *this;
}
@@ -161,33 +159,8 @@ private:
*/
template<typename Registry>
class basic_snapshot_loader {
using entity_traits = entt_traits<typename Registry::entity_type>;
template<typename Component, typename Archive>
void assign(Archive &archive) const {
typename entity_traits::entity_type length{};
entity_type entt;
archive(length);
if constexpr(ignore_as_empty_v<Component>) {
while(length--) {
archive(entt);
const auto entity = reg->valid(entt) ? entt : reg->create(entt);
ENTT_ASSERT(entity == entt, "Entity not available for use");
reg->template emplace<Component>(entt);
}
} else {
Component instance;
while(length--) {
archive(entt, instance);
const auto entity = reg->valid(entt) ? entt : reg->create(entt);
ENTT_ASSERT(entity == entt, "Entity not available for use");
reg->template emplace<Component>(entt, std::move(instance));
}
}
}
static_assert(!std::is_const_v<Registry>, "Non-const registry type required");
using traits_type = typename Registry::traits_type;
public:
/*! Basic registry type. */
@@ -202,7 +175,7 @@ public:
basic_snapshot_loader(registry_type &source) noexcept
: reg{&source} {
// restoring a snapshot as a whole requires a clean registry
ENTT_ASSERT(reg->empty(), "Registry must be empty");
ENTT_ASSERT(reg->template storage<entity_type>().free_list() == 0u, "Registry must be empty");
}
/*! @brief Default move constructor. */
@@ -212,47 +185,52 @@ public:
basic_snapshot_loader &operator=(basic_snapshot_loader &&) noexcept = default;
/**
* @brief Restores entities that were in use during serialization.
*
* This function restores the entities that were in use during serialization
* and gives them the versions they originally had.
*
* @brief Restores all elements of a type with associated identifiers.
* @tparam Type Type of elements to restore.
* @tparam Archive Type of input archive.
* @param archive A valid reference to an input archive.
* @param id Optional name used to map the storage within the registry.
* @return A valid loader to continue restoring data.
*/
template<typename Archive>
const basic_snapshot_loader &entities(Archive &archive) const {
typename entity_traits::entity_type length{};
template<typename Type, typename Archive>
basic_snapshot_loader &get(Archive &archive, const id_type id = type_hash<Type>::value()) {
auto &storage = reg->template storage<Type>(id);
typename traits_type::entity_type length{};
archive(length);
std::vector<entity_type> all(length);
for(std::size_t pos{}; pos < length; ++pos) {
archive(all[pos]);
if constexpr(std::is_same_v<Type, entity_type>) {
typename traits_type::entity_type count{};
storage.reserve(length);
archive(count);
for(entity_type entity = null; length; --length) {
archive(entity);
storage.emplace(entity);
}
storage.free_list(count);
} else {
auto &other = reg->template storage<entity_type>();
entity_type entt{null};
while(length--) {
if(archive(entt); entt != null) {
const auto entity = other.contains(entt) ? entt : other.emplace(entt);
ENTT_ASSERT(entity == entt, "Entity not available for use");
if constexpr(std::tuple_size_v<decltype(storage.get_as_tuple({}))> == 0u) {
storage.emplace(entity);
} else {
Type elem{};
archive(elem);
storage.emplace(entity, std::move(elem));
}
}
}
}
reg->assign(++all.cbegin(), all.cend(), all[0u]);
return *this;
}
/**
* @brief Restores components and assigns them to the right entities.
*
* The template parameter list must be exactly the same used during
* serialization. In the event that the entity to which the component is
* assigned doesn't exist yet, the loader will take care to create it with
* the version it originally had.
*
* @tparam Component Types of components to restore.
* @tparam Archive Type of input archive.
* @param archive A valid reference to an input archive.
* @return A valid loader to continue restoring data.
*/
template<typename... Component, typename Archive>
const basic_snapshot_loader &component(Archive &archive) const {
(assign<Component>(archive), ...);
return *this;
}
@@ -262,17 +240,12 @@ public:
* In case all the entities were serialized but only part of the components
* was saved, it could happen that some of the entities have no components
* once restored.<br/>
* This functions helps to identify and destroy those entities.
* This function helps to identify and destroy those entities.
*
* @return A valid loader to continue restoring data.
*/
const basic_snapshot_loader &orphans() const {
reg->each([this](const auto entt) {
if(reg->orphan(entt)) {
reg->release(entt);
}
});
basic_snapshot_loader &orphans() {
internal::orphans(*reg);
return *this;
}
@@ -290,7 +263,7 @@ private:
* Identifiers that entities originally had are not transferred to the target.
* Instead, the loader maps remote identifiers to local ones while restoring a
* snapshot.<br/>
* An example of use is the implementation of a client-server applications with
* An example of use is the implementation of a client-server application with
* the requirement of transferring somehow parts of the representation side to
* side.
*
@@ -298,29 +271,16 @@ private:
*/
template<typename Registry>
class basic_continuous_loader {
using entity_traits = entt_traits<typename Registry::entity_type>;
void destroy(typename Registry::entity_type entt) {
if(const auto it = remloc.find(entt); it == remloc.cend()) {
const auto local = reg->create();
remloc.emplace(entt, std::make_pair(local, true));
reg->destroy(local);
}
}
static_assert(!std::is_const_v<Registry>, "Non-const registry type required");
using traits_type = typename Registry::traits_type;
void restore(typename Registry::entity_type entt) {
const auto it = remloc.find(entt);
if(it == remloc.cend()) {
const auto local = reg->create();
remloc.emplace(entt, std::make_pair(local, true));
} else {
if(!reg->valid(remloc[entt].first)) {
remloc[entt].first = reg->create();
if(const auto entity = to_entity(entt); remloc.contains(entity) && remloc[entity].first == entt) {
if(!reg->valid(remloc[entity].second)) {
remloc[entity].second = reg->create();
}
// set the dirty flag
remloc[entt].second = true;
} else {
remloc.insert_or_assign(entity, std::make_pair(entt, reg->create()));
}
}
@@ -369,42 +329,6 @@ class basic_continuous_loader {
}
}
template<typename Component>
void remove_if_exists() {
for(auto &&ref: remloc) {
const auto local = ref.second.first;
if(reg->valid(local)) {
reg->template remove<Component>(local);
}
}
}
template<typename Component, typename Archive, typename... Other, typename... Member>
void assign(Archive &archive, [[maybe_unused]] Member Other::*...member) {
typename entity_traits::entity_type length{};
entity_type entt;
archive(length);
if constexpr(ignore_as_empty_v<Component>) {
while(length--) {
archive(entt);
restore(entt);
reg->template emplace_or_replace<Component>(map(entt));
}
} else {
Component instance;
while(length--) {
archive(entt, instance);
(update(instance, member), ...);
restore(entt);
reg->template emplace_or_replace<Component>(map(entt), std::move(instance));
}
}
}
public:
/*! Basic registry type. */
using registry_type = Registry;
@@ -416,7 +340,8 @@ public:
* @param source A valid reference to a registry.
*/
basic_continuous_loader(registry_type &source) noexcept
: reg{&source} {}
: remloc{source.get_allocator()},
reg{&source} {}
/*! @brief Default move constructor. */
basic_continuous_loader(basic_continuous_loader &&) = default;
@@ -425,87 +350,66 @@ public:
basic_continuous_loader &operator=(basic_continuous_loader &&) = default;
/**
* @brief Restores entities that were in use during serialization.
* @brief Restores all elements of a type with associated identifiers.
*
* This function restores the entities that were in use during serialization
* and creates local counterparts for them if required.
* It creates local counterparts for remote elements as needed.<br/>
* Members are either data members of type entity_type or containers of
* entities. In both cases, a loader visits them and replaces entities with
* their local counterpart.
*
* @tparam Type Type of elements to restore.
* @tparam Archive Type of input archive.
* @param archive A valid reference to an input archive.
* @return A non-const reference to this loader.
* @param id Optional name used to map the storage within the registry.
* @return A valid loader to continue restoring data.
*/
template<typename Archive>
basic_continuous_loader &entities(Archive &archive) {
typename entity_traits::entity_type length{};
entity_type entt{};
template<typename Type, typename Archive>
basic_continuous_loader &get(Archive &archive, const id_type id = type_hash<Type>::value()) {
auto &storage = reg->template storage<Type>(id);
typename traits_type::entity_type length{};
entity_type entt{null};
archive(length);
// discards the head of the list of destroyed entities
archive(entt);
for(std::size_t pos{}, last = length - 1u; pos < last; ++pos) {
archive(entt);
if constexpr(std::is_same_v<Type, entity_type>) {
typename traits_type::entity_type in_use{};
if(const auto entity = entity_traits::to_entity(entt); entity == pos) {
storage.reserve(length);
archive(in_use);
for(std::size_t pos{}; pos < in_use; ++pos) {
archive(entt);
restore(entt);
} else {
destroy(entt);
}
}
return *this;
}
for(std::size_t pos = in_use; pos < length; ++pos) {
archive(entt);
/**
* @brief Restores components and assigns them to the right entities.
*
* The template parameter list must be exactly the same used during
* serialization. In the event that the entity to which the component is
* assigned doesn't exist yet, the loader will take care to create a local
* counterpart for it.<br/>
* Members can be either data members of type entity_type or containers of
* entities. In both cases, the loader will visit them and update the
* entities by replacing each one with its local counterpart.
*
* @tparam Component Type of component to restore.
* @tparam Archive Type of input archive.
* @tparam Other Types of components to update with local counterparts.
* @tparam Member Types of members to update with their local counterparts.
* @param archive A valid reference to an input archive.
* @param member Members to update with their local counterparts.
* @return A non-const reference to this loader.
*/
template<typename... Component, typename Archive, typename... Other, typename... Member>
basic_continuous_loader &component(Archive &archive, Member Other::*...member) {
(remove_if_exists<Component>(), ...);
(assign<Component>(archive, member...), ...);
return *this;
}
if(const auto entity = to_entity(entt); remloc.contains(entity)) {
if(reg->valid(remloc[entity].second)) {
reg->destroy(remloc[entity].second);
}
/**
* @brief Helps to purge entities that no longer have a conterpart.
*
* Users should invoke this member function after restoring each snapshot,
* unless they know exactly what they are doing.
*
* @return A non-const reference to this loader.
*/
basic_continuous_loader &shrink() {
auto it = remloc.begin();
while(it != remloc.cend()) {
const auto local = it->second.first;
bool &dirty = it->second.second;
if(dirty) {
dirty = false;
++it;
} else {
if(reg->valid(local)) {
reg->destroy(local);
remloc.erase(entity);
}
}
} else {
for(auto &&ref: remloc) {
storage.remove(ref.second.second);
}
it = remloc.erase(it);
while(length--) {
if(archive(entt); entt != null) {
restore(entt);
if constexpr(std::tuple_size_v<decltype(storage.get_as_tuple({}))> == 0u) {
storage.emplace(map(entt));
} else {
Type elem{};
archive(elem);
storage.emplace(map(entt), std::move(elem));
}
}
}
}
@@ -518,17 +422,12 @@ public:
* In case all the entities were serialized but only part of the components
* was saved, it could happen that some of the entities have no components
* once restored.<br/>
* This functions helps to identify and destroy those entities.
* This function helps to identify and destroy those entities.
*
* @return A non-const reference to this loader.
*/
basic_continuous_loader &orphans() {
reg->each([this](const auto entt) {
if(reg->orphan(entt)) {
reg->release(entt);
}
});
internal::orphans(*reg);
return *this;
}
@@ -538,7 +437,8 @@ public:
* @return True if `entity` is managed by the loader, false otherwise.
*/
[[nodiscard]] bool contains(entity_type entt) const noexcept {
return (remloc.find(entt) != remloc.cend());
const auto it = remloc.find(to_entity(entt));
return it != remloc.cend() && it->second.first == entt;
}
/**
@@ -547,18 +447,15 @@ public:
* @return The local identifier if any, the null entity otherwise.
*/
[[nodiscard]] entity_type map(entity_type entt) const noexcept {
const auto it = remloc.find(entt);
entity_type other = null;
if(it != remloc.cend()) {
other = it->second.first;
if(const auto it = remloc.find(to_entity(entt)); it != remloc.cend() && it->second.first == entt) {
return it->second.second;
}
return other;
return null;
}
private:
dense_map<entity_type, std::pair<entity_type, bool>> remloc;
dense_map<typename traits_type::entity_type, std::pair<entity_type, entity_type>> remloc;
registry_type *reg;
};

View File

@@ -17,11 +17,7 @@
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename Container>
@@ -88,6 +84,10 @@ struct sparse_set_iterator final {
return *operator->();
}
[[nodiscard]] constexpr pointer data() const noexcept {
return packed ? packed->data() : nullptr;
}
[[nodiscard]] constexpr difference_type index() const noexcept {
return offset - 1;
}
@@ -97,55 +97,43 @@ private:
difference_type offset;
};
template<typename Type, typename Other>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) noexcept {
template<typename Container>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
return rhs.index() - lhs.index();
}
template<typename Type, typename Other>
[[nodiscard]] constexpr bool operator==(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) noexcept {
template<typename Container>
[[nodiscard]] constexpr bool operator==(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
return lhs.index() == rhs.index();
}
template<typename Type, typename Other>
[[nodiscard]] constexpr bool operator!=(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) noexcept {
template<typename Container>
[[nodiscard]] constexpr bool operator!=(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
return !(lhs == rhs);
}
template<typename Type, typename Other>
[[nodiscard]] constexpr bool operator<(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) noexcept {
template<typename Container>
[[nodiscard]] constexpr bool operator<(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
return lhs.index() > rhs.index();
}
template<typename Type, typename Other>
[[nodiscard]] constexpr bool operator>(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) noexcept {
return lhs.index() < rhs.index();
template<typename Container>
[[nodiscard]] constexpr bool operator>(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
return rhs < lhs;
}
template<typename Type, typename Other>
[[nodiscard]] constexpr bool operator<=(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) noexcept {
template<typename Container>
[[nodiscard]] constexpr bool operator<=(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
return !(lhs > rhs);
}
template<typename Type, typename Other>
[[nodiscard]] constexpr bool operator>=(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) noexcept {
template<typename Container>
[[nodiscard]] constexpr bool operator>=(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
return !(lhs < rhs);
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @brief Sparse set deletion policy. */
enum class deletion_policy : std::uint8_t {
/*! @brief Swap-and-pop deletion policy. */
swap_and_pop = 0u,
/*! @brief In-place deletion policy. */
in_place = 1u
};
/*! @endcond */
/**
* @brief Basic sparse set implementation.
@@ -154,10 +142,6 @@ enum class deletion_policy : std::uint8_t {
* Two arrays: an _external_ one and an _internal_ one; a _sparse_ one and a
* _packed_ one; one used for direct access through contiguous memory, the other
* one used to get the data through an extra level of indirection.<br/>
* This is largely used by the registry to offer users the fastest access ever
* to the components. Views and groups in general are almost entirely designed
* around sparse sets.
*
* This type of data structure is widely documented in the literature and on the
* web. This is nothing more than a customized implementation suitable for the
* purpose of the framework.
@@ -167,7 +151,7 @@ enum class deletion_policy : std::uint8_t {
* no guarantees that entities are returned in the insertion order when iterate
* a sparse set. Do not make assumption on the order in any case.
*
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Entity A valid entity type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Entity, typename Allocator>
@@ -176,37 +160,40 @@ class basic_sparse_set {
static_assert(std::is_same_v<typename alloc_traits::value_type, Entity>, "Invalid value type");
using sparse_container_type = std::vector<typename alloc_traits::pointer, typename alloc_traits::template rebind_alloc<typename alloc_traits::pointer>>;
using packed_container_type = std::vector<Entity, Allocator>;
using entity_traits = entt_traits<Entity>;
using underlying_type = typename entt_traits<Entity>::entity_type;
[[nodiscard]] auto sparse_ptr(const Entity entt) const {
const auto pos = static_cast<size_type>(entity_traits::to_entity(entt));
const auto page = pos / entity_traits::page_size;
return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, entity_traits::page_size)) : nullptr;
const auto pos = static_cast<size_type>(traits_type::to_entity(entt));
const auto page = pos / traits_type::page_size;
return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, traits_type::page_size)) : nullptr;
}
[[nodiscard]] auto &sparse_ref(const Entity entt) const {
ENTT_ASSERT(sparse_ptr(entt), "Invalid element");
const auto pos = static_cast<size_type>(entity_traits::to_entity(entt));
return sparse[pos / entity_traits::page_size][fast_mod(pos, entity_traits::page_size)];
const auto pos = static_cast<size_type>(traits_type::to_entity(entt));
return sparse[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)];
}
[[nodiscard]] auto to_iterator(const Entity entt) const {
return --(end() - index(entt));
}
[[nodiscard]] auto &assure_at_least(const Entity entt) {
const auto pos = static_cast<size_type>(entity_traits::to_entity(entt));
const auto page = pos / entity_traits::page_size;
const auto pos = static_cast<size_type>(traits_type::to_entity(entt));
const auto page = pos / traits_type::page_size;
if(!(page < sparse.size())) {
sparse.resize(page + 1u, nullptr);
}
if(!sparse[page]) {
constexpr entity_type init = null;
auto page_allocator{packed.get_allocator()};
sparse[page] = alloc_traits::allocate(page_allocator, entity_traits::page_size);
std::uninitialized_fill(sparse[page], sparse[page] + entity_traits::page_size, null);
sparse[page] = alloc_traits::allocate(page_allocator, traits_type::page_size);
std::uninitialized_fill(sparse[page], sparse[page] + traits_type::page_size, init);
}
auto &elem = sparse[page][fast_mod(pos, entity_traits::page_size)];
ENTT_ASSERT(elem == null, "Slot not available");
return elem;
return sparse[page][fast_mod(pos, traits_type::page_size)];
}
void release_sparse_pages() {
@@ -214,34 +201,60 @@ class basic_sparse_set {
for(auto &&page: sparse) {
if(page != nullptr) {
std::destroy(page, page + entity_traits::page_size);
alloc_traits::deallocate(page_allocator, page, entity_traits::page_size);
std::destroy(page, page + traits_type::page_size);
alloc_traits::deallocate(page_allocator, page, traits_type::page_size);
page = nullptr;
}
}
}
void swap_at(const std::size_t from, const std::size_t to) {
auto &lhs = packed[from];
auto &rhs = packed[to];
sparse_ref(lhs) = traits_type::combine(static_cast<typename traits_type::entity_type>(to), traits_type::to_integral(lhs));
sparse_ref(rhs) = traits_type::combine(static_cast<typename traits_type::entity_type>(from), traits_type::to_integral(rhs));
std::swap(lhs, rhs);
}
underlying_type policy_to_head() {
return traits_type::entity_mask * (mode != deletion_policy::swap_only);
}
private:
virtual const void *get_at(const std::size_t) const {
return nullptr;
}
virtual void swap_at(const std::size_t, const std::size_t) {}
virtual void move_element(const std::size_t, const std::size_t) {}
virtual void swap_or_move([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) {
ENTT_ASSERT((mode != deletion_policy::swap_only) || (((lhs < free_list()) + (rhs < free_list())) != 1u), "Cross swapping is not supported");
}
protected:
/*! @brief Random access iterator type. */
using basic_iterator = internal::sparse_set_iterator<packed_container_type>;
/**
* @brief Erases an entity from a sparse set.
* @param it An iterator to the element to pop.
*/
void swap_only(const basic_iterator it) {
ENTT_ASSERT(mode == deletion_policy::swap_only, "Deletion policy mismatch");
const auto pos = static_cast<underlying_type>(index(*it));
bump(traits_type::next(*it));
swap_at(pos, static_cast<size_type>(head -= (pos < head)));
}
/**
* @brief Erases an entity from a sparse set.
* @param it An iterator to the element to pop.
*/
void swap_and_pop(const basic_iterator it) {
ENTT_ASSERT(mode == deletion_policy::swap_and_pop, "Deletion policy mismatched");
ENTT_ASSERT(mode == deletion_policy::swap_and_pop, "Deletion policy mismatch");
auto &self = sparse_ref(*it);
const auto entt = entity_traits::to_entity(self);
sparse_ref(packed.back()) = entity_traits::combine(entt, entity_traits::to_integral(packed.back()));
const auto entt = traits_type::to_entity(self);
sparse_ref(packed.back()) = traits_type::combine(entt, traits_type::to_integral(packed.back()));
packed[static_cast<size_type>(entt)] = packed.back();
// unnecessary but it helps to detect nasty bugs
ENTT_ASSERT((packed.back() = null, true), "");
@@ -255,9 +268,9 @@ protected:
* @param it An iterator to the element to pop.
*/
void in_place_pop(const basic_iterator it) {
ENTT_ASSERT(mode == deletion_policy::in_place, "Deletion policy mismatched");
const auto entt = entity_traits::to_entity(std::exchange(sparse_ref(*it), null));
packed[static_cast<size_type>(entt)] = std::exchange(free_list, entity_traits::combine(entt, entity_traits::reserved));
ENTT_ASSERT(mode == deletion_policy::in_place, "Deletion policy mismatch");
const auto entt = traits_type::to_entity(std::exchange(sparse_ref(*it), null));
packed[static_cast<size_type>(entt)] = traits_type::combine(std::exchange(head, entt), tombstone);
}
protected:
@@ -267,17 +280,50 @@ protected:
* @param last An iterator past the last element of the range of entities.
*/
virtual void pop(basic_iterator first, basic_iterator last) {
if(mode == deletion_policy::swap_and_pop) {
switch(mode) {
case deletion_policy::swap_and_pop:
for(; first != last; ++first) {
swap_and_pop(first);
}
} else {
break;
case deletion_policy::in_place:
for(; first != last; ++first) {
in_place_pop(first);
}
break;
case deletion_policy::swap_only:
for(; first != last; ++first) {
swap_only(first);
}
break;
}
}
/*! @brief Erases all entities of a sparse set. */
virtual void pop_all() {
switch(mode) {
case deletion_policy::in_place:
if(head != traits_type::to_entity(null)) {
for(auto first = begin(); !(first.index() < 0); ++first) {
if(*first != tombstone) {
sparse_ref(*first) = null;
}
}
break;
}
[[fallthrough]];
case deletion_policy::swap_only:
case deletion_policy::swap_and_pop:
for(auto first = begin(); !(first.index() < 0); ++first) {
sparse_ref(*first) = null;
}
break;
}
head = policy_to_head();
packed.clear();
}
/**
* @brief Assigns an entity to a sparse set.
* @param entt A valid identifier.
@@ -285,29 +331,55 @@ protected:
* @return Iterator pointing to the emplaced element.
*/
virtual basic_iterator try_emplace(const Entity entt, const bool force_back, const void * = nullptr) {
ENTT_ASSERT(!contains(entt), "Set already contains entity");
auto &elem = assure_at_least(entt);
auto pos = size();
if(auto &elem = assure_at_least(entt); free_list == null || force_back) {
switch(mode) {
case deletion_policy::in_place:
if(head != traits_type::to_entity(null) && !force_back) {
pos = static_cast<size_type>(head);
ENTT_ASSERT(elem == null, "Slot not available");
elem = traits_type::combine(head, traits_type::to_integral(entt));
head = traits_type::to_entity(std::exchange(packed[pos], entt));
break;
}
[[fallthrough]];
case deletion_policy::swap_and_pop:
packed.push_back(entt);
elem = entity_traits::combine(static_cast<typename entity_traits::entity_type>(packed.size() - 1u), entity_traits::to_integral(entt));
return begin();
} else {
const auto pos = static_cast<size_type>(entity_traits::to_entity(free_list));
elem = entity_traits::combine(entity_traits::to_integral(free_list), entity_traits::to_integral(entt));
free_list = std::exchange(packed[pos], entt);
return --(end() - pos);
ENTT_ASSERT(elem == null, "Slot not available");
elem = traits_type::combine(static_cast<typename traits_type::entity_type>(packed.size() - 1u), traits_type::to_integral(entt));
break;
case deletion_policy::swap_only:
if(elem == null) {
packed.push_back(entt);
elem = traits_type::combine(static_cast<typename traits_type::entity_type>(packed.size() - 1u), traits_type::to_integral(entt));
} else {
ENTT_ASSERT(!(traits_type::to_entity(elem) < head), "Slot not available");
bump(entt);
}
if(force_back) {
pos = static_cast<size_type>(head++);
swap_at(static_cast<size_type>(traits_type::to_entity(elem)), pos);
}
break;
}
return --(end() - pos);
}
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Entity traits. */
using traits_type = entt_traits<Entity>;
/*! @brief Underlying entity identifier. */
using entity_type = typename entity_traits::value_type;
using entity_type = typename traits_type::value_type;
/*! @brief Underlying version type. */
using version_type = typename entity_traits::version_type;
using version_type = typename traits_type::version_type;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Pointer type to contained entities. */
using pointer = typename packed_container_type::const_pointer;
/*! @brief Random access iterator type. */
@@ -317,7 +389,7 @@ public:
/*! @brief Reverse iterator type. */
using reverse_iterator = std::reverse_iterator<iterator>;
/*! @brief Constant reverse iterator type. */
using const_reverse_iterator = reverse_iterator;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
/*! @brief Default constructor. */
basic_sparse_set()
@@ -341,16 +413,16 @@ public:
/**
* @brief Constructs an empty container with the given value type, policy
* and allocator.
* @param value Returned value type, if any.
* @param elem Returned value type, if any.
* @param pol Type of deletion policy.
* @param allocator The allocator to use (possibly default-constructed).
*/
explicit basic_sparse_set(const type_info &value, deletion_policy pol = deletion_policy::swap_and_pop, const allocator_type &allocator = {})
explicit basic_sparse_set(const type_info &elem, deletion_policy pol = deletion_policy::swap_and_pop, const allocator_type &allocator = {})
: sparse{allocator},
packed{allocator},
info{&value},
free_list{tombstone},
mode{pol} {}
info{&elem},
mode{pol},
head{policy_to_head()} {}
/**
* @brief Move constructor.
@@ -360,8 +432,8 @@ public:
: sparse{std::move(other.sparse)},
packed{std::move(other.packed)},
info{other.info},
free_list{std::exchange(other.free_list, tombstone)},
mode{other.mode} {}
mode{other.mode},
head{std::exchange(other.head, policy_to_head())} {}
/**
* @brief Allocator-extended move constructor.
@@ -372,8 +444,8 @@ public:
: sparse{std::move(other.sparse), allocator},
packed{std::move(other.packed), allocator},
info{other.info},
free_list{std::exchange(other.free_list, tombstone)},
mode{other.mode} {
mode{other.mode},
head{std::exchange(other.head, policy_to_head())} {
ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed");
}
@@ -394,8 +466,8 @@ public:
sparse = std::move(other.sparse);
packed = std::move(other.packed);
info = other.info;
free_list = std::exchange(other.free_list, tombstone);
mode = other.mode;
head = std::exchange(other.head, policy_to_head());
return *this;
}
@@ -408,8 +480,8 @@ public:
swap(sparse, other.sparse);
swap(packed, other.packed);
swap(info, other.info);
swap(free_list, other.free_list);
swap(mode, other.mode);
swap(head, other.head);
}
/**
@@ -428,6 +500,23 @@ public:
return mode;
}
/**
* @brief Returns the head of the free list, if any.
* @return The head of the free list.
*/
[[nodiscard]] size_type free_list() const noexcept {
return static_cast<size_type>(head);
}
/**
* @brief Sets the head of the free list, if possible.
* @param len The value to use as the new head of the free list.
*/
void free_list(const size_type len) noexcept {
ENTT_ASSERT((mode == deletion_policy::swap_only) && !(len > packed.size()), "Invalid value");
head = static_cast<underlying_type>(len);
}
/**
* @brief Increases the capacity of a sparse set.
*
@@ -465,7 +554,7 @@ public:
* @return Extent of the sparse set.
*/
[[nodiscard]] size_type extent() const noexcept {
return sparse.size() * entity_traits::page_size;
return sparse.size() * traits_type::page_size;
}
/**
@@ -490,6 +579,14 @@ public:
return packed.empty();
}
/**
* @brief Checks whether a sparse set is fully packed.
* @return True if the sparse set is fully packed, false otherwise.
*/
[[nodiscard]] bool contiguous() const noexcept {
return (mode != deletion_policy::in_place) || (head == traits_type::to_entity(null));
}
/**
* @brief Direct access to the internal packed array.
* @return A pointer to the internal packed array.
@@ -501,13 +598,12 @@ public:
/**
* @brief Returns an iterator to the beginning.
*
* The returned iterator points to the first entity of the internal packed
* array. If the sparse set is empty, the returned iterator will be equal to
* If the sparse set is empty, the returned iterator will be equal to
* `end()`.
*
* @return An iterator to the first entity of the sparse set.
*/
[[nodiscard]] const_iterator begin() const noexcept {
[[nodiscard]] iterator begin() const noexcept {
const auto pos = static_cast<typename iterator::difference_type>(packed.size());
return iterator{packed, pos};
}
@@ -519,11 +615,6 @@ public:
/**
* @brief Returns an iterator to the end.
*
* The returned iterator points to the element following the last entity in
* a sparse set. Attempting to dereference the returned iterator results in
* undefined behavior.
*
* @return An iterator to the element following the last entity of a sparse
* set.
*/
@@ -539,14 +630,13 @@ public:
/**
* @brief Returns a reverse iterator to the beginning.
*
* The returned iterator points to the first entity of the reversed internal
* packed array. If the sparse set is empty, the returned iterator will be
* equal to `rend()`.
* If the sparse set is empty, the returned iterator will be equal to
* `rend()`.
*
* @return An iterator to the first entity of the reversed internal packed
* array.
*/
[[nodiscard]] const_reverse_iterator rbegin() const noexcept {
[[nodiscard]] reverse_iterator rbegin() const noexcept {
return std::make_reverse_iterator(end());
}
@@ -557,11 +647,6 @@ public:
/**
* @brief Returns a reverse iterator to the end.
*
* The returned iterator points to the element following the last entity in
* the reversed sparse set. Attempting to dereference the returned iterator
* results in undefined behavior.
*
* @return An iterator to the element following the last entity of the
* reversed sparse set.
*/
@@ -574,14 +659,54 @@ public:
return rend();
}
/*! @copydoc begin Useful only in case of swap-only policy. */
[[nodiscard]] iterator begin(int) const noexcept {
return (mode == deletion_policy::swap_only) ? (end() - static_cast<typename iterator::difference_type>(head)) : begin();
}
/*! @copydoc cbegin Useful only in case of swap-only policy. */
[[nodiscard]] const_iterator cbegin(int) const noexcept {
return begin(0);
}
/*! @copydoc end Useful only in case of swap-only policy. */
[[nodiscard]] iterator end(int) const noexcept {
return end();
}
/*! @copydoc cend Useful only in case of swap-only policy. */
[[nodiscard]] const_iterator cend(int) const noexcept {
return end(0);
}
/*! @copydoc rbegin Useful only in case of swap-only policy. */
[[nodiscard]] reverse_iterator rbegin(int) const noexcept {
return std::make_reverse_iterator(end(0));
}
/*! @copydoc rbegin Useful only in case of swap-only policy. */
[[nodiscard]] const_reverse_iterator crbegin(int) const noexcept {
return rbegin(0);
}
/*! @copydoc rbegin Useful only in case of swap-only policy. */
[[nodiscard]] reverse_iterator rend(int) const noexcept {
return std::make_reverse_iterator(begin(0));
}
/*! @copydoc rbegin Useful only in case of swap-only policy. */
[[nodiscard]] const_reverse_iterator crend(int) const noexcept {
return rend(0);
}
/**
* @brief Finds an entity.
* @param entt A valid identifier.
* @return An iterator to the given entity if it's found, past the end
* iterator otherwise.
*/
[[nodiscard]] iterator find(const entity_type entt) const noexcept {
return contains(entt) ? --(end() - index(entt)) : end();
[[nodiscard]] const_iterator find(const entity_type entt) const noexcept {
return contains(entt) ? to_iterator(entt) : end();
}
/**
@@ -591,9 +716,10 @@ public:
*/
[[nodiscard]] bool contains(const entity_type entt) const noexcept {
const auto elem = sparse_ptr(entt);
constexpr auto cap = entity_traits::to_entity(null);
constexpr auto cap = traits_type::entity_mask;
constexpr auto mask = traits_type::to_integral(null) & ~cap;
// testing versions permits to avoid accessing the packed array
return elem && (((~cap & entity_traits::to_integral(entt)) ^ entity_traits::to_integral(*elem)) < cap);
return elem && (((mask & traits_type::to_integral(entt)) ^ traits_type::to_integral(*elem)) < cap);
}
/**
@@ -604,8 +730,8 @@ public:
*/
[[nodiscard]] version_type current(const entity_type entt) const noexcept {
const auto elem = sparse_ptr(entt);
constexpr auto fallback = entity_traits::to_version(tombstone);
return elem ? entity_traits::to_version(*elem) : fallback;
constexpr auto fallback = traits_type::to_version(tombstone);
return elem ? traits_type::to_version(*elem) : fallback;
}
/**
@@ -620,7 +746,7 @@ public:
*/
[[nodiscard]] size_type index(const entity_type entt) const noexcept {
ENTT_ASSERT(contains(entt), "Set does not contain entity");
return static_cast<size_type>(entity_traits::to_entity(sparse_ref(entt)));
return static_cast<size_type>(traits_type::to_entity(sparse_ref(entt)));
}
/**
@@ -628,7 +754,7 @@ public:
* @param pos The position for which to return the entity.
* @return The entity at specified location if any, a null entity otherwise.
*/
[[nodiscard]] entity_type at(const size_type pos) const noexcept {
[[deprecated("use .begin()[pos] instead")]] [[nodiscard]] entity_type at(const size_type pos) const noexcept {
return pos < packed.size() ? packed[pos] : null;
}
@@ -652,13 +778,13 @@ public:
* @param entt A valid identifier.
* @return An opaque pointer to the element assigned to the entity, if any.
*/
[[nodiscard]] const void *get(const entity_type entt) const noexcept {
[[nodiscard]] const void *value(const entity_type entt) const noexcept {
return get_at(index(entt));
}
/*! @copydoc get */
[[nodiscard]] void *get(const entity_type entt) noexcept {
return const_cast<void *>(std::as_const(*this).get(entt));
/*! @copydoc value */
[[nodiscard]] void *value(const entity_type entt) noexcept {
return const_cast<void *>(std::as_const(*this).value(entt));
}
/**
@@ -669,28 +795,12 @@ public:
* results in undefined behavior.
*
* @param entt A valid identifier.
* @param value Optional opaque value to forward to mixins, if any.
* @param elem Optional opaque element to forward to mixins, if any.
* @return Iterator pointing to the emplaced element in case of success, the
* `end()` iterator otherwise.
*/
iterator emplace(const entity_type entt, const void *value = nullptr) {
return try_emplace(entt, false, value);
}
/**
* @brief Bump the version number of an entity.
*
* @warning
* Attempting to bump the version of an entity that doesn't belong to the
* sparse set results in undefined behavior.
*
* @param entt A valid identifier.
*/
void bump(const entity_type entt) {
auto &entity = sparse_ref(entt);
ENTT_ASSERT(entt != tombstone && entity != null, "Cannot set the required version");
entity = entity_traits::combine(entity_traits::to_integral(entity), entity_traits::to_integral(entt));
packed[static_cast<size_type>(entity_traits::to_entity(entity))] = entt;
iterator push(const entity_type entt, const void *elem = nullptr) {
return try_emplace(entt, (mode == deletion_policy::swap_only), elem);
}
/**
@@ -707,7 +817,7 @@ public:
* success, the `end()` iterator otherwise.
*/
template<typename It>
iterator insert(It first, It last) {
iterator push(It first, It last) {
for(auto it = first; it != last; ++it) {
try_emplace(*it, true);
}
@@ -715,6 +825,24 @@ public:
return first == last ? end() : find(*first);
}
/**
* @brief Bump the version number of an entity.
*
* @warning
* Attempting to bump the version of an entity that doesn't belong to the
* sparse set results in undefined behavior.
*
* @param entt A valid identifier.
* @return The version of the given identifier.
*/
version_type bump(const entity_type entt) {
auto &entity = sparse_ref(entt);
ENTT_ASSERT(entt != tombstone && entity != null, "Cannot set the required version");
entity = traits_type::combine(traits_type::to_integral(entity), traits_type::to_integral(entt));
packed[static_cast<size_type>(traits_type::to_entity(entity))] = entt;
return traits_type::to_version(entt);
}
/**
* @brief Erases an entity from a sparse set.
*
@@ -725,7 +853,7 @@ public:
* @param entt A valid identifier.
*/
void erase(const entity_type entt) {
const auto it = --(end() - index(entt));
const auto it = to_iterator(entt);
pop(it, it + 1u);
}
@@ -769,35 +897,52 @@ public:
size_type remove(It first, It last) {
size_type count{};
for(; first != last; ++first) {
count += remove(*first);
if constexpr(std::is_same_v<It, basic_iterator>) {
while(first != last) {
while(first != last && !contains(*first)) {
++first;
}
const auto it = first;
while(first != last && contains(*first)) {
++first;
}
count += std::distance(it, first);
erase(it, first);
}
} else {
for(; first != last; ++first) {
count += remove(*first);
}
}
return count;
}
/*! @brief Removes all tombstones from the packed array of a sparse set. */
/*! @brief Removes all tombstones from a sparse set. */
void compact() {
size_type from = packed.size();
for(; from && packed[from - 1u] == tombstone; --from) {}
if(mode == deletion_policy::in_place) {
size_type from = packed.size();
for(; from && packed[from - 1u] == tombstone; --from) {}
underlying_type pos = std::exchange(head, traits_type::entity_mask);
for(auto *it = &free_list; *it != null && from; it = std::addressof(packed[entity_traits::to_entity(*it)])) {
if(const size_type to = entity_traits::to_entity(*it); to < from) {
--from;
move_element(from, to);
while(pos != traits_type::to_entity(null)) {
if(const auto to = static_cast<size_type>(std::exchange(pos, traits_type::to_entity(packed[pos]))); to < from) {
--from;
swap_or_move(from, to);
using std::swap;
swap(packed[from], packed[to]);
packed[to] = packed[from];
const auto entity = static_cast<typename traits_type::entity_type>(to);
sparse_ref(packed[to]) = traits_type::combine(entity, traits_type::to_integral(packed[to]));
const auto entity = static_cast<typename entity_traits::entity_type>(to);
sparse_ref(packed[to]) = entity_traits::combine(entity, entity_traits::to_integral(packed[to]));
*it = entity_traits::combine(static_cast<typename entity_traits::entity_type>(from), entity_traits::reserved);
for(; from && packed[from - 1u] == tombstone; --from) {}
for(; from && packed[from - 1u] == tombstone; --from) {}
}
}
}
free_list = tombstone;
packed.resize(from);
packed.erase(packed.begin() + from, packed.end());
}
}
/**
@@ -814,21 +959,12 @@ public:
* @param rhs A valid identifier.
*/
void swap_elements(const entity_type lhs, const entity_type rhs) {
ENTT_ASSERT(contains(lhs) && contains(rhs), "Set does not contain entities");
const auto from = index(lhs);
const auto to = index(rhs);
auto &entt = sparse_ref(lhs);
auto &other = sparse_ref(rhs);
const auto from = entity_traits::to_entity(entt);
const auto to = entity_traits::to_entity(other);
// basic no-leak guarantee (with invalid state) if swapping throws
swap_at(static_cast<size_type>(from), static_cast<size_type>(to));
entt = entity_traits::combine(to, entity_traits::to_integral(packed[from]));
other = entity_traits::combine(from, entity_traits::to_integral(packed[to]));
using std::swap;
swap(packed[from], packed[to]);
// basic no-leak guarantee if swapping throws
swap_or_move(from, to);
swap_at(from, to);
}
/**
@@ -863,8 +999,8 @@ public:
*/
template<typename Compare, typename Sort = std_sort, typename... Args>
void sort_n(const size_type length, Compare compare, Sort algo = Sort{}, Args &&...args) {
ENTT_ASSERT((mode != deletion_policy::in_place) || (head == traits_type::to_entity(null)), "Sorting with tombstones not allowed");
ENTT_ASSERT(!(length > packed.size()), "Length exceeds the number of elements");
ENTT_ASSERT(free_list == null, "Partial sorting with tombstones is not supported");
algo(packed.rend() - length, packed.rend(), std::move(compare), std::forward<Args>(args)...);
@@ -876,9 +1012,9 @@ public:
const auto idx = index(packed[next]);
const auto entt = packed[curr];
swap_at(next, idx);
const auto entity = static_cast<typename entity_traits::entity_type>(curr);
sparse_ref(entt) = entity_traits::combine(entity, entity_traits::to_integral(packed[curr]));
swap_or_move(next, idx);
const auto entity = static_cast<typename traits_type::entity_type>(curr);
sparse_ref(entt) = traits_type::combine(entity, traits_type::to_integral(packed[curr]));
curr = std::exchange(next, idx);
}
}
@@ -898,57 +1034,51 @@ public:
*/
template<typename Compare, typename Sort = std_sort, typename... Args>
void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
compact();
sort_n(packed.size(), std::move(compare), std::move(algo), std::forward<Args>(args)...);
sort_n(static_cast<size_type>(end(0) - begin(0)), std::move(compare), std::move(algo), std::forward<Args>(args)...);
}
/**
* @brief Sort entities according to their order in another sparse set.
* @brief Sort entities according to their order in a range.
*
* Entities that are part of both the sparse sets are ordered internally
* according to the order they have in `other`. All the other entities goes
* to the end of the list and there are no guarantees on their order.<br/>
* In other terms, this function can be used to impose the same order on two
* sets by using one of them as a master and the other one as a slave.
* Entities that are part of both the sparse set and the range are ordered
* internally according to the order they have in the range.<br/>
* All other entities goes to the end of the sparse set and there are no
* guarantees on their order.
*
* Iterating the sparse set with a couple of iterators returns elements in
* the expected order after a call to `respect`. See `begin` and `end` for
* more details.
*
* @param other The sparse sets that imposes the order of the entities.
* @tparam It Type of input iterator.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
*/
void respect(const basic_sparse_set &other) {
compact();
template<typename It>
void sort_as(It first, It last) {
ENTT_ASSERT((mode != deletion_policy::in_place) || (head == traits_type::to_entity(null)), "Sorting with tombstones not allowed");
const auto to = other.end();
auto from = other.begin();
for(size_type pos = packed.size() - 1; pos && from != to; ++from) {
if(contains(*from)) {
if(*from != packed[pos]) {
for(auto it = begin(0); it.index() && first != last; ++first) {
if(const auto curr = *first; contains(curr)) {
if(const auto entt = *it; entt != curr) {
// basic no-leak guarantee (with invalid state) if swapping throws
swap_elements(packed[pos], *from);
swap_elements(entt, curr);
}
--pos;
++it;
}
}
}
/**
* @copybrief sort_as
* @param other The sparse sets that imposes the order of the entities.
*/
[[deprecated("use iterator based sort_as instead")]] void sort_as(const basic_sparse_set &other) {
sort_as(other.begin(), other.end());
}
/*! @brief Clears a sparse set. */
void clear() {
if(const auto last = end(); free_list == null) {
pop(begin(), last);
} else {
for(auto &&entity: *this) {
// tombstone filter on itself
if(const auto it = find(entity); it != last) {
pop(it, it + 1u);
}
}
}
free_list = tombstone;
pop_all();
// sanity check to avoid subtle issues due to storage classes
ENTT_ASSERT((compact(), size()) == 0u, "Non-empty set");
head = policy_to_head();
packed.clear();
}
@@ -967,8 +1097,8 @@ private:
sparse_container_type sparse;
packed_container_type packed;
const type_info *info;
entity_type free_list;
deletion_policy mode;
underlying_type head;
};
} // namespace entt

View File

@@ -9,7 +9,6 @@
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../core/compressed_pair.hpp"
#include "../core/iterator.hpp"
#include "../core/memory.hpp"
#include "../core/type_info.hpp"
@@ -17,24 +16,18 @@
#include "entity.hpp"
#include "fwd.hpp"
#include "sparse_set.hpp"
#include "storage_mixin.hpp"
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename Container>
template<typename Container, std::size_t Size>
class storage_iterator final {
friend storage_iterator<const Container>;
friend storage_iterator<const Container, Size>;
using container_type = std::remove_const_t<Container>;
using alloc_traits = std::allocator_traits<typename container_type::allocator_type>;
using comp_traits = component_traits<std::remove_pointer_t<typename container_type::value_type>>;
using iterator_traits = std::iterator_traits<std::conditional_t<
std::is_const_v<Container>,
@@ -51,12 +44,12 @@ public:
constexpr storage_iterator() noexcept = default;
constexpr storage_iterator(Container *ref, const difference_type idx) noexcept
: packed{ref},
: payload{ref},
offset{idx} {}
template<bool Const = std::is_const_v<Container>, typename = std::enable_if_t<Const>>
constexpr storage_iterator(const storage_iterator<std::remove_const_t<Container>> &other) noexcept
: storage_iterator{other.packed, other.offset} {}
constexpr storage_iterator(const storage_iterator<std::remove_const_t<Container>, Size> &other) noexcept
: storage_iterator{other.payload, other.offset} {}
constexpr storage_iterator &operator++() noexcept {
return --offset, *this;
@@ -96,12 +89,12 @@ public:
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
const auto pos = index() - value;
return (*packed)[pos / comp_traits::page_size][fast_mod(pos, comp_traits::page_size)];
return (*payload)[pos / Size][fast_mod(pos, Size)];
}
[[nodiscard]] constexpr pointer operator->() const noexcept {
const auto pos = index();
return (*packed)[pos / comp_traits::page_size] + fast_mod(pos, comp_traits::page_size);
return (*payload)[pos / Size] + fast_mod(pos, Size);
}
[[nodiscard]] constexpr reference operator*() const noexcept {
@@ -113,42 +106,42 @@ public:
}
private:
Container *packed;
Container *payload;
difference_type offset;
};
template<typename CLhs, typename CRhs>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
return rhs.index() - lhs.index();
}
template<typename CLhs, typename CRhs>
[[nodiscard]] constexpr bool operator==(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator==(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
return lhs.index() == rhs.index();
}
template<typename CLhs, typename CRhs>
[[nodiscard]] constexpr bool operator!=(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator!=(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
return !(lhs == rhs);
}
template<typename CLhs, typename CRhs>
[[nodiscard]] constexpr bool operator<(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator<(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
return lhs.index() > rhs.index();
}
template<typename CLhs, typename CRhs>
[[nodiscard]] constexpr bool operator>(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) noexcept {
return lhs.index() < rhs.index();
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator>(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
return rhs < lhs;
}
template<typename CLhs, typename CRhs>
[[nodiscard]] constexpr bool operator<=(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator<=(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
return !(lhs > rhs);
}
template<typename CLhs, typename CRhs>
[[nodiscard]] constexpr bool operator>=(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator>=(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
return !(lhs < rhs);
}
@@ -158,16 +151,18 @@ class extended_storage_iterator final {
friend class extended_storage_iterator;
public:
using iterator_type = It;
using difference_type = std::ptrdiff_t;
using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::forward_as_tuple(*std::declval<Other>()...)));
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
using iterator_concept = std::forward_iterator_tag;
constexpr extended_storage_iterator()
: it{} {}
constexpr extended_storage_iterator(It base, Other... other)
constexpr extended_storage_iterator(iterator_type base, Other... other)
: it{base, other...} {}
template<typename... Args, typename = std::enable_if_t<(!std::is_same_v<Other, Args> && ...) && (std::is_constructible_v<Other, Args> && ...)>>
@@ -191,29 +186,29 @@ public:
return {*std::get<It>(it), *std::get<Other>(it)...};
}
template<typename... CLhs, typename... CRhs>
friend constexpr bool operator==(const extended_storage_iterator<CLhs...> &, const extended_storage_iterator<CRhs...> &) noexcept;
[[nodiscard]] constexpr iterator_type base() const noexcept {
return std::get<It>(it);
}
template<typename... Lhs, typename... Rhs>
friend constexpr bool operator==(const extended_storage_iterator<Lhs...> &, const extended_storage_iterator<Rhs...> &) noexcept;
private:
std::tuple<It, Other...> it;
};
template<typename... CLhs, typename... CRhs>
[[nodiscard]] constexpr bool operator==(const extended_storage_iterator<CLhs...> &lhs, const extended_storage_iterator<CRhs...> &rhs) noexcept {
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator==(const extended_storage_iterator<Lhs...> &lhs, const extended_storage_iterator<Rhs...> &rhs) noexcept {
return std::get<0>(lhs.it) == std::get<0>(rhs.it);
}
template<typename... CLhs, typename... CRhs>
[[nodiscard]] constexpr bool operator!=(const extended_storage_iterator<CLhs...> &lhs, const extended_storage_iterator<CRhs...> &rhs) noexcept {
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator!=(const extended_storage_iterator<Lhs...> &lhs, const extended_storage_iterator<Rhs...> &rhs) noexcept {
return !(lhs == rhs);
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
/**
* @brief Basic storage implementation.
@@ -227,43 +222,41 @@ template<typename... CLhs, typename... CRhs>
* normally available for non-empty types will not be available for empty ones.
*
* @tparam Type Type of objects assigned to the entities.
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Entity A valid entity type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Type, typename Entity, typename Allocator, typename>
class basic_storage: public basic_sparse_set<Entity, typename std::allocator_traits<Allocator>::template rebind_alloc<Entity>> {
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
using underlying_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
using container_type = std::vector<typename alloc_traits::pointer, typename alloc_traits::template rebind_alloc<typename alloc_traits::pointer>>;
using comp_traits = component_traits<Type>;
static constexpr bool is_pinned_type_v = !(std::is_move_constructible_v<Type> && std::is_move_assignable_v<Type>);
using underlying_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
using underlying_iterator = typename underlying_type::basic_iterator;
[[nodiscard]] auto &element_at(const std::size_t pos) const {
return packed.first()[pos / comp_traits::page_size][fast_mod(pos, comp_traits::page_size)];
return payload[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)];
}
auto assure_at_least(const std::size_t pos) {
auto &&container = packed.first();
const auto idx = pos / comp_traits::page_size;
const auto idx = pos / traits_type::page_size;
if(!(idx < container.size())) {
auto curr = container.size();
container.resize(idx + 1u, nullptr);
if(!(idx < payload.size())) {
auto curr = payload.size();
allocator_type allocator{get_allocator()};
payload.resize(idx + 1u, nullptr);
ENTT_TRY {
for(const auto last = container.size(); curr < last; ++curr) {
container[curr] = alloc_traits::allocate(packed.second(), comp_traits::page_size);
for(const auto last = payload.size(); curr < last; ++curr) {
payload[curr] = alloc_traits::allocate(allocator, traits_type::page_size);
}
}
ENTT_CATCH {
container.resize(curr);
payload.resize(curr);
ENTT_THROW;
}
}
return container[idx] + fast_mod(pos, comp_traits::page_size);
return payload[idx] + fast_mod(pos, traits_type::page_size);
}
template<typename... Args>
@@ -272,7 +265,7 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
ENTT_TRY {
auto elem = assure_at_least(static_cast<size_type>(it.index()));
entt::uninitialized_construct_using_allocator(to_address(elem), packed.second(), std::forward<Args>(args)...);
entt::uninitialized_construct_using_allocator(to_address(elem), get_allocator(), std::forward<Args>(args)...);
}
ENTT_CATCH {
base_type::pop(it, it + 1u);
@@ -283,25 +276,24 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
}
void shrink_to_size(const std::size_t sz) {
const auto from = (sz + traits_type::page_size - 1u) / traits_type::page_size;
allocator_type allocator{get_allocator()};
for(auto pos = sz, length = base_type::size(); pos < length; ++pos) {
if constexpr(comp_traits::in_place_delete) {
if(base_type::at(pos) != tombstone) {
std::destroy_at(std::addressof(element_at(pos)));
if constexpr(traits_type::in_place_delete) {
if(base_type::data()[pos] != tombstone) {
alloc_traits::destroy(allocator, std::addressof(element_at(pos)));
}
} else {
std::destroy_at(std::addressof(element_at(pos)));
alloc_traits::destroy(allocator, std::addressof(element_at(pos)));
}
}
auto &&container = packed.first();
auto page_allocator{packed.second()};
const auto from = (sz + comp_traits::page_size - 1u) / comp_traits::page_size;
for(auto pos = from, last = container.size(); pos < last; ++pos) {
alloc_traits::deallocate(page_allocator, container[pos], comp_traits::page_size);
for(auto pos = from, last = payload.size(); pos < last; ++pos) {
alloc_traits::deallocate(allocator, payload[pos], traits_type::page_size);
}
container.resize(from);
payload.resize(from);
}
private:
@@ -309,54 +301,69 @@ private:
return std::addressof(element_at(pos));
}
void swap_at([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) final {
// use a runtime value to avoid compile-time suppression that drives the code coverage tool crazy
ENTT_ASSERT((lhs + 1u) && !is_pinned_type_v, "Pinned type");
if constexpr(!is_pinned_type_v) {
using std::swap;
swap(element_at(lhs), element_at(rhs));
}
}
void move_element([[maybe_unused]] const std::size_t from, [[maybe_unused]] const std::size_t to) final {
void swap_or_move([[maybe_unused]] const std::size_t from, [[maybe_unused]] const std::size_t to) override {
static constexpr bool is_pinned_type_v = !(std::is_move_constructible_v<Type> && std::is_move_assignable_v<Type>);
// use a runtime value to avoid compile-time suppression that drives the code coverage tool crazy
ENTT_ASSERT((from + 1u) && !is_pinned_type_v, "Pinned type");
if constexpr(!is_pinned_type_v) {
auto &elem = element_at(from);
entt::uninitialized_construct_using_allocator(to_address(assure_at_least(to)), packed.second(), std::move(elem));
std::destroy_at(std::addressof(elem));
if constexpr(traits_type::in_place_delete) {
if(base_type::operator[](to) == tombstone) {
allocator_type allocator{get_allocator()};
entt::uninitialized_construct_using_allocator(to_address(assure_at_least(to)), allocator, std::move(elem));
alloc_traits::destroy(allocator, std::addressof(elem));
return;
}
}
using std::swap;
swap(elem, element_at(to));
}
}
protected:
/*! @brief Random access iterator type. */
using basic_iterator = typename underlying_type::basic_iterator;
/**
* @brief Erases entities from a sparse set.
* @brief Erases entities from a storage.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
*/
void pop(basic_iterator first, basic_iterator last) override {
for(; first != last; ++first) {
void pop(underlying_iterator first, underlying_iterator last) override {
for(allocator_type allocator{get_allocator()}; first != last; ++first) {
// cannot use first.index() because it would break with cross iterators
auto &elem = element_at(base_type::index(*first));
if constexpr(comp_traits::in_place_delete) {
if constexpr(traits_type::in_place_delete) {
base_type::in_place_pop(first);
std::destroy_at(std::addressof(elem));
alloc_traits::destroy(allocator, std::addressof(elem));
} else {
auto &other = element_at(base_type::size() - 1u);
// destroying on exit allows reentrant destructors
[[maybe_unused]] auto unused = std::exchange(elem, std::move(other));
std::destroy_at(std::addressof(other));
alloc_traits::destroy(allocator, std::addressof(other));
base_type::swap_and_pop(first);
}
}
}
/*! @brief Erases all entities of a storage. */
void pop_all() override {
allocator_type allocator{get_allocator()};
for(auto first = base_type::begin(); !(first.index() < 0); ++first) {
if constexpr(traits_type::in_place_delete) {
if(*first != tombstone) {
base_type::in_place_pop(first);
alloc_traits::destroy(allocator, std::addressof(element_at(static_cast<size_type>(first.index()))));
}
} else {
base_type::swap_and_pop(first);
alloc_traits::destroy(allocator, std::addressof(element_at(static_cast<size_type>(first.index()))));
}
}
}
/**
* @brief Assigns an entity to a storage.
* @param entt A valid identifier.
@@ -364,7 +371,7 @@ protected:
* @param force_back Force back insertion.
* @return Iterator pointing to the emplaced element.
*/
basic_iterator try_emplace([[maybe_unused]] const Entity entt, [[maybe_unused]] const bool force_back, const void *value) override {
underlying_iterator try_emplace([[maybe_unused]] const Entity entt, [[maybe_unused]] const bool force_back, const void *value) override {
if(value) {
if constexpr(std::is_copy_constructible_v<value_type>) {
return emplace_element(entt, force_back, *static_cast<const value_type *>(value));
@@ -383,22 +390,24 @@ protected:
public:
/*! @brief Base type. */
using base_type = underlying_type;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Type of the objects assigned to entities. */
using value_type = Type;
/*! @brief Component traits. */
using traits_type = component_traits<value_type>;
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Pointer type to contained elements. */
using pointer = typename container_type::pointer;
/*! @brief Constant pointer type to contained elements. */
using const_pointer = typename alloc_traits::template rebind_traits<typename alloc_traits::const_pointer>::const_pointer;
/*! @brief Random access iterator type. */
using iterator = internal::storage_iterator<container_type>;
using iterator = internal::storage_iterator<container_type, traits_type::page_size>;
/*! @brief Constant random access iterator type. */
using const_iterator = internal::storage_iterator<const container_type>;
using const_iterator = internal::storage_iterator<const container_type, traits_type::page_size>;
/*! @brief Reverse iterator type. */
using reverse_iterator = std::reverse_iterator<iterator>;
/*! @brief Constant reverse iterator type. */
@@ -407,6 +416,10 @@ public:
using iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::iterator, iterator>>;
/*! @brief Constant extended iterable storage proxy. */
using const_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_iterator, const_iterator>>;
/*! @brief Extended reverse iterable storage proxy. */
using reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::reverse_iterator, reverse_iterator>>;
/*! @brief Constant extended reverse iterable storage proxy. */
using const_reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_reverse_iterator, const_reverse_iterator>>;
/*! @brief Default constructor. */
basic_storage()
@@ -417,8 +430,8 @@ public:
* @param allocator The allocator to use.
*/
explicit basic_storage(const allocator_type &allocator)
: base_type{type_id<value_type>(), deletion_policy{comp_traits::in_place_delete}, allocator},
packed{container_type{allocator}, allocator} {}
: base_type{type_id<value_type>(), deletion_policy{traits_type::in_place_delete}, allocator},
payload{allocator} {}
/**
* @brief Move constructor.
@@ -426,7 +439,7 @@ public:
*/
basic_storage(basic_storage &&other) noexcept
: base_type{std::move(other)},
packed{std::move(other.packed)} {}
payload{std::move(other.payload)} {}
/**
* @brief Allocator-extended move constructor.
@@ -435,8 +448,8 @@ public:
*/
basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept
: base_type{std::move(other), allocator},
packed{container_type{std::move(other.packed.first()), allocator}, allocator} {
ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.second() == other.packed.second(), "Copying a storage is not allowed");
payload{std::move(other.payload), allocator} {
ENTT_ASSERT(alloc_traits::is_always_equal::value || payload.get_allocator() == other.payload.get_allocator(), "Copying a storage is not allowed");
}
/*! @brief Default destructor. */
@@ -450,12 +463,11 @@ public:
* @return This storage.
*/
basic_storage &operator=(basic_storage &&other) noexcept {
ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.second() == other.packed.second(), "Copying a storage is not allowed");
ENTT_ASSERT(alloc_traits::is_always_equal::value || payload.get_allocator() == other.payload.get_allocator(), "Copying a storage is not allowed");
shrink_to_size(0u);
base_type::operator=(std::move(other));
packed.first() = std::move(other.packed.first());
propagate_on_container_move_assignment(packed.second(), other.packed.second());
payload = std::move(other.payload);
return *this;
}
@@ -465,9 +477,8 @@ public:
*/
void swap(basic_storage &other) {
using std::swap;
underlying_type::swap(other);
propagate_on_container_swap(packed.second(), other.packed.second());
swap(packed.first(), other.packed.first());
base_type::swap(other);
swap(payload, other.payload);
}
/**
@@ -475,7 +486,7 @@ public:
* @return The associated allocator.
*/
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
return allocator_type{packed.second()};
return payload.get_allocator();
}
/**
@@ -499,7 +510,7 @@ public:
* @return Capacity of the storage.
*/
[[nodiscard]] size_type capacity() const noexcept override {
return packed.first().size() * comp_traits::page_size;
return payload.size() * traits_type::page_size;
}
/*! @brief Requests the removal of unused capacity. */
@@ -513,25 +524,24 @@ public:
* @return A pointer to the array of objects.
*/
[[nodiscard]] const_pointer raw() const noexcept {
return packed.first().data();
return payload.data();
}
/*! @copydoc raw */
[[nodiscard]] pointer raw() noexcept {
return packed.first().data();
return payload.data();
}
/**
* @brief Returns an iterator to the beginning.
*
* The returned iterator points to the first instance of the internal array.
* If the storage is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first instance of the internal array.
*/
[[nodiscard]] const_iterator cbegin() const noexcept {
const auto pos = static_cast<typename iterator::difference_type>(base_type::size());
return const_iterator{&packed.first(), pos};
return const_iterator{&payload, pos};
}
/*! @copydoc cbegin */
@@ -542,21 +552,16 @@ public:
/*! @copydoc begin */
[[nodiscard]] iterator begin() noexcept {
const auto pos = static_cast<typename iterator::difference_type>(base_type::size());
return iterator{&packed.first(), pos};
return iterator{&payload, pos};
}
/**
* @brief Returns an iterator to the end.
*
* The returned iterator points to the element following the last instance
* of the internal array. Attempting to dereference the returned iterator
* results in undefined behavior.
*
* @return An iterator to the element following the last instance of the
* internal array.
*/
[[nodiscard]] const_iterator cend() const noexcept {
return const_iterator{&packed.first(), {}};
return const_iterator{&payload, {}};
}
/*! @copydoc cend */
@@ -566,15 +571,13 @@ public:
/*! @copydoc end */
[[nodiscard]] iterator end() noexcept {
return iterator{&packed.first(), {}};
return iterator{&payload, {}};
}
/**
* @brief Returns a reverse iterator to the beginning.
*
* The returned iterator points to the first instance of the reversed
* internal array. If the storage is empty, the returned iterator will be
* equal to `rend()`.
* If the storage is empty, the returned iterator will be equal to `rend()`.
*
* @return An iterator to the first instance of the reversed internal array.
*/
@@ -594,11 +597,6 @@ public:
/**
* @brief Returns a reverse iterator to the end.
*
* The returned iterator points to the element following the last instance
* of the reversed internal array. Attempting to dereference the returned
* iterator results in undefined behavior.
*
* @return An iterator to the element following the last instance of the
* reversed internal array.
*/
@@ -663,7 +661,7 @@ public:
*/
template<typename... Args>
value_type &emplace(const entity_type entt, Args &&...args) {
if constexpr(std::is_aggregate_v<value_type>) {
if constexpr(std::is_aggregate_v<value_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<value_type>)) {
const auto it = emplace_element(entt, false, Type{std::forward<Args>(args)...});
return element_at(static_cast<size_type>(it.index()));
} else {
@@ -699,12 +697,15 @@ public:
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @param value An instance of the object to construct.
* @return Iterator pointing to the last element inserted, if any.
*/
template<typename It>
void insert(It first, It last, const value_type &value = {}) {
iterator insert(It first, It last, const value_type &value = {}) {
for(; first != last; ++first) {
emplace_element(*first, true, value);
}
return begin();
}
/**
@@ -718,12 +719,15 @@ public:
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @param from An iterator to the first element of the range of objects.
* @return Iterator pointing to the first element inserted, if any.
*/
template<typename EIt, typename CIt, typename = std::enable_if_t<std::is_same_v<typename std::iterator_traits<CIt>::value_type, value_type>>>
void insert(EIt first, EIt last, CIt from) {
iterator insert(EIt first, EIt last, CIt from) {
for(; first != last; ++first, ++from) {
emplace_element(*first, true, *from);
}
return begin();
}
/**
@@ -743,34 +747,54 @@ public:
return {internal::extended_storage_iterator{base_type::cbegin(), cbegin()}, internal::extended_storage_iterator{base_type::cend(), cend()}};
}
/**
* @brief Returns a reverse iterable object to use to _visit_ a storage.
*
* @sa each
*
* @return A reverse iterable object to use to _visit_ the storage.
*/
[[nodiscard]] reverse_iterable reach() noexcept {
return {internal::extended_storage_iterator{base_type::rbegin(), rbegin()}, internal::extended_storage_iterator{base_type::rend(), rend()}};
}
/*! @copydoc reach */
[[nodiscard]] const_reverse_iterable reach() const noexcept {
return {internal::extended_storage_iterator{base_type::crbegin(), crbegin()}, internal::extended_storage_iterator{base_type::crend(), crend()}};
}
private:
compressed_pair<container_type, allocator_type> packed;
container_type payload;
};
/*! @copydoc basic_storage */
template<typename Type, typename Entity, typename Allocator>
class basic_storage<Type, Entity, Allocator, std::enable_if_t<ignore_as_empty_v<Type>>>
class basic_storage<Type, Entity, Allocator, std::enable_if_t<component_traits<Type>::page_size == 0u>>
: public basic_sparse_set<Entity, typename std::allocator_traits<Allocator>::template rebind_alloc<Entity>> {
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
using underlying_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
using comp_traits = component_traits<Type>;
public:
/*! @brief Base type. */
using base_type = underlying_type;
/*! @brief Allocator type. */
using allocator_type = Allocator;
using base_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
/*! @brief Type of the objects assigned to entities. */
using value_type = Type;
/*! @brief Component traits. */
using traits_type = component_traits<value_type>;
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Extended iterable storage proxy. */
using iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::iterator>>;
/*! @brief Constant extended iterable storage proxy. */
using const_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_iterator>>;
/*! @brief Extended reverse iterable storage proxy. */
using reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::reverse_iterator>>;
/*! @brief Constant extended reverse iterable storage proxy. */
using const_reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_reverse_iterator>>;
/*! @brief Default constructor. */
basic_storage()
@@ -781,7 +805,7 @@ public:
* @param allocator The allocator to use.
*/
explicit basic_storage(const allocator_type &allocator)
: base_type{type_id<value_type>(), deletion_policy{comp_traits::in_place_delete}, allocator} {}
: base_type{type_id<value_type>(), deletion_policy{traits_type::in_place_delete}, allocator} {}
/**
* @brief Move constructor.
@@ -809,7 +833,12 @@ public:
* @return The associated allocator.
*/
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
return allocator_type{base_type::get_allocator()};
// std::allocator<void> has no cross constructors (waiting for C++20)
if constexpr(std::is_void_v<value_type> && !std::is_constructible_v<allocator_type, typename base_type::allocator_type>) {
return allocator_type{};
} else {
return allocator_type{base_type::get_allocator()};
}
}
/**
@@ -896,6 +925,264 @@ public:
[[nodiscard]] const_iterable each() const noexcept {
return {internal::extended_storage_iterator{base_type::cbegin()}, internal::extended_storage_iterator{base_type::cend()}};
}
/**
* @brief Returns a reverse iterable object to use to _visit_ a storage.
*
* @sa each
*
* @return A reverse iterable object to use to _visit_ the storage.
*/
[[nodiscard]] reverse_iterable reach() noexcept {
return {internal::extended_storage_iterator{base_type::rbegin()}, internal::extended_storage_iterator{base_type::rend()}};
}
/*! @copydoc reach */
[[nodiscard]] const_reverse_iterable reach() const noexcept {
return {internal::extended_storage_iterator{base_type::crbegin()}, internal::extended_storage_iterator{base_type::crend()}};
}
};
/**
* @brief Swap-only entity storage specialization.
* @tparam Entity A valid entity type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Entity, typename Allocator>
class basic_storage<Entity, Entity, Allocator>
: public basic_sparse_set<Entity, Allocator> {
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, Entity>, "Invalid value type");
using underlying_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
using underlying_iterator = typename underlying_type::basic_iterator;
auto entity_at(const std::size_t pos) const noexcept {
ENTT_ASSERT(pos < underlying_type::traits_type::to_entity(null), "Invalid element");
return underlying_type::traits_type::combine(static_cast<typename underlying_type::traits_type::entity_type>(pos), {});
}
protected:
/**
* @brief Assigns an entity to a storage.
* @param hint A valid identifier.
* @return Iterator pointing to the emplaced element.
*/
underlying_iterator try_emplace(const Entity hint, const bool, const void *) override {
return base_type::find(emplace(hint));
}
public:
/*! @brief Base type. */
using base_type = basic_sparse_set<Entity, Allocator>;
/*! @brief Type of the objects assigned to entities. */
using value_type = Entity;
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Extended iterable storage proxy. */
using iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::iterator>>;
/*! @brief Constant extended iterable storage proxy. */
using const_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_iterator>>;
/*! @brief Extended reverse iterable storage proxy. */
using reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::reverse_iterator>>;
/*! @brief Constant extended reverse iterable storage proxy. */
using const_reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_reverse_iterator>>;
/*! @brief Default constructor. */
basic_storage()
: basic_storage{allocator_type{}} {
}
/**
* @brief Constructs an empty container with a given allocator.
* @param allocator The allocator to use.
*/
explicit basic_storage(const allocator_type &allocator)
: base_type{type_id<void>(), deletion_policy::swap_only, allocator} {}
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
basic_storage(basic_storage &&other) noexcept
: base_type{std::move(other)} {}
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept
: base_type{std::move(other), allocator} {}
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This storage.
*/
basic_storage &operator=(basic_storage &&other) noexcept {
base_type::operator=(std::move(other));
return *this;
}
/**
* @brief Returns the object assigned to an entity, that is `void`.
*
* @warning
* Attempting to use an entity that doesn't belong to the storage results in
* undefined behavior.
*
* @param entt A valid identifier.
*/
void get([[maybe_unused]] const entity_type entt) const noexcept {
ENTT_ASSERT(base_type::index(entt) < base_type::free_list(), "The requested entity is not a live one");
}
/**
* @brief Returns an empty tuple.
*
* @warning
* Attempting to use an entity that doesn't belong to the storage results in
* undefined behavior.
*
* @param entt A valid identifier.
* @return Returns an empty tuple.
*/
[[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const noexcept {
ENTT_ASSERT(base_type::index(entt) < base_type::free_list(), "The requested entity is not a live one");
return std::tuple{};
}
/**
* @brief Creates a new identifier or recycles a destroyed one.
* @return A valid identifier.
*/
entity_type emplace() {
const auto len = base_type::free_list();
const auto entt = (len == base_type::size()) ? entity_at(len) : base_type::data()[len];
return *base_type::try_emplace(entt, true);
}
/**
* @brief Creates a new identifier or recycles a destroyed one.
*
* If the requested identifier isn't in use, the suggested one is used.
* Otherwise, a new identifier is returned.
*
* @param hint Required identifier.
* @return A valid identifier.
*/
entity_type emplace(const entity_type hint) {
if(hint == null || hint == tombstone) {
return emplace();
} else if(const auto curr = underlying_type::traits_type::construct(underlying_type::traits_type::to_entity(hint), base_type::current(hint)); curr == tombstone) {
const auto pos = static_cast<size_type>(underlying_type::traits_type::to_entity(hint));
const auto entt = *base_type::try_emplace(hint, true);
while(!(pos < base_type::size())) {
base_type::try_emplace(entity_at(base_type::size() - 1u), false);
}
return entt;
} else if(const auto idx = base_type::index(curr); idx < base_type::free_list()) {
return emplace();
} else {
return *base_type::try_emplace(hint, true);
}
}
/**
* @brief Updates a given identifier.
* @tparam Func Types of the function objects to invoke.
* @param entt A valid identifier.
* @param func Valid function objects.
*/
template<typename... Func>
void patch([[maybe_unused]] const entity_type entt, Func &&...func) {
ENTT_ASSERT(base_type::index(entt) < base_type::free_list(), "The requested entity is not a live one");
(std::forward<Func>(func)(), ...);
}
/**
* @brief Assigns each element in a range an identifier.
* @tparam It Type of mutable forward iterator.
* @param first An iterator to the first element of the range to generate.
* @param last An iterator past the last element of the range to generate.
*/
template<typename It>
void insert(It first, It last) {
for(const auto sz = base_type::size(); first != last && base_type::free_list() != sz; ++first) {
*first = *base_type::try_emplace(base_type::data()[base_type::free_list()], true);
}
for(; first != last; ++first) {
*first = *base_type::try_emplace(entity_at(base_type::free_list()), true);
}
}
/**
* @brief Makes all elements in a range contiguous.
* @tparam It Type of forward iterator.
* @param first An iterator to the first element of the range to pack.
* @param last An iterator past the last element of the range to pack.
* @return The number of elements within the newly created range.
*/
template<typename It>
[[deprecated("use sort_as instead")]] size_type pack(It first, It last) {
base_type::sort_as(first, last);
return static_cast<size_type>(std::distance(first, last));
}
/**
* @brief Returns the number of elements considered still in use.
* @return The number of elements considered still in use.
*/
[[deprecated("use free_list() instead")]] [[nodiscard]] size_type in_use() const noexcept {
return base_type::free_list();
}
/**
* @brief Sets the number of elements considered still in use.
* @param len The number of elements considered still in use.
*/
[[deprecated("use free_list(len) instead")]] void in_use(const size_type len) noexcept {
base_type::free_list(len);
}
/**
* @brief Returns an iterable object to use to _visit_ a storage.
*
* The iterable object returns a tuple that contains the current entity.
*
* @return An iterable object to use to _visit_ the storage.
*/
[[nodiscard]] iterable each() noexcept {
return {internal::extended_storage_iterator{base_type::begin(0)}, internal::extended_storage_iterator{base_type::end(0)}};
}
/*! @copydoc each */
[[nodiscard]] const_iterable each() const noexcept {
return {internal::extended_storage_iterator{base_type::cbegin(0)}, internal::extended_storage_iterator{base_type::cend(0)}};
}
/**
* @brief Returns a reverse iterable object to use to _visit_ a storage.
*
* @sa each
*
* @return A reverse iterable object to use to _visit_ the storage.
*/
[[nodiscard]] reverse_iterable reach() noexcept {
return {internal::extended_storage_iterator{base_type::rbegin()}, internal::extended_storage_iterator{base_type::rend(0)}};
}
/*! @copydoc reach */
[[nodiscard]] const_reverse_iterable reach() const noexcept {
return {internal::extended_storage_iterator{base_type::crbegin()}, internal::extended_storage_iterator{base_type::crend(0)}};
}
};
} // namespace entt

View File

@@ -1,236 +0,0 @@
#ifndef ENTT_ENTITY_SIGH_STORAGE_MIXIN_HPP
#define ENTT_ENTITY_SIGH_STORAGE_MIXIN_HPP
#include <utility>
#include "../config/config.h"
#include "../core/any.hpp"
#include "../signal/sigh.hpp"
#include "fwd.hpp"
namespace entt {
/**
* @brief Mixin type used to add signal support to storage types.
*
* The function type of a listener is equivalent to:
*
* @code{.cpp}
* void(basic_registry<entity_type> &, entity_type);
* @endcode
*
* This applies to all signals made available.
*
* @tparam Type The type of the underlying storage.
*/
template<typename Type>
class sigh_storage_mixin final: public Type {
using basic_registry_type = basic_registry<typename Type::entity_type, typename Type::base_type::allocator_type>;
using sigh_type = sigh<void(basic_registry_type &, const typename Type::entity_type), typename Type::allocator_type>;
using basic_iterator = typename Type::basic_iterator;
void pop(basic_iterator first, basic_iterator last) override {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
for(; first != last; ++first) {
const auto entt = *first;
destruction.publish(*owner, entt);
const auto it = Type::find(entt);
Type::pop(it, it + 1u);
}
}
basic_iterator try_emplace(const typename basic_registry_type::entity_type entt, const bool force_back, const void *value) final {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
Type::try_emplace(entt, force_back, value);
construction.publish(*owner, entt);
return Type::find(entt);
}
public:
/*! @brief Allocator type. */
using allocator_type = typename Type::allocator_type;
/*! @brief Underlying entity identifier. */
using entity_type = typename Type::entity_type;
/*! @brief Expected registry type. */
using registry_type = basic_registry_type;
/*! @brief Default constructor. */
sigh_storage_mixin()
: sigh_storage_mixin{allocator_type{}} {}
/**
* @brief Constructs an empty storage with a given allocator.
* @param allocator The allocator to use.
*/
explicit sigh_storage_mixin(const allocator_type &allocator)
: Type{allocator},
owner{},
construction{allocator},
destruction{allocator},
update{allocator} {}
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
sigh_storage_mixin(sigh_storage_mixin &&other) noexcept
: Type{std::move(other)},
owner{other.owner},
construction{std::move(other.construction)},
destruction{std::move(other.destruction)},
update{std::move(other.update)} {}
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
sigh_storage_mixin(sigh_storage_mixin &&other, const allocator_type &allocator) noexcept
: Type{std::move(other), allocator},
owner{other.owner},
construction{std::move(other.construction), allocator},
destruction{std::move(other.destruction), allocator},
update{std::move(other.update), allocator} {}
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This storage.
*/
sigh_storage_mixin &operator=(sigh_storage_mixin &&other) noexcept {
Type::operator=(std::move(other));
owner = other.owner;
construction = std::move(other.construction);
destruction = std::move(other.destruction);
update = std::move(other.update);
return *this;
}
/**
* @brief Exchanges the contents with those of a given storage.
* @param other Storage to exchange the content with.
*/
void swap(sigh_storage_mixin &other) {
using std::swap;
Type::swap(other);
swap(owner, other.owner);
swap(construction, other.construction);
swap(destruction, other.destruction);
swap(update, other.update);
}
/**
* @brief Returns a sink object.
*
* The sink returned by this function can be used to receive notifications
* whenever a new instance is created and assigned to an entity.<br/>
* Listeners are invoked after the object has been assigned to the entity.
*
* @sa sink
*
* @return A temporary sink object.
*/
[[nodiscard]] auto on_construct() noexcept {
return sink{construction};
}
/**
* @brief Returns a sink object.
*
* The sink returned by this function can be used to receive notifications
* whenever an instance is explicitly updated.<br/>
* Listeners are invoked after the object has been updated.
*
* @sa sink
*
* @return A temporary sink object.
*/
[[nodiscard]] auto on_update() noexcept {
return sink{update};
}
/**
* @brief Returns a sink object.
*
* The sink returned by this function can be used to receive notifications
* whenever an instance is removed from an entity and thus destroyed.<br/>
* Listeners are invoked before the object has been removed from the entity.
*
* @sa sink
*
* @return A temporary sink object.
*/
[[nodiscard]] auto on_destroy() noexcept {
return sink{destruction};
}
/**
* @brief Assigns entities to a storage.
* @tparam Args Types of arguments to use to construct the object.
* @param entt A valid identifier.
* @param args Parameters to use to initialize the object.
* @return A reference to the newly created object.
*/
template<typename... Args>
decltype(auto) emplace(const entity_type entt, Args &&...args) {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
Type::emplace(entt, std::forward<Args>(args)...);
construction.publish(*owner, entt);
return this->get(entt);
}
/**
* @brief Patches the given instance for an entity.
* @tparam Func Types of the function objects to invoke.
* @param entt A valid identifier.
* @param func Valid function objects.
* @return A reference to the patched instance.
*/
template<typename... Func>
decltype(auto) patch(const entity_type entt, Func &&...func) {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
Type::patch(entt, std::forward<Func>(func)...);
update.publish(*owner, entt);
return this->get(entt);
}
/**
* @brief Assigns entities to a storage.
* @tparam It Type of input iterator.
* @tparam Args Types of arguments to use to construct the objects assigned
* to the entities.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @param args Parameters to use to initialize the objects assigned to the
* entities.
*/
template<typename It, typename... Args>
void insert(It first, It last, Args &&...args) {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
Type::insert(first, last, std::forward<Args>(args)...);
for(auto it = construction.empty() ? last : first; it != last; ++it) {
construction.publish(*owner, *it);
}
}
/**
* @brief Forwards variables to derived classes, if any.
* @param value A variable wrapped in an opaque container.
*/
void bind(any value) noexcept final {
auto *reg = any_cast<basic_registry_type>(&value);
owner = reg ? reg : owner;
Type::bind(std::move(value));
}
private:
basic_registry_type *owner;
sigh_type construction;
sigh_type destruction;
sigh_type update;
};
} // namespace entt
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -24,6 +24,7 @@
#include "entity/group.hpp"
#include "entity/handle.hpp"
#include "entity/helper.hpp"
#include "entity/mixin.hpp"
#include "entity/observer.hpp"
#include "entity/organizer.hpp"
#include "entity/registry.hpp"
@@ -31,7 +32,6 @@
#include "entity/snapshot.hpp"
#include "entity/sparse_set.hpp"
#include "entity/storage.hpp"
#include "entity/storage_mixin.hpp"
#include "entity/view.hpp"
#include "graph/adjacency_matrix.hpp"
#include "graph/dot.hpp"

View File

@@ -13,11 +13,7 @@
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename It>
@@ -30,6 +26,7 @@ public:
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
using iterator_concept = std::forward_iterator_tag;
constexpr edge_iterator() noexcept
: it{},
@@ -87,11 +84,7 @@ template<typename Container>
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
/**
* @brief Basic implementation of a directed adjacency matrix.
@@ -258,7 +251,7 @@ public:
[[nodiscard]] iterable_adaptor<out_edge_iterator> out_edges(const vertex_type vertex) const noexcept {
const auto it = matrix.cbegin();
const auto from = vertex * vert;
const auto to = vertex * vert + vert;
const auto to = from + vert;
return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}};
}
@@ -270,7 +263,7 @@ public:
[[nodiscard]] iterable_adaptor<in_edge_iterator> in_edges(const vertex_type vertex) const noexcept {
const auto it = matrix.cbegin();
const auto from = vertex;
const auto to = vert * (vert - 1u) + vertex;
const auto to = vert * vert + from;
return {{it, vert, from, to, vert}, {it, vert, to, to, vert}};
}

View File

@@ -32,6 +32,7 @@ class basic_flow {
using task_container_type = dense_set<id_type, identity, std::equal_to<id_type>, typename alloc_traits::template rebind_alloc<id_type>>;
using ro_rw_container_type = std::vector<std::pair<std::size_t, bool>, typename alloc_traits::template rebind_alloc<std::pair<std::size_t, bool>>>;
using deps_container_type = dense_map<id_type, ro_rw_container_type, identity, std::equal_to<id_type>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, ro_rw_container_type>>>;
using adjacency_matrix_type = adjacency_matrix<directed_tag, typename alloc_traits::template rebind_alloc<std::size_t>>;
void emplace(const id_type res, const bool is_rw) {
ENTT_ASSERT(index.first() < vertices.size(), "Invalid node");
@@ -43,6 +44,76 @@ class basic_flow {
deps[res].emplace_back(index.first(), is_rw);
}
void setup_graph(adjacency_matrix_type &matrix) const {
for(const auto &elem: deps) {
const auto last = elem.second.cend();
auto it = elem.second.cbegin();
while(it != last) {
if(it->second) {
// rw item
if(auto curr = it++; it != last) {
if(it->second) {
matrix.insert(curr->first, it->first);
} else if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) {
for(; it != next; ++it) {
matrix.insert(curr->first, it->first);
matrix.insert(it->first, next->first);
}
} else {
for(; it != next; ++it) {
matrix.insert(curr->first, it->first);
}
}
}
} else {
// ro item (first iteration only)
if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) {
for(; it != next; ++it) {
matrix.insert(it->first, next->first);
}
} else {
it = last;
}
}
}
}
}
void transitive_closure(adjacency_matrix_type &matrix) const {
const auto length = matrix.size();
for(std::size_t vk{}; vk < length; ++vk) {
for(std::size_t vi{}; vi < length; ++vi) {
for(std::size_t vj{}; vj < length; ++vj) {
if(matrix.contains(vi, vk) && matrix.contains(vk, vj)) {
matrix.insert(vi, vj);
}
}
}
}
}
void transitive_reduction(adjacency_matrix_type &matrix) const {
const auto length = matrix.size();
for(std::size_t vert{}; vert < length; ++vert) {
matrix.erase(vert, vert);
}
for(std::size_t vj{}; vj < length; ++vj) {
for(std::size_t vi{}; vi < length; ++vi) {
if(matrix.contains(vi, vj)) {
for(std::size_t vk{}; vk < length; ++vk) {
if(matrix.contains(vj, vk)) {
matrix.erase(vi, vk);
}
}
}
}
}
}
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
@@ -50,6 +121,8 @@ public:
using size_type = std::size_t;
/*! @brief Iterable task list. */
using iterable = iterable_adaptor<typename task_container_type::const_iterator>;
/*! @brief Adjacency matrix type. */
using graph_type = adjacency_matrix_type;
/*! @brief Default constructor. */
basic_flow()
@@ -61,8 +134,8 @@ public:
*/
explicit basic_flow(const allocator_type &allocator)
: index{0u, allocator},
vertices{},
deps{},
vertices{allocator},
deps{allocator},
sync_on{} {}
/*! @brief Default copy constructor. */
@@ -124,9 +197,10 @@ public:
/*! @brief Clears the flow builder. */
void clear() noexcept {
index.first() = sync_on = {};
index.first() = {};
vertices.clear();
deps.clear();
sync_on = {};
}
/**
@@ -245,72 +319,12 @@ public:
* @brief Generates a task graph for the current content.
* @return The adjacency matrix of the task graph.
*/
[[nodiscard]] adjacency_matrix<directed_tag> graph() const {
const auto length = vertices.size();
adjacency_matrix<directed_tag> matrix{length};
[[nodiscard]] graph_type graph() const {
graph_type matrix{vertices.size(), get_allocator()};
// creates the adjacency matrix
for(const auto &elem: deps) {
const auto last = elem.second.cend();
auto it = elem.second.cbegin();
while(it != last) {
if(it->second) {
// rw item
if(auto curr = it++; it != last) {
if(it->second) {
matrix.insert(curr->first, it->first);
} else if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) {
for(; it != next; ++it) {
matrix.insert(curr->first, it->first);
matrix.insert(it->first, next->first);
}
} else {
for(; it != next; ++it) {
matrix.insert(curr->first, it->first);
}
}
}
} else {
// ro item (first iteration only)
if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) {
for(; it != next; ++it) {
matrix.insert(it->first, next->first);
}
} else {
it = last;
}
}
}
}
// computes the transitive closure
for(std::size_t vk{}; vk < length; ++vk) {
for(std::size_t vi{}; vi < length; ++vi) {
for(std::size_t vj{}; vj < length; ++vj) {
if(matrix.contains(vi, vk) && matrix.contains(vk, vj)) {
matrix.insert(vi, vj);
}
}
}
}
// applies the transitive reduction
for(std::size_t vert{}; vert < length; ++vert) {
matrix.erase(vert, vert);
}
for(std::size_t vj{}; vj < length; ++vj) {
for(std::size_t vi{}; vi < length; ++vi) {
if(matrix.contains(vi, vj)) {
for(std::size_t vk{}; vk < length; ++vk) {
if(matrix.contains(vj, vk)) {
matrix.erase(vi, vk);
}
}
}
}
}
setup_graph(matrix);
transitive_closure(matrix);
transitive_reduction(matrix);
return matrix;
}

View File

@@ -70,40 +70,40 @@ public:
* cases, they are discarded.
*
* @tparam Args Types of arguments to use to construct the fallback service.
* @tparam Impl Fallback service type.
* @tparam Type Fallback service type.
* @param args Parameters to use to construct the fallback service.
* @return A reference to a valid service.
*/
template<typename Impl = Service, typename... Args>
template<typename Type = Service, typename... Args>
[[nodiscard]] static Service &value_or(Args &&...args) {
return service ? *service : emplace<Impl>(std::forward<Args>(args)...);
return service ? *service : emplace<Type>(std::forward<Args>(args)...);
}
/**
* @brief Sets or replaces a service.
* @tparam Impl Service type.
* @tparam Type Service type.
* @tparam Args Types of arguments to use to construct the service.
* @param args Parameters to use to construct the service.
* @return A reference to a valid service.
*/
template<typename Impl = Service, typename... Args>
template<typename Type = Service, typename... Args>
static Service &emplace(Args &&...args) {
service = std::make_shared<Impl>(std::forward<Args>(args)...);
service = std::make_shared<Type>(std::forward<Args>(args)...);
return *service;
}
/**
* @brief Sets or replaces a service using a given allocator.
* @tparam Impl Service type.
* @tparam Type Service type.
* @tparam Allocator Type of allocator used to manage memory and elements.
* @tparam Args Types of arguments to use to construct the service.
* @param alloc The allocator to use.
* @param args Parameters to use to construct the service.
* @return A reference to a valid service.
*/
template<typename Impl = Service, typename Allocator, typename... Args>
static Service &allocate_emplace(Allocator alloc, Args &&...args) {
service = std::allocate_shared<Impl>(alloc, std::forward<Args>(args)...);
template<typename Type = Service, typename Allocator, typename... Args>
static Service &emplace(std::allocator_arg_t, Allocator alloc, Args &&...args) {
service = std::allocate_shared<Type>(alloc, std::forward<Args>(args)...);
return *service;
}
@@ -125,6 +125,18 @@ public:
service = other.value;
}
/**
* @brief Resets or replaces a service.
* @tparam Type Service type.
* @tparam Deleter Deleter type.
* @param elem A pointer to a service to manage.
* @param deleter A deleter to use to destroy the service.
*/
template<typename Type, typename Deleter = std::default_delete<Type>>
static void reset(Type *elem, Deleter deleter = {}) {
service = std::shared_ptr<Service>{elem, std::move(deleter)};
}
private:
// std::shared_ptr because of its type erased allocator which is useful here
inline static std::shared_ptr<Service> service{};

View File

@@ -19,145 +19,284 @@
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename, typename = void>
struct is_dynamic_sequence_container: std::false_type {};
struct fixed_size_sequence_container: std::true_type {};
template<typename Type>
struct is_dynamic_sequence_container<Type, std::void_t<decltype(&Type::clear)>>: std::true_type {};
struct fixed_size_sequence_container<Type, std::void_t<decltype(&Type::clear)>>: std::false_type {};
template<typename Type>
inline constexpr bool fixed_size_sequence_container_v = fixed_size_sequence_container<Type>::value;
template<typename, typename = void>
struct is_key_only_meta_associative_container: std::true_type {};
struct key_only_associative_container: std::true_type {};
template<typename Type>
struct is_key_only_meta_associative_container<Type, std::void_t<typename Type::mapped_type>>: std::false_type {};
struct key_only_associative_container<Type, std::void_t<typename Type::mapped_type>>: std::false_type {};
template<typename Type>
struct basic_meta_sequence_container_traits {
using iterator = meta_sequence_container::iterator;
using size_type = std::size_t;
inline constexpr bool key_only_associative_container_v = key_only_associative_container<Type>::value;
[[nodiscard]] static size_type size(const any &container) noexcept {
return any_cast<const Type &>(container).size();
}
[[nodiscard]] static bool resize([[maybe_unused]] any &container, [[maybe_unused]] size_type sz) {
if constexpr(is_dynamic_sequence_container<Type>::value) {
if(auto *const cont = any_cast<Type>(&container); cont) {
cont->resize(sz);
return true;
}
}
return false;
}
[[nodiscard]] static iterator iter(const meta_ctx &ctx, any &container, const bool as_end) {
if(auto *const cont = any_cast<Type>(&container); cont) {
return iterator{ctx, as_end ? cont->end() : cont->begin()};
}
const Type &as_const = any_cast<const Type &>(container);
return iterator{ctx, as_end ? as_const.end() : as_const.begin()};
}
[[nodiscard]] static iterator insert_or_erase([[maybe_unused]] const meta_ctx &ctx, [[maybe_unused]] any &container, [[maybe_unused]] const any &handle, [[maybe_unused]] meta_any &value) {
if constexpr(is_dynamic_sequence_container<Type>::value) {
if(auto *const cont = any_cast<Type>(&container); cont) {
typename Type::const_iterator it{};
if(auto *non_const = any_cast<typename Type::iterator>(&handle); non_const) {
it = *non_const;
} else {
it = any_cast<const typename Type::const_iterator &>(handle);
}
if(value) {
// this abomination is necessary because only on macos value_type and const_reference are different types for std::vector<bool>
if(value.allow_cast<typename Type::const_reference>() || value.allow_cast<typename Type::value_type>()) {
const auto *element = value.try_cast<std::remove_reference_t<typename Type::const_reference>>();
return iterator{ctx, cont->insert(it, element ? *element : value.cast<typename Type::value_type>())};
}
} else {
return iterator{ctx, cont->erase(it)};
}
}
}
return iterator{};
}
};
template<typename, typename = void>
struct reserve_aware_container: std::false_type {};
template<typename Type>
struct basic_meta_associative_container_traits {
using iterator = meta_associative_container::iterator;
using size_type = std::size_t;
struct reserve_aware_container<Type, std::void_t<decltype(&Type::reserve)>>: std::true_type {};
static constexpr auto key_only = is_key_only_meta_associative_container<Type>::value;
[[nodiscard]] static size_type size(const any &container) noexcept {
return any_cast<const Type &>(container).size();
}
[[nodiscard]] static bool clear(any &container) {
if(auto *const cont = any_cast<Type>(&container); cont) {
cont->clear();
return true;
}
return false;
}
[[nodiscard]] static iterator iter(const meta_ctx &ctx, any &container, const bool as_end) {
if(auto *const cont = any_cast<Type>(&container); cont) {
return iterator{ctx, std::bool_constant<key_only>{}, as_end ? cont->end() : cont->begin()};
}
const auto &as_const = any_cast<const Type &>(container);
return iterator{ctx, std::bool_constant<key_only>{}, as_end ? as_const.end() : as_const.begin()};
}
[[nodiscard]] static size_type insert_or_erase(any &container, meta_any &key, meta_any &value) {
if(auto *const cont = any_cast<Type>(&container); cont && key.allow_cast<const typename Type::key_type &>()) {
if(value) {
if constexpr(key_only) {
return cont->insert(key.cast<const typename Type::key_type &>()).second;
} else {
return value.allow_cast<const typename Type::mapped_type &>() && cont->emplace(key.cast<const typename Type::key_type &>(), value.cast<const typename Type::mapped_type &>()).second;
}
} else {
return cont->erase(key.cast<const typename Type::key_type &>());
}
}
return 0u;
}
[[nodiscard]] static iterator find(const meta_ctx &ctx, any &container, meta_any &key) {
if(key.allow_cast<const typename Type::key_type &>()) {
if(auto *const cont = any_cast<Type>(&container); cont) {
return iterator{ctx, std::bool_constant<key_only>{}, cont->find(key.cast<const typename Type::key_type &>())};
}
return iterator{ctx, std::bool_constant<key_only>{}, any_cast<const Type &>(container).find(key.cast<const typename Type::key_type &>())};
}
return iterator{};
}
};
template<typename Type>
inline constexpr bool reserve_aware_container_v = reserve_aware_container<Type>::value;
} // namespace internal
/*! @endcond */
/**
* Internal details not to be documented.
* @endcond
* @brief General purpose implementation of meta sequence container traits.
* @tparam Type Type of underlying sequence container.
*/
template<typename Type>
struct basic_meta_sequence_container_traits {
static_assert(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>, "Unexpected type");
/*! @brief True in case of key-only containers, false otherwise. */
static constexpr bool fixed_size = internal::fixed_size_sequence_container_v<Type>;
/*! @brief Unsigned integer type. */
using size_type = typename meta_sequence_container::size_type;
/*! @brief Meta iterator type. */
using iterator = typename meta_sequence_container::iterator;
/**
* @brief Returns the number of elements in a container.
* @param container Opaque pointer to a container of the given type.
* @return Number of elements.
*/
[[nodiscard]] static size_type size(const void *container) {
return static_cast<const Type *>(container)->size();
}
/**
* @brief Clears a container.
* @param container Opaque pointer to a container of the given type.
* @return True in case of success, false otherwise.
*/
[[nodiscard]] static bool clear([[maybe_unused]] void *container) {
if constexpr(fixed_size) {
return false;
} else {
static_cast<Type *>(container)->clear();
return true;
}
}
/**
* @brief Increases the capacity of a container.
* @param container Opaque pointer to a container of the given type.
* @param sz Desired capacity.
* @return True in case of success, false otherwise.
*/
[[nodiscard]] static bool reserve([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) {
if constexpr(internal::reserve_aware_container_v<Type>) {
static_cast<Type *>(container)->reserve(sz);
return true;
} else {
return false;
}
}
/**
* @brief Resizes a container.
* @param container Opaque pointer to a container of the given type.
* @param sz The new number of elements.
* @return True in case of success, false otherwise.
*/
[[nodiscard]] static bool resize([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) {
if constexpr(fixed_size || !std::is_default_constructible_v<typename Type::value_type>) {
return false;
} else {
static_cast<Type *>(container)->resize(sz);
return true;
}
}
/**
* @brief Returns a possibly const iterator to the beginning.
* @param area The context to pass to the newly created iterator.
* @param container Opaque pointer to a container of the given type.
* @param as_const Const opaque pointer fallback.
* @return An iterator to the first element of the container.
*/
static iterator begin(const meta_ctx &area, void *container, const void *as_const) {
return container ? iterator{area, static_cast<Type *>(container)->begin()}
: iterator{area, static_cast<const Type *>(as_const)->begin()};
}
/**
* @brief Returns a possibly const iterator to the end.
* @param area The context to pass to the newly created iterator.
* @param container Opaque pointer to a container of the given type.
* @param as_const Const opaque pointer fallback.
* @return An iterator that is past the last element of the container.
*/
static iterator end(const meta_ctx &area, void *container, const void *as_const) {
return container ? iterator{area, static_cast<Type *>(container)->end()}
: iterator{area, static_cast<const Type *>(as_const)->end()};
}
/**
* @brief Assigns one element to a container and constructs its object from
* a given opaque instance.
* @param area The context to pass to the newly created iterator.
* @param container Opaque pointer to a container of the given type.
* @param value Optional opaque instance of the object to construct (as
* value type).
* @param cref Optional opaque instance of the object to construct (as
* decayed const reference type).
* @param it Iterator before which the element will be inserted.
* @return A possibly invalid iterator to the inserted element.
*/
[[nodiscard]] static iterator insert(const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const void *value, [[maybe_unused]] const void *cref, [[maybe_unused]] const iterator &it) {
if constexpr(fixed_size) {
return iterator{area};
} else {
auto *const non_const = any_cast<typename Type::iterator>(&it.base());
return {area, static_cast<Type *>(container)->insert(
non_const ? *non_const : any_cast<const typename Type::const_iterator &>(it.base()),
value ? *static_cast<const typename Type::value_type *>(value) : *static_cast<const std::remove_reference_t<typename Type::const_reference> *>(cref))};
}
}
/**
* @brief Erases an element from a container.
* @param area The context to pass to the newly created iterator.
* @param container Opaque pointer to a container of the given type.
* @param it An opaque iterator to the element to erase.
* @return A possibly invalid iterator following the last removed element.
*/
[[nodiscard]] static iterator erase(const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const iterator &it) {
if constexpr(fixed_size) {
return iterator{area};
} else {
auto *const non_const = any_cast<typename Type::iterator>(&it.base());
return {area, static_cast<Type *>(container)->erase(non_const ? *non_const : any_cast<const typename Type::const_iterator &>(it.base()))};
}
}
};
/**
* @brief General purpose implementation of meta associative container traits.
* @tparam Type Type of underlying associative container.
*/
template<typename Type>
struct basic_meta_associative_container_traits {
static_assert(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>, "Unexpected type");
/*! @brief True in case of key-only containers, false otherwise. */
static constexpr bool key_only = internal::key_only_associative_container_v<Type>;
/*! @brief Unsigned integer type. */
using size_type = typename meta_associative_container::size_type;
/*! @brief Meta iterator type. */
using iterator = typename meta_associative_container::iterator;
/**
* @brief Returns the number of elements in a container.
* @param container Opaque pointer to a container of the given type.
* @return Number of elements.
*/
[[nodiscard]] static size_type size(const void *container) {
return static_cast<const Type *>(container)->size();
}
/**
* @brief Clears a container.
* @param container Opaque pointer to a container of the given type.
* @return True in case of success, false otherwise.
*/
[[nodiscard]] static bool clear(void *container) {
static_cast<Type *>(container)->clear();
return true;
}
/**
* @brief Increases the capacity of a container.
* @param container Opaque pointer to a container of the given type.
* @param sz Desired capacity.
* @return True in case of success, false otherwise.
*/
[[nodiscard]] static bool reserve([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) {
if constexpr(internal::reserve_aware_container_v<Type>) {
static_cast<Type *>(container)->reserve(sz);
return true;
} else {
return false;
}
}
/**
* @brief Returns a possibly const iterator to the beginning.
* @param area The context to pass to the newly created iterator.
* @param container Opaque pointer to a container of the given type.
* @param as_const Const opaque pointer fallback.
* @return An iterator to the first element of the container.
*/
static iterator begin(const meta_ctx &area, void *container, const void *as_const) {
return container ? iterator{area, std::bool_constant<key_only>{}, static_cast<Type *>(container)->begin()}
: iterator{area, std::bool_constant<key_only>{}, static_cast<const Type *>(as_const)->begin()};
}
/**
* @brief Returns a possibly const iterator to the end.
* @param area The context to pass to the newly created iterator.
* @param container Opaque pointer to a container of the given type.
* @param as_const Const opaque pointer fallback.
* @return An iterator that is past the last element of the container.
*/
static iterator end(const meta_ctx &area, void *container, const void *as_const) {
return container ? iterator{area, std::bool_constant<key_only>{}, static_cast<Type *>(container)->end()}
: iterator{area, std::bool_constant<key_only>{}, static_cast<const Type *>(as_const)->end()};
}
/**
* @brief Inserts an element into a container, if the key does not exist.
* @param container Opaque pointer to a container of the given type.
* @param key An opaque key value of an element to insert.
* @param value Optional opaque value to insert (key-value containers).
* @return True if the insertion took place, false otherwise.
*/
[[nodiscard]] static bool insert(void *container, const void *key, [[maybe_unused]] const void *value) {
if constexpr(key_only) {
return static_cast<Type *>(container)->insert(*static_cast<const typename Type::key_type *>(key)).second;
} else {
return static_cast<Type *>(container)->emplace(*static_cast<const typename Type::key_type *>(key), *static_cast<const typename Type::mapped_type *>(value)).second;
}
}
/**
* @brief Removes an element from a container.
* @param container Opaque pointer to a container of the given type.
* @param key An opaque key value of an element to remove.
* @return Number of elements removed (either 0 or 1).
*/
[[nodiscard]] static size_type erase(void *container, const void *key) {
return static_cast<Type *>(container)->erase(*static_cast<const typename Type::key_type *>(key));
}
/**
* @brief Finds an element with a given key.
* @param area The context to pass to the newly created iterator.
* @param container Opaque pointer to a container of the given type.
* @param as_const Const opaque pointer fallback.
* @param key Opaque key value of an element to search for.
* @return An iterator to the element with the given key, if any.
*/
static iterator find(const meta_ctx &area, void *container, const void *as_const, const void *key) {
return container ? iterator{area, std::bool_constant<key_only>{}, static_cast<Type *>(container)->find(*static_cast<const typename Type::key_type *>(key))}
: iterator{area, std::bool_constant<key_only>{}, static_cast<const Type *>(as_const)->find(*static_cast<const typename Type::key_type *>(key))};
}
};
/**
* @brief Meta sequence container traits for `std::vector`s of any type.
@@ -165,7 +304,7 @@ struct basic_meta_associative_container_traits {
*/
template<typename... Args>
struct meta_sequence_container_traits<std::vector<Args...>>
: internal::basic_meta_sequence_container_traits<std::vector<Args...>> {};
: basic_meta_sequence_container_traits<std::vector<Args...>> {};
/**
* @brief Meta sequence container traits for `std::array`s of any type.
@@ -174,7 +313,7 @@ struct meta_sequence_container_traits<std::vector<Args...>>
*/
template<typename Type, auto N>
struct meta_sequence_container_traits<std::array<Type, N>>
: internal::basic_meta_sequence_container_traits<std::array<Type, N>> {};
: basic_meta_sequence_container_traits<std::array<Type, N>> {};
/**
* @brief Meta sequence container traits for `std::list`s of any type.
@@ -182,7 +321,7 @@ struct meta_sequence_container_traits<std::array<Type, N>>
*/
template<typename... Args>
struct meta_sequence_container_traits<std::list<Args...>>
: internal::basic_meta_sequence_container_traits<std::list<Args...>> {};
: basic_meta_sequence_container_traits<std::list<Args...>> {};
/**
* @brief Meta sequence container traits for `std::deque`s of any type.
@@ -190,7 +329,7 @@ struct meta_sequence_container_traits<std::list<Args...>>
*/
template<typename... Args>
struct meta_sequence_container_traits<std::deque<Args...>>
: internal::basic_meta_sequence_container_traits<std::deque<Args...>> {};
: basic_meta_sequence_container_traits<std::deque<Args...>> {};
/**
* @brief Meta associative container traits for `std::map`s of any type.
@@ -198,7 +337,7 @@ struct meta_sequence_container_traits<std::deque<Args...>>
*/
template<typename... Args>
struct meta_associative_container_traits<std::map<Args...>>
: internal::basic_meta_associative_container_traits<std::map<Args...>> {};
: basic_meta_associative_container_traits<std::map<Args...>> {};
/**
* @brief Meta associative container traits for `std::unordered_map`s of any
@@ -207,7 +346,7 @@ struct meta_associative_container_traits<std::map<Args...>>
*/
template<typename... Args>
struct meta_associative_container_traits<std::unordered_map<Args...>>
: internal::basic_meta_associative_container_traits<std::unordered_map<Args...>> {};
: basic_meta_associative_container_traits<std::unordered_map<Args...>> {};
/**
* @brief Meta associative container traits for `std::set`s of any type.
@@ -215,7 +354,7 @@ struct meta_associative_container_traits<std::unordered_map<Args...>>
*/
template<typename... Args>
struct meta_associative_container_traits<std::set<Args...>>
: internal::basic_meta_associative_container_traits<std::set<Args...>> {};
: basic_meta_associative_container_traits<std::set<Args...>> {};
/**
* @brief Meta associative container traits for `std::unordered_set`s of any
@@ -224,7 +363,7 @@ struct meta_associative_container_traits<std::set<Args...>>
*/
template<typename... Args>
struct meta_associative_container_traits<std::unordered_set<Args...>>
: internal::basic_meta_associative_container_traits<std::unordered_set<Args...>> {};
: basic_meta_associative_container_traits<std::unordered_set<Args...>> {};
/**
* @brief Meta associative container traits for `dense_map`s of any type.
@@ -232,7 +371,7 @@ struct meta_associative_container_traits<std::unordered_set<Args...>>
*/
template<typename... Args>
struct meta_associative_container_traits<dense_map<Args...>>
: internal::basic_meta_associative_container_traits<dense_map<Args...>> {};
: basic_meta_associative_container_traits<dense_map<Args...>> {};
/**
* @brief Meta associative container traits for `dense_set`s of any type.
@@ -240,7 +379,7 @@ struct meta_associative_container_traits<dense_map<Args...>>
*/
template<typename... Args>
struct meta_associative_container_traits<dense_set<Args...>>
: internal::basic_meta_associative_container_traits<dense_set<Args...>> {};
: basic_meta_associative_container_traits<dense_set<Args...>> {};
} // namespace entt

View File

@@ -9,11 +9,7 @@ namespace entt {
class meta_ctx;
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
struct meta_type_node;
@@ -21,16 +17,12 @@ struct meta_type_node;
struct meta_context {
dense_map<id_type, meta_type_node, identity> value{};
static inline meta_context &from(meta_ctx &ctx);
static inline const meta_context &from(const meta_ctx &ctx);
[[nodiscard]] inline static meta_context &from(meta_ctx &ctx);
[[nodiscard]] inline static const meta_context &from(const meta_ctx &ctx);
};
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
/*! @brief Disambiguation tag for constructors and the like. */
class meta_ctx_arg_t final {};
@@ -40,27 +32,19 @@ inline constexpr meta_ctx_arg_t meta_ctx_arg{};
/*! @brief Opaque meta context type. */
class meta_ctx: private internal::meta_context {
/*! @brief Attorney idiom like model to access the base class. */
// attorney idiom like model to access the base class
friend struct internal::meta_context;
};
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
inline internal::meta_context &internal::meta_context::from(meta_ctx &ctx) {
/*! @cond TURN_OFF_DOXYGEN */
[[nodiscard]] inline internal::meta_context &internal::meta_context::from(meta_ctx &ctx) {
return ctx;
}
inline const internal::meta_context &internal::meta_context::from(const meta_ctx &ctx) {
[[nodiscard]] inline const internal::meta_context &internal::meta_context::from(const meta_ctx &ctx) {
return ctx;
}
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
} // namespace entt

View File

@@ -22,35 +22,15 @@
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
inline decltype(auto) owner(meta_ctx &ctx, const type_info &info) {
[[nodiscard]] inline decltype(auto) owner(meta_ctx &ctx, const type_info &info) {
auto &&context = internal::meta_context::from(ctx);
ENTT_ASSERT(context.value.contains(info.hash()), "Type not available");
return context.value[info.hash()];
}
inline meta_base_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_base_node node) {
return parent.details->base.insert_or_assign(id, std::move(node)).first->second;
}
inline meta_conv_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_conv_node node) {
return parent.details->conv.insert_or_assign(id, std::move(node)).first->second;
}
inline meta_ctor_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_ctor_node node) {
return parent.details->ctor.insert_or_assign(id, std::move(node)).first->second;
}
inline meta_dtor_node &meta_extend(internal::meta_type_node &parent, meta_dtor_node node) {
return (parent.dtor = std::move(node));
}
inline meta_data_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_data_node node) {
return parent.details->data.insert_or_assign(id, std::move(node)).first->second;
}
@@ -72,16 +52,8 @@ inline meta_func_node &meta_extend(internal::meta_type_node &parent, const id_ty
return parent.details->func.insert_or_assign(id, std::move(node)).first->second;
}
inline meta_prop_node &meta_extend(dense_map<id_type, meta_prop_node, identity> &prop, const id_type id, meta_prop_node node) {
return (prop[id] = std::move(node));
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
/**
* @brief Basic meta factory to be used for reflection purposes.
@@ -135,7 +107,7 @@ public:
/**
* @brief Assigns a custom unique identifier to a meta type.
* @param id A custom unique identifier.
* @return An extended meta factory for the given type.
* @return A meta factory for the given type.
*/
auto type(const id_type id) noexcept {
auto &&elem = internal::owner(*ctx, *info);
@@ -156,16 +128,8 @@ public:
template<typename Base>
auto base() noexcept {
static_assert(!std::is_same_v<Type, Base> && std::is_base_of_v<Base, Type>, "Invalid base type");
internal::meta_extend(
internal::owner(*ctx, *info),
type_id<Base>().hash(),
internal::meta_base_node{
&internal::resolve<Base>,
+[](const void *instance) noexcept {
return static_cast<const void *>(static_cast<const Base *>(static_cast<const Type *>(instance)));
}});
auto *const op = +[](const void *instance) noexcept { return static_cast<const void *>(static_cast<const Base *>(static_cast<const Type *>(instance))); };
internal::owner(*ctx, *info).details->base.insert_or_assign(type_id<Base>().hash(), internal::meta_base_node{&internal::resolve<Base>, op});
bucket = nullptr;
return *this;
}
@@ -185,15 +149,8 @@ public:
template<auto Candidate>
auto conv() noexcept {
using conv_type = std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(Candidate), Type &>>>;
internal::meta_extend(
internal::owner(*ctx, *info),
type_id<conv_type>().hash(),
internal::meta_conv_node{
+[](const meta_ctx &area, const void *instance) {
return forward_as_meta(area, std::invoke(Candidate, *static_cast<const Type *>(instance)));
}});
auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, std::invoke(Candidate, *static_cast<const Type *>(instance))); };
internal::owner(*ctx, *info).details->conv.insert_or_assign(type_id<conv_type>().hash(), internal::meta_conv_node{op});
bucket = nullptr;
return *this;
}
@@ -210,15 +167,8 @@ public:
template<typename To>
auto conv() noexcept {
using conv_type = std::remove_cv_t<std::remove_reference_t<To>>;
internal::meta_extend(
internal::owner(*ctx, *info),
type_id<conv_type>().hash(),
internal::meta_conv_node{
+[](const meta_ctx &area, const void *instance) {
return forward_as_meta(area, static_cast<To>(*static_cast<const Type *>(instance)));
}});
auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, static_cast<To>(*static_cast<const Type *>(instance))); };
internal::owner(*ctx, *info).details->conv.insert_or_assign(type_id<conv_type>().hash(), internal::meta_conv_node{op});
bucket = nullptr;
return *this;
}
@@ -234,22 +184,14 @@ public:
*
* @tparam Candidate The actual function to use as a constructor.
* @tparam Policy Optional policy (no policy set by default).
* @return An extended meta factory for the parent type.
* @return A meta factory for the parent type.
*/
template<auto Candidate, typename Policy = as_is_t>
auto ctor() noexcept {
using descriptor = meta_function_helper_t<Type, decltype(Candidate)>;
static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy");
static_assert(std::is_same_v<std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>, Type>, "The function doesn't return an object of the required type");
internal::meta_extend(
internal::owner(*ctx, *info),
type_id<typename descriptor::args_type>().hash(),
internal::meta_ctor_node{
descriptor::args_type::size,
&meta_arg<typename descriptor::args_type>,
&meta_construct<Type, Candidate, Policy>});
internal::owner(*ctx, *info).details->ctor.insert_or_assign(type_id<typename descriptor::args_type>().hash(), internal::meta_ctor_node{descriptor::args_type::size, &meta_arg<typename descriptor::args_type>, &meta_construct<Type, Candidate, Policy>});
bucket = nullptr;
return *this;
}
@@ -262,21 +204,14 @@ public:
* type that can be invoked with parameters whose types are those given.
*
* @tparam Args Types of arguments to use to construct an instance.
* @return An extended meta factory for the parent type.
* @return A meta factory for the parent type.
*/
template<typename... Args>
auto ctor() noexcept {
// default constructor is already implicitly generated, no need for redundancy
if constexpr(sizeof...(Args) != 0u) {
using descriptor = meta_function_helper_t<Type, Type (*)(Args...)>;
internal::meta_extend(
internal::owner(*ctx, *info),
type_id<typename descriptor::args_type>().hash(),
internal::meta_ctor_node{
descriptor::args_type::size,
&meta_arg<typename descriptor::args_type>,
&meta_construct<Type, Args...>});
internal::owner(*ctx, *info).details->ctor.insert_or_assign(type_id<typename descriptor::args_type>().hash(), internal::meta_ctor_node{descriptor::args_type::size, &meta_arg<typename descriptor::args_type>, &meta_construct<Type, Args...>});
}
bucket = nullptr;
@@ -304,12 +239,8 @@ public:
template<auto Func>
auto dtor() noexcept {
static_assert(std::is_invocable_v<decltype(Func), Type &>, "The function doesn't accept an object of the type provided");
internal::meta_extend(
internal::owner(*ctx, *info),
internal::meta_dtor_node{
+[](void *instance) { std::invoke(Func, *static_cast<Type *>(instance)); }});
auto *const op = +[](void *instance) { std::invoke(Func, *static_cast<Type *>(instance)); };
internal::owner(*ctx, *info).dtor = internal::meta_dtor_node{op};
bucket = nullptr;
return *this;
}
@@ -325,37 +256,44 @@ public:
* @tparam Data The actual variable to attach to the meta type.
* @tparam Policy Optional policy (no policy set by default).
* @param id Unique identifier.
* @return An extended meta factory for the parent type.
* @return A meta factory for the parent type.
*/
template<auto Data, typename Policy = as_is_t>
auto data(const id_type id) noexcept {
if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
using data_type = std::remove_reference_t<std::invoke_result_t<decltype(Data), Type &>>;
using data_type = std::invoke_result_t<decltype(Data), Type &>;
static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
auto &&elem = internal::meta_extend(
internal::owner(*ctx, *info),
id,
internal::meta_data_node{
/* this is never static */
std::is_const_v<data_type> ? internal::meta_traits::is_const : internal::meta_traits::is_none,
std::is_const_v<std::remove_reference_t<data_type>> ? internal::meta_traits::is_const : internal::meta_traits::is_none,
1u,
&internal::resolve<std::remove_cv_t<data_type>>,
&meta_arg<type_list<std::remove_cv_t<data_type>>>,
&internal::resolve<std::remove_cv_t<std::remove_reference_t<data_type>>>,
&meta_arg<type_list<std::remove_cv_t<std::remove_reference_t<data_type>>>>,
&meta_setter<Type, Data>,
&meta_getter<Type, Data, Policy>});
bucket = &elem.prop;
} else {
using data_type = std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>;
using data_type = std::remove_pointer_t<decltype(Data)>;
if constexpr(std::is_pointer_v<decltype(Data)>) {
static_assert(Policy::template value<decltype(*Data)>, "Invalid return type for the given policy");
} else {
static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
}
auto &&elem = internal::meta_extend(
internal::owner(*ctx, *info),
id,
internal::meta_data_node{
((std::is_same_v<Type, std::remove_cv_t<data_type>> || std::is_const_v<data_type>) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static,
((std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<data_type>>> || std::is_const_v<std::remove_reference_t<data_type>>) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static,
1u,
&internal::resolve<std::remove_cv_t<data_type>>,
&meta_arg<type_list<std::remove_cv_t<data_type>>>,
&internal::resolve<std::remove_cv_t<std::remove_reference_t<data_type>>>,
&meta_arg<type_list<std::remove_cv_t<std::remove_reference_t<data_type>>>>,
&meta_setter<Type, Data>,
&meta_getter<Type, Data, Policy>});
@@ -383,7 +321,7 @@ public:
* @tparam Getter The actual function to use as a getter.
* @tparam Policy Optional policy (no policy set by default).
* @param id Unique identifier.
* @return An extended meta factory for the parent type.
* @return A meta factory for the parent type.
*/
template<auto Setter, auto Getter, typename Policy = as_is_t>
auto data(const id_type id) noexcept {
@@ -440,7 +378,7 @@ public:
* @tparam Getter The actual getter function.
* @tparam Policy Optional policy (no policy set by default).
* @param id Unique identifier.
* @return An extended meta factory for the parent type.
* @return A meta factory for the parent type.
*/
template<typename Setter, auto Getter, typename Policy = as_is_t>
auto data(const id_type id) noexcept {
@@ -459,7 +397,7 @@ public:
* @tparam Candidate The actual function to attach to the meta type.
* @tparam Policy Optional policy (no policy set by default).
* @param id Unique identifier.
* @return An extended meta factory for the parent type.
* @return A meta factory for the parent type.
*/
template<auto Candidate, typename Policy = as_is_t>
auto func(const id_type id) noexcept {
@@ -488,25 +426,18 @@ public:
* @tparam Value Optional type of the property value.
* @param id Property key.
* @param value Optional property value.
* @return An extended meta factory for the given type.
* @return A meta factory for the parent type.
*/
template<typename... Value>
meta_factory prop(id_type id, [[maybe_unused]] Value &&...value) {
ENTT_ASSERT(bucket != nullptr, "Meta object does not support properties");
if constexpr(sizeof...(Value) == 0u) {
internal::meta_extend(
*bucket,
id,
internal::meta_prop_node{
&internal::resolve<void>});
(*bucket)[id] = internal::meta_prop_node{&internal::resolve<void>};
} else {
internal::meta_extend(
*bucket,
id,
internal::meta_prop_node{
&internal::resolve<std::decay_t<Value>>...,
std::make_shared<std::decay_t<Value>>(std::forward<Value>(value))...});
(*bucket)[id] = internal::meta_prop_node{
&internal::resolve<std::decay_t<Value>>...,
std::make_shared<std::decay_t<Value>>(std::forward<Value>(value))...};
}
return *this;

File diff suppressed because it is too large Load Diff

View File

@@ -22,11 +22,7 @@ class meta_any;
class meta_type;
struct meta_handle;
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
enum class meta_traits : std::uint32_t {
@@ -131,6 +127,23 @@ struct meta_type_node {
std::shared_ptr<meta_type_descriptor> details{};
};
template<auto Member>
auto *look_for(const meta_context &context, const meta_type_node &node, const id_type id) {
if(node.details) {
if(const auto it = (node.details.get()->*Member).find(id); it != (node.details.get()->*Member).cend()) {
return &it->second;
}
for(auto &&curr: node.details->base) {
if(auto *elem = look_for<Member>(context, curr.second.type(context), id); elem) {
return elem;
}
}
}
return static_cast<typename std::remove_reference_t<decltype(node.details.get()->*Member)>::mapped_type *>(nullptr);
}
template<typename Type>
meta_type_node resolve(const meta_context &) noexcept;
@@ -159,6 +172,31 @@ template<typename... Args>
return nullptr;
}
template<typename Func>
[[nodiscard]] inline auto try_convert(const meta_context &context, const meta_type_node &from, const type_info &to, const bool arithmetic_or_enum, const void *instance, Func func) {
if(from.info && *from.info == to) {
return func(instance, from);
}
if(from.details) {
if(auto it = from.details->conv.find(to.hash()); it != from.details->conv.cend()) {
return func(instance, it->second);
}
for(auto &&curr: from.details->base) {
if(auto other = try_convert(context, curr.second.type(context), to, arithmetic_or_enum, curr.second.cast(instance), func); other) {
return other;
}
}
}
if(from.conversion_helper && arithmetic_or_enum) {
return func(instance, from.conversion_helper);
}
return func(instance);
}
[[nodiscard]] inline const meta_type_node *try_resolve(const meta_context &context, const type_info &info) noexcept {
const auto it = context.value.find(info.hash());
return it != context.value.end() ? &it->second : nullptr;
@@ -204,7 +242,7 @@ template<typename Type>
};
}
if constexpr(!std::is_same_v<Type, void> && !std::is_function_v<Type>) {
if constexpr(!std::is_void_v<Type> && !std::is_function_v<Type>) {
node.from_void = +[](const meta_ctx &ctx, void *element, const void *as_const) {
if(element) {
return meta_any{ctx, std::in_place_type<std::decay_t<Type> &>, *static_cast<std::decay_t<Type> *>(element)};
@@ -225,11 +263,7 @@ template<typename Type>
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
} // namespace entt

View File

@@ -7,58 +7,34 @@ namespace entt {
/*! @brief Empty class type used to request the _as ref_ policy. */
struct as_ref_t final {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
template<typename Type>
static constexpr bool value = std::is_reference_v<Type> && !std::is_const_v<std::remove_reference_t<Type>>;
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
};
/*! @brief Empty class type used to request the _as cref_ policy. */
struct as_cref_t final {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
template<typename Type>
static constexpr bool value = std::is_reference_v<Type>;
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
};
/*! @brief Empty class type used to request the _as-is_ policy. */
struct as_is_t final {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
template<typename>
static constexpr bool value = true;
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
};
/*! @brief Empty class type used to request the _as void_ policy. */
struct as_void_t final {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
template<typename>
static constexpr bool value = true;
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
};
/**
@@ -68,11 +44,7 @@ struct as_void_t final {
*/
template<typename Type>
struct is_meta_policy
: std::disjunction<
std::is_same<Type, as_ref_t>,
std::is_same<Type, as_cref_t>,
std::is_same<Type, as_is_t>,
std::is_same<Type, as_void_t>> {};
: std::bool_constant<std::is_same_v<Type, as_ref_t> || std::is_same_v<Type, as_cref_t> || std::is_same_v<Type, as_is_t> || std::is_same_v<Type, as_void_t>> {};
/**
* @brief Helper variable template.

View File

@@ -10,11 +10,7 @@
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename Type, typename It>
@@ -24,20 +20,21 @@ struct meta_range_iterator final {
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
using iterator_concept = std::random_access_iterator_tag;
meta_range_iterator() noexcept
constexpr meta_range_iterator() noexcept
: it{},
ctx{} {}
meta_range_iterator(const meta_ctx &area, const It iter) noexcept
constexpr meta_range_iterator(const meta_ctx &area, const It iter) noexcept
: it{iter},
ctx{&area} {}
meta_range_iterator &operator++() noexcept {
constexpr meta_range_iterator &operator++() noexcept {
return ++it, *this;
}
meta_range_iterator operator++(int) noexcept {
constexpr meta_range_iterator operator++(int) noexcept {
meta_range_iterator orig = *this;
return ++(*this), orig;
}
@@ -131,11 +128,7 @@ template<typename... Args>
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
/**
* @brief Iterable range to use to iterate all types of meta objects.

View File

@@ -30,7 +30,6 @@ struct meta_associative_container_traits;
/**
* @brief Provides the member constant `value` to true if a given type is a
* pointer-like type from the point of view of the meta system, false otherwise.
* @tparam Type Potentially pointer-like type.
*/
template<typename>
struct is_meta_pointer_like: std::false_type {};

View File

@@ -92,9 +92,12 @@ template<typename Type, typename Ret, typename MaybeType, typename... Args>
struct meta_function_descriptor<Type, Ret (*)(MaybeType, Args...)>
: meta_function_descriptor_traits<
Ret,
std::conditional_t<std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>, type_list<Args...>, type_list<MaybeType, Args...>>,
!std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>,
std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> && std::is_const_v<std::remove_reference_t<MaybeType>>> {};
std::conditional_t<
std::is_same_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>,
type_list<Args...>,
type_list<MaybeType, Args...>>,
!(std::is_same_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>),
std::is_const_v<std::remove_reference_t<MaybeType>> && (std::is_same_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>)> {};
/**
* @brief Meta function descriptor.
@@ -162,7 +165,7 @@ using meta_function_helper_t = typename meta_function_helper<Type, Candidate>::t
* @return A meta any containing the returned value, if any.
*/
template<typename Policy = as_is_t, typename Type>
std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) {
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) {
if constexpr(std::is_same_v<Policy, as_void_t>) {
return meta_any{ctx, std::in_place_type<void>};
} else if constexpr(std::is_same_v<Policy, as_ref_t>) {
@@ -183,7 +186,7 @@ std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(const meta_ct
* @return A meta any containing the returned value, if any.
*/
template<typename Policy = as_is_t, typename Type>
std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(Type &&value) {
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(Type &&value) {
return meta_dispatch<Policy, Type>(locator<meta_ctx>::value_or(), std::forward<Type>(value));
}
@@ -310,16 +313,12 @@ template<typename Type, auto Data, typename Policy = as_is_t>
return meta_getter<Type, Data, Policy>(locator<meta_ctx>::value_or(), std::move(instance));
}
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename Policy, typename Candidate, typename... Args>
[[nodiscard]] meta_any meta_invoke_with_args(const meta_ctx &ctx, Candidate &&candidate, Args &&...args) {
if constexpr(std::is_same_v<decltype(std::invoke(std::forward<Candidate>(candidate), args...)), void>) {
if constexpr(std::is_void_v<decltype(std::invoke(std::forward<Candidate>(candidate), args...))>) {
std::invoke(std::forward<Candidate>(candidate), args...);
return meta_any{ctx, std::in_place_type<void>};
} else {
@@ -336,7 +335,7 @@ template<typename Type, typename Policy, typename Candidate, std::size_t... Inde
return meta_invoke_with_args<Policy>(ctx, std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
}
} else if constexpr(std::is_invocable_v<std::remove_reference_t<Candidate>, Type &, type_list_element_t<Index, typename descriptor::args_type>...>) {
if(auto *const clazz = instance->try_cast<Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
if(auto *const clazz = instance->try_cast<Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) { // NOLINT
return meta_invoke_with_args<Policy>(ctx, std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
}
} else {
@@ -358,11 +357,7 @@ template<typename Type, typename... Args, std::size_t... Index>
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
/**
* @brief Tries to _invoke_ an object given a list of erased parameters.

View File

@@ -1,11 +1,7 @@
#ifndef ENTT_PLATFORM_ANDROID_NDK_R17_HPP
#define ENTT_PLATFORM_ANDROID_NDK_R17_HPP
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
#ifdef __ANDROID__
# include <android/ndk-version.h>
# if __NDK_MAJOR__ == 17
@@ -58,10 +54,6 @@ using invoke_result_t = typename std::invoke_result<Func, Args...>::type;
# endif
#endif
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
#endif

View File

@@ -19,7 +19,7 @@ struct poly_inspector {
* @brief Generic conversion operator (definition only).
* @tparam Type Type to which conversion is requested.
*/
template<class Type>
template<typename Type>
operator Type &&() const;
/**
@@ -190,7 +190,6 @@ decltype(auto) poly_call(Poly &&self, Args &&...args) {
*/
template<typename Concept, std::size_t Len, std::size_t Align>
class basic_poly: private Concept::template type<poly_base<basic_poly<Concept, Len, Align>>> {
/*! @brief A poly base is allowed to snoop into a poly object. */
friend struct poly_base<basic_poly>;
public:

View File

@@ -1,13 +1,19 @@
#ifndef ENTT_PROCESS_FWD_HPP
#define ENTT_PROCESS_FWD_HPP
#include <cstdint>
#include <memory>
namespace entt {
template<typename, typename>
class process;
template<typename>
class scheduler;
template<typename = std::uint32_t, typename = std::allocator<void>>
class basic_scheduler;
/*! @brief Alias declaration for the most common use case. */
using scheduler = basic_scheduler<>;
} // namespace entt

View File

@@ -4,6 +4,7 @@
#include <cstdint>
#include <type_traits>
#include <utility>
#include "fwd.hpp"
namespace entt {
@@ -175,13 +176,13 @@ public:
* The function is idempotent and it does nothing if the process isn't
* alive.
*
* @param immediately Requests an immediate operation.
* @param immediate Requests an immediate operation.
*/
void abort(const bool immediately = false) {
void abort(const bool immediate = false) {
if(alive()) {
current = state::aborted;
if(immediately) {
if(immediate) {
tick({});
}
}

View File

@@ -1,16 +1,56 @@
#ifndef ENTT_PROCESS_SCHEDULER_HPP
#define ENTT_PROCESS_SCHEDULER_HPP
#include <algorithm>
#include <iterator>
#include <cstddef>
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../core/compressed_pair.hpp"
#include "fwd.hpp"
#include "process.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename Delta>
struct basic_process_handler {
virtual ~basic_process_handler() = default;
virtual bool update(const Delta, void *) = 0;
virtual void abort(const bool) = 0;
// std::shared_ptr because of its type erased allocator which is useful here
std::shared_ptr<basic_process_handler> next;
};
template<typename Delta, typename Type>
struct process_handler final: basic_process_handler<Delta> {
template<typename... Args>
process_handler(Args &&...args)
: process{std::forward<Args>(args)...} {}
bool update(const Delta delta, void *data) override {
if(process.tick(delta, data); process.rejected()) {
this->next.reset();
}
return (process.rejected() || process.finished());
}
void abort(const bool immediate) override {
process.abort(immediate);
}
Type process;
};
} // namespace internal
/*! @endcond */
/**
* @brief Cooperative scheduler for processes.
*
@@ -36,93 +76,90 @@ namespace entt {
* @sa process
*
* @tparam Delta Type to use to provide elapsed time.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Delta>
class scheduler {
struct process_handler {
using instance_type = std::unique_ptr<void, void (*)(void *)>;
using update_fn_type = bool(scheduler &, std::size_t, Delta, void *);
using abort_fn_type = void(scheduler &, std::size_t, bool);
using next_type = std::unique_ptr<process_handler>;
template<typename Delta, typename Allocator>
class basic_scheduler {
template<typename Type>
using handler_type = internal::process_handler<Delta, Type>;
instance_type instance;
update_fn_type *update;
abort_fn_type *abort;
next_type next;
};
// std::shared_ptr because of its type erased allocator which is useful here
using process_type = std::shared_ptr<internal::basic_process_handler<Delta>>;
struct continuation {
continuation(process_handler *ref) noexcept
: handler{ref} {}
template<typename Proc, typename... Args>
continuation then(Args &&...args) {
static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type");
auto proc = typename process_handler::instance_type{new Proc{std::forward<Args>(args)...}, &scheduler::deleter<Proc>};
handler->next.reset(new process_handler{std::move(proc), &scheduler::update<Proc>, &scheduler::abort<Proc>, nullptr});
handler = handler->next.get();
return *this;
}
template<typename Func>
continuation then(Func &&func) {
return then<process_adaptor<std::decay_t<Func>, Delta>>(std::forward<Func>(func));
}
private:
process_handler *handler;
};
template<typename Proc>
[[nodiscard]] static bool update(scheduler &owner, std::size_t pos, const Delta delta, void *data) {
auto *process = static_cast<Proc *>(owner.handlers[pos].instance.get());
process->tick(delta, data);
if(process->rejected()) {
return true;
} else if(process->finished()) {
if(auto &&handler = owner.handlers[pos]; handler.next) {
handler = std::move(*handler.next);
// forces the process to exit the uninitialized state
return handler.update(owner, pos, {}, nullptr);
}
return true;
}
return false;
}
template<typename Proc>
static void abort(scheduler &owner, std::size_t pos, const bool immediately) {
static_cast<Proc *>(owner.handlers[pos].instance.get())->abort(immediately);
}
template<typename Proc>
static void deleter(void *proc) {
delete static_cast<Proc *>(proc);
}
using alloc_traits = std::allocator_traits<Allocator>;
using container_allocator = typename alloc_traits::template rebind_alloc<process_type>;
using container_type = std::vector<process_type, container_allocator>;
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Unsigned integer type. */
using delta_type = Delta;
/*! @brief Default constructor. */
scheduler()
: handlers{} {}
basic_scheduler()
: basic_scheduler{allocator_type{}} {}
/*! @brief Default move constructor. */
scheduler(scheduler &&) = default;
/**
* @brief Constructs a scheduler with a given allocator.
* @param allocator The allocator to use.
*/
explicit basic_scheduler(const allocator_type &allocator)
: handlers{allocator, allocator} {}
/*! @brief Default move assignment operator. @return This scheduler. */
scheduler &operator=(scheduler &&) = default;
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
basic_scheduler(basic_scheduler &&other) noexcept
: handlers{std::move(other.handlers)} {}
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
basic_scheduler(basic_scheduler &&other, const allocator_type &allocator) noexcept
: handlers{container_type{std::move(other.handlers.first()), allocator}, allocator} {
ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying a scheduler is not allowed");
}
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This scheduler.
*/
basic_scheduler &operator=(basic_scheduler &&other) noexcept {
ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying a scheduler is not allowed");
handlers = std::move(other.handlers);
return *this;
}
/**
* @brief Exchanges the contents with those of a given scheduler.
* @param other Scheduler to exchange the content with.
*/
void swap(basic_scheduler &other) {
using std::swap;
swap(handlers, other.handlers);
}
/**
* @brief Returns the associated allocator.
* @return The associated allocator.
*/
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
return handlers.second();
}
/**
* @brief Number of processes currently scheduled.
* @return Number of processes currently scheduled.
*/
[[nodiscard]] size_type size() const noexcept {
return handlers.size();
return handlers.first().size();
}
/**
@@ -130,7 +167,7 @@ public:
* @return True if there are scheduled processes, false otherwise.
*/
[[nodiscard]] bool empty() const noexcept {
return handlers.empty();
return handlers.first().empty();
}
/**
@@ -140,15 +177,15 @@ public:
* and never executed again.
*/
void clear() {
handlers.clear();
handlers.first().clear();
}
/**
* @brief Schedules a process for the next tick.
*
* Returned value is an opaque object that can be used to attach a child to
* the given process. The child is automatically scheduled when the process
* terminates and only if the process returns with success.
* Returned value can be used to attach a continuation for the last process.
* The continutation is scheduled automatically when the process terminates
* and only if the process returns with success.
*
* Example of use (pseudocode):
*
@@ -166,16 +203,15 @@ public:
* @tparam Proc Type of process to schedule.
* @tparam Args Types of arguments to use to initialize the process.
* @param args Parameters to use to initialize the process.
* @return An opaque object to use to concatenate processes.
* @return This process scheduler.
*/
template<typename Proc, typename... Args>
auto attach(Args &&...args) {
basic_scheduler &attach(Args &&...args) {
static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type");
auto proc = typename process_handler::instance_type{new Proc{std::forward<Args>(args)...}, &scheduler::deleter<Proc>};
auto &&ref = handlers.emplace_back(process_handler{std::move(proc), &scheduler::update<Proc>, &scheduler::abort<Proc>, nullptr});
auto &ref = handlers.first().emplace_back(std::allocate_shared<handler_type<Proc>>(handlers.second(), std::forward<Args>(args)...));
// forces the process to exit the uninitialized state
ref.update(*this, handlers.size() - 1u, {}, nullptr);
return continuation{&handlers.back()};
ref->update({}, nullptr);
return *this;
}
/**
@@ -204,9 +240,9 @@ public:
* void();
* @endcode
*
* Returned value is an opaque object that can be used to attach a child to
* the given process. The child is automatically scheduled when the process
* terminates and only if the process returns with success.
* Returned value can be used to attach a continuation for the last process.
* The continutation is scheduled automatically when the process terminates
* and only if the process returns with success.
*
* Example of use (pseudocode):
*
@@ -227,14 +263,43 @@ public:
*
* @tparam Func Type of process to schedule.
* @param func Either a lambda or a functor to use as a process.
* @return An opaque object to use to concatenate processes.
* @return This process scheduler.
*/
template<typename Func>
auto attach(Func &&func) {
basic_scheduler &attach(Func &&func) {
using Proc = process_adaptor<std::decay_t<Func>, Delta>;
return attach<Proc>(std::forward<Func>(func));
}
/**
* @brief Sets a process as a continuation of the last scheduled process.
* @tparam Proc Type of process to use as a continuation.
* @tparam Args Types of arguments to use to initialize the process.
* @param args Parameters to use to initialize the process.
* @return This process scheduler.
*/
template<typename Proc, typename... Args>
basic_scheduler &then(Args &&...args) {
static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type");
ENTT_ASSERT(!handlers.first().empty(), "Process not available");
auto *curr = handlers.first().back().get();
for(; curr->next; curr = curr->next.get()) {}
curr->next = std::allocate_shared<handler_type<Proc>>(handlers.second(), std::forward<Args>(args)...);
return *this;
}
/**
* @brief Sets a process as a continuation of the last scheduled process.
* @tparam Func Type of process to use as a continuation.
* @param func Either a lambda or a functor to use as a process.
* @return This process scheduler.
*/
template<typename Func>
basic_scheduler &then(Func &&func) {
using Proc = process_adaptor<std::decay_t<Func>, Delta>;
return then<Proc>(std::forward<Func>(func));
}
/**
* @brief Updates all scheduled processes.
*
@@ -246,13 +311,18 @@ public:
* @param delta Elapsed time.
* @param data Optional data.
*/
void update(const Delta delta, void *data = nullptr) {
for(auto pos = handlers.size(); pos; --pos) {
const auto curr = pos - 1u;
if(const auto dead = handlers[curr].update(*this, curr, delta, data); dead) {
std::swap(handlers[curr], handlers.back());
handlers.pop_back();
void update(const delta_type delta, void *data = nullptr) {
for(auto next = handlers.first().size(); next; --next) {
if(const auto pos = next - 1u; handlers.first()[pos]->update(delta, data)) {
// updating might spawn/reallocate, cannot hold refs until here
if(auto &curr = handlers.first()[pos]; curr->next) {
curr = std::move(curr->next);
// forces the process to exit the uninitialized state
curr->update({}, nullptr);
} else {
curr = std::move(handlers.first().back());
handlers.first().pop_back();
}
}
}
}
@@ -265,17 +335,16 @@ public:
* Once a process is fully aborted and thus finished, it's discarded along
* with its child, if any.
*
* @param immediately Requests an immediate operation.
* @param immediate Requests an immediate operation.
*/
void abort(const bool immediately = false) {
for(auto pos = handlers.size(); pos; --pos) {
const auto curr = pos - 1u;
handlers[curr].abort(*this, curr, immediately);
void abort(const bool immediate = false) {
for(auto &&curr: handlers.first()) {
curr->abort(immediate);
}
}
private:
std::vector<process_handler> handlers{};
compressed_pair<container_type, allocator_type> handlers;
};
} // namespace entt

View File

@@ -19,11 +19,7 @@
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename Type, typename It>
@@ -37,6 +33,7 @@ public:
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
using iterator_concept = std::random_access_iterator_tag;
constexpr resource_cache_iterator() noexcept = default;
@@ -95,60 +92,56 @@ public:
return operator*();
}
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
friend constexpr std::ptrdiff_t operator-(const resource_cache_iterator<TLhs, ILhs> &, const resource_cache_iterator<TRhs, IRhs> &) noexcept;
template<typename... Lhs, typename... Rhs>
friend constexpr std::ptrdiff_t operator-(const resource_cache_iterator<Lhs...> &, const resource_cache_iterator<Rhs...> &) noexcept;
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
friend constexpr bool operator==(const resource_cache_iterator<TLhs, ILhs> &, const resource_cache_iterator<TRhs, IRhs> &) noexcept;
template<typename... Lhs, typename... Rhs>
friend constexpr bool operator==(const resource_cache_iterator<Lhs...> &, const resource_cache_iterator<Rhs...> &) noexcept;
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
friend constexpr bool operator<(const resource_cache_iterator<TLhs, ILhs> &, const resource_cache_iterator<TRhs, IRhs> &) noexcept;
template<typename... Lhs, typename... Rhs>
friend constexpr bool operator<(const resource_cache_iterator<Lhs...> &, const resource_cache_iterator<Rhs...> &) noexcept;
private:
It it;
};
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) noexcept {
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
return lhs.it - rhs.it;
}
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
[[nodiscard]] constexpr bool operator==(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) noexcept {
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator==(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
return lhs.it == rhs.it;
}
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
[[nodiscard]] constexpr bool operator!=(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) noexcept {
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator!=(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
return !(lhs == rhs);
}
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
[[nodiscard]] constexpr bool operator<(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) noexcept {
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator<(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
return lhs.it < rhs.it;
}
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
[[nodiscard]] constexpr bool operator>(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) noexcept {
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator>(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
return rhs < lhs;
}
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
[[nodiscard]] constexpr bool operator<=(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) noexcept {
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator<=(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
return !(lhs > rhs);
}
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
[[nodiscard]] constexpr bool operator>=(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) noexcept {
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator>=(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
return !(lhs < rhs);
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
/**
* @brief Basic cache for resources of any type.
@@ -158,7 +151,7 @@ template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
*/
template<typename Type, typename Loader, typename Allocator>
class resource_cache {
using alloc_traits = typename std::allocator_traits<Allocator>;
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
using container_allocator = typename alloc_traits::template rebind_alloc<std::pair<const id_type, typename Loader::result_type>>;
using container_type = dense_map<id_type, typename Loader::result_type, identity, std::equal_to<id_type>, container_allocator>;
@@ -241,8 +234,7 @@ public:
/**
* @brief Returns an iterator to the beginning.
*
* The returned iterator points to the first instance of the cache. If the
* cache is empty, the returned iterator will be equal to `end()`.
* If the cache is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first instance of the internal cache.
*/
@@ -262,11 +254,6 @@ public:
/**
* @brief Returns an iterator to the end.
*
* The returned iterator points to the element following the last instance
* of the cache. Attempting to dereference the returned iterator results in
* undefined behavior.
*
* @return An iterator to the element following the last instance of the
* internal cache.
*/

View File

@@ -20,7 +20,6 @@ namespace entt {
*/
template<typename Type>
class resource {
/*! @brief Resource handles are friends with each other. */
template<typename>
friend class resource;
@@ -163,81 +162,81 @@ private:
/**
* @brief Compares two handles.
* @tparam Res Type of resource managed by the first handle.
* @tparam Other Type of resource managed by the second handle.
* @tparam Lhs Type of resource managed by the first handle.
* @tparam Rhs Type of resource managed by the second handle.
* @param lhs A valid handle.
* @param rhs A valid handle.
* @return True if both handles refer to the same resource, false otherwise.
*/
template<typename Res, typename Other>
[[nodiscard]] bool operator==(const resource<Res> &lhs, const resource<Other> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] bool operator==(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
return (std::addressof(*lhs) == std::addressof(*rhs));
}
/**
* @brief Compares two handles.
* @tparam Res Type of resource managed by the first handle.
* @tparam Other Type of resource managed by the second handle.
* @tparam Lhs Type of resource managed by the first handle.
* @tparam Rhs Type of resource managed by the second handle.
* @param lhs A valid handle.
* @param rhs A valid handle.
* @return False if both handles refer to the same registry, true otherwise.
* @return False if both handles refer to the same resource, true otherwise.
*/
template<typename Res, typename Other>
[[nodiscard]] bool operator!=(const resource<Res> &lhs, const resource<Other> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] bool operator!=(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
return !(lhs == rhs);
}
/**
* @brief Compares two handles.
* @tparam Res Type of resource managed by the first handle.
* @tparam Other Type of resource managed by the second handle.
* @tparam Lhs Type of resource managed by the first handle.
* @tparam Rhs Type of resource managed by the second handle.
* @param lhs A valid handle.
* @param rhs A valid handle.
* @return True if the first handle is less than the second, false otherwise.
*/
template<typename Res, typename Other>
[[nodiscard]] bool operator<(const resource<Res> &lhs, const resource<Other> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] bool operator<(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
return (std::addressof(*lhs) < std::addressof(*rhs));
}
/**
* @brief Compares two handles.
* @tparam Res Type of resource managed by the first handle.
* @tparam Other Type of resource managed by the second handle.
* @tparam Lhs Type of resource managed by the first handle.
* @tparam Rhs Type of resource managed by the second handle.
* @param lhs A valid handle.
* @param rhs A valid handle.
* @return True if the first handle is greater than the second, false otherwise.
*/
template<typename Res, typename Other>
[[nodiscard]] bool operator>(const resource<Res> &lhs, const resource<Other> &rhs) noexcept {
return (std::addressof(*lhs) > std::addressof(*rhs));
template<typename Lhs, typename Rhs>
[[nodiscard]] bool operator>(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
return rhs < lhs;
}
/**
* @brief Compares two handles.
* @tparam Res Type of resource managed by the first handle.
* @tparam Other Type of resource managed by the second handle.
* @tparam Lhs Type of resource managed by the first handle.
* @tparam Rhs Type of resource managed by the second handle.
* @param lhs A valid handle.
* @param rhs A valid handle.
* @return True if the first handle is less than or equal to the second, false
* otherwise.
*/
template<typename Res, typename Other>
[[nodiscard]] bool operator<=(const resource<Res> &lhs, const resource<Other> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] bool operator<=(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
return !(lhs > rhs);
}
/**
* @brief Compares two handles.
* @tparam Res Type of resource managed by the first handle.
* @tparam Other Type of resource managed by the second handle.
* @tparam Lhs Type of resource managed by the first handle.
* @tparam Rhs Type of resource managed by the second handle.
* @param lhs A valid handle.
* @param rhs A valid handle.
* @return True if the first handle is greater than or equal to the second,
* false otherwise.
*/
template<typename Res, typename Other>
[[nodiscard]] bool operator>=(const resource<Res> &lhs, const resource<Other> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] bool operator>=(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
return !(lhs < rhs);
}

View File

@@ -12,11 +12,7 @@
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename Ret, typename... Args>
@@ -43,11 +39,7 @@ template<typename... Class, typename Ret, typename... Args>
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
/**
* @brief Basic delegate implementation.
@@ -76,7 +68,13 @@ class delegate<Ret(Args...)> {
[[nodiscard]] auto wrap(std::index_sequence<Index...>) noexcept {
return [](const void *, Args... args) -> Ret {
[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
return static_cast<Ret>(std::invoke(Candidate, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), type_list_element_t<Index, type_list<Args...>>...>) {
return static_cast<Ret>(std::invoke(Candidate, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
} else {
constexpr auto offset = sizeof...(Args) - sizeof...(Index);
return static_cast<Ret>(std::invoke(Candidate, std::forward<type_list_element_t<Index + offset, type_list<Args...>>>(std::get<Index + offset>(arguments))...));
}
};
}
@@ -85,7 +83,13 @@ class delegate<Ret(Args...)> {
return [](const void *payload, Args... args) -> Ret {
[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
return static_cast<Ret>(std::invoke(Candidate, *curr, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type &, type_list_element_t<Index, type_list<Args...>>...>) {
return static_cast<Ret>(std::invoke(Candidate, *curr, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
} else {
constexpr auto offset = sizeof...(Args) - sizeof...(Index);
return static_cast<Ret>(std::invoke(Candidate, *curr, std::forward<type_list_element_t<Index + offset, type_list<Args...>>>(std::get<Index + offset>(arguments))...));
}
};
}
@@ -94,7 +98,13 @@ class delegate<Ret(Args...)> {
return [](const void *payload, Args... args) -> Ret {
[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
return static_cast<Ret>(std::invoke(Candidate, curr, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type *, type_list_element_t<Index, type_list<Args...>>...>) {
return static_cast<Ret>(std::invoke(Candidate, curr, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
} else {
constexpr auto offset = sizeof...(Args) - sizeof...(Index);
return static_cast<Ret>(std::invoke(Candidate, curr, std::forward<type_list_element_t<Index + offset, type_list<Args...>>>(std::get<Index + offset>(arguments))...));
}
};
}
@@ -233,6 +243,14 @@ public:
fn = nullptr;
}
/**
* @brief Returns a pointer to the stored callable function target, if any.
* @return An opaque pointer to the stored callable function target.
*/
[[nodiscard]] function_type *target() const noexcept {
return fn;
}
/**
* @brief Returns the instance or the payload linked to a delegate, if any.
* @return An opaque pointer to the underlying data.

View File

@@ -17,11 +17,7 @@
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
struct basic_dispatcher_handler {
@@ -75,14 +71,14 @@ public:
template<typename... Args>
void enqueue(Args &&...args) {
if constexpr(std::is_aggregate_v<Type>) {
if constexpr(std::is_aggregate_v<Type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<Type>)) {
events.push_back(Type{std::forward<Args>(args)...});
} else {
events.emplace_back(std::forward<Args>(args)...);
}
}
std::size_t size() const noexcept override {
[[nodiscard]] std::size_t size() const noexcept override {
return events.size();
}
@@ -92,11 +88,7 @@ private:
};
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @endcond */
/**
* @brief Basic dispatcher implementation.
@@ -179,7 +171,9 @@ public:
* @param allocator The allocator to use.
*/
basic_dispatcher(basic_dispatcher &&other, const allocator_type &allocator) noexcept
: pools{container_type{std::move(other.pools.first()), allocator}, allocator} {}
: pools{container_type{std::move(other.pools.first()), allocator}, allocator} {
ENTT_ASSERT(alloc_traits::is_always_equal::value || pools.second() == other.pools.second(), "Copying a dispatcher is not allowed");
}
/**
* @brief Move assignment operator.
@@ -187,6 +181,7 @@ public:
* @return This dispatcher.
*/
basic_dispatcher &operator=(basic_dispatcher &&other) noexcept {
ENTT_ASSERT(alloc_traits::is_always_equal::value || pools.second() == other.pools.second(), "Copying a dispatcher is not allowed");
pools = std::move(other.pools);
return *this;
}

View File

@@ -76,7 +76,9 @@ public:
* @param allocator The allocator to use.
*/
emitter(emitter &&other, const allocator_type &allocator) noexcept
: handlers{container_type{std::move(other.handlers.first()), allocator}, allocator} {}
: handlers{container_type{std::move(other.handlers.first()), allocator}, allocator} {
ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying an emitter is not allowed");
}
/**
* @brief Move assignment operator.
@@ -84,6 +86,8 @@ public:
* @return This dispatcher.
*/
emitter &operator=(emitter &&other) noexcept {
ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying an emitter is not allowed");
handlers = std::move(other.handlers);
return *this;
}

View File

@@ -1,8 +1,8 @@
#ifndef ENTT_SIGNAL_SIGH_HPP
#define ENTT_SIGNAL_SIGH_HPP
#include <algorithm>
#include <functional>
#include <cstddef>
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
@@ -52,11 +52,11 @@ class sigh;
*/
template<typename Ret, typename... Args, typename Allocator>
class sigh<Ret(Args...), Allocator> {
/*! @brief A sink is allowed to modify a signal. */
friend class sink<sigh<Ret(Args...), Allocator>>;
using alloc_traits = std::allocator_traits<Allocator>;
using container_type = std::vector<delegate<Ret(Args...)>, typename alloc_traits::template rebind_alloc<delegate<Ret(Args...)>>>;
using delegate_type = delegate<Ret(Args...)>;
using container_type = std::vector<delegate_type, typename alloc_traits::template rebind_alloc<delegate_type>>;
public:
/*! @brief Allocator type. */
@@ -168,8 +168,8 @@ public:
* @param args Arguments to use to invoke listeners.
*/
void publish(Args... args) const {
for(auto &&call: std::as_const(calls)) {
call(args...);
for(auto pos = calls.size(); pos; --pos) {
calls[pos - 1u](args...);
}
}
@@ -189,24 +189,24 @@ public:
*/
template<typename Func>
void collect(Func func, Args... args) const {
for(auto &&call: calls) {
if constexpr(std::is_void_v<Ret>) {
for(auto pos = calls.size(); pos; --pos) {
if constexpr(std::is_void_v<Ret> || !std::is_invocable_v<Func, Ret>) {
calls[pos - 1u](args...);
if constexpr(std::is_invocable_r_v<bool, Func>) {
call(args...);
if(func()) {
break;
}
} else {
call(args...);
func();
}
} else {
if constexpr(std::is_invocable_r_v<bool, Func, Ret>) {
if(func(call(args...))) {
if(func(calls[pos - 1u](args...))) {
break;
}
} else {
func(call(args...));
func(calls[pos - 1u](args...));
}
}
}
@@ -224,7 +224,6 @@ private:
* the sink that generated it.
*/
class connection {
/*! @brief A sink is allowed to create connection objects. */
template<typename>
friend class sink;
@@ -358,6 +357,7 @@ private:
template<typename Ret, typename... Args, typename Allocator>
class sink<sigh<Ret(Args...), Allocator>> {
using signal_type = sigh<Ret(Args...), Allocator>;
using delegate_type = typename signal_type::delegate_type;
using difference_type = typename signal_type::container_type::difference_type;
template<auto Candidate, typename Type>
@@ -370,13 +370,14 @@ class sink<sigh<Ret(Args...), Allocator>> {
sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>();
}
auto before(delegate<Ret(Args...)> call) {
const auto &calls = signal->calls;
const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call));
sink other{*this};
other.offset = calls.cend() - it;
return other;
template<typename Func>
void disconnect_if(Func callback) {
for(auto pos = signal->calls.size(); pos; --pos) {
if(auto &elem = signal->calls[pos - 1u]; callback(elem)) {
elem = std::move(signal->calls.back());
signal->calls.pop_back();
}
}
}
public:
@@ -385,8 +386,7 @@ public:
* @param ref A valid reference to a signal object.
*/
sink(sigh<Ret(Args...), Allocator> &ref) noexcept
: offset{},
signal{&ref} {}
: signal{&ref} {}
/**
* @brief Returns false if at least a listener is connected to the sink.
@@ -396,89 +396,9 @@ public:
return signal->calls.empty();
}
/**
* @brief Returns a sink that connects before a given free function or an
* unbound member.
* @tparam Function A valid free function pointer.
* @return A properly initialized sink object.
*/
template<auto Function>
[[nodiscard]] sink before() {
delegate<Ret(Args...)> call{};
call.template connect<Function>();
return before(std::move(call));
}
/**
* @brief Returns a sink that connects before a free function with payload
* or a bound member.
* @tparam Candidate Member or free function to look for.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid object that fits the purpose.
* @return A properly initialized sink object.
*/
template<auto Candidate, typename Type>
[[nodiscard]] sink before(Type &&value_or_instance) {
delegate<Ret(Args...)> call{};
call.template connect<Candidate>(value_or_instance);
return before(std::move(call));
}
/**
* @brief Returns a sink that connects before a given instance or specific
* payload.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid object that fits the purpose.
* @return A properly initialized sink object.
*/
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<std::remove_pointer_t<Type>>, void>, sink>>
[[nodiscard]] sink before(Type &value_or_instance) {
return before(&value_or_instance);
}
/**
* @brief Returns a sink that connects before a given instance or specific
* payload.
* @param value_or_instance A valid pointer that fits the purpose.
* @return A properly initialized sink object.
*/
[[nodiscard]] sink before(const void *value_or_instance) {
sink other{*this};
if(value_or_instance) {
const auto &calls = signal->calls;
const auto it = std::find_if(calls.cbegin(), calls.cend(), [value_or_instance](const auto &delegate) {
return delegate.data() == value_or_instance;
});
other.offset = calls.cend() - it;
}
return other;
}
/**
* @brief Returns a sink that connects before anything else.
* @return A properly initialized sink object.
*/
[[nodiscard]] sink before() {
sink other{*this};
other.offset = signal->calls.size();
return other;
}
/**
* @brief Connects a free function (with or without payload), a bound or an
* unbound member to a signal.
*
* The signal isn't responsible for the connected object or the payload, if
* any. Users must guarantee that the lifetime of the instance overcomes the
* one of the signal. On the other side, the signal handler performs
* checks to avoid multiple connections for the same function.<br/>
* When used to connect a free function with payload, its signature must be
* such that the instance is the first argument before the ones used to
* define the signal itself.
*
* @tparam Candidate Function or member to connect to the signal.
* @tparam Type Type of class or type of payload, if any.
* @param value_or_instance A valid object that fits the purpose, if any.
@@ -488,9 +408,9 @@ public:
connection connect(Type &&...value_or_instance) {
disconnect<Candidate>(value_or_instance...);
delegate<Ret(Args...)> call{};
delegate_type call{};
call.template connect<Candidate>(value_or_instance...);
signal->calls.insert(signal->calls.end() - offset, std::move(call));
signal->calls.push_back(std::move(call));
delegate<void(void *)> conn{};
conn.template connect<&release<Candidate, Type...>>(value_or_instance...);
@@ -506,21 +426,9 @@ public:
*/
template<auto Candidate, typename... Type>
void disconnect(Type &&...value_or_instance) {
auto &calls = signal->calls;
delegate<Ret(Args...)> call{};
delegate_type call{};
call.template connect<Candidate>(value_or_instance...);
calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end());
}
/**
* @brief Disconnects free functions with payload or bound members from a
* signal.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid object that fits the purpose.
*/
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<std::remove_pointer_t<Type>>, void>>>
void disconnect(Type &value_or_instance) {
disconnect(&value_or_instance);
disconnect_if([&call](const auto &elem) { return elem == call; });
}
/**
@@ -530,9 +438,7 @@ public:
*/
void disconnect(const void *value_or_instance) {
if(value_or_instance) {
auto &calls = signal->calls;
auto predicate = [value_or_instance](const auto &delegate) { return delegate.data() == value_or_instance; };
calls.erase(std::remove_if(calls.begin(), calls.end(), std::move(predicate)), calls.end());
disconnect_if([value_or_instance](const auto &elem) { return elem.data() == value_or_instance; });
}
}
@@ -542,7 +448,6 @@ public:
}
private:
difference_type offset;
signal_type *signal;
};

1
test/.bazelrc Normal file
View File

@@ -0,0 +1 @@
import "%workspace%/../.bazelrc"

0
test/BUILD.bazel Normal file
View File

View File

@@ -1,6 +1,4 @@
#
# Tests configuration
#
include(FetchContent)
include(CheckCXXSourceCompiles)
@@ -34,9 +32,16 @@ else()
add_library(GTest::Main ALIAS gtest_main)
target_compile_features(gtest PUBLIC cxx_std_17)
set_target_properties(gtest PROPERTIES CXX_CLANG_TIDY "")
target_compile_features(gtest_main PUBLIC cxx_std_17)
set_target_properties(gtest_main PROPERTIES CXX_CLANG_TIDY "")
target_compile_features(gmock PUBLIC cxx_std_17)
set_target_properties(gmock PROPERTIES CXX_CLANG_TIDY "")
target_compile_features(gmock_main PUBLIC cxx_std_17)
set_target_properties(gmock_main PROPERTIES CXX_CLANG_TIDY "")
endif()
include_directories($<TARGET_PROPERTY:EnTT,INTERFACE_INCLUDE_DIRECTORIES>)
@@ -51,10 +56,33 @@ function(SETUP_TARGET TARGET_NAME)
target_compile_options(
${TARGET_NAME}
PRIVATE
/EHsc /W1 /wd4996 /w14800
$<$<CONFIG:Debug>:/Od>
# vs2017 emits too many false positives for my tastes
$<IF:$<EQUAL:${MSVC_TOOLSET_VERSION},141>, /W1, /W4>
# clang-cl goes a little wrong with some warnings instead
$<$<STREQUAL:"${CMAKE_CXX_COMPILER_ID}","Clang">:
-Wno-deprecated-declarations
-Wno-ignored-qualifiers
-Wno-unknown-warning-option
-Wno-exceptions
-Wno-unused-local-typedef
-Wno-unused-private-field
>
# documentation diagnostic turned on for clang-cl only
$<$<STREQUAL:"${CMAKE_CXX_COMPILER_ID}","Clang">:-Wdocumentation>
# warnings from compilers that think I don't know what I'm doing
$<$<STREQUAL:"${CMAKE_CXX_COMPILER_ID}","Clang">:-Wcomma>
/EHsc /wd4324 /wd4996
# disabling INCREMENTAL is required by SizeBench
$<$<CONFIG:Debug>:/Od /INCREMENTAL:NO>
$<$<CONFIG:Release>:/O2>
)
target_link_options(
${TARGET_NAME}
PRIVATE
# disabling INCREMENTAL is required by SizeBench
$<$<CONFIG:Debug>:/INCREMENTAL:NO>
)
else()
target_compile_options(
${TARGET_NAME}
@@ -92,29 +120,36 @@ function(SETUP_BASIC_TEST TEST_NAME TEST_SOURCES)
target_link_libraries(${TEST_NAME} PRIVATE GTest::Main Threads::Threads)
SETUP_TARGET(${TEST_NAME} ${ARGN})
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME})
set_tests_properties(${TEST_NAME} PROPERTIES TIMEOUT 60)
endfunction()
function(SETUP_LIB_TEST TEST_NAME)
add_library(_${TEST_NAME} SHARED $<TARGET_OBJECTS:odr> lib/${TEST_NAME}/lib.cpp)
SETUP_TARGET(_${TEST_NAME} ENTT_API_EXPORT)
SETUP_BASIC_TEST(lib_${TEST_NAME} lib/${TEST_NAME}/main.cpp ENTT_API_IMPORT)
target_link_libraries(lib_${TEST_NAME} PRIVATE _${TEST_NAME})
function(SETUP_LIB_SHARED_TEST TEST_NAME SUB_PATH)
set(TARGET_NAME ${TEST_NAME}_${SUB_PATH})
add_library(_${TARGET_NAME} SHARED $<TARGET_OBJECTS:odr> lib/${TEST_NAME}/${SUB_PATH}/lib.cpp)
SETUP_TARGET(_${TARGET_NAME} ENTT_API_EXPORT)
SETUP_BASIC_TEST(lib_${TARGET_NAME} lib/${TEST_NAME}/${SUB_PATH}/main.cpp ENTT_API_IMPORT)
set_target_properties(lib_${TARGET_NAME} PROPERTIES CXX_CLANG_TIDY "")
target_link_libraries(lib_${TARGET_NAME} PRIVATE _${TARGET_NAME})
endfunction()
function(SETUP_PLUGIN_TEST TEST_NAME)
add_library(_${TEST_NAME} MODULE $<TARGET_OBJECTS:odr> lib/${TEST_NAME}/plugin.cpp)
SETUP_TARGET(_${TEST_NAME} ${ARGVN})
SETUP_BASIC_TEST(lib_${TEST_NAME} lib/${TEST_NAME}/main.cpp PLUGIN="$<TARGET_FILE:_${TEST_NAME}>" ${ARGVN})
target_include_directories(_${TEST_NAME} PRIVATE ${cr_INCLUDE_DIR})
target_include_directories(lib_${TEST_NAME} PRIVATE ${cr_INCLUDE_DIR})
target_link_libraries(lib_${TEST_NAME} PRIVATE ${CMAKE_DL_LIBS})
add_dependencies(lib_${TEST_NAME} _${TEST_NAME})
function(SETUP_LIB_PLUGIN_TEST TEST_NAME SUB_PATH)
set(TARGET_NAME ${TEST_NAME}_${SUB_PATH})
add_library(_${TARGET_NAME} MODULE $<TARGET_OBJECTS:odr> lib/${TEST_NAME}/${SUB_PATH}/plugin.cpp)
SETUP_TARGET(_${TARGET_NAME} ${ARGVN})
SETUP_BASIC_TEST(lib_${TARGET_NAME} lib/${TEST_NAME}/${SUB_PATH}/main.cpp PLUGIN="$<TARGET_FILE:_${TARGET_NAME}>" ${ARGVN})
set_target_properties(_${TARGET_NAME} PROPERTIES CXX_CLANG_TIDY "")
set_target_properties(lib_${TARGET_NAME} PROPERTIES CXX_CLANG_TIDY "")
target_include_directories(_${TARGET_NAME} PRIVATE ${cr_INCLUDE_DIR})
target_include_directories(lib_${TARGET_NAME} PRIVATE ${cr_INCLUDE_DIR})
target_link_libraries(lib_${TARGET_NAME} PRIVATE ${CMAKE_DL_LIBS})
add_dependencies(lib_${TARGET_NAME} _${TARGET_NAME})
endfunction()
# Test benchmark
if(ENTT_BUILD_BENCHMARK)
SETUP_BASIC_TEST(benchmark benchmark/benchmark.cpp)
set_target_properties(benchmark PROPERTIES CXX_CLANG_TIDY "")
endif()
# Test example
@@ -122,6 +157,7 @@ endif()
if(ENTT_BUILD_EXAMPLE)
SETUP_BASIC_TEST(custom_identifier example/custom_identifier.cpp)
SETUP_BASIC_TEST(entity_copy example/entity_copy.cpp)
SETUP_BASIC_TEST(reserved_bits example/reserved_bits.cpp)
SETUP_BASIC_TEST(signal_less example/signal_less.cpp)
endif()
@@ -142,19 +178,21 @@ if(ENTT_BUILD_LIB)
set(cr_INCLUDE_DIR ${cr_SOURCE_DIR})
endif()
SETUP_LIB_TEST(dispatcher)
SETUP_LIB_TEST(emitter)
SETUP_LIB_TEST(locator)
SETUP_LIB_TEST(meta)
SETUP_LIB_TEST(registry)
SETUP_LIB_SHARED_TEST(dispatcher shared)
SETUP_LIB_PLUGIN_TEST(dispatcher plugin)
SETUP_PLUGIN_TEST(dispatcher_plugin)
SETUP_PLUGIN_TEST(emitter_plugin)
SETUP_PLUGIN_TEST(locator_plugin)
SETUP_PLUGIN_TEST(meta_plugin)
SETUP_PLUGIN_TEST(registry_plugin)
SETUP_LIB_SHARED_TEST(emitter shared)
SETUP_LIB_PLUGIN_TEST(emitter plugin)
SETUP_PLUGIN_TEST(meta_plugin_std ENTT_STANDARD_CPP)
SETUP_LIB_SHARED_TEST(locator shared)
SETUP_LIB_PLUGIN_TEST(locator plugin)
SETUP_LIB_SHARED_TEST(meta shared)
SETUP_LIB_PLUGIN_TEST(meta plugin)
SETUP_LIB_PLUGIN_TEST(meta plugin_std ENTT_STANDARD_CPP)
SETUP_LIB_SHARED_TEST(registry shared)
SETUP_LIB_PLUGIN_TEST(registry plugin)
endif()
# Test snapshot
@@ -163,7 +201,7 @@ if(ENTT_BUILD_SNAPSHOT)
FetchContent_Declare(
cereal
GIT_REPOSITORY https://github.com/USCiLab/cereal.git
GIT_TAG v1.2.2
GIT_TAG v1.3.2
GIT_SHALLOW 1
)
@@ -175,6 +213,8 @@ if(ENTT_BUILD_SNAPSHOT)
endif()
SETUP_BASIC_TEST(cereal snapshot/snapshot.cpp)
set_target_properties(cereal PROPERTIES CXX_CLANG_TIDY "")
target_include_directories(cereal PRIVATE ${cereal_INCLUDE_DIR})
endif()
@@ -215,10 +255,13 @@ SETUP_BASIC_TEST(observer entt/entity/observer.cpp)
SETUP_BASIC_TEST(organizer entt/entity/organizer.cpp)
SETUP_BASIC_TEST(registry entt/entity/registry.cpp)
SETUP_BASIC_TEST(runtime_view entt/entity/runtime_view.cpp)
SETUP_BASIC_TEST(sigh_mixin entt/entity/sigh_mixin.cpp)
SETUP_BASIC_TEST(snapshot entt/entity/snapshot.cpp)
SETUP_BASIC_TEST(sparse_set entt/entity/sparse_set.cpp)
SETUP_BASIC_TEST(storage entt/entity/storage.cpp)
SETUP_BASIC_TEST(storage_mixin entt/entity/storage_mixin.cpp)
SETUP_BASIC_TEST(storage_entity entt/entity/storage_entity.cpp)
SETUP_BASIC_TEST(storage_no_instance entt/entity/storage_no_instance.cpp)
SETUP_BASIC_TEST(storage_utility entt/entity/storage_utility.cpp)
SETUP_BASIC_TEST(view entt/entity/view.cpp)
# Test graph

Some files were not shown because too many files have changed in this diff Show More