Compare commits

...

962 Commits

Author SHA1 Message Date
Michele Caini
4a2d1a8541 test: few last changes to please the linter 2024-10-18 09:15:40 +02:00
Michele Caini
59061ec8d3 test: cleanup 2024-10-17 09:35:23 +02:00
Michele Caini
c32b9032fb test: try to please the linter 2024-10-17 09:09:23 +02:00
Michele Caini
48583b6f55 doc: updated links 2024-10-17 09:09:01 +02:00
Michele Caini
68adf8615c view: return the best size for entity storage from size_hint 2024-10-17 09:08:50 +02:00
Michele Caini
8c4a2f79ab doc: updated links 2024-10-16 11:11:56 +02:00
Michele Caini
1098b61060 poly: refined inheritance support - close #1184 2024-10-16 11:05:53 +02:00
Michele Caini
2f8bdb2505 *: updated TODO 2024-10-14 12:32:20 +02:00
Michele Caini
e50f20ebf4 test: minor changes to please the linter 2024-10-14 12:32:08 +02:00
Michele Caini
6b20a3d3ea test: linter directives on deprecated type (no waste of time here) 2024-10-11 19:06:52 +02:00
Michele Caini
aecdb84606 delegate: internal const correctness on return types 2024-10-11 19:06:18 +02:00
Michele Caini
031f340d62 test: make the linter happy 2024-10-11 14:05:56 +02:00
Michele Caini
f9a509a82b test: typo 2024-10-11 13:57:43 +02:00
Michele Caini
3f23bfed61 meta: avoid warnings due to deprecated meta_prop class - close #1182 2024-10-11 13:56:44 +02:00
Michele Caini
771603f8f6 test: minor changes 2024-10-10 09:16:17 +02:00
Michele Caini
331a8257be sigh mixin: const correctness for the return type of ::emplace 2024-10-10 09:14:15 +02:00
Michele Caini
c97e3d4f96 meta: remove const qualifiers from non-reference/non-pointer types 2024-10-10 09:01:19 +02:00
Michele Caini
5388c9b432 any: remove const qualifiers from non-reference/non-pointer types 2024-10-10 09:00:08 +02:00
Michele Caini
95f84d1488 test: let the linter know that things are done on purpose sometimes 2024-10-10 08:48:19 +02:00
Michele Caini
e904300c1f mixin: minor changes 2024-10-10 08:21:06 +02:00
Michele Caini
8df81238e7 test: make the linter happy 2024-10-09 17:10:13 +02:00
Michele Caini
8c6bbda4db test: cleanup 2024-10-09 08:30:13 +02:00
Michele Caini
e1bbbee858 test: avoid using collector directly 2024-10-09 08:29:57 +02:00
Michele Caini
88c77f87fb test: cleanup 2024-10-09 08:29:37 +02:00
Michele Caini
c5e98d3287 test: avoid short names 2024-10-08 17:13:41 +02:00
Michele Caini
b967dbcb90 test: cleanup 2024-10-08 17:11:18 +02:00
Michele Caini
b7cd6fadf4 test: avoid short names 2024-10-08 17:09:33 +02:00
Michele Caini
26000fa019 test: avoid short names 2024-10-08 17:08:54 +02:00
Michele Caini
b8977df86f test: explicit checks 2024-10-08 17:07:35 +02:00
Michele Caini
2cd0ef323d test: avoid short names 2024-10-08 17:06:24 +02:00
Michele Caini
30b685411b test: avoid short names 2024-10-08 09:42:05 +02:00
Michele Caini
a15751a33b test: avoid short names 2024-10-08 09:41:46 +02:00
Michele Caini
8837c7cc1f test: minor changes 2024-10-08 09:41:22 +02:00
Michele Caini
d9ad574a69 storage: internal changes 2024-10-08 09:41:11 +02:00
Michele Caini
d87b7a928d test: const correctness 2024-10-07 15:46:16 +02:00
Michele Caini
a3f449408a test: avoid short names 2024-10-07 15:46:04 +02:00
Michele Caini
53cb4864ab test: make functions static as needed 2024-10-07 15:45:46 +02:00
Michele Caini
aec84b6598 test: avoid short names 2024-10-07 15:45:22 +02:00
Michele Caini
5b7402468f test: avoid short names 2024-10-07 12:56:25 +02:00
Michele Caini
688a1905d8 test: avoid short names 2024-10-07 12:56:09 +02:00
Michele Caini
f4e4dac376 test: use .data() if possible 2024-10-07 12:55:46 +02:00
Michele Caini
628261b5bf test: avoid invoking static members on instance 2024-10-07 10:46:23 +02:00
Michele Caini
2bb7b07191 test: explicit checks + braces 2024-10-07 10:46:00 +02:00
Michele Caini
b331e88b8a test: avoid short names 2024-10-07 10:45:36 +02:00
Michele Caini
9788a3e6c3 test: use data() as it ought to be 2024-10-07 10:45:17 +02:00
Michele Caini
341fd03dc8 test: avoid short names 2024-10-07 10:44:45 +02:00
Michele Caini
c40c2d41de test: const correctness 2024-10-07 10:44:23 +02:00
Michele Caini
8ca5a6c305 meta: explicit checks 2024-10-07 10:43:20 +02:00
Michele Caini
6738e3e77d view: avoid implicit conversions 2024-10-07 10:42:53 +02:00
Michele Caini
36896978b1 view: avoid else after return 2024-10-04 15:55:42 +02:00
Michele Caini
6db5d78e87 meta: typo 2024-10-04 15:09:20 +02:00
Michele Caini
414365c770 meta: avoid implicit conversions 2024-10-04 15:08:13 +02:00
Michele Caini
8cfd635f1d meta: typo 2024-10-04 12:29:36 +02:00
Michele Caini
1de5b066fd meta: avoid implicit conversions 2024-10-04 12:26:29 +02:00
Michele Caini
02f8f00340 test: identifier length 2024-10-04 12:25:07 +02:00
Michele Caini
44ee192b41 meta: explicit check 2024-10-04 12:21:11 +02:00
Michele Caini
00c2ddb300 test: const correctness 2024-10-04 12:18:27 +02:00
Michele Caini
a9e575ee8b any: avoid implicit conversions 2024-10-04 12:16:17 +02:00
Michele Caini
76f988e975 test: identifier length 2024-10-04 12:13:43 +02:00
Michele Caini
a75f864c4f test: isolate declarations 2024-10-04 12:12:58 +02:00
Michele Caini
d8a98fe5f0 linter: check options for readability-magic-numbers 2024-10-04 12:10:16 +02:00
Michele Caini
a348ff7dd1 storage: explicit checks 2024-10-04 11:50:24 +02:00
Michele Caini
1620b63a2f sparse set: avoid implicit conversions 2024-10-04 11:48:36 +02:00
Michele Caini
a70177a725 linter: try to make readability-function-cognitive-complexity ignore gtest 2024-10-04 11:46:27 +02:00
Michele Caini
87ac4c0121 registry: avoid else after return 2024-10-04 11:39:23 +02:00
Michele Caini
14a4788417 registry: avoid implicit bool conversion 2024-10-04 11:39:07 +02:00
Michele Caini
5f18f90571 linter: set MinimumVariableNameLength for readability-identifier-length 2024-10-04 11:35:00 +02:00
Michele Caini
cc79817af3 any: avoid else after return 2024-10-04 11:12:20 +02:00
Michele Caini
f39c68793e sigh: drop redundant initializer 2024-10-04 11:10:52 +02:00
Michele Caini
28adaa5a8b registry: avoid else after return 2024-10-04 11:03:34 +02:00
Michele Caini
cee7c799ce sparse set: make it clear the elem is a pointer 2024-10-04 11:03:23 +02:00
Michele Caini
47ff28d92d sparse set: drop redundant access specifier 2024-10-04 11:02:29 +02:00
Michele Caini
f50750bafd linter: set MinimumVariableNameLength for readability-identifier-length 2024-10-04 10:59:28 +02:00
Michele Caini
fc10b39dc4 linter: disable a couple of checks that I don't like much 2024-10-04 10:48:59 +02:00
Michele Caini
035e17ff1d linter: avoid const params in decls 2024-10-04 10:39:55 +02:00
Michele Caini
41f1b4e97b build: fixed typo in the clang-tidy file - close #1178
Related Work Items: #11, #1178
2024-10-04 10:21:08 +02:00
Michele Caini
1f474e785a test: cleanup 2024-10-03 08:25:26 +02:00
Michele Caini
bf0e78fef9 test: avoid magic numbers 2024-10-03 08:21:24 +02:00
Michele Caini
83ebbea352 mixin: use auto to please the linter 2024-10-03 08:20:27 +02:00
Michele Caini
79e1a9a3d9 organizer: instruct the linter on known uses of C-style arrays 2024-10-03 08:19:34 +02:00
Michele Caini
c9d7feccfa doc: more about the reactive mixin 2024-10-02 10:46:14 +02:00
Michele Caini
4f0b7d31dd mixin: full support for derived registry types 2024-10-02 10:31:07 +02:00
Michele Caini
9eef230340 mixin: internal changes 2024-10-02 10:30:23 +02:00
Michele Caini
69aeeeee30 *: updated TODO 2024-10-01 08:58:09 +02:00
Michele Caini
734dfa2c6f doc: minor changes 2024-10-01 08:57:28 +02:00
Michele Caini
61b5c193e8 sigh mixin: operator bool and registry functions 2024-10-01 08:56:58 +02:00
Michele Caini
84308361ae observer: deprecated, use reactive mixin instead 2024-09-30 10:27:52 +02:00
Michele Caini
db22dd8e69 meta: suppress (wrong) warnings by g++ 2024-09-30 09:32:07 +02:00
Michele Caini
805cbf9c79 reactive mixin: return *this from on_<op> functions 2024-09-30 09:23:28 +02:00
Michele Caini
50e41076c5 doc: minor changes 2024-09-28 19:13:04 +02:00
Michele Caini
1a95c9d03e test: suppress warning due to unused variable 2024-09-28 19:12:36 +02:00
Michele Caini
0b01e14f01 doc. minor changes 2024-09-28 19:10:34 +02:00
Michele Caini
445b11e88c doc: minor changes 2024-09-28 19:10:14 +02:00
Michele Caini
c22224692d doc: reactive mixin/storage 2024-09-28 19:08:45 +02:00
Michele Caini
0207ee7624 *: updated TODO 2024-09-27 15:22:47 +02:00
Michele Caini
84cab81c40 test: reactive mixin view functions 2024-09-27 15:22:28 +02:00
Michele Caini
4859f41c24 test: reactive mixin and entity lifecycle (managed vs standalone) 2024-09-27 13:43:00 +02:00
Michele Caini
1de4b20d1f test: reactive mixin and custom allocators 2024-09-26 15:09:48 +02:00
Michele Caini
2ffd38a355 test: registry and custom registry support for reactive mixin 2024-09-26 10:57:42 +02:00
Michele Caini
ea660aa466 test: reactive mixin on_destroy 2024-09-25 17:20:15 +02:00
Michele Caini
f9fd8432fd test: reactive mixin on_update 2024-09-25 14:59:18 +02:00
Michele Caini
f28917aec7 test: reactive mixin on_construct 2024-09-25 08:37:25 +02:00
Michele Caini
96480cdd54 test: reactive mixin with throwing allocator 2024-09-24 15:08:58 +02:00
Michele Caini
55b91798c3 test: reactive mixin swap 2024-09-24 14:15:24 +02:00
Michele Caini
4b9be6ddb7 test: reactive mixin move 2024-09-24 08:57:28 +02:00
Michele Caini
d6f4bc9909 test: reactive mixin ctors 2024-09-24 08:34:20 +02:00
Michele Caini
76eb4f4ded test: reactive mixin suite 2024-09-24 07:57:32 +02:00
Michele Caini
ff33c2fc02 test: reactive mixin tests setup 2024-09-23 12:16:51 +02:00
Michele Caini
401a8cbb6a *: updated TODO 2024-09-23 12:15:08 +02:00
Michele Caini
a329659e2f test: cleanup 2024-09-23 12:13:06 +02:00
Michele Caini
8efd951561 reactive mixin: precooked storage_type specialization 2024-09-23 10:45:34 +02:00
Michele Caini
911e0d2900 test: shared basic_custom_registry type 2024-09-23 10:13:24 +02:00
Michele Caini
d4f2651e5f reactive mixin: minor changes 2024-09-23 10:12:46 +02:00
Michele Caini
a74a0e4f4e test: refine throwing_allocator impl 2024-09-20 15:16:48 +02:00
Michele Caini
f51352467d reactive mixin: template keyword for g++ although not necessary 2024-09-20 10:16:37 +02:00
Michele Caini
9fafb83a41 reactive mixin: try to make all compilers happy 2024-09-20 09:24:01 +02:00
Michele Caini
e1e1d37506 mixin: avoid shadow warnings/errors 2024-09-20 08:27:37 +02:00
Michele Caini
f8eb94c7ad reactive mixin: const correctness 2024-09-19 14:17:28 +02:00
Michele Caini
4f6246cd11 test: cleanup 2024-09-19 10:49:05 +02:00
Michele Caini
dc6a03e08c reactive mixin: first few fixes 2024-09-19 10:09:50 +02:00
Michele Caini
0bb22f273a entity: fwd declare basic_reactive_mixin and reactive_mixin 2024-09-18 16:51:52 +02:00
Michele Caini
bb6304ecac mixin: basic_reactive_mixin draft 2024-09-18 16:51:00 +02:00
Michele Caini
a46905b2d3 test: minor changes 2024-09-17 17:13:40 +02:00
Michele Caini
a5ce90b268 core: noexcept-ness review 2024-09-17 11:12:10 +02:00
Michele Caini
a92be68bf7 signal: noexcept-ness review 2024-09-17 11:11:53 +02:00
Michele Caini
13223125d7 resource: noexcept-ness review 2024-09-17 11:11:38 +02:00
Michele Caini
9819234238 process: noexcept-ness review 2024-09-17 11:11:24 +02:00
Michele Caini
83641a997d graph: noexcept-ness review 2024-09-17 11:11:15 +02:00
Michele Caini
9907a84b26 entity: noexcept-ness review 2024-09-17 11:10:16 +02:00
Michele Caini
24eed3da7a container: noexcept-ness review 2024-09-17 11:09:44 +02:00
Michele Caini
56f83640a0 storage: let the linter know that I know what I am doing 2024-09-17 09:23:48 +02:00
Michele Caini
e8d007a970 test: make the linter happy 2024-09-17 09:02:02 +02:00
Michele Caini
6e128c47f5 view: minor changes 2024-09-17 08:05:48 +02:00
Michele Caini
9105f742dd view: misplaced assert 2024-09-16 18:18:49 +02:00
Michele Caini
57b46385a9 test: implicit noexcept-ness for dtors 2024-09-16 17:19:05 +02:00
Michele Caini
3f8efac37d signal: implicit noexcept-ness for dtors 2024-09-16 17:18:58 +02:00
Michele Caini
33f00f400b resource: implicit noexcept-ness for dtors 2024-09-16 17:18:52 +02:00
Michele Caini
044bfeefba entity: implicit noexcept-ness for dtors 2024-09-16 17:18:44 +02:00
Michele Caini
21829cc27f process: implicit noexcept-ness for dtors 2024-09-16 17:18:35 +02:00
Michele Caini
c742fcf371 meta: implicit noexcept-ness for dtors 2024-09-16 17:18:29 +02:00
Michele Caini
55f2c0fdc1 locator: implicit noexcept-ness for dtors 2024-09-16 17:18:18 +02:00
Michele Caini
aa0e16fc93 graph: implicit noexcept-ness for dtors 2024-09-16 17:18:09 +02:00
Michele Caini
01fa3a06a7 core: implicit noexcept-ness for dtors 2024-09-16 17:18:01 +02:00
Michele Caini
5bb5f396ed container: implicit noexcept-ness for dtors 2024-09-16 17:17:53 +02:00
Michele Caini
ef552922b3 doc: updated entity.md 2024-09-16 11:13:44 +02:00
Michele Caini
19da9d2358 test: use sparse set generic bind function 2024-09-16 10:56:18 +02:00
Michele Caini
893eb8fabb registry: pass *this directly to sparse set bind function 2024-09-16 10:55:51 +02:00
Michele Caini
9630977c2d sparse set: generic bind function 2024-09-16 10:55:15 +02:00
Michele Caini
dce9abb2f0 doc: more about signals - close #1169 2024-09-13 12:45:18 +02:00
Michele Caini
d62204df4e group: impressed, cannot get all compilers to agree :) 2024-09-12 18:12:45 +02:00
Michele Caini
5f1a14cd43 registry: defaulted trivial destructor with implicit noexcept specification 2024-09-12 18:05:49 +02:00
Michele Caini
80f12a7ac2 group: implicit noexcept specification 2024-09-12 18:05:09 +02:00
Michele Caini
84f8b5d5bb observer: implicit noexcept specification 2024-09-12 18:04:45 +02:00
Michele Caini
24751f6158 observer: defaulted trivial destructor 2024-09-12 17:51:40 +02:00
Michele Caini
fd3e275d86 sigh mixin: avoid nonsense warnings with g++ 2024-09-12 17:50:59 +02:00
Michele Caini
1175bdca62 meta: move node as it ought to be 2024-09-12 17:46:12 +02:00
Michele Caini
8899c9df7e group: defaulted trivial destructor 2024-09-12 17:45:28 +02:00
Michele Caini
3d10b764eb sigh mixin: expect noexcept move ctor/op 2024-09-12 17:38:51 +02:00
Michele Caini
33578dd6da meta: [[nodiscard]] 2024-09-12 13:55:24 +02:00
Michele Caini
2ab5e4deb3 natvis: meta_prop 2024-09-12 11:59:02 +02:00
Michele Caini
a4a87995af meta: reduce indirection for meta_prop 2024-09-12 10:16:58 +02:00
Michele Caini
b5fbc68baa sigh mixin: refine auto signals 2024-09-11 10:46:12 +02:00
Terens
42a1905690 sigh mixin: automatic signal registration for components (#1168) 2024-09-11 08:29:20 +02:00
Michele Caini
6d03f4d6f2 test: avoid unused variables 2024-09-11 08:29:10 +02:00
Michele Caini
e4ba5c31d2 meta: deprecate meta properties in favor of custom data 2024-09-10 16:12:28 +02:00
Michele Caini
355010df3b doc: refine mesta custom doc 2024-09-10 16:04:59 +02:00
Michele Caini
dc7ab63b6f view: drop pointless check 2024-09-10 15:46:08 +02:00
Michele Caini
5ecd5739d0 registry: cleanup/improvement 2024-09-10 15:45:47 +02:00
Michele Caini
8fd4cf7c45 registry: storage reset function - close #1173 2024-09-09 12:42:52 +02:00
Michele Caini
6f7dc0558b *: TODO 2024-09-09 12:11:42 +02:00
Michele Caini
eabe7be7da resource: swap 2024-09-09 12:02:33 +02:00
Michele Caini
7ad5eb8c17 *: updated TODO 2024-09-06 15:48:07 +02:00
Michele Caini
b485d72ec7 signal: swap based move assignment operators 2024-09-06 15:48:07 +02:00
Michele Caini
63d6db37d8 scheduler: swap based move assignment operator 2024-09-06 15:48:07 +02:00
Michele Caini
3b4864aeae registry: swap based move assignment operator 2024-09-06 15:48:07 +02:00
Michele Caini
d40e5f3dd5 table: swap based move assignment operator 2024-09-06 15:48:07 +02:00
Michele Caini
5bb706ceea observer: propagate noexcept-ness 2024-09-06 15:47:26 +02:00
Michele Caini
0966cc9063 entity: try to get around an issue with g++9 2024-09-06 15:47:14 +02:00
Michele Caini
5e1c44195a registry: propagate noexcept(false) to registry destructor 2024-09-06 15:40:37 +02:00
Michele Caini
2147e4bbb2 storage: refine noexcept-ness 2024-09-06 15:30:03 +02:00
Michele Caini
9f36bec353 sparse_set/storage: swap based move assignment operator 2024-09-06 15:24:21 +02:00
Michele Caini
c3826c78aa mixin: refine noexcept-ness 2024-09-06 14:59:53 +02:00
Michele Caini
d0a23fdf49 sparse_set/storage: make it clear that dtor and move op are not noexcept 2024-09-06 14:43:38 +02:00
Michele Caini
a39fd8548d config: wrap macro arguments to avoid warnings 2024-09-06 11:11:55 +02:00
Michele Caini
9c9816110e view: clear dead path 2024-09-05 16:04:30 +02:00
Michele Caini
156bc470ce natvis: update views for meta containers 2024-09-05 12:31:09 +02:00
Michele Caini
50db6f2193 meta: cleanup/reuse internal code 2024-09-04 14:29:33 +02:00
Michele Caini
628bfbd4ca natvis: meta 2024-09-03 15:11:41 +02:00
Michele Caini
ea4470b4a7 meta: minor changes 2024-09-02 15:49:16 +02:00
Michele Caini
90c975904f meta: refine and share find_member/find_overload 2024-09-02 15:05:11 +02:00
Michele Caini
27f7e43b05 meta: internal changes 2024-08-27 16:27:57 +02:00
Michele Caini
c909b40b5a *: updated TODO 2024-08-27 16:27:50 +02:00
Michele Caini
07e1ac5350 meta: asserts to prevent future errors 2024-08-27 16:27:01 +02:00
Michele Caini
c3758edb83 meta: cleanup meta_range impl 2024-08-27 16:26:25 +02:00
Michele Caini
328286d201 meta: minor changes 2024-08-27 16:16:39 +02:00
Michele Caini
8d149ecfc3 meta: minor changes 2024-08-27 15:25:02 +02:00
Michele Caini
c6479e06ae build: clang-format 2024-08-27 15:24:47 +02:00
Michele Caini
eee348f6b1 meta: reduce cost of meta base 2024-08-27 12:28:58 +02:00
Michele Caini
c7e8a998ff meta: reduce cost of meta func 2024-08-27 12:23:10 +02:00
Michele Caini
a4ad1ac3aa *: updated TODO 2024-08-27 12:22:57 +02:00
Michele Caini
942df26402 meta: reduce cost of meta data 2024-08-27 11:58:13 +02:00
Michele Caini
0fc5551998 *: updated TODO 2024-08-27 11:57:56 +02:00
Michele Caini
da615cc8e0 meta: reduce cost of meta prop 2024-08-27 11:19:15 +02:00
Michele Caini
9c6780c0a3 meta: reduce cost of meta conv 2024-08-27 11:05:54 +02:00
Michele Caini
0b5cb5ac84 *: updated TODO 2024-08-27 10:54:13 +02:00
Michele Caini
5da2c3c3c2 meta: reduce cost of meta ctor 2024-08-27 10:18:43 +02:00
Michele Caini
94735d04b3 meta: prepare meta_base/meta_conv/meta_ctor rework 2024-08-26 16:44:00 +02:00
Michele Caini
116bb91123 meta: prepare meta_prop rework 2024-08-26 15:11:20 +02:00
Michele Caini
010e5d4d3a meta: prepare meta_func rework 2024-08-26 14:57:12 +02:00
Michele Caini
0cf60202ff meta: prepare meta_data rework 2024-08-26 14:54:09 +02:00
Michele Caini
becfd01fd4 meta: cleanup 2024-08-26 14:28:22 +02:00
Michele Caini
8163a54b71 meta: prepare for further changes 2024-08-26 12:31:47 +02:00
Michele Caini
07a372e994 build: move googletest to FetchContent_MakeAvailable 2024-08-23 16:31:15 +02:00
Michele Caini
818e8755c2 config: try to make gcc happy 2024-08-20 14:44:22 +02:00
Michele Caini
4a4e2a38d1 delegate: refine function_pointer implementation - see #1167 (same issue) 2024-08-20 14:39:25 +02:00
Michele Caini
236747dd6b view: add missing typename (thanks as usual MSVC) 2024-08-20 14:36:58 +02:00
Michele Caini
1d288e9c42 meta: refine meta_function_helper implementation - close #1167 2024-08-20 14:34:25 +02:00
Michele Caini
9369c94df0 view: just a check to prevent future errors 2024-08-20 08:24:18 +02:00
Michele Caini
e33e1b5e2b view: avoid checking for filter validity 2024-08-19 18:41:18 +02:00
Chris Thrasher
80ee5e43c2 debug: print assertion message upon failure (#1164) 2024-08-19 16:02:54 +02:00
Michele Caini
3d89f04e95 doc: coding style 2024-08-03 11:33:33 +02:00
Chris Thrasher
1923028add doc: fix typos in README (#1162) 2024-08-03 11:31:52 +02:00
Michele Caini
97befad59a storage: suppress wrong use-after-move warning 2024-08-02 17:09:24 +02:00
Michele Caini
cdb1f23cbb *: linter directives 2024-08-02 17:06:50 +02:00
Michele Caini
c97a5f1b50 test: missing include 2024-08-02 17:03:23 +02:00
Michele Caini
a040353632 cmake: cleanup 2024-08-02 10:02:24 +02:00
Michele Caini
55615c118f test: cleanup 2024-08-02 09:24:02 +02:00
Michele Caini
93f7d516f2 view: drop useless [[nodiscard]] 2024-08-01 09:47:02 +02:00
Michele Caini
2c9aa2f544 meta: minor changes for coding style 2024-08-01 09:25:49 +02:00
Michele Caini
89767dc398 meta: update overloaded meta functions and user data (with non-regression tests) 2024-08-01 09:23:47 +02:00
Michele Caini
2310e5143a doc: hazel engine 2024-08-01 08:37:37 +02:00
Michele Caini
587a012ae1 view: further reduce instantiations 2024-07-31 10:50:23 +02:00
Michele Caini
bbd5676bad *: updated TODO 2024-07-31 10:08:25 +02:00
Michele Caini
eb6e29167d meta: avoid multiple lookups 2024-07-31 08:32:28 +02:00
Michele Caini
ebd077f71c natvis: meta custom views 2024-07-30 09:04:06 +02:00
Michele Caini
11e6b65108 meta: non-const access to meta custom 2024-07-30 08:49:30 +02:00
Michele Caini
1beac44dda meta: avoid overwriting traits/custom data on rebind 2024-07-29 15:48:45 +02:00
Michele Caini
7acd6e7f53 meta: check traits before saving 2024-07-29 12:56:49 +02:00
skypjack
8b0ef2b942 meta: support creating multiple types with different factories 2024-07-26 17:48:26 +02:00
skypjack
13284b7013 meta: store parent in the base factory 2024-07-26 17:23:00 +02:00
Michele Caini
689005d452 meta: guaranteed order on overloaded meta functions 2024-07-26 16:00:45 +02:00
Michele Caini
a8118c9389 meta: drop unnecessary attribute 2024-07-26 16:00:24 +02:00
Michele Caini
f4c6aa3f16 meta: avoid using meta type nodes in the base factory 2024-07-26 16:00:23 +02:00
Michele Caini
aa52c301fe meta: maybe unused context 2024-07-26 16:00:23 +02:00
Michele Caini
197ba29276 doc: updated meta.md 2024-07-26 16:00:23 +02:00
Michele Caini
af6dd28c66 meta: drop seek functions 2024-07-26 16:00:23 +02:00
Michele Caini
c91fa066af meta: drop unused data 2024-07-26 16:00:23 +02:00
Michele Caini
872f1c8dd9 meta: persistent traits in the factory class 2024-07-26 16:00:23 +02:00
Michele Caini
bb16607a7d meta: cleanup 2024-07-26 16:00:23 +02:00
Michele Caini
c94014d057 meta: persistent custom data in the factory class 2024-07-26 16:00:23 +02:00
Michele Caini
ca0202934c meta: persistent bucket in the factory class 2024-07-26 16:00:23 +02:00
Michele Caini
9824b44d72 meta: refine debug asserts 2024-07-26 16:00:23 +02:00
Michele Caini
5744119e6b meta: store owner in base factory directly 2024-07-26 16:00:23 +02:00
Michele Caini
4ed58d2230 core: instruct the linter 2024-07-26 09:17:24 +02:00
Michele Caini
4a681e4d00 test: suppress warnings 2024-07-26 09:14:38 +02:00
Michele Caini
85488bf757 *: updated TODO 2024-07-25 16:54:55 +02:00
Michele Caini
42a3d21638 meta: internal changes 2024-07-25 15:38:04 +02:00
Michele Caini
7b0b5ad779 meta: internal changes 2024-07-25 15:26:43 +02:00
Michele Caini
4e10e60c18 meta: rename things to avoid confusion 2024-07-25 12:32:11 +02:00
Michele Caini
8b5fd1ff2e meta: def initialize nodes 2024-07-25 12:30:14 +02:00
Michele Caini
f4ad2bd11b doc: meta custom data 2024-07-24 09:22:19 +02:00
Michele Caini
a5a03de483 test: code coverage 2024-07-23 11:58:14 +02:00
Michele Caini
ab56b89982 *: updated TODO 2024-07-23 11:47:03 +02:00
Michele Caini
ba63284d7a test: meta_custom 2024-07-23 11:40:14 +02:00
Michele Caini
a8f988893e meta: non-const meta_custom_node in meta_custom 2024-07-23 11:39:41 +02:00
Michele Caini
6695bdcb52 meta: fwd declare meta_custom 2024-07-23 11:39:05 +02:00
Michele Caini
baf582fce5 meta: checked meta_custom object 2024-07-23 10:30:46 +02:00
Michele Caini
e76d58c76c meta: prepare to a safer approach to custom data 2024-07-23 09:35:01 +02:00
Michele Caini
24808b11bb meta: support to user defined arbitrary data 2024-07-22 17:51:15 +02:00
Michele Caini
86226ed886 doc: cleanup 2024-07-22 17:32:31 +02:00
Michele Caini
04827bd8dc snapshot: use sparse set iterators 2024-07-22 17:23:12 +02:00
skypjack
8e6c72fdc1 group: [[nodiscard]] as needed 2024-07-19 17:56:34 +02:00
skypjack
b5e7e8c6f6 group: use override rather than virtual 2024-07-19 17:55:59 +02:00
skypjack
566e38b1d1 registry: cleanup to make all compilers happy again 2024-07-19 11:11:44 +02:00
skypjack
fb94471b8d adjacency_matrix: instruct the linter to trust me 2024-07-19 10:45:48 +02:00
skypjack
852aa2969d algorithm: instruct the linter to trust me 2024-07-19 10:44:24 +02:00
skypjack
0f64c2341e group: avoid pointer arithmetic 2024-07-19 10:44:08 +02:00
skypjack
a33212438a runtime_view: instruct the linter to trust me 2024-07-19 10:37:34 +02:00
skypjack
7431d93691 group: simplify non-critical virtual function 2024-07-19 10:31:30 +02:00
skypjack
d16fa5c605 meta: refine user defined traits support 2024-07-18 10:40:45 +02:00
skypjack
aee98b3c0a *: updated TODO 2024-07-17 17:01:26 +02:00
skypjack
2de5b6dd87 meta: self assignment for meta any 2024-07-17 16:11:13 +02:00
skypjack
7e9b27f9c1 any: self assignment 2024-07-17 16:10:58 +02:00
skypjack
f104c5de99 *: updated TODO 2024-07-17 16:10:44 +02:00
skypjack
0475a2404b view: [[nodiscard]] as needed 2024-07-17 14:56:44 +02:00
skypjack
94250af280 meta: internal changes 2024-07-17 14:56:27 +02:00
skypjack
0a00584cc0 doc: typo 2024-07-17 12:42:18 +02:00
skypjack
ff09c4bcbb delegate: suppress warnings due to unused variables 2024-07-17 09:14:56 +02:00
skypjack
dfc2c85be0 *: updated TODO 2024-07-17 09:14:37 +02:00
skypjack
5080289a73 meta: try to get around an issue with g++9 2024-07-16 16:13:56 +02:00
skypjack
34d4b50d0e doc: meta traits and meta properties 2024-07-16 15:38:57 +02:00
skypjack
5a2c092cde test: user defined meta traits 2024-07-16 15:38:43 +02:00
skypjack
56ef214bf3 meta: user defined traits for meta objects 2024-07-16 15:38:28 +02:00
skypjack
d331f750f0 meta: default constructors and member initializers 2024-07-16 15:37:05 +02:00
skypjack
784235c923 meta: factory class review 2024-07-15 17:46:53 +02:00
skypjack
dc070b04e2 meta: explicit return types on public members 2024-07-15 16:53:33 +02:00
skypjack
1ddc887ac4 meta: factory seek functions for meta data and meta functions 2024-07-15 12:43:11 +02:00
skypjack
fea24b1a22 meta: drop unnecessary enum class 2024-07-15 12:04:51 +02:00
skypjack
c4501d09b2 meta: reintroduce fast access to details 2024-07-15 11:44:21 +02:00
skypjack
7f4f779732 meta: seek on request 2024-07-15 11:07:05 +02:00
skypjack
88db7a8f09 meta: prepare to seeking functions 2024-07-15 11:02:04 +02:00
skypjack
36df10fd2c meta: cleanup 2024-07-14 15:35:49 +02:00
skypjack
e5ef550bcf meta: improved meta factory perf (quick access to details) 2024-07-14 15:25:20 +02:00
skypjack
d67f25c23a meta: drop unused function 2024-07-14 15:00:08 +02:00
skypjack
cf099b4511 meta: further reduce cost of creating meta objects 2024-07-14 14:55:51 +02:00
skypjack
8b7f5e5668 meta: reduce cost of constructing meta factories 2024-07-14 14:55:29 +02:00
skypjack
c2f87a70c5 meta: further reduce cost of creating meta func objects 2024-07-14 14:25:04 +02:00
skypjack
1bfe022a57 meta: further reduce cost of creating meta data objects 2024-07-14 12:43:24 +02:00
skypjack
0a996f5682 meta: move context to basic meta factory 2024-07-14 12:32:27 +02:00
Michele Caini
9e76dbce1d *: updated TODO 2024-07-11 16:39:20 +02:00
Michele Caini
86850ca65b meta: move info to basic_meta_factory 2024-07-11 16:32:57 +02:00
Michele Caini
ad320cfa2e meta: common basic_meta_factory class 2024-07-11 09:46:47 +02:00
Michele Caini
873617fbdf view: internal changes 2024-07-10 16:48:25 +02:00
Michele Caini
312c2b1cdf view: drop empty func each support for non-empty types in single views 2024-07-10 16:48:25 +02:00
Michele Caini
32a83ecf50 view: that's why the if was there :) right 2024-07-10 16:48:25 +02:00
Michele Caini
181f33ae7c view: race against the compiler 2024-07-10 16:48:25 +02:00
Michele Caini
b275af5848 view: drop pointless if 2024-07-10 16:48:25 +02:00
Michele Caini
023adf0b38 *: updated TODO 2024-07-10 16:48:25 +02:00
Michele Caini
b4d3ad7279 view: avoid protected data members 2024-07-10 16:48:25 +02:00
Michele Caini
2c7cc005cd *: updated TODO 2024-07-10 16:48:25 +02:00
Michele Caini
7cfbe89ecf test: minor changes 2024-07-10 16:48:25 +02:00
Michele Caini
f433b7fb41 *: updated TODO 2024-07-10 16:48:25 +02:00
Michele Caini
cad70bcd60 test: more tests for in-place single type views 2024-07-10 16:48:25 +02:00
Michele Caini
0753dd1aaa view: enable in-place single type view specialization 2024-07-10 16:48:25 +02:00
Michele Caini
d56a0a5fbd test: more tests for swap-only single type views 2024-07-10 16:48:25 +02:00
Michele Caini
4a5f021496 view: ::each in-place support for single type views 2024-07-10 16:48:25 +02:00
Michele Caini
60019b45c2 view: find/back/front in-place support for single type views 2024-07-10 16:48:25 +02:00
Michele Caini
ce543cf3fb view: ::empty support for in-place 2024-07-10 16:48:25 +02:00
Michele Caini
81b30c41a6 view: begin/end support for in-place 2024-07-10 16:48:25 +02:00
Michele Caini
185dfaa08a view: conditional in-place iterator for single type views 2024-07-10 16:48:25 +02:00
Michele Caini
b2cdc6aa57 view: ::contains in_place support for single type views 2024-07-10 16:48:25 +02:00
Michele Caini
d233a40c3d view: disable rbegin/rend when in_place for single type views 2024-07-10 16:48:24 +02:00
Michele Caini
d1073bb181 view: ::size/::size_hint in_place support for single type views 2024-07-10 16:48:24 +02:00
Michele Caini
fce847820e view: improved entity view (with tests) 2024-07-10 16:48:24 +02:00
Michele Caini
a53b944afc meta: linter directives (waiting for C++20 and std::span) 2024-07-10 16:43:01 +02:00
Michele Caini
98a35f0f0a *: updated TODO 2024-07-10 11:39:24 +02:00
Michele Caini
367f80dca4 test: storage based tests for runtime views 2024-07-10 11:30:19 +02:00
Michele Caini
65f7944b49 doc: add Donner to links.md 2024-07-10 09:58:29 +02:00
Michele Caini
473a53d796 test: cleanup 2024-07-10 09:54:31 +02:00
Michele Caini
9e7985363e entity: update guard for ranges 2024-07-09 10:29:01 +02:00
Michele Caini
fe76478dbc core: update guard for ranges 2024-07-09 10:28:56 +02:00
Michele Caini
59d5e59aa4 core: fix include guard 2024-07-09 10:28:54 +02:00
Michele Caini
6f967b3b72 test: drop ENTT_HAS_HEADER_VERSION 2024-07-09 10:28:50 +02:00
Michele Caini
44b1ae2c3a meta: linter directives (waiting for C++20 and std::span) 2024-07-09 10:28:30 +02:00
Michele Caini
836847b2a7 algorithm: linter directive 2024-07-09 10:28:25 +02:00
Michele Caini
5f7edc4447 sparse_set: cleanup 2024-07-09 10:28:22 +02:00
Michele Caini
e98ccf71b4 view: missed [[nodiscard]] 2024-07-09 10:28:18 +02:00
Michele Caini
b48666d3d1 test: cleanup 2024-07-09 10:28:16 +02:00
Michele Caini
54881e58b5 organizer: cleanup 2024-07-09 10:28:12 +02:00
Michele Caini
013d70a572 any: linter directive 2024-07-09 10:28:11 +02:00
Michele Caini
636e023f5f test: cleanup 2024-07-09 10:28:08 +02:00
Michele Caini
7d8461bae4 test: non-regression test for storage iterator page size deduction 2024-07-03 19:15:22 +02:00
Michele Caini
060590b14f storage: fix component_traits usage in storage_iterator 2024-07-03 18:52:20 +02:00
Michele Caini
b7090b14b7 view: get around an issue of msvc toolset v142 2024-07-01 12:50:28 +02:00
Michele Caini
a220803b09 test: suppress warnings 2024-06-30 16:39:27 +02:00
Michele Caini
8bb83ae64d view: get rid of a nasty trick on views 2024-06-30 16:10:35 +02:00
Michele Caini
bf6c2aa1ee *: updated TODO 2024-06-30 16:09:01 +02:00
Michele Caini
ecb5c9df59 test: standalone view tests 2024-06-30 00:27:39 +02:00
Michele Caini
cd9b564ce9 test: avoid using registry for view tests (wip) 2024-06-28 11:17:53 +02:00
Michele Caini
f420308ff0 test: typo 2024-06-27 16:58:55 +02:00
Michele Caini
b5d850c741 test: internal changes 2024-06-27 16:57:29 +02:00
Michele Caini
c78c0c2bf1 view: make a check that is going to disappear private 2024-06-27 16:50:26 +02:00
Michele Caini
1bf23c5989 test: internal changes 2024-06-27 16:43:07 +02:00
Michele Caini
24256188d0 doc: minor changes 2024-06-27 12:29:10 +02:00
Michele Caini
cb2bf04522 test: minor changes 2024-06-27 09:58:42 +02:00
Michele Caini
b3967dcd39 test: cleanup 2024-06-27 09:58:40 +02:00
Michele Caini
6949f9b945 view: internal changes 2024-06-26 19:42:45 +02:00
Michele Caini
b1b014ebdd *: updated TODO 2024-06-26 16:12:46 +02:00
Michele Caini
758c116c58 observer: decouple things internally 2024-06-26 12:19:55 +02:00
Michele Caini
53b85ecb39 observer: drop mask parameter 2024-06-26 08:38:58 +02:00
Michele Caini
c15b1f778a observer: oops, fixed typo 2024-06-25 11:58:13 +02:00
Michele Caini
d361e73aed doc: update to_entity doc - close #1151 2024-06-25 11:21:46 +02:00
Michele Caini
5ab4c1c5d9 observer: minor changes 2024-06-25 10:32:29 +02:00
Michele Caini
b4e437f1b1 hyelper: avoid using delegate if not needed 2024-06-25 09:49:20 +02:00
Michele Caini
96250f0abb *: use c-style arrays where it makes sense 2024-06-25 09:48:43 +02:00
Michele Caini
3340cf1b3e observer: drop unnecessary delegate 2024-06-23 00:38:11 +02:00
Michele Caini
6bf7e2596a observer: minor changes 2024-06-22 23:37:46 +02:00
Michele Caini
4d3efa7fb9 any: use plain C-style arrays when it makes sense 2024-06-21 18:15:22 +02:00
Michele Caini
834cefc35f *: updated TODO 2024-06-21 18:14:52 +02:00
Michele Caini
ed62adb744 observer: avoid unnecessary local lambda 2024-06-21 15:28:08 +02:00
Michele Caini
cc6e578f63 observer: share discard_if 2024-06-21 15:21:48 +02:00
Michele Caini
f4ff91143f observer: no need to inherit from storage here 2024-06-21 15:15:22 +02:00
Michele Caini
d9e5dbb587 observer: use base_type as argument to listeners 2024-06-21 15:08:38 +02:00
Michele Caini
3927ddeb58 sigh: internal changes 2024-06-21 11:37:04 +02:00
Michele Caini
b7c3cb267c delegate: cleanup 2024-06-21 10:02:01 +02:00
Michele Caini
9ec6f050c4 any: error message 2024-06-20 15:25:05 +02:00
Michele Caini
6c3202e429 any: no args to new[] (fair enough) 2024-06-20 15:24:14 +02:00
Michele Caini
133f6a0a28 any: get around an issue with latest MSVC and C++20 2024-06-20 09:26:39 +02:00
Michele Caini
c2c5342ea4 snapshot: thanks msvc for accepting invalid code 2024-06-19 09:26:42 +02:00
Michele Caini
97ed546e7b snapshot: decouple from component_traits 2024-06-19 09:23:18 +02:00
Michele Caini
5130420dd2 group: decouple from component_traits 2024-06-19 09:23:07 +02:00
Michele Caini
442821b061 storage: use void value_type as needed 2024-06-19 09:11:52 +02:00
Michele Caini
88889c5a8a storage: entity storage value_type 2024-06-19 09:04:13 +02:00
Michele Caini
3acf37100e view: decouple from compopnent_traits 2024-06-19 08:41:58 +02:00
Michele Caini
4fed6228a3 *: updated TODO 2024-06-19 08:41:47 +02:00
Michele Caini
9e2bc6c856 mixin: avoid using component_traits 2024-06-18 15:39:51 +02:00
Michele Caini
f49a405dce test: cleanup 2024-06-18 14:39:30 +02:00
Michele Caini
57a567629f view: rollback of some changes, they didn't lead where expected 2024-06-18 14:31:35 +02:00
Michele Caini
342db5c24f mixin: typo 2024-06-17 23:57:36 +02:00
Michele Caini
41fe71ebf4 doc: typo 2024-06-17 10:22:36 +02:00
Michele Caini
9e6e09fe2b view: internal changes to avoid errors 2024-06-17 10:11:19 +02:00
Michele Caini
8b3a2f7ce9 *: updated TODO 2024-06-14 16:31:19 +02:00
Michele Caini
5f5907c228 view: minor changes 2024-06-14 16:22:39 +02:00
Michele Caini
d67fd1e72c view: drop unnecessary member 2024-06-14 15:16:50 +02:00
Michele Caini
12ae4cd4c1 entity: minor changes 2024-06-13 18:39:57 +02:00
Michele Caini
04460d336e container: minor changes 2024-06-13 18:39:48 +02:00
Michele Caini
e262b60225 test: cleanup 2024-06-13 18:39:35 +02:00
Michele Caini
aca7872ba7 view: internal changes 2024-06-13 15:43:50 +02:00
Michele Caini
c9e48c0a14 view: path towards further reducing istantiations 2024-06-13 15:43:17 +02:00
Michele Caini
f931687ff0 view: avoid pointer arithmetic if possible 2024-06-12 14:31:20 +02:00
Michele Caini
f7cdf202e9 view: drop redundant check 2024-06-12 11:36:03 +02:00
Ezekiel Warren
154b91e835 chore: cleanup bazel files + sync bzlmod version (#1147) 2024-06-12 09:12:19 +02:00
Michele Caini
109434235f doc: core 2024-06-11 09:41:02 +02:00
Michele Caini
081c8f799e core: bit.hpp (waiting for C++20) 2024-06-11 09:34:19 +02:00
Michele Caini
786329c1d0 core: is_power_of_two -> has_single_bit 2024-06-11 09:01:25 +02:00
Michele Caini
fb47314d6f core: public popcount (waiting for C++20) 2024-06-10 18:32:28 +02:00
Michele Caini
3cec7c1065 *: updated TODO 2024-06-10 18:31:12 +02:00
Michele Caini
514e6e710c any: use std::array internally 2024-06-10 12:46:29 +02:00
Michele Caini
e197a04412 linter: exclude cppcoreguidelines-pro-bounds-constant-array-index (too annoying) 2024-06-10 12:46:17 +02:00
Michele Caini
87d69c4e77 meta: ctor review 2024-06-10 12:45:58 +02:00
Michele Caini
75307337ea meta: drop redundant argument 2024-06-07 16:47:25 +02:00
Michele Caini
7ce821f92e linter: I prefer my previous approach :) to coding style 2024-06-07 14:41:09 +02:00
Michele Caini
da8a8650cb core/entity: enable enable_borrowed_range/enable_view for a few types - close #1119 2024-06-07 08:37:52 +02:00
Michele Caini
29bc422a6a iwyu: update entt.imp 2024-06-07 08:33:35 +02:00
Michele Caini
3299fa1e40 sparse_set: review/cleanup constructors 2024-06-06 11:00:10 +02:00
Michele Caini
f8336ba5a1 any: review/cleanup constructors 2024-06-06 10:59:28 +02:00
Michele Caini
c28981e30f meta: minor changes (linter) 2024-06-06 10:58:55 +02:00
Michele Caini
339b78f47c test: minor changes (linter) 2024-06-06 10:58:31 +02:00
Michele Caini
8062bcfb3c registry: review remove/erase to use iterators 2024-06-06 10:58:17 +02:00
Michele Caini
47d71fa6cf group: minor changes (linter) 2024-06-06 10:57:52 +02:00
Michele Caini
43e9083456 dense_set: avoid pointer arithmetic 2024-06-05 13:56:12 +02:00
Michele Caini
059fa5a7a5 test: minor changes (coding style) 2024-06-05 12:52:56 +02:00
Michele Caini
d20a5607c0 test: thanks msvc for accepting invalid code 2024-06-05 12:51:57 +02:00
Michele Caini
58acc7edd1 registry: more stable assure function (with tests) - close #1148 2024-06-05 12:49:55 +02:00
Michele Caini
898dc2a122 registry: minor changes 2024-06-05 09:03:51 +02:00
Michele Caini
bf7fe3e42d test: try to make all compilers happy again 2024-06-04 16:14:51 +02:00
Michele Caini
89e3698599 hashed_string: internal review 2024-06-04 14:42:26 +02:00
Michele Caini
cc1a71ec9d test: minor changes 2024-06-04 14:41:59 +02:00
Michele Caini
b3e07785fc meta: cleanup 2024-06-04 14:41:48 +02:00
Michele Caini
f068b1f005 poly: linter directive on basic_poly 2024-06-04 09:43:25 +02:00
Michele Caini
f5790697ff meta: fool the linter 2024-06-04 09:42:27 +02:00
Michele Caini
00b32cdfe5 meta: coding style and nothing more 2024-06-04 09:41:55 +02:00
Michele Caini
ac8369d393 adjacency_matrix: correctly initialize members 2024-06-04 09:41:38 +02:00
Michele Caini
cb8f695584 any: coding style and nothing more 2024-06-04 09:41:07 +02:00
Michele Caini
2ab27a9b59 hashed_string: cleanup 2024-06-03 23:07:45 +02:00
Michele Caini
eae659301b observer: minor changes 2024-06-03 23:05:35 +02:00
Michele Caini
86cdb6f55d monostate: make it clear to the linter that we want value and monostate_v to be global 2024-06-03 23:05:25 +02:00
Michele Caini
627219f8a2 family: make it clear to the linter that we want identifier to be global 2024-06-03 23:04:59 +02:00
Michele Caini
e56d0a6fc9 test: try to make all compilers happy again 2024-06-03 14:44:46 +02:00
Michele Caini
126f3caba0 hashed_string: internal changes 2024-06-03 14:44:31 +02:00
Michele Caini
69864955af type_traits: try to make clang-cl happy again 2024-06-03 12:54:56 +02:00
Michele Caini
216bd0a629 type_traits: drop repeated branches in conditional chains 2024-06-03 10:20:12 +02:00
Michele Caini
05f758f9ba test: suppress annoying warnings by gcc 2024-06-03 09:35:51 +02:00
Michele Caini
6b84227626 hashed_string: suppress annoying warnings by gcc 2024-05-31 12:03:00 +02:00
Michele Caini
404b6bd99c *: updated TODO 2024-05-31 12:02:43 +02:00
Michele Caini
c51c6da61b locator: make it clear to the linter that we want services to be global 2024-05-31 11:32:23 +02:00
Michele Caini
0405126c25 process: rule of five 2024-05-31 11:31:56 +02:00
Michele Caini
274d9ea89d hashed_string: linter directives for accepted C-like arrays 2024-05-31 08:20:55 +02:00
Michele Caini
659bcbc98c core: linter directive on basic_any 2024-05-31 08:20:23 +02:00
Michele Caini
3291862371 meta: trick the linter about adjacent parameters 2024-05-31 08:19:46 +02:00
Michele Caini
22683f843a sparse_set: trick the linter about adjacent parameters 2024-05-31 08:19:25 +02:00
Michele Caini
9d1b9933c8 adjacency_matrix: minor changes 2024-05-31 08:18:01 +02:00
Michele Caini
e786a3b93d process: avoid C varargs functions 2024-05-31 08:17:42 +02:00
Michele Caini
f2e825b848 meta: linter directive on is_meta_pointer_like 2024-05-31 08:17:13 +02:00
Michele Caini
13a51c84a6 adjacency_matrix: cleanup 2024-05-30 08:39:19 +02:00
Michele Caini
e35f4e751f meta: avoid unnecessary copies 2024-05-30 08:30:17 +02:00
Michele Caini
2b07fae839 meta: linter directive on is_meta_pointer_like 2024-05-30 08:25:51 +02:00
Michele Caini
c4552892aa linter: update config 2024-05-30 08:25:33 +02:00
Michele Caini
51391a9381 sparse_set: linter directive on bind 2024-05-30 08:22:21 +02:00
Michele Caini
0349f4f615 type_info: explicit cast to avoid warnings 2024-05-30 08:22:02 +02:00
Michele Caini
d951b9e817 storage: cleanup 2024-05-30 08:18:27 +02:00
Michele Caini
2dd38bbff2 build: updated workflow 2024-05-30 08:18:14 +02:00
Michele Caini
06929572be compressed_pair: try to make g++ happy again 2024-05-29 09:11:26 +02:00
Michele Caini
c7f412bd7e view: make leading pool private in single type views 2024-05-29 08:26:40 +02:00
Michele Caini
483cc2b913 group: avoid unnecessary pointer arithmetic 2024-05-29 08:25:52 +02:00
Michele Caini
8138094f0a compressed_pair: cleanup 2024-05-29 08:23:23 +02:00
Michele Caini
06a8b05cf8 core: suppress linter warnings on type_info 2024-05-29 08:23:04 +02:00
Michele Caini
d3328accae dense_map: avoid unnecessary arithmetic 2024-05-28 18:39:41 +02:00
Michele Caini
a5e5fded0b view: make unchecked_refresh private as it should be 2024-05-28 18:38:45 +02:00
Michele Caini
a16b9cc092 meta: avoid C-like arrays 2024-05-28 18:37:59 +02:00
Michele Caini
539cf5f011 delegate: cleanup 2024-05-28 18:37:37 +02:00
Michele Caini
abdf06625b clang-tidy: refine config (sometimes I know what I'm doing) 2024-05-28 09:33:01 +02:00
Michele Caini
3c99189ff4 doc: Exhibition of Speed :) 2024-05-28 09:32:13 +02:00
Michele Caini
cd1fd890ea storage: avoid duplicate branches 2024-05-28 09:31:53 +02:00
Michele Caini
d03c0d0b1a sparse_set: minor changes 2024-05-28 09:31:00 +02:00
Michele Caini
7db890ba15 sparse set: avoid C-arrays 2024-05-27 16:59:03 +02:00
Michele Caini
8807499548 storage: rule of five 2024-05-27 16:58:51 +02:00
Michele Caini
bea578ac87 view: avoid C-arrays 2024-05-27 16:58:15 +02:00
Michele Caini
c474fe5b51 spars set: rule of five 2024-05-27 16:57:52 +02:00
Michele Caini
864ae25a2b *: updated TODO 2024-05-27 11:45:43 +02:00
Michele Caini
e864037968 meta: instruct the linter to accept some of my choices 2024-05-27 11:45:29 +02:00
Michele Caini
76d4f00e9f storage: avoid move/swap after move/swap 2024-05-27 11:45:01 +02:00
Michele Caini
4cb70d3c2d mixin: avoid move/swap after move/swap 2024-05-27 11:44:27 +02:00
Michele Caini
4c3fca4673 handle: instruct the linter to accept some of my choices 2024-05-27 11:43:44 +02:00
Michele Caini
7718b1bb9a any: make return type explicit for operator= 2024-05-24 09:25:44 +02:00
Michele Caini
32ffd625ff meta: make return type explicit for operator= 2024-05-24 09:25:25 +02:00
Michele Caini
176b9dafba mixin: cleanup 2024-05-24 09:24:20 +02:00
Michele Caini
82cd118adf storage: cleanup 2024-05-24 09:24:05 +02:00
Michele Caini
88828d2eb5 sparse_set: cleanup 2024-05-24 09:23:44 +02:00
Michele Caini
d9a3990f03 registry: [[nodiscard]] storage<T>(...) const 2024-05-24 09:23:27 +02:00
Michele Caini
699dff81b7 test: minor changes 2024-05-24 09:22:45 +02:00
Michele Caini
7bfc92d20e emitter: cleanup 2024-05-24 09:22:23 +02:00
Michele Caini
c5f6691c72 dispatcher: cleanup 2024-05-24 09:22:13 +02:00
Michele Caini
8433060092 resource: make return type explicit for operator= 2024-05-24 09:22:02 +02:00
Michele Caini
09da09500a scheduler: cleanup 2024-05-24 09:21:25 +02:00
Michele Caini
6ca5b9c1af scheduler: rule of five 2024-05-24 09:21:17 +02:00
Michele Caini
5f6f9b7b23 table: cleanup 2024-05-24 09:20:53 +02:00
Michele Caini
f0191b9afe doc: typo 2024-05-24 08:01:06 +02:00
Michele Caini
c885044ce4 registry: rule of five 2024-05-24 08:00:55 +02:00
Michele Caini
ee64ca4afb registry: avoid c-arrays 2024-05-24 08:00:45 +02:00
Michele Caini
9788610124 organizer: avoid c-arrays 2024-05-24 08:00:10 +02:00
Michele Caini
01f851199b table: rule of five 2024-05-24 07:59:54 +02:00
Michele Caini
d278a0f864 dispatcher: rule of five 2024-05-23 08:40:08 +02:00
Michele Caini
f9bf4c341d locator: rule of three 2024-05-23 08:40:00 +02:00
Michele Caini
2777bade73 snapshot: rule of five 2024-05-23 08:34:25 +02:00
Michele Caini
55637a7284 basic_sigh_mixin: rule of five 2024-05-23 08:24:58 +02:00
Michele Caini
0bd37df6f3 doc: minor changes 2024-05-23 08:23:06 +02:00
Michele Caini
aad65bf436 version: suppress macro usage linter messages 2024-05-23 08:22:25 +02:00
Michele Caini
9db1d085f9 macro: suppress macro usage linter messages 2024-05-23 08:22:18 +02:00
Michele Caini
dccc621f07 emitter: rule of five 2024-05-22 16:08:47 +02:00
Michele Caini
a014656f04 poly: cleanup 2024-05-22 16:07:11 +02:00
Michele Caini
1ffbd845dc config: suppress macro usage linter messages 2024-05-22 16:06:58 +02:00
Michele Caini
d0f8974e08 entity: default destructors and noexcept-ness review 2024-05-21 10:32:05 +02:00
Michele Caini
505bb4c8cb any: minor changes 2024-05-21 10:31:36 +02:00
Michele Caini
5ecf9eb893 signal: default destructor and noexcept-ness review 2024-05-21 10:22:39 +02:00
Michele Caini
ab532af047 resource: default destructor 2024-05-21 10:21:58 +02:00
Michele Caini
16b05ad8a0 basic_scheduler: default destructor 2024-05-21 10:21:27 +02:00
Michele Caini
54ef3c8850 meta_handle: default destructor 2024-05-21 10:20:50 +02:00
Michele Caini
4516210518 locator: minor changes 2024-05-21 10:20:28 +02:00
Michele Caini
387528bdcc adjacency_matrix: default destructor 2024-05-21 10:20:14 +02:00
Michele Caini
84b48c1a30 snapshot: default destructor(s) 2024-05-21 10:19:51 +02:00
Michele Caini
6615792703 compressed_pair: refine dtor noexcept spec 2024-05-21 10:19:29 +02:00
Michele Caini
991d16084d test: noexcept-ness review 2024-05-21 10:19:03 +02:00
Michele Caini
d7c9609ee6 registry: default destructor 2024-05-21 09:33:28 +02:00
Michele Caini
af27c43436 test: iwyu docet 2024-05-21 09:33:15 +02:00
Michele Caini
89e3fcfda0 flow: default destructor 2024-05-21 09:32:46 +02:00
Michele Caini
52de32291f dense_map/dense_set: cleanup 2024-05-21 09:32:30 +02:00
Michele Caini
a530b078d7 basic_sigh_mixin: default dtor 2024-05-21 08:19:19 +02:00
Michele Caini
a68bb70d43 sigh: default dtor 2024-05-21 08:18:25 +02:00
Michele Caini
f9a243b92a adjacency_matrix: internal changes 2024-05-21 08:13:59 +02:00
Michele Caini
4c4bbed0a2 dense_map/dense_set: default dtor 2024-05-21 08:13:38 +02:00
Michele Caini
9ec4d69b1e table: default dtor 2024-05-21 08:13:21 +02:00
Michele Caini
68f3f650eb registry: [[nodiscard]] as it should be 2024-05-20 13:21:48 +02:00
Michele Caini
2cb44f2590 group: [[nodiscard]] as it should be 2024-05-20 13:21:31 +02:00
Michele Caini
f7fd153ee0 group: cleanup 2024-05-20 13:21:19 +02:00
Michele Caini
e5804ac0a2 *: use transparent comparators 2024-05-20 13:20:57 +02:00
Michele Caini
11ade1cbc0 snapshot: noexcept move ctor/op 2024-05-20 13:19:29 +02:00
Michele Caini
46ed48ec48 adjacency_matrix: cleanup 2024-05-20 13:19:09 +02:00
Michele Caini
b3b4facaed sigh/sink: avoid unnecessary moves 2024-05-20 13:18:33 +02:00
Michele Caini
fbe92fc708 dispatcher: [[nodiscard]] as it should be 2024-05-20 13:18:05 +02:00
Michele Caini
3b6a987814 basic_flow: cleanup 2024-05-20 13:17:21 +02:00
Michele Caini
10749d445a resource_cache: noexcept move ctor/op 2024-05-20 13:16:48 +02:00
Michele Caini
83495c967e test. avoid warnings due to unused variables 2024-05-20 13:16:25 +02:00
Michele Caini
32ae3eef08 monstate: return *this from operator= 2024-05-20 13:16:00 +02:00
Michele Caini
efc68bdf39 compressed_pari: cleanup 2024-05-20 13:15:39 +02:00
Michele Caini
1e90bee7ad algorithm: avoid C-arrays 2024-05-20 13:15:26 +02:00
Michele Caini
77606c2d20 test: use transparent comparators 2024-05-19 00:14:58 +02:00
Michele Caini
ca303e24ff organizer: minor changes 2024-05-19 00:12:16 +02:00
Michele Caini
288fcdaf53 hashed_string: avoid magic numbers in return statements 2024-05-19 00:12:01 +02:00
Michele Caini
8f7480ebd1 compressed_pair: default dtor 2024-05-19 00:11:08 +02:00
Michele Caini
a15511d5ff entity: use =default as needed 2024-05-17 12:52:17 +02:00
Michele Caini
16ed46967f compressed-pair: add initializer to suppress warnings 2024-05-17 12:52:00 +02:00
Michele Caini
4d460ce763 test: minor changes 2024-05-17 12:51:39 +02:00
Michele Caini
4dd427aa4d group: mark functions [[nodiscard]] as needed 2024-05-17 12:51:29 +02:00
Michele Caini
4302842b79 poly: mark functions [[nodiscard]] as needed 2024-05-17 12:27:11 +02:00
Michele Caini
5724644653 organizer: mark functions [[nodiscard]] as needed 2024-05-17 12:27:06 +02:00
Michele Caini
a82a5958b3 workflow: increase tools timeout 2024-05-17 12:19:08 +02:00
Michele Caini
59935230dc entity: mark functions [[nodiscard]] as needed 2024-05-17 12:16:23 +02:00
Michele Caini
e9d30cb10e compressed_pair: mark functions [[nodiscard]] as needed 2024-05-17 12:16:07 +02:00
Michele Caini
db35537966 sparse_set/storage: mark functions [[nodiscard]] as needed 2024-05-17 12:08:04 +02:00
Michele Caini
62e990ed8a registry: mark functions [[nodiscard]] as needed 2024-05-17 12:05:55 +02:00
Michele Caini
1f0efb14a0 *: updated TODO 2024-05-17 12:02:32 +02:00
Michele Caini
5ac56d6fec workflow: refine tools 2024-05-17 11:58:02 +02:00
Michele Caini
68a6aef155 workflow:
* add linter job
* bind both linter and iwyu to a specific branch
2024-05-17 11:52:07 +02:00
Michele Caini
b8af4c6d71 workflow: minor changes 2024-05-17 11:51:29 +02:00
Michele Caini
c9ecfa049f doc: updated links.md 2024-05-17 08:28:43 +02:00
Michele Caini
0c0ce5dfc5 compressed_pair: stop supporting ancient compilers 2024-05-16 14:47:10 +02:00
Michele Caini
138347320e build: cleanup workflow 2024-05-16 14:33:06 +02:00
Michele Caini
4f58be5762 runtime_view: internal changes 2024-05-16 14:16:13 +02:00
Michele Caini
ffd3f950f3 build: maybe GH fixed it, let's see 2024-05-16 14:10:14 +02:00
Michele Caini
9ac19020d5 natvis: minimal view for basic tables 2024-05-15 17:33:17 +02:00
Michele Caini
feeb10a5f8 doc: table 2024-05-14 14:39:44 +02:00
Michele Caini
950509a950 *: updated TODO 2024-05-14 14:39:38 +02:00
Michele Caini
0b23b8f342 table: move it to the container module 2024-05-14 11:48:44 +02:00
Michele Caini
1d1761a13b doc: removed dead link 2024-05-14 11:47:12 +02:00
Michele Caini
d42d324adb *: updated TODO 2024-05-13 11:55:57 +02:00
Michele Caini
42cef49a7f doc: general update 2024-05-13 11:55:30 +02:00
Michele Caini
b57758c61e doc: update minimum doxygen version 2024-05-13 11:54:03 +02:00
Michele Caini
e4ef61ca93 build: stop supporting msvc toolset v141 2024-05-13 11:43:35 +02:00
Michele Caini
640f02bd16 test: drop another NOLINT 2024-05-11 12:28:59 +02:00
Michele Caini
2a86b8fe08 test: cleanup 2024-05-11 12:22:35 +02:00
Michele Caini
9edeb731f5 test: ENTT_NO_MIXIN 2024-05-10 15:25:20 +02:00
Michele Caini
8efe02f28f entity: use ENTT_STORAGE to filter mixin as needed 2024-05-10 15:24:36 +02:00
Michele Caini
581f2b0a0c config: ENTT_NO_MIXIN/ENTT_STORAGE 2024-05-10 15:23:52 +02:00
Michele Caini
00e34a2dc7 view: make toolset v141 happy again (for the last time hopefully) 2024-05-10 11:22:58 +02:00
Michele Caini
2590b6c3ba test: avoid using user entity storage 2024-05-10 10:50:52 +02:00
Michele Caini
fb6403f801 doc: cleanup 2024-05-10 10:26:48 +02:00
Michele Caini
e2da374e24 view: refine tombstone check requirements 2024-05-10 10:16:39 +02:00
Michele Caini
ceb4259ae3 storage: make deletion policy constexpr available as data member 2024-05-10 10:16:05 +02:00
Michele Caini
d6d72e6b03 registry: assert on user entity storage - close #1143 2024-05-10 10:14:04 +02:00
Michele Caini
35ebf5c413 hashed_string: avoid unnecessary class specializations 2024-05-09 09:44:00 +02:00
Michele Caini
39e7595b3b hashed_string: drop unnecessary internal type member 2024-05-09 09:29:06 +02:00
Michele Caini
7de152428e type_info: make comparison operators inline when non constexpr 2024-05-09 09:13:39 +02:00
Michele Caini
e2648e418f test: minor changes 2024-05-09 09:12:29 +02:00
Michele Caini
f7e533d061 test: code coverage 2024-05-08 12:02:02 +02:00
Michele Caini
2f8b4835ca table: redesign it as an adaptor rather than a container 2024-05-08 11:56:19 +02:00
Michele Caini
2a0eb2bcd4 test: NOLINT cleanup 2024-05-08 11:54:49 +02:00
Michele Caini
4692e72671 test: minor changes 2024-05-07 10:47:09 +02:00
Michele Caini
30d16b850c view: operator-> for single type views 2024-05-07 10:44:04 +02:00
Michele Caini
81b878d038 platform: drop support to android ndk r17 2024-05-07 10:09:49 +02:00
Michele Caini
cf3d751097 test: cleanup 2024-05-06 16:21:23 +02:00
Michele Caini
1e9ad0fb9c organizer: in/out-edges support - close #1069 2024-05-06 13:38:58 +02:00
Michele Caini
d0786338a9 doc: coding style 2024-05-05 18:19:14 +02:00
Michele Caini
7a605742ee table: refine tests with exceptions enabled 2024-05-03 14:31:24 +02:00
Michele Caini
739bc802b5 table: test with custom/throwing allocators 2024-05-03 14:07:10 +02:00
Michele Caini
97a50c23a9 table: test erase functions 2024-05-03 11:09:32 +02:00
Michele Caini
b6a71dbdbe table: test emplace function 2024-05-03 09:47:23 +02:00
Michele Caini
3a97180b63 table: non-defaulted table iterator's default ctor 2024-05-03 09:46:25 +02:00
Michele Caini
d92ae29132 test: suppress linter warnings 2024-05-03 09:44:46 +02:00
Michele Caini
1e26f3fd81 table: refined implementation/cleanup 2024-05-02 16:31:24 +02:00
Michele Caini
640a06bc68 table: test iterators 2024-05-02 09:40:28 +02:00
Michele Caini
41aab920b0 test: minor changes 2024-04-30 11:03:15 +02:00
Michele Caini
a3cea32f93 table: check pos when invoking operator[] 2024-04-30 11:02:56 +02:00
Michele Caini
aadbe3501e table: test ::clear 2024-04-30 10:12:21 +02:00
Michele Caini
3ba8268bfd table: test indexing 2024-04-30 09:17:31 +02:00
Michele Caini
45b4241f62 table: test ::capacity and related functions 2024-04-30 09:00:26 +02:00
Michele Caini
0a48f25beb table: test ::swap 2024-04-30 08:47:58 +02:00
Michele Caini
7ca3f84891 table: more tests 2024-04-29 10:38:34 +02:00
Michele Caini
3729eab7d5 *: updated TODO 2024-04-29 10:38:09 +02:00
Michele Caini
09ef06db71 test: oops :) 2024-04-29 10:34:50 +02:00
Michele Caini
9f211bacad test: minor changes 2024-04-29 09:02:10 +02:00
Michele Caini
bf854b0b5f table: let's write some tests 2024-04-29 09:01:54 +02:00
Michele Caini
3823d60932 table: clean up 2024-04-29 09:00:58 +02:00
Michele Caini
48353cd4dc table: fixed operator< for table iterators 2024-04-29 09:00:44 +02:00
Michele Caini
caaf332af3 table: fixed operator- for table iterators 2024-04-29 09:00:20 +02:00
Michele Caini
89c87c6d10 table: fixed operator-> for table iterators 2024-04-29 08:59:57 +02:00
Michele Caini
80c05856c2 sparse_set: minor changes 2024-04-26 16:45:49 +02:00
Michele Caini
d990d4e928 table: ::erase functions 2024-04-26 16:45:21 +02:00
Michele Caini
965a7ba4f0 table: make table iterators friends to allow non-const to const conversions 2024-04-26 16:45:04 +02:00
Michele Caini
17c9da4329 table: use compressed local void allocator 2024-04-26 10:37:30 +02:00
Michele Caini
7abe76e4f7 dispatcher: minor changes 2024-04-24 18:33:20 +02:00
Michele Caini
6e9be219ba scheduler: minor changes 2024-04-24 18:33:04 +02:00
Michele Caini
a360d14dad table: expand parameter pack in return type 2024-04-24 18:23:29 +02:00
Michele Caini
43d6b0865f doc: minor changes 2024-04-24 18:22:57 +02:00
Michele Caini
e44b1e9024 table: fixed table_iterator operator[](value) 2024-04-24 18:22:48 +02:00
Michele Caini
c0e68b96f7 table: emplace (back) function 2024-04-24 18:10:43 +02:00
Michele Caini
12e886340c table: operator[] overloads 2024-04-23 13:22:22 +02:00
Michele Caini
9f7668071d table: (c)rbegin/(c)rend functions 2024-04-23 13:21:51 +02:00
Michele Caini
736b0fe92b table: a bunch of fixes (thanks msvc for accepting invalid code) 2024-04-22 14:19:47 +02:00
Michele Caini
a89d1a853a table: (c)begin/(c)end functions 2024-04-22 13:54:11 +02:00
Michele Caini
452e9f8b51 table: iterator types 2024-04-22 13:52:33 +02:00
Michele Caini
c6ec062a4d table: table iterator 2024-04-19 09:13:03 +02:00
Michele Caini
7e413590c6 doc: minor changes 2024-04-19 09:10:20 +02:00
Michele Caini
4a3f83a7b8 table: ::clear function 2024-04-18 16:44:54 +02:00
Michele Caini
dd4a291272 table: ::empty function 2024-04-18 16:44:30 +02:00
Michele Caini
32bd18bf08 table: ::size function 2024-04-18 16:44:13 +02:00
Michele Caini
7d92b42d59 cache: minor changes 2024-04-18 16:41:57 +02:00
Michele Caini
4a542c3ad0 meta: minor changes 2024-04-18 16:40:05 +02:00
Michele Caini
950a48b5aa storage: minor changes 2024-04-18 16:39:52 +02:00
Michele Caini
bcdc6f9ad8 sparse_set: minor changes 2024-04-18 16:39:39 +02:00
Michele Caini
6b06fa9990 registry: minor changes 2024-04-18 16:39:27 +02:00
Michele Caini
8dbfa40c76 container: minor changes 2024-04-18 16:39:15 +02:00
Michele Caini
bb423bb503 *: updated TODO 2024-04-17 15:39:41 +02:00
Michele Caini
3d07cf8781 *: coding style 2024-04-17 15:38:40 +02:00
Michele Caini
f180927380 table: reserve/capacity/shrink_to_fit 2024-04-17 10:28:18 +02:00
Michele Caini
a1a1e9fd28 table: container type 2024-04-17 09:48:13 +02:00
Michele Caini
0d6056fee4 doc: minor changes 2024-04-17 09:45:41 +02:00
Michele Caini
c7a9bbf853 table: basic iface (placeholder) 2024-04-17 09:45:09 +02:00
Michele Caini
206110caaf test: suppress warning due to unused variable 2024-04-16 09:09:00 +02:00
Michele Caini
f223638350 table: constructors and operators 2024-04-16 09:08:40 +02:00
Michele Caini
a863536982 storage: better log message to avoid ambiguity 2024-04-15 17:28:31 +02:00
Michele Caini
430822aaa2 sparse_set/storage: minor changes 2024-04-15 17:26:07 +02:00
Michele Caini
b69390f283 table: refine prototype 2024-04-15 09:42:11 +02:00
Michele Caini
57d3c53014 *: coding style 2024-04-15 09:04:38 +02:00
Michele Caini
d6588f9b59 entity: table class placeholder 2024-04-15 08:45:29 +02:00
Michele Caini
19f510743e test: internal changes 2024-04-12 11:43:51 +02:00
Michele Caini
02a8803f98 test: use shared types as much as possible 2024-04-12 11:42:26 +02:00
Michele Caini
a78c2d0592 test: common bitmask types 2024-04-12 11:41:52 +02:00
Michele Caini
60e5ae22d5 test: use shared types as much as possible 2024-04-12 11:18:23 +02:00
Michele Caini
aa178dd2cf resource: add ::reset functions (with tests) 2024-04-12 11:17:57 +02:00
Michele Caini
0b6e325109 doc: fixed links 2024-04-12 11:17:03 +02:00
Michele Caini
453d32d3fd test: use common types as much as possible 2024-04-10 09:30:32 +02:00
Michele Caini
9891e70a20 test: more common empty types 2024-04-10 09:29:25 +02:00
Michele Caini
6ca01d2faf group: make older compilers happy too 2024-04-10 09:01:06 +02:00
Michele Caini
4c98e3aa1e group: share common setup 2024-04-10 09:01:06 +02:00
Michele Caini
2c700bb812 group: finally decouple handlers and types to further reduce instantiations 2024-04-10 09:01:06 +02:00
Michele Caini
03f94a8834 registry: oops :) forgot to pack storage when constructing groups 2024-04-10 09:01:06 +02:00
Michele Caini
fb87ae392f group: use group_id rather than handler type to mark groups 2024-04-10 09:01:06 +02:00
Michele Caini
48c122fb28 meta: minor changes 2024-04-10 09:01:06 +02:00
Michele Caini
77d646f05d group: add missing include 2024-04-10 09:01:06 +02:00
Michele Caini
35d048304f group: decouple ::owned and owned types 2024-04-10 09:01:06 +02:00
Michele Caini
0d0880d81f group: drop unnecessary index sequence 2024-04-10 09:01:06 +02:00
Michele Caini
08e9e059b5 group: prepare to decouple handlers from types 2024-04-10 09:01:06 +02:00
Michele Caini
4602de245c group: pass common type as template parameter, prepare to get rid of storage types 2024-04-10 09:01:05 +02:00
Michele Caini
0b9d7b3538 group: required template keywords (thanks as usual msvc) 2024-04-10 09:01:05 +02:00
Michele Caini
38c7cddac0 group: move a static assert on storage types out from handlers 2024-04-10 09:01:05 +02:00
Michele Caini
d4588208e3 group: prepare to decouple group handlers from types 2024-04-10 09:01:05 +02:00
Michele Caini
666e988995 group: prepare to further reduce instantiations for groups too 2024-04-10 09:01:05 +02:00
Michele Caini
f8e452c7c8 group: drop unnecessary lambdas/std::apply calls 2024-04-10 09:01:05 +02:00
Michele Caini
f9a9034001 test: minor changes 2024-04-10 09:01:05 +02:00
Michele Caini
d2b6eed671 doc: minor changes 2024-04-10 09:01:05 +02:00
Michele Caini
d734c92ee4 memory: class level new/delete support - close #1131 2024-04-10 09:01:05 +02:00
Michele Caini
69f12a1110 any: class level new/delete support 2024-04-10 09:01:05 +02:00
Michele Caini
717696e7a3 test: common class level new/delete type - see #1131 2024-04-10 09:01:05 +02:00
Michele Caini
affbc668df test: storage type checks 2024-04-10 09:01:05 +02:00
Michele Caini
6ede10fa47 test: suppress a warning by clang 2024-04-10 09:01:05 +02:00
Michele Caini
1ba01ca3e2 test: drop unused alias 2024-04-10 09:01:05 +02:00
Michele Caini
8739e9185a snapshot: suppress a warning by clang 2024-04-10 09:01:05 +02:00
Michele Caini
40208e6b37 build: try to add more compilers on the CI 2024-04-10 09:01:05 +02:00
Michele Caini
32078f033f natvis: basic handle view - see #1052 2024-04-10 09:01:05 +02:00
Michele Caini
54f27382ca handle: a few changes to make natvis views easier 2024-04-10 09:01:05 +02:00
Michele Caini
5908d01017 view. branch-less centralized swap-only mode check 2024-04-10 09:01:05 +02:00
Michele Caini
2f8998bd92 sparse_set: branch-less paths 2024-04-10 09:01:05 +02:00
Michele Caini
48c953d23e update single include file 2024-04-10 09:00:16 +02:00
Michele Caini
df87206c94 snapshot: full support to stable types - close #1118 2024-03-28 10:49:18 +01:00
Michele Caini
abaa5c468e *: updated TODO 2024-03-28 10:48:31 +01:00
Michele Caini
f4431368e3 handle: drop a [[nodiscard]] that breaks toolset v141 2024-03-27 15:32:20 +01:00
Michele Caini
6bc8f4cfa4 handle: full review with test coverage - close #1129 2024-03-27 14:41:19 +01:00
Michele Caini
70c4b1f4c4 clang-tidy: suppress a good amount of wrong warnings 2024-03-27 14:37:29 +01:00
Michele Caini
1be0175a18 sparse_set: drop scoped iterators 2024-03-26 11:40:30 +01:00
Michele Caini
3796f055d6 sigh_mixin: avoid using scoped iterators 2024-03-25 11:19:45 +01:00
Michele Caini
0128620803 sparse_set: review ::push 2024-03-25 11:19:07 +01:00
Michele Caini
a8a86ba090 sparse_set: avoid using scoped iterators internally when sorting 2024-03-25 11:18:35 +01:00
Michele Caini
430d1dda86 test: avoid using scoped iterators if not needed 2024-03-25 10:50:37 +01:00
Michele Caini
f4e7e9738f sparse_set: avoid annoying warnings 2024-03-25 10:25:12 +01:00
Michele Caini
3dfc65a357 view: stop using scoped iterators 2024-03-25 10:24:40 +01:00
Michele Caini
fe83215f52 doc: links 2024-03-25 10:23:53 +01:00
Michele Caini
4df0f7ebf1 doc: updated similar projects 2024-03-25 10:20:55 +01:00
Michele Caini
3d40651112 test: suppress a clang-tidy warning 2024-03-25 08:10:39 +01:00
Michele Caini
b96c8e2eff test: stop using scoped iterators 2024-03-25 08:10:10 +01:00
Michele Caini
ad081f41db sigh_mixin: minor changes 2024-03-22 23:25:34 +01:00
Michele Caini
38b6fb2133 test: drop pointless checks 2024-03-22 14:38:32 +01:00
Michele Caini
b373bb13d1 sparse_set: avoid using scoped iterators (simple cases) 2024-03-22 12:30:54 +01:00
Michele Caini
53895c1b9e view: avoid using scoped iterators (simple cases) 2024-03-22 12:30:29 +01:00
Michele Caini
29af24dac1 test: avoid using scoped iterators (simple cases) 2024-03-22 12:29:57 +01:00
Michele Caini
bb57d94185 registry: avoid using scoped iterators when destroying 2024-03-22 08:24:45 +01:00
Michele Caini
c8a5812cea entity: minor changes around iterable objects 2024-03-22 08:22:15 +01:00
Michele Caini
02836f1f43 handle: explicit iterable type 2024-03-21 11:24:44 +01:00
Michele Caini
0cf7262d2d handle: struct -> class 2024-03-21 10:58:38 +01:00
Michele Caini
ec92b7672e handle: minor changes 2024-03-21 10:56:05 +01:00
Michele Caini
294792060c handle: support comparison with null_t - close #1130 2024-03-21 10:27:14 +01:00
Michele Caini
4bedef459e doc: minor changes 2024-03-21 10:25:49 +01:00
Michele Caini
65da029ff9 view: use iterator traits as needed 2024-03-20 16:49:59 +01:00
Michele Caini
ef563b05e1 runtime_view: use iterator traits as needed 2024-03-20 16:49:46 +01:00
Michele Caini
7952266492 doc: minor change 2024-03-20 16:49:20 +01:00
Michele Caini
9b81532ced storage: branch-less each/reach, no scoped iterators 2024-03-20 12:39:24 +01:00
Michele Caini
4a17395b6a storage: reduce uses of scoped iterators 2024-03-20 09:47:15 +01:00
Michele Caini
56edea562a type_traits: lambda support for nth_argument - close #1127 2024-03-20 09:45:31 +01:00
Michele Caini
bc5dfe3489 test: fix typo from last PR and make test local paths local 2024-03-19 10:19:57 +01:00
Michele Caini
e0e3786089 clang-format: suppress include main regex 2024-03-19 10:19:01 +01:00
Ezekiel Warren
1c30113338 build: update bazel common test target (#1124) 2024-03-18 16:21:20 +01:00
Michele Caini
4a8efa2703 test: try to make toolset v141 happy again 2024-03-18 11:54:13 +01:00
Michele Caini
4fa9767edd iwyu: export a few symbols 2024-03-18 09:30:13 +01:00
Michele Caini
a3b305b708 iwyu: minor changes 2024-03-18 09:29:55 +01:00
Michele Caini
809fdad185 test: cleanup 2024-03-17 09:55:19 +01:00
Michele Caini
bf7db4c585 doc: minor changes 2024-03-17 09:55:09 +01:00
Erik Scholz
17d06d96ad doc: cleanup grammar nitpicks in the context section (#1125) 2024-03-17 09:53:18 +01:00
Michele Caini
b8a0031d9c doc: component <-> element 2024-03-15 11:37:00 +01:00
Michele Caini
283753a3ff *: updated TODO 2024-03-15 09:55:45 +01:00
Michele Caini
2c753ff085 storage: make traits_type private 2024-03-15 09:55:39 +01:00
Michele Caini
12a1e7fffc sigh_mixin: avoid using storage traits_type 2024-03-15 09:54:47 +01:00
Michele Caini
098343d275 view: avoid using storage traits_type 2024-03-15 09:54:18 +01:00
Michele Caini
fc8beac28f group: avoid using storage traits_type 2024-03-15 09:53:37 +01:00
Michele Caini
a4c37a4a93 helper: avoid using storage traits_type 2024-03-15 09:53:12 +01:00
Michele Caini
4e66f6bf68 storage: element_type vs value_type for finer control 2024-03-15 09:52:49 +01:00
Michele Caini
f9d509251e meta: add iwyu pragmas to spec-only files 2024-03-14 09:36:47 +01:00
Michele Caini
0f2ee043ae test: iwyu docet 2024-03-14 09:36:29 +01:00
Michele Caini
af56c554af iwyu: it's a wild beast apparently, remove internal includes for now 2024-03-14 09:36:13 +01:00
Michele Caini
fb31c923db iwyu: add flow mapping end tokens 2024-03-14 08:35:25 +01:00
Michele Caini
f4a4ab339a iwyu: oops, forgot a comma :) 2024-03-14 08:30:45 +01:00
Michele Caini
02ec48ddc5 sigh_mixin: avoid using weak ranges twice - close #1123 2024-03-14 08:25:01 +01:00
Michele Caini
369bebd38b *: updated TODO 2024-03-14 08:24:20 +01:00
Michele Caini
9f29ac8f65 iwyu: add a few dependencies to reduce white noise 2024-03-14 08:24:08 +01:00
Michele Caini
4e89acb2ef test: suppress warnings due to unused aliases 2024-03-13 16:33:39 +01:00
Michele Caini
231ff77283 test: iwyu docet 2024-03-13 12:10:15 +01:00
Michele Caini
a26e35248b test: more on sigh mixin 2024-03-13 10:43:56 +01:00
Michele Caini
bcac7a6ea2 test: further review sigh mixin 2024-03-13 10:39:17 +01:00
Michele Caini
0e567f8f3b test: minor changes 2024-03-13 10:31:03 +01:00
Michele Caini
f03a6c6a92 test: review sigh mixin 2024-03-13 10:27:10 +01:00
Michele Caini
5f9e4344ba test: minor changes 2024-03-13 09:49:04 +01:00
Michele Caini
41a3c190ec *: updated TODO 2024-03-12 10:53:26 +01:00
Michele Caini
5c53209e5d sparse_set: make traits_type private 2024-03-12 10:53:18 +01:00
Michele Caini
d70664c0ad storage: avoid using traits_type from base 2024-03-12 10:52:54 +01:00
Michele Caini
55c257c905 test: avoid using sparse set traits_type 2024-03-12 08:41:11 +01:00
Michele Caini
f0ef2e7270 test: avoid using storage traits_type 2024-03-12 08:40:20 +01:00
Michele Caini
1064857764 test: cleanup 2024-03-12 08:39:51 +01:00
Michele Caini
dc9f0781fc registry: avoid using sparse set traits_type 2024-03-11 11:35:37 +01:00
Michele Caini
5adf7172d1 test: avoid using sparse set traits_type 2024-03-11 11:34:51 +01:00
Michele Caini
1f78a8e768 registry: make traits_type private as it ought to be 2024-03-11 11:15:43 +01:00
Michele Caini
7870cbfa56 snapshot: avoid using registry traits_type 2024-03-11 11:14:23 +01:00
Michele Caini
69d15470b4 test: avoid using registry traits_type 2024-03-11 11:13:30 +01:00
Michele Caini
fefcbb214a sparse_set: drop an extra typename that msvc happily accepted 2024-03-08 12:43:38 +01:00
Michele Caini
94f53958a0 *: updated TODO 2024-03-08 12:14:51 +01:00
Michele Caini
d7ed4dcedb natvis: updated entity views 2024-03-08 12:14:45 +01:00
Michele Caini
1178b8e0cf natvis: updated meta views 2024-03-08 12:14:37 +01:00
Michele Caini
f18234abbd doc: minor changes 2024-03-07 11:44:59 +01:00
Michele Caini
cc0bd48233 sparse_set: turn head into a size (position/length) 2024-03-07 10:25:26 +01:00
Michele Caini
921160a483 sparse_set: use aliases as much as possible 2024-03-07 10:24:40 +01:00
Michele Caini
95d8370da4 sparse_set: shared policy_to_head function 2024-03-06 14:25:05 +01:00
Michele Caini
2fba813e23 sparse_set: const correctness 2024-03-06 10:40:31 +01:00
Michele Caini
6b707d6af1 sparse_set: minor changes 2024-03-06 10:39:07 +01:00
Michele Caini
f481bc307f sparse_set: avoid using free_list internally, prepare to drop scoped iterators 2024-03-06 09:05:16 +01:00
Michele Caini
ebc6f64af0 sparse_set: better return value for range ::push 2024-03-06 08:49:26 +01:00
Michele Caini
ef61d4c3d0 natvis: update dispatcher/emitter views 2024-03-04 10:01:47 +01:00
Michele Caini
cb669d43a2 *: I love when git goes crazy :) 2024-03-04 10:01:21 +01:00
Michele Caini
7dbd00e7e7 natvis: update resource cache view 2024-03-04 10:00:31 +01:00
Michele Caini
698866df76 natvis: meta context 2024-03-04 09:59:31 +01:00
Michele Caini
6922f15095 meta: refine meta_func/data prop views 2024-03-04 09:59:21 +01:00
Michele Caini
cd82501ed2 natvis: internal changes 2024-03-04 09:58:10 +01:00
Michele Caini
84fb9af076 natvis: reintroduce simple views for containers 2024-03-04 09:49:59 +01:00
Michele Caini
062837dd31 test: try to make all compilers happy again 2024-03-01 19:05:21 +01:00
Michele Caini
97d46ce64d nativs: minor changes 2024-03-01 18:22:43 +01:00
Michele Caini
29ec97f464 *: updated TODO 2024-03-01 15:26:30 +01:00
Michele Caini
dc424f7d4c entity: try to make all compilers happy again 2024-03-01 15:22:42 +01:00
Michele Caini
1893a8c78d entity: zero sized version support 2024-03-01 14:46:56 +01:00
Michele Caini
56c881e087 test: linter feedback 2024-03-01 14:42:20 +01:00
Michele Caini
0dcf6c5690 nativs: support to zero-sized versions 2024-03-01 14:33:56 +01:00
Michele Caini
e7d29479e4 natvis: drop simple views for ecs module 2024-02-29 15:11:56 +01:00
Michele Caini
ee55085f3a view: try to make msvc happy again 2024-02-29 14:41:38 +01:00
Michele Caini
c802b2d687 natvis: drop simple views for containers 2024-02-29 11:45:12 +01:00
Michele Caini
dca2fefaba view: avoid duplicating data on iterators 2024-02-29 09:36:54 +01:00
Michele Caini
d8ef21b08b view: internal changes 2024-02-29 09:35:54 +01:00
Michele Caini
2f852806aa workflow: updated iwyu version 2024-02-28 09:47:16 +01:00
Michele Caini
945b8881b6 view: drop unnecessary argument 2024-02-28 09:29:13 +01:00
Michele Caini
0d71afacd8 view: drop redundant member 2024-02-28 09:26:43 +01:00
Michele Caini
0f9ee0ac03 natvis: refine view definition, prepare to drop stuff on the C++ side 2024-02-28 08:46:32 +01:00
Michele Caini
c3fedcc151 storage: drop unnecessary template parameter 2024-02-27 15:04:33 +01:00
Michele Caini
2f1c99ab81 entity: refine checks to fully support zero-sized versions/fully defined entity masks 2024-02-27 13:52:59 +01:00
Michele Caini
83576bf94e view: check the index directly before invoking ::each 2024-02-27 10:36:00 +01:00
Michele Caini
42c8df773c sparse_set: drop an useless check of the mode 2024-02-27 10:23:16 +01:00
Michele Caini
536c536563 test: minor changes 2024-02-27 10:22:29 +01:00
Michele Caini
2212f91c97 test: suppress warnings due to unused types 2024-02-26 15:13:30 +01:00
Michele Caini
f18c8f14bf storage: prevent ::next from spinning forever 2024-02-26 09:20:39 +01:00
Michele Caini
4e5b6de873 doc: minor changes 2024-02-26 09:18:27 +01:00
Michele Caini
84e4b8c3a0 test: use common types 2024-02-26 09:18:01 +01:00
Michele Caini
e33d8b28ca test: more entity types 2024-02-26 09:16:44 +01:00
Michele Caini
72f718458b test: use shared listener type, drop local definitions 2024-02-23 14:03:44 +01:00
Michele Caini
84ce6163a7 test: shared listener type 2024-02-23 14:02:40 +01:00
Michele Caini
a28ff99b08 test: boxed_type<T>::operator T() 2024-02-23 14:02:13 +01:00
Michele Caini
44b3cb1294 test: boxed_ char type 2024-02-23 14:01:50 +01:00
Michele Caini
79dfb454e2 doc: typo 2024-02-23 14:01:10 +01:00
Michele Caini
7b24bc0005 test: minor changes 2024-02-23 14:00:16 +01:00
Michele Caini
463e8e0098 tombstone: zero-sized version support 2024-02-23 13:59:53 +01:00
Michele Caini
a272676738 test: storage entity emplace-with-hint with misplaced placeholder 2024-02-22 09:21:57 +01:00
Michele Caini
3a51f71716 *: updated TODO 2024-02-22 09:20:50 +01:00
Michele Caini
a922216ab4 sparse_set: null/tombstone check when try_emplace-ing 2024-02-22 09:18:28 +01:00
Michele Caini
ba116ba949 sparse_set: drop force_back check for swap_only policy 2024-02-22 09:17:41 +01:00
Michele Caini
8b31b165b5 sparse_set: minor changes 2024-02-22 09:16:29 +01:00
Michele Caini
91a095dba4 storage: avoid emplacing extra entities when using a hint - close #1113 2024-02-21 08:55:25 +01:00
Michele Caini
371f547d59 nativs: storage entity 2024-02-20 09:43:19 +01:00
Michele Caini
bec1f60859 storage: prepare to directly create the single entity upon hint 2024-02-20 09:42:53 +01:00
Michele Caini
9db2daf3a7 test: minor changes 2024-02-19 17:50:39 +01:00
Michele Caini
f2a8c0ffe6 storage: reset next on clear 2024-02-19 17:26:38 +01:00
Michele Caini
70eceea864 *: updated TODO 2024-02-19 17:26:20 +01:00
Michele Caini
15aa6c7727 storage: decouple size and next identifier - see #1113 2024-02-19 14:30:00 +01:00
Michele Caini
948fb8337d test: cleanup 2024-02-19 14:28:31 +01:00
Michele Caini
ac3a3d2fbe test: make things work despite the unofficial name generation function 2024-02-16 14:54:59 +01:00
Michele Caini
20dd52410e test: make all compilers happy again 2024-02-16 14:54:02 +01:00
Michele Caini
48011dcbf8 test: get around an unexpected optimiziation by clang 2024-02-16 14:53:46 +01:00
Michele Caini
f4f7bdbf69 *: updated TODO 2024-02-16 10:36:00 +01:00
Michele Caini
eb531fd7ba test: avoid _t types 2024-02-16 10:35:54 +01:00
Michele Caini
6cab881c51 test: suppress a false warning by clang 2024-02-15 10:36:55 +01:00
Michele Caini
209768cdbe core: make all compilers happy again 2024-02-15 10:31:52 +01:00
Michele Caini
5ecc254a91 core: export more types as fwd decl - close #1114 2024-02-15 10:09:40 +01:00
Michele Caini
d013c69bf1 test: use common types for lib tests (meta) 2024-02-14 09:52:19 +01:00
Michele Caini
1a469a96ce test: use common types for lib tests (locator) 2024-02-14 09:52:07 +01:00
Michele Caini
4f1e0805db test: suppress a wrong warning by clang 2024-02-13 08:52:30 +01:00
Michele Caini
4cc5745346 test: use common types for lib tests (emitter) 2024-02-13 08:29:14 +01:00
Michele Caini
b7622a373b test: use common types for lib tests (dispatcher) 2024-02-13 08:28:41 +01:00
Michele Caini
a0a2ad4404 test: use common types for lib tests (registry) 2024-02-13 08:26:04 +01:00
Michele Caini
1962027c80 test: reuse types if possible 2024-02-12 14:17:17 +01:00
Michele Caini
8c1c899cb8 test: review ident tests 2024-02-09 10:49:08 +01:00
Michele Caini
3b74db258e test: review process tests 2024-02-09 10:48:55 +01:00
Michele Caini
696f78c8a0 test: review emitter tests 2024-02-09 10:48:39 +01:00
Michele Caini
c31e1a939e test: common emitter test class 2024-02-09 10:48:13 +01:00
Michele Caini
dd95c35309 *: updated TODO 2024-02-09 09:32:50 +01:00
Michele Caini
2c9d37efc3 test: make common really common 2024-02-09 09:29:09 +01:00
Michele Caini
1777db0a09 *: updated TODO 2024-02-08 10:12:34 +01:00
Michele Caini
6f7839f8c4 meta: drop a bunch of NOLINT 2024-02-08 10:12:28 +01:00
Michele Caini
ee78d39860 natvis: meta_type_node representation 2024-02-08 10:12:04 +01:00
Michele Caini
8a4a312250 meta: update meta_type::is_pointer to use is_pointer meta traits 2024-02-08 10:11:41 +01:00
Michele Caini
ed21813170 meta: is_pointer meta traits 2024-02-08 10:10:49 +01:00
Michele Caini
9ec8adf5c9 natvis: typo 2024-02-07 13:01:05 +01:00
Michele Caini
48c3f58462 view: use ::index when picking the right ::each 2024-02-07 11:57:09 +01:00
Michele Caini
d0528c7a4d doc: minor changes 2024-02-07 11:50:40 +01:00
Michele Caini
4e9b4371e2 group: try to make vs happy again 2024-02-07 10:16:50 +01:00
Michele Caini
e3fef531e6 test: drop a bunch of NOLINT 2024-02-07 09:27:29 +01:00
Michele Caini
5ca2a95f36 registry: improved ::valid check 2024-02-07 09:25:23 +01:00
Michele Caini
df1968255b group: suppress shadow warning 2024-02-07 09:23:17 +01:00
Michele Caini
6227ff5fcf doc: updated copyright 2024-02-07 09:18:16 +01:00
Michele Caini
3192f20877 test: drop a bunch of NOLINT 2024-02-07 07:59:36 +01:00
Michele Caini
bcdc1ebad9 test: drop a bunch of NOLINT 2024-02-07 07:59:19 +01:00
Michele Caini
a43ffa8f0d test: workaround for a nasty bug of toolset v141 that sees 4 as member pointer (yeah, I'm amazed too) 2024-02-06 10:48:31 +01:00
Michele Caini
3a89dadb30 build: update iwyu version 2024-02-06 08:59:55 +01:00
Michele Caini
4d7b830b20 iwyu: just a few changes 2024-02-06 08:58:26 +01:00
Michele Caini
b92d2c98ae test: minor changes 2024-02-06 08:30:46 +01:00
Michele Caini
5d3f155164 test: final touch for containers 2024-02-06 08:27:47 +01:00
Michele Caini
fa56839bcf doc: typo 2024-02-05 09:00:15 +01:00
Ullrich Praetz
a6a4bbfc8e doc: add friflo-engine-ecs to the list of similar projects (#1108) 2024-02-05 08:59:22 +01:00
Michele Caini
f6aa7dc725 test: registry 2024-02-05 08:54:50 +01:00
Michele Caini
7ea79932ed test: cleanup 2024-02-03 00:31:13 +01:00
Michele Caini
df6da5c208 test: full review of entt::any tests 2024-02-02 11:20:49 +01:00
Michele Caini
a69e0eb661 update single include file 2024-02-02 10:10:49 +01:00
Michele Caini
83aabf7cfc registry: assert when emplacing invalid entities - close #1095 2024-02-02 09:47:06 +01:00
Michele Caini
61f4ad8837 linter: minor changes 2024-02-01 14:31:36 +01:00
Michele Caini
872e0c6dde test: avoid using custom entities when emplacing in a registry - see #1095 2024-02-01 14:29:47 +01:00
Michele Caini
ad743ff327 hashed_string: refine ctos and avoid uninitialized ::length in all cases 2024-02-01 14:28:30 +01:00
Michele Caini
9d07ff18b2 *: updated TODO 2024-01-31 13:50:39 +01:00
Michele Caini
1150b980fa test: drop a bunch of NOLINT 2024-01-31 13:50:06 +01:00
Michele Caini
3ad137f597 test: drop a bunch of NOLINT 2024-01-31 10:27:57 +01:00
Michele Caini
5a4531baba test: drop a bunch of NOLINT 2024-01-31 10:26:41 +01:00
Michele Caini
53f8600ae5 runtime_view: bool operator to test for validity 2024-01-31 10:26:10 +01:00
Michele Caini
11d609314c test: drop a bunch of NOLINT 2024-01-31 10:25:20 +01:00
Michele Caini
97b2b6c5b8 test: drop a bunch of NOLINT 2024-01-30 11:12:47 +01:00
Michele Caini
c1b7229238 adjacency_matrix: ::empty function 2024-01-30 11:12:31 +01:00
Michele Caini
c1a211d422 test: drop a bunch of NOLINT 2024-01-30 11:12:00 +01:00
Michele Caini
00ac590e2f flow: ::empty function 2024-01-30 11:07:00 +01:00
Michele Caini
31a91c764b test: drop a bunch of NOLINT 2024-01-30 11:05:25 +01:00
Michele Caini
553accd5f0 test: drop a bunch of NOLINT 2024-01-29 17:22:43 +01:00
Michele Caini
1ab1676d0b test: drop a bunch of NOLINT 2024-01-29 14:53:38 +01:00
Michele Caini
14ac11e9ca test: drop a bunch of NOLINT 2024-01-29 14:52:19 +01:00
Michele Caini
d45ef425a2 test: drop a bunch of NOLINT 2024-01-27 14:25:07 +01:00
Michele Caini
fdbb6829fa test: suppress a gcc sign-compare warning 2024-01-27 14:24:02 +01:00
Michele Caini
38ede6d0c4 test: drop a bunch of NOLINT 2024-01-26 16:56:19 +01:00
Michele Caini
4b4c038dc7 test: drop a bunch of NOLINT 2024-01-26 16:55:56 +01:00
Michele Caini
7a71084a8e test: drop a bunch of NOLINT 2024-01-26 08:16:40 +01:00
Michele Caini
8a57b94a18 test: drop a bunch of NOLINT 2024-01-26 08:16:17 +01:00
Michele Caini
79d72bd568 test: drop a bunch of NOLINT 2024-01-26 08:15:59 +01:00
Michele Caini
ece46d4aa3 doc: Trollworks engine 2024-01-26 08:14:56 +01:00
Michele Caini
f95b9f0ac9 test: suppress clang missing-braces warnings 2024-01-25 09:38:39 +01:00
Michele Caini
9e9bed06af storage: drop deprecated member pack 2024-01-25 08:30:06 +01:00
Michele Caini
bba8b6bdd8 registry: review destroy to use sort_as 2024-01-25 08:28:58 +01:00
Michele Caini
b96631c486 sparse_set: make sort_as return an iterator past the last shared element 2024-01-25 08:28:28 +01:00
Michele Caini
469a3cd568 meta: drop deprecated function owner 2024-01-24 14:57:45 +01:00
Michele Caini
d0e2edc13f meta: drop deprecated function key_only 2024-01-24 14:57:26 +01:00
Michele Caini
1eb4b36643 any: drop deprecated function owner 2024-01-24 14:56:35 +01:00
Michele Caini
325d3a9552 test: minor changes 2024-01-24 09:37:10 +01:00
Michele Caini
71bd869fc8 test: drop a bunch of NOLINT 2024-01-24 08:21:07 +01:00
Michele Caini
7d0cf207fd helper: drop deprecated function to_entity 2024-01-24 08:18:28 +01:00
Michele Caini
2f625b0a6f storage: drop deprecated member in_use 2024-01-24 08:12:07 +01:00
Michele Caini
01957a73fd meta: const correctness 2024-01-23 13:30:35 +01:00
Michele Caini
93f20984a6 test: drop a bunch of NOLINT 2024-01-23 10:51:04 +01:00
Michele Caini
00a58db0a7 view: drop deprecated member operator[] 2024-01-23 10:27:55 +01:00
Michele Caini
e3cb863f94 sparse_set: drop deprecated member at 2024-01-23 08:06:16 +01:00
Michele Caini
e5381cee86 sparse_set: drop deprecated member sort_as 2024-01-22 17:10:46 +01:00
Michele Caini
406ba3cb7d group: drop deprecated member sort_as 2024-01-22 16:51:44 +01:00
Ezekiel Warren
89622127db build: don't install include/BUILD.bazel (#1107) 2024-01-22 09:52:58 +01:00
Michele Caini
5f73b2e4f2 test: boxed_int -> boxed_type 2024-01-22 09:49:59 +01:00
Michele Caini
baab46e77b *: updated TODO (pending checks verified) 2024-01-21 23:24:28 +01:00
Michele Caini
6889d9d87b test: drop a bunch of NOLINT 2024-01-19 15:25:11 +01:00
Michele Caini
dd3feb87cd test: avoid warnings due to unused variables 2024-01-19 14:35:26 +01:00
Michele Caini
2375c0489b group: avoid swapping observed types - close #1100 2024-01-19 14:22:07 +01:00
Michele Caini
353b3d19ee snapshot: refine the check in the constructor of the loader class - close #1106 2024-01-19 09:21:46 +01:00
Michele Caini
68688b2263 *: updated TODO 2024-01-18 15:22:16 +01:00
Michele Caini
324716f82e test: drop a bunch of NOLINT 2024-01-18 09:20:30 +01:00
Michele Caini
51a063206b test: minor changes 2024-01-17 17:41:43 +01:00
Michele Caini
5e0caa6110 *: updated TODO 2024-01-17 17:41:25 +01:00
Michele Caini
235ed63e3b test: make toolset v141 happy again :) 2024-01-17 10:43:49 +01:00
Michele Caini
1dbeedcf12 test: drop a bunch of NOLINT 2024-01-17 08:47:36 +01:00
Michele Caini
d695bc1b29 test: minor changes 2024-01-17 08:47:08 +01:00
Michele Caini
f08e1ea21c view: avoid unchecked_refresh loops on single get type 2024-01-16 16:11:14 +01:00
Michele Caini
4510ba32b5 now working on version 3.14.0 2024-01-16 16:09:59 +01:00
230 changed files with 13572 additions and 9286 deletions

View File

@@ -1,17 +1,16 @@
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
# 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 --announce_rc
common:ci --verbose_failures
common:ci --keep_going
test:ci --test_output=errors
try-import %workspace%/user.bazelrc
test:ci --test_output=errors
try-import %workspace%/user.bazelrc

View File

@@ -2,10 +2,11 @@ BasedOnStyle: llvm
---
AccessModifierOffset: -4
AlignEscapedNewlines: DontAlign
AllowShortBlocksOnASingleLine: Empty
AllowShortBlocksOnASingleLine: Always
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: WithoutElse
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: true
AlwaysBreakTemplateDeclarations: Yes
BreakBeforeBinaryOperators: NonAssignment
@@ -23,6 +24,7 @@ IncludeCategories:
Priority: 4
- Regex: '.*'
Priority: 5
IncludeIsMainRegex: "^$"
IndentPPDirectives: AfterHash
IndentWidth: 4
KeepEmptyLinesAtTheStartOfBlocks: false

View File

@@ -2,25 +2,47 @@ Checks: >
bugprone-*,
concurrency-*,
cppcoreguidelines-*,
-cppcoreguidelines-owning-memory,
-cppcoreguidelines-pro-bounds-constant-array-index,
-cppcoreguidelines-pro-type-const-cast,
-cppcoreguidelines-pro-type-member-init,
-cppcoreguidelines-pro-type-reinterpret-cast,
-cppcoreguidelines-pro-type-union-access,
misc-*,
-misc-include-cleaner,
-misc-no-recursion,
modernize-*,
-modernize-use-trailing-return-type,
performance-*,
portability-*,
readibility-*
readability-*,
-readability-function-cognitive-complexity,
-readability-named-parameter,
-readability-uppercase-literal-suffix,
CheckOptions:
- key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor
- key: cppcoreguidelines-avoid-magic-numbers.IgnoreAllFloatingPointValues
value: true
- key: cppcoreguidelines-avoid-magic-numbers.IgnorePowersOf2IntegerValues
value: true
- key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions
value: true
- key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctionsWhenCopyIsDeleted
value: true
- key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor
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
- key: modernize-avoid-c-arrays.AllowStringArrays
value: true
- key: cppcoreguidelines-avoid-magic-numbers.IgnoreAllFloatingPointValues
- key: readability-function-cognitive-complexity.IgnoreMacros
value: true
- key: readability-identifier-length.MinimumParameterNameLength
value: 2
- key: readability-identifier-length.MinimumVariableNameLength
value: 2
- key: readability-magic-numbers.IgnoreAllFloatingPointValues
value: true
- key: readability-magic-numbers.IgnorePowersOf2IntegerValues
value: true

View File

@@ -13,7 +13,10 @@ jobs:
permissions:
contents: write
steps:
- uses: actions/setup-go@v5
- run: go install github.com/bazelbuild/buildtools/buildozer@latest
- uses: actions/checkout@v4
- run: ./scripts/sync_bzlmod_version.sh
- 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:

View File

@@ -3,6 +3,7 @@ name: bazel
on: [push, pull_request]
jobs:
test:
strategy:
matrix:
@@ -10,8 +11,10 @@ jobs:
- ubuntu-latest
- windows-latest
- macos-latest
runs-on: ${{ matrix.os }}
continue-on-error: true
steps:
- uses: actions/checkout@v4
- run: bazelisk test --config=ci ...

View File

@@ -9,48 +9,45 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, ubuntu-20.04]
os: [ubuntu-latest, ubuntu-24.04]
compiler:
- { 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: g++, exe: 'g++', version: 13 }
- { pkg: g++, exe: 'g++', version: 14 }
- { pkg: clang, exe: 'clang++', version: 13 }
- { pkg: clang, exe: 'clang++', version: 14 }
- { pkg: clang, exe: 'clang++', version: 15 }
- { pkg: clang, exe: 'clang++', version: 16 }
- { pkg: clang, exe: 'clang++', version: 17 }
- { pkg: clang, exe: 'clang++', version: 18 }
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
- os: ubuntu-latest
compiler: { pkg: g++, exe: 'g++', version: 13 }
- os: ubuntu-latest
compiler: { pkg: g++, exe: 'g++', version: 14 }
- os: ubuntu-latest
compiler: { pkg: clang, exe: 'clang++', version: 16 }
- os: ubuntu-latest
compiler: { pkg: clang, exe: 'clang++', version: 17 }
- os: ubuntu-latest
compiler: { pkg: clang, exe: 'clang++', version: 18 }
- os: ubuntu-24.04
compiler: { pkg: g++, exe: 'g++', version: 9 }
- os: ubuntu-24.04
compiler: { pkg: g++, exe: 'g++', version: 10 }
- os: ubuntu-24.04
compiler: { pkg: g++, exe: 'g++', version: 11 }
- os: ubuntu-24.04
compiler: { pkg: clang, exe: 'clang++', version: 13 }
- os: ubuntu-20.04
- os: ubuntu-24.04
compiler: { pkg: clang, exe: 'clang++', version: 14 }
- os: ubuntu-24.04
compiler: { pkg: clang, exe: 'clang++', version: 15 }
runs-on: ${{ matrix.os }}
@@ -78,10 +75,8 @@ jobs:
strategy:
matrix:
toolset: [default, v141, v142, clang-cl]
toolset: [default, v142, clang-cl]
include:
- toolset: v141
toolset_option: -T"v141"
- toolset: v142
toolset_option: -T"v142"
- toolset: clang-cl

View File

@@ -17,8 +17,6 @@ jobs:
steps:
- 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:

View File

@@ -1,19 +1,18 @@
name: analyzer
name: tools
on:
push:
branches:
- master
- wip
- tools
jobs:
iwyu:
timeout-minutes: 30
timeout-minutes: 60
env:
IWYU: "0.20"
LLVM: "16"
IWYU: "0.22"
LLVM: "18"
runs-on: ubuntu-latest
continue-on-error: true
@@ -59,3 +58,24 @@ jobs:
-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
clang-tidy:
timeout-minutes: 60
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v4
- name: Compile tests
working-directory: build
env:
CXX: clang++
run: |
cmake -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON -DENTT_USE_CLANG_TIDY=ON ..
make -j4
- name: Run tests
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest -C Debug -j4

1
.gitignore vendored
View File

@@ -13,3 +13,4 @@ cpp.hint
/bazel-*
/test/bazel-*
/user.bazelrc
*.bazel.lock

View File

@@ -25,7 +25,7 @@ endif()
message(VERBOSE "*")
message(VERBOSE "* ${PROJECT_NAME} v${PROJECT_VERSION} (${CMAKE_BUILD_TYPE})")
message(VERBOSE "* Copyright (c) 2017-2023 Michele Caini <michele.caini@gmail.com>")
message(VERBOSE "* Copyright (c) 2017-2024 Michele Caini <michele.caini@gmail.com>")
message(VERBOSE "*")
# CMake stuff
@@ -118,10 +118,12 @@ if(ENTT_INCLUDE_HEADERS)
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/config/version.h>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/container/dense_map.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/container/dense_set.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/container/table.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/container/fwd.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/algorithm.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/any.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/attribute.h>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/bit.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/compressed_pair.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/enum.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/family.hpp>
@@ -131,6 +133,7 @@ if(ENTT_INCLUDE_HEADERS)
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/iterator.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/memory.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/monostate.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/ranges.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/tuple.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/type_info.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/type_traits.hpp>
@@ -144,6 +147,7 @@ if(ENTT_INCLUDE_HEADERS)
$<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>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/ranges.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/registry.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/runtime_view.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/snapshot.hpp>
@@ -169,7 +173,6 @@ if(ENTT_INCLUDE_HEADERS)
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/template.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/type_traits.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/utility.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/platform/android-ndk-r17.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/poly/fwd.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/poly/poly.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/process/fwd.hpp>
@@ -200,7 +203,6 @@ if(ENTT_HAS_NATVIS)
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/graph.natvis>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/locator.natvis>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/meta.natvis>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/platform.natvis>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/poly.natvis>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/process.natvis>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/resource.natvis>
@@ -214,7 +216,7 @@ if(ENTT_HAS_SANITIZER)
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")
set(CMAKE_CXX_CLANG_TIDY "${ENTT_CLANG_TIDY_EXECUTABLE};--config-file=${EnTT_SOURCE_DIR}/.clang-tidy;--header-filter=${EnTT_SOURCE_DIR}/src/entt/.*")
endif()
if(ENTT_HAS_LIBCPP)
@@ -282,7 +284,13 @@ 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)
@@ -318,7 +326,7 @@ endif()
option(ENTT_BUILD_DOCS "Enable building with documentation." OFF)
if(ENTT_BUILD_DOCS)
find_package(Doxygen 1.8)
find_package(Doxygen 1.10)
if(DOXYGEN_FOUND)
add_subdirectory(docs)

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2017-2023 Michele Caini, author of EnTT
Copyright (c) 2017-2024 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

View File

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

View File

@@ -1,8 +1,5 @@
![EnTT: Gaming meets modern C++](https://user-images.githubusercontent.com/1812216/103550016-90752280-4ea8-11eb-8667-12ed2219e137.png)
<!--
@cond TURN_OFF_DOXYGEN
-->
[![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)
@@ -24,7 +21,7 @@ in [**Minecraft**](https://minecraft.net/en-us/attribution/) by Mojang, the
[**ArcGIS Runtime SDKs**](https://developers.arcgis.com/arcgis-runtime/) by Esri
and the amazing [**Ragdoll**](https://ragdolldynamics.com/).<br/>
If you don't see your project in the list, please open an issue, submit a PR or
add the [#entt](https://github.com/topics/entt) tag to your _topics_! :+1:
add the [\#entt](https://github.com/topics/entt) tag to your _topics_! :+1:
---
@@ -64,9 +61,6 @@ Many thanks to [these people](https://skypjack.github.io/sponsorship/) and
* [EnTT in Action](#entt-in-action)
* [Contributors](#contributors)
* [License](#license)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
@@ -242,9 +236,9 @@ To use `EnTT` from a `CMake` project, just link an existing target to the
The library offers everything you need for locating (as in `find_package`),
embedding (as in `add_subdirectory`), fetching (as in `FetchContent`) or using
it in many of the ways that you can think of and that involve `CMake`.<br/>
Covering all possible cases would require a treaty and not a simple README file,
but I'm confident that anyone reading this section also knows what it's about
and can use `EnTT` from a `CMake` project without problems.
Covering all possible cases would require a treatise and not a simple README
file, but I'm confident that anyone reading this section also knows what it's
about and can use `EnTT` from a `CMake` project without problems.
## Natvis support
@@ -354,16 +348,10 @@ To navigate it with your favorite browser:
$ cd build
$ your_favorite_browser docs/html/index.html
<!--
@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.<br/>
Moreover, there exists a [wiki](https://github.com/skypjack/entt/wiki) dedicated
to the project where users can find all related documentation pages.
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Tests
@@ -380,9 +368,6 @@ To build the most basic set of tests:
Note that benchmarks are not part of this set.
<!--
@cond TURN_OFF_DOXYGEN
-->
# EnTT in Action
`EnTT` is widely used in private and commercial applications. I cannot even
@@ -400,7 +385,7 @@ open an issue or a PR and I'll be glad to add them to the list.
# Contributors
Requests for features, PRs, suggestions ad feedback are highly appreciated.
Requests for features, PRs, suggestions and feedback are highly appreciated.
If you find you can help and want to contribute to the project with your
experience or you do want to get part of the project for some other reason, feel
@@ -410,18 +395,15 @@ I can't promise that each and every contribution will be accepted, but I can
assure that I'll do my best to take them all as soon as possible.
If you decide to participate, please see the guidelines for
[contributing](CONTRIBUTING.md) before to create issues or pull
requests.<br/>
[contributing](https://github.com/skypjack/entt/blob/master/CONTRIBUTING.md)
before to create issues or pull requests.<br/>
Take also a look at the
[contributors list](https://github.com/skypjack/entt/blob/master/AUTHORS) to
know who has participated so far.
<!--
@endcond TURN_OFF_DOXYGEN
-->
# License
Code and documentation Copyright (c) 2017-2023 Michele Caini.<br/>
Code and documentation Copyright (c) 2017-2024 Michele Caini.<br/>
Colorful logo Copyright (c) 2018-2021 Richard Caseres.
Code released under

33
TODO
View File

@@ -10,10 +10,6 @@ DOC:
* bump entities, reserved bits on identifiers
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
* 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
@@ -21,11 +17,28 @@ TODO:
* 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
* test: push sharing types further
* after non-continuous generation for entity storage:
- get/reset placeholder to position after saving/loading (avoid long lookup)
- allow skipping/reserving entity identifiers
- documentation for reserved entities
* storage entity: no emplace/insert, rename and add a fast range-push from above
* view: propagate tombstone check request to iterator
* table: pop back to support swap and pop, single column access, empty type optimization
* checkout tools workflow
* improve front (no multiple checks) and back (ie no contains) for multi-type view
* cleanup common view from tricks to handle single swap-only and in-place, if constexpr branches
* entity based component_traits
* review cmake warning about FetchContent_Populate (need .28 and EXCLUDE_FROM_ALL for FetchContent)
* after removing meta prop vectors, copy meta objects in their handles directly
* suppress -Wself-move on CI with g++13
* view and view iterator specializations for multi, single and filtered elements
* organizer support to groups
* meta range: move id to meta objects and return plain types (?), then remove id from meta base and meta ctor too
* refine the storage fallback mechanism for views (ie alloc?)
* sigh_mixin: automatic signal registration
* sigh_mixin: change cb signature from reg/entt to storage/entt (breaking for the good)
* don't pass reactive storage by default to callback
* runtime types support for meta for types that aren't backed by C++ types

View File

@@ -15,7 +15,7 @@ void update(entt::registry &registry) {
auto view = registry.view<position, velocity>();
for(auto entity: view) {
// gets only the components that are going to be used ...
// gets only the elements that are going to be used ...
auto &vel = view.get<velocity>(entity);
@@ -28,7 +28,7 @@ void update(entt::registry &registry) {
void update(std::uint64_t dt, entt::registry &registry) {
registry.view<position, velocity>().each([dt](auto &pos, auto &vel) {
// gets all the components of the view at once ...
// gets all the elements of the view at once ...
pos.x += vel.dx * dt;
pos.y += vel.dy * dt;

View File

@@ -1,4 +1,4 @@
# Doxyfile 1.9.7
# Doxyfile 1.10.0
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
@@ -63,6 +63,12 @@ PROJECT_BRIEF =
PROJECT_LOGO =
# With the PROJECT_ICON tag one can specify an icon that is included in the tabs
# when the HTML document is shown. Doxygen will copy the logo to the output
# directory.
PROJECT_ICON =
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
# into which the generated documentation will be written. If a relative path is
# entered, it will be relative to the location where doxygen was started. If
@@ -356,13 +362,13 @@ 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..
# 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 punctuation characters removed.
# The default value is: DOXYGEN.
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
MARKDOWN_ID_STYLE = DOXYGEN
MARKDOWN_ID_STYLE = GITHUB
# When enabled doxygen tries to link words that correspond to documented
# classes, or namespaces to their corresponding documentation. Such a link can
@@ -968,12 +974,12 @@ INPUT_FILE_ENCODING =
# Note the list of default checked file patterns might differ from the list of
# default file extension mappings.
#
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,
# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C
# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
# *.vhdl, *.ucf, *.qsf and *.ice.
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm,
# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl,
# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d,
# *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to
# be provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
FILE_PATTERNS = *.h \
*.hpp \
@@ -1129,7 +1135,8 @@ FORTRAN_COMMENT_AFTER = 72
SOURCE_BROWSER = YES
# Setting the INLINE_SOURCES tag to YES will include the body of functions,
# classes and enums directly into the documentation.
# multi-line macros, enums or list initialized variables directly into the
# documentation.
# The default value is: NO.
INLINE_SOURCES = NO
@@ -1417,6 +1424,33 @@ HTML_DYNAMIC_MENUS = YES
HTML_DYNAMIC_SECTIONS = NO
# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be
# dynamically folded and expanded in the generated HTML source code.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_CODE_FOLDING = YES
# If the HTML_COPY_CLIPBOARD tag is set to YES then doxygen will show an icon in
# the top right corner of code and text fragments that allows the user to copy
# its content to the clipboard. Note this only works if supported by the browser
# and the web page is served via a secure context (see:
# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file:
# protocol.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COPY_CLIPBOARD = YES
# Doxygen stores a couple of settings persistently in the browser (via e.g.
# cookies). By default these settings apply to all HTML pages generated by
# doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store
# the settings under a project specific key, such that the user preferences will
# be stored separately.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_PROJECT_COOKIE =
# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
# shown in the various tree structured indices initially; the user can expand
# and collapse entries dynamically later on. Doxygen will expand the tree to
@@ -2045,7 +2079,7 @@ PDF_HYPERLINKS = YES
USE_PDFLATEX = YES
# The LATEX_BATCHMODE tag ignals the behavior of LaTeX in case of an error.
# The LATEX_BATCHMODE tag signals 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
@@ -2247,6 +2281,32 @@ DOCBOOK_OUTPUT = docbook
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# Configuration options related to Sqlite3 output
#---------------------------------------------------------------------------
# If the GENERATE_SQLITE3 tag is set to YES doxygen will generate a Sqlite3
# database with symbols found by doxygen stored in tables.
# The default value is: NO.
GENERATE_SQLITE3 = NO
# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be
# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put
# in front of it.
# The default directory is: sqlite3.
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
SQLITE3_OUTPUT = sqlite3
# The SQLITE3_RECREATE_DB tag is set to YES, the existing doxygen_sqlite3.db
# database file will be recreated with each doxygen run. If set to NO, doxygen
# will warn if a database file is already found and not modify it.
# The default value is: YES.
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
SQLITE3_RECREATE_DB = YES
#---------------------------------------------------------------------------
# Configuration options related to the Perl module output
#---------------------------------------------------------------------------
@@ -2389,15 +2449,15 @@ TAGFILES =
GENERATE_TAGFILE =
# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
# the class index. If set to NO, only the inherited external classes will be
# listed.
# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces
# will be listed in the class and namespace index. If set to NO, only the
# inherited external classes will be listed.
# The default value is: NO.
ALLEXTERNALS = NO
# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
# in the modules index. If set to NO, only the current project's groups will be
# in the topic index. If set to NO, only the current project's groups will be
# listed.
# The default value is: YES.
@@ -2482,7 +2542,11 @@ DOT_FONTPATH =
# 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.
# relations will be shown as texts / links. Explicit enabling an inheritance
# graph or choosing a different representation for an inheritance graph of a
# specific class, can be accomplished by means of the command \inheritancegraph.
# Disabling an inheritance graph can be accomplished by means of the command
# \hideinheritancegraph.
# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN.
# The default value is: YES.
@@ -2491,15 +2555,21 @@ CLASS_GRAPH = YES
# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
# graph for each documented class showing the direct and indirect implementation
# dependencies (inheritance, containment, and class references variables) of the
# class with other documented classes.
# class with other documented classes. Explicit enabling a collaboration graph,
# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the
# command \collaborationgraph. Disabling a collaboration graph can be
# accomplished by means of the command \hidecollaborationgraph.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
COLLABORATION_GRAPH = YES
# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
# groups, showing the direct groups dependencies. See also the chapter Grouping
# in the manual.
# groups, showing the direct groups dependencies. Explicit enabling a group
# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means
# of the command \groupgraph. Disabling a directory graph can be accomplished by
# means of the command \hidegroupgraph. See also the chapter Grouping in the
# manual.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
@@ -2541,8 +2611,8 @@ DOT_UML_DETAILS = NO
# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
# to display on a single line. If the actual line length exceeds this threshold
# significantly it will wrapped across multiple lines. Some heuristics are apply
# to avoid ugly line breaks.
# significantly it will be wrapped across multiple lines. Some heuristics are
# applied to avoid ugly line breaks.
# Minimum value: 0, maximum value: 1000, default value: 17.
# This tag requires that the tag HAVE_DOT is set to YES.
@@ -2559,7 +2629,9 @@ TEMPLATE_RELATIONS = NO
# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
# YES then doxygen will generate a graph for each documented file showing the
# direct and indirect include dependencies of the file with other documented
# files.
# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO,
# can be accomplished by means of the command \includegraph. Disabling an
# include graph can be accomplished by means of the command \hideincludegraph.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
@@ -2568,7 +2640,10 @@ INCLUDE_GRAPH = YES
# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
# set to YES then doxygen will generate a graph for each documented file showing
# the direct and indirect include dependencies of the file with other documented
# files.
# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set
# to NO, can be accomplished by means of the command \includedbygraph. Disabling
# an included by graph can be accomplished by means of the command
# \hideincludedbygraph.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
@@ -2608,7 +2683,10 @@ GRAPHICAL_HIERARCHY = YES
# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
# dependencies a directory has on other directories in a graphical way. The
# dependency relations are determined by the #include relations between the
# files in the directories.
# files in the directories. Explicit enabling a directory graph, when
# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command
# \directorygraph. Disabling a directory graph can be accomplished by means of
# the command \hidedirectorygraph.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.

View File

@@ -1,8 +1,5 @@
# Crash Course: configuration
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
@@ -17,9 +14,6 @@
* [ENTT_DISABLE_ASSERT](#entt_disable_assert)
* [ENTT_NO_ETO](#entt_no_eto)
* [ENTT_STANDARD_CPP](#entt_standard_cpp)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction

View File

@@ -1,32 +1,29 @@
# Crash Course: containers
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [Containers](#containers)
* [Dense map](#dense-map)
* [Dense set](#dense-set)
<!--
@endcond TURN_OFF_DOXYGEN
-->
* [Adaptors](#adaptors)
* [Table](#table)
# Introduction
The standard C++ library offers a wide range of containers and it's really
difficult to do better (although it's very easy to do worse, as many examples
available online demonstrate).<br/>
The standard C++ library offers a wide range of containers and adaptors already.
It's really 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 functionalities by
making available some containers initially developed for internal use.
making available some containers and adaptors initially developed for internal
use.
This section of the library is likely to grow larger over time. However, for the
moment it's quite small and mainly aimed at satisfying some internal needs.<br/>
For all containers made available, full test coverage and stability over time is
guaranteed as usual.
For all containers and adaptors made available, full test coverage and stability
over time is guaranteed as usual.
# Containers
@@ -65,3 +62,23 @@ The interface is in all respects similar to its counterpart in the standard
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`).
# Adaptors
## Table
The `basic_table` class is a container adaptor which manages multiple sequential
containers together, treating them as different columns of the same table.<br/>
The `table` alias allows users to provide only the types to handle, using
`std::vector` as the default sequential container.
Only a small set of functions is provided, although very close to what the API
of the `std::vector` class offers.<br/>
The internal implementation is purposely supported by a tuple of containers
rather than a container of tuples. The purpose is to allow efficient access to
single columns and not just access to the entire data set of the table.
When a row is accessed, all data are returned in the form of a tuple containing
(possibly const) references to the elements of the row itself.<br/>
Similarly, when a table is iterated, tuples of references to table elements are
returned for each row.

View File

@@ -1,14 +1,12 @@
# Crash Course: core functionalities
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [Any as in any type](#any-as-in-any-type)
* [Small buffer optimization](#small-buffer-optimization)
* [Alignment requirement](#alignment-requirement)
* [Bit](#bit)
* [Compressed pair](#compressed-pair)
* [Enum as bitmask](#enum-as-bitmask)
* [Hashed strings](#hashed-strings)
@@ -19,7 +17,6 @@
* [Iota iterator](#iota-iterator)
* [Iterable adaptor](#iterable-adaptor)
* [Memory](#memory)
* [Power of two and fast modulus](#power-of-two-and-fast-modulus)
* [Allocator aware unique pointers](#allocator-aware-unique-pointers)
* [Monostate](#monostate)
* [Type support](#type-support)
@@ -39,9 +36,6 @@
* [Compile-time generator](#compile-time-generator)
* [Runtime generator](#runtime-generator)
* [Utilities](#utilities)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
@@ -202,6 +196,22 @@ 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.
# Bit
Finding out the population count of an unsigned integral value (`popcount`),
whether a number is a power of two or not (`has_single_bit`) as well as the next
power of two given a random value (`next_power_of_two`) can be useful.<br/>
For example, it helps to allocate memory in pages having a size suitable for the
fast modulus:
```cpp
const std::size_t result = entt::fast_mod(value, modulus);
```
Where `modulus` is necessarily a power of two. Perhaps not everyone knows that
this type of operation is far superior in terms of performance to the basic
modulus and for this reason preferred in many areas.
# Compressed pair
Primarily designed for internal use and far from being feature complete, the
@@ -442,22 +452,6 @@ acronyms like _POCCA_, _POCMA_ or _POCS_.<br/>
I won't describe them here in detail. Instead, I recommend reading the inline
documentation to those interested in the subject.
## Power of two and fast modulus
Finding out if a number is a power of two (`is_power_of_two`) or what the next
power of two is given a random value (`next_power_of_two`) is very useful at
times.<br/>
For example, it helps to allocate memory in pages having a size suitable for the
fast modulus:
```cpp
const std::size_t result = entt::fast_mod(value, modulus);
```
Where `modulus` is necessarily a power of two. Perhaps not everyone knows that
this type of operation is far superior in terms of performance to the basic
modulus and for this reason preferred in many areas.
## Allocator aware unique pointers
A nasty thing in C++ (at least up to C++20) is the fact that shared pointers

View File

@@ -1,8 +1,5 @@
# Crash Course: entity-component system
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
@@ -16,7 +13,7 @@
* [Observe changes](#observe-changes)
* [Entity lifecycle](#entity-lifecycle)
* [Listeners disconnection](#listeners-disconnection)
* [They call me Reactive System](#they-call-me-reactive-system)
* [They call me reactive storage](#they-call-me-reactive-storage)
* [Sorting: is it possible?](#sorting-is-it-possible)
* [Helpers](#helpers)
* [Null entity](#null-entity)
@@ -65,9 +62,6 @@
* [Iterators](#iterators)
* [Const registry](#const-registry)
* [Beyond this document](#beyond-this-document)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
@@ -382,19 +376,17 @@ In all cases, the function type of a listener is equivalent to the following:
void(entt::registry &, entt::entity);
```
In all cases, listeners are provided with the registry that triggered the
notification and the involved entity.
All listeners are provided with the registry that triggered the notification and
the involved entity. Note also that:
Note also that:
* Listeners for the construction signals are invoked **after** components have
been assigned to entities.
* Listeners for construction signals are invoked **after** components have been
created.
* Listeners designed to observe changes are invoked **after** components have
been updated.
* Listeners for the destruction signals are invoked **before** components have
been removed from entities.
* Listeners for destruction signals are invoked **before** components have been
destroyed.
There are also some limitations on what a listener can and cannot do:
@@ -441,7 +433,11 @@ registry.patch<entt::entity>(entity);
```
Destroying an entity and then updating the version of an identifier **does not**
give rise to these types of signals under any circumstances instead.
give rise to these types of signals under any circumstances instead.<br/>
Finally, note that listeners that _observe_ the destruction of an entity are
invoked **after** all components have been removed, not **before**. This is
because the entity would be invalidated before deleting its elements otherwise,
making it difficult for the user to write component listeners.
### Listeners disconnection
@@ -459,11 +455,11 @@ As a result, a listener that wants to access components, entities, or pools can
safely do so against a still valid registry, while checking for the existence of
the various elements as appropriate.
### They call me Reactive System
## They call me reactive storage
Signals are the basic tools to construct reactive systems, even if they aren't
enough on their own. `EnTT` tries to take another step in that direction with
the `observer` class template.<br/>
its _reactive mixin_.<br/>
In order to explain what reactive systems are, this is a slightly revised quote
from the documentation of the library that first introduced this tool,
[Entitas](https://github.com/sschmid/Entitas-CSharp):
@@ -479,100 +475,168 @@ On these words, however, the similarities with the proposal of `Entitas` also
end. The rules of the language and the design of the library obviously impose
and allow different things.
An `observer` is initialized with an instance of a registry and a set of _rules_
that describes what are the entities to intercept. As an example:
A reactive mixin can be used on a standalone storage with any value type
(perhaps using an alias to simplify its use):
```cpp
entt::observer observer{registry, entt::collector.update<sprite>()};
using reactive_storage = entt::reactive_mixin<entt::storage<void>>;
entt::registry registry{};
reactive_storage storage{};
storage.bind(registry);
```
The class is default constructible and is reconfigured at any time by means of
the `connect` member function. Moreover, an observer is disconnected from the
underlying registry through the `disconnect` member function.<br/>
The `observer` offers also what is needed to query its _internal state_ and to
know if it's empty or how many entities it contains. Moreover, it can return a
raw pointer to the list of entities it contains.
However, the most important features of this class are that:
* It's iterable and therefore users can easily walk through the list of entities
by means of a range-for loop or the `each` member function.
* It's clearable and therefore users can consume the entities and literally
reset the observer after each iteration.
These aspects make the observer an incredibly powerful tool to know at any time
what are the entities that matched the given rules since the last time one
asked:
In this case, it must be provided with a reference registry for subsequent
operations.<br/>
Alternatively, when using the value type provided by `EnTT`, it's also possible
to create a reactive storage directly inside a registry:
```cpp
for(const auto entity: observer) {
// ...
entt::registry registry{};
auto &storage = registry.storage<entt::reactive>("observer"_hs);
```
In the latter case there is the advantage that, in the event of destruction of
an entity, this storage is also automatically cleaned up.<br/>
Also note that, unlike all other storage, these classes don't support signals by
default (although they can be enabled if necessary).
Once it has been created and associated with a registry, the reactive mixin
needs to be informed about what it should _observe_.<br/>
Here the choice boils down to three main events affecting all elements (entities
or components), namely creation, update or destruction:
```cpp
storage
// observe position component construction
.on_construct<position>()
// observe velocity component update
.on_update<velocity>()
// observe renderable component destruction
.on_destroy<renderable>();
```
It goes without saying that it's possible to observe multiple events of the same
type or of different types with the same storage.<br/>
For example, to know which entities have been assigned or updated a component of
a certain type:
```cpp
storage
.on_construct<my_type>()
.on_update<my_type>();
```
Note that all configurations are in _or_ and never in _and_. Therefore, to track
entities that have been assigned two different components, there are a couple of
options:
* Create two reactive storage, then combine them in a view:
```cpp
first_storage.on_construct<position>();
second_storage.on_construct<velocity>();
for(auto entity: entt::basic_view{first_storage, second_storage}) {
// ...
}
```
* Use a reactive storage with a non-`void` value type and a custom tracking
function for the purpose:
```cpp
using my_reactive_storage = entt::reactive_mixin<entt::storage<bool>>;
void callback(my_reactive_storage &storage, const entt::registry &, const entt::entity entity) {
storage.contains(entity) ? (storage.get(entity) = true) : storage.emplace(entity, false);
}
// ...
my_reactive_storage storage{};
storage
.on_construct<position, &callback>()
.on_construct<velocity, &callback>();
// ...
for(auto [entity, both_were_added]: storage.each()) {
if(both_were_added) {
// ...
}
}
```
As highlighted in the last example, the reactive mixin tracks the entities that
match the given conditions and saves them aside. However, this behavior can be
changed.<br/>
For example, it's possible to _capture_ all and only the entities for which a
certain component has been updated but only if a specific value is within a
given range:
```cpp
void callback(reactive_storage &storage, const entt::registry &registry, const entt::entity entity) {
storage.remove(entity);
if(const auto x = registry.get<position>(entity).x; x >= min_x && x <= max_x) {
storage.emplace(entity);
}
}
observer.clear();
// ...
storage.on_update<position, &callback>();
```
The snippet above is equivalent to the following:
This makes reactive storage extremely flexible and usable in a large number of
cases.<br/>
Finally, once the entities of interest have been collected, it's possible to
_visit_ the storage like any other:
```cpp
observer.each([](const auto entity) {
for(auto entity: storage) {
// ...
});
}
```
At least as long as the `observer` isn't const. This means that the non-const
overload of `each` does also reset the underlying data structure before to
return to the caller, while the const overload does not for obvious reasons.
A `collector` is a utility aimed to generate a list of `matcher`s (the actual
rules) to use with an `observer`.<br/>
There are two types of `matcher`s:
* Observing matcher: an observer returns at least the entities for which one or
more of the given components have been updated and not yet destroyed.
```cpp
entt::collector.update<sprite>();
```
Where _updated_ means that all listeners attached to `on_update` are invoked.
In order for this to happen, specific functions such as `patch` must be used.
Refer to the specific documentation for more details.
* Grouping matcher: an observer returns at least the entities that would have
entered the given group if it existed and that would have not yet left it.
```cpp
entt::collector.group<position, velocity>(entt::exclude<destroyed>);
```
A grouping matcher supports also exclusion lists as well as single components.
Roughly speaking, an observing matcher intercepts the entities for which the
given components are updated while a grouping matcher tracks the entities that
have assigned the given components since the last time one asked.<br/>
If an entity already has all the components except one and the missing type is
assigned to it, the entity is intercepted by a grouping matcher.
In addition, matchers support filtering by means of a `where` clause:
Wrapping it in a view and combining it with other views is another option:
```cpp
entt::collector.update<sprite>().where<position>(entt::exclude<velocity>);
for(auto [entity, pos]: (entt:.basic_view{storage} | registry.view<position>(entt::exclude<velocity>)).each()) {
// ...
}
```
This clause introduces a way to intercept entities if and only if they are
already part of a hypothetical group. If they are not, they aren't returned by
the observer, no matter if they matched the given rule.<br/>
In the example above, whenever the component `sprite` of an entity is updated,
the observer checks the entity itself to verify that it has at least `position`
and has not `velocity`. If one of the two conditions isn't satisfied, the entity
is discarded, no matter what.
In order to simplify this last use case, the reactive mixin also provides a
specific function that returns a view of the storage already filtered according
to the provided requirements:
A `where` clause accepts a theoretically unlimited number of types as well as
multiple elements in the exclusion list. Moreover, every matcher can have its
own clause and multiple clauses for the same matcher are combined in a single
one.
```cpp
for(auto [entity, pos]: storage.view<position>(entt::exclude<velocity>).each()) {
// ...
}
```
The registry used in this case is the one associated with the storage and also
available via the `registry` function.
It should be noted that a reactive storage never deletes its entities (and
elements, if any). To process and then discard entities at regular intervals,
refer to the `clear` function available by default for each storage type.<br/>
Similarly, the reactive mixin doesn't disconnect itself from observed storages
upon destruction. Therefore, users have to do this themselves:
```cpp
entt::registry = storage.registry();
registry.on_construct<position>().disconnect(&storage);
registry.on_construct<velocity>().disconnect(&storage);
```
Destroying a reactive storage without disconnecting it from observed pools will
result in undefined behavior.
## Sorting: is it possible?
@@ -692,14 +756,15 @@ to create tombstones.
### To entity
This function accepts a registry and an instance of a component and returns the
entity associated with the latter:
This function accepts a storage and an instance of a component of the storage
type, then it returns the entity associated with the latter:
```cpp
const auto entity = entt::to_entity(registry, position);
const auto entity = entt::to_entity(registry.storage<position>(), instance);
```
A null entity is returned in case the component doesn't belong to the registry.
Where `instance` is a component of type `position`. A null entity is returned in
case the instance doesn't belong to the registry.
### Dependencies
@@ -919,7 +984,7 @@ accessible by both type and _name_ for convenience. The _name_ isn't really a
name though. In fact, it's a numeric id of type `id_type` used as a key for the
variable. Any value is accepted, even runtime ones.<br/>
The context is returned via the `ctx` functions and offers a minimal set of
feature including the following:
features including the following:
```cpp
// creates a new context variable by type and returns it
@@ -947,9 +1012,9 @@ registry.ctx().erase<my_type>();
registry.ctx().erase<my_type>("my_variable"_hs);
```
Context variable must be both default constructible and movable. If the supplied
type doesn't match that of the variable when using a _name_, the operation
fails.<br/>
A context variable must be both default constructible and movable. If the
supplied type doesn't match that of the variable when using a _name_, the
operation fails.<br/>
For all users who want to use the context but don't want to create elements, the
`contains` and `find` functions are also available:
@@ -1320,16 +1385,6 @@ fact, entities are subject to different rules with respect to components
marked as _ready for reuse_. To iterate all the entities it's necessary to
iterate the underlying sparse set instead.
Moreover, the entity storage offers a couple of additional utilities such as:
* The `in_use` function which is used to know how many entities are still
_in use_. When combined with `size`, it also makes it possible to know how
many entities are available for recycling.
* The `pack` function which is used to make a given set of entities contiguous.
This is particularly useful to pass valid lists of entities via iterators
(with access usually optimized within the library).
This kind of storage is designed to be used where any other storage is fine and
can therefore be combined with views, groups and so on.
@@ -1486,11 +1541,11 @@ the mixins. The latter can then make use of any information, which is set via
`bind`:
```cpp
base.bind(entt::forward_as_any(registry));
base.bind(registry);
```
The `bind` function accepts an `entt::any` object, that is a _typed type-erased_
value.<br/>
The `bind` function accepts any element by reference or by value and forwards it
to derived classes.<br/>
This is how a registry _passes_ itself to all pools that support signals and
also why a storage keeps sending events without requiring the registry to be
passed to it every time.
@@ -1510,7 +1565,7 @@ pointer and behaves differently depending on the case:
* When the pointer is null, the function tries to default-construct an instance
of the object to bind to the entity and returns true on success.
* When the pointer is non-null, the function tries to copy-construct an instance
* When the pointer is not null, the function tries to copy-construct an instance
of the object to bind to the entity and returns true on success.
This means that, starting from a reference to the base, it's possible to bind

View File

@@ -1,8 +1,5 @@
# Frequently Asked Questions
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
@@ -10,14 +7,10 @@
* [Why is my debug build on Windows so slow?](#why-is-my-debug-build-on-windows-so-slow)
* [How can I represent hierarchies with my components?](#how-can-i-represent-hierarchies-with-my-components)
* [Custom entity identifiers: yay or nay?](#custom-entity-identifiers-yay-or-nay)
* [Warning C4307: integral constant overflow](#warning-C4307-integral-constant-overflow)
* [Warning C4003: the min, the max and the macro](#warning-C4003-the-min-the-max-and-the-macro)
* [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
-->
# Introduction
@@ -105,32 +98,6 @@ enum class entity: std::uint32_t {};
There is no limit to the number of identifiers that can be defined.
## Warning C4307: integral constant overflow
According to [this](https://github.com/skypjack/entt/issues/121) issue, using a
hashed string under VS (toolset v141) could generate a warning.<br/>
First of all, I want to reassure you: it's expected and harmless. However, it
can be annoying.
To suppress it and if you don't want to suppress all the other warnings as well,
here is a workaround in the form of a macro:
```cpp
#if defined(_MSC_VER)
#define HS(str) __pragma(warning(suppress:4307)) entt::hashed_string{str}
#else
#define HS(str) entt::hashed_string{str}
#endif
```
With an example of use included:
```cpp
constexpr auto identifier = HS("my/resource/identifier");
```
Thanks to [huwpascoe](https://github.com/huwpascoe) for the courtesy.
## Warning C4003: the min, the max and the macro
On Windows, a header file defines two macros `min` and `max` which may result in

View File

@@ -1,8 +1,5 @@
# Crash Course: graph
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
@@ -14,9 +11,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
-->
# Introduction

View File

@@ -1,17 +1,11 @@
# Push EnTT across boundaries
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Working across boundaries](#working-across-boundaries)
* [Smooth until proven otherwise](#smooth-until-proven-otherwise)
* [Meta context](#meta-context)
* [Memory management](#memory-management)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Working across boundaries

View File

@@ -1,8 +1,5 @@
# EnTT in Action
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
@@ -11,9 +8,6 @@
* [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
@@ -58,7 +52,7 @@ I hope the following lists can grow much more in the future.
* Apparently [D&D Dark Alliance](https://darkalliance.wizards.com) by
[Wizards of the Coast](https://company.wizards.com): your party, their
funeral.
* [TiltedOnline](https://github.com/tiltedphoques/TiltedOnline) by
* [TiltedEvolution](https://github.com/tiltedphoques/TiltedEvolution) by
[Tilted Phoques](https://github.com/tiltedphoques): Skyrim and Fallout 4 mod
to play online.
* [Antkeeper](https://github.com/antkeeper/antkeeper-source): an ant colony
@@ -137,9 +131,20 @@ I hope the following lists can grow much more in the future.
Interstellar Observation Network (a space shooter game).
* [EnTT Boids](https://github.com/DanielEliasib/entt_boids): a simple boids
implementation using `EnTT` and `Raylib`.
* [PalmRide: After Flight](https://store.steampowered.com/app/2812540/PalmRide_After_Flight/):
an on-rails shooter with retro outrun aesthetics.
* [Exhibition of Speed](https://store.steampowered.com/app/2947450/Exhibition_of_Speed/):
build your own car and go racing.
* [Lichgate](https://buas.itch.io/lichgate): top-down action rogue-like where
users unlock abilities to fight horde of enemies in an endless world.
* [Letalka](https://github.com/dviglo2d-learn/mini_games/tree/main/letalka):
small demo game with ships and bullets flying everywhere on the screen.
## Engines and the like:
* [Hazel Engine](https://github.com/TheCherno/Hazel): a work in progress
engine created by [The Cherno](https://github.com/TheCherno/Hazel) during
one of his most famous video series.
* [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
@@ -221,6 +226,14 @@ I hope the following lists can grow much more in the future.
libary that combines its built-in reflection system with `ImGui`.
* [Era Game Engine](https://github.com/EldarMuradov/EraGameEngine): a modern
ECS-based game engine.
* [Core SDK of Trollworks engine](https://github.com/trollworks/sdk-core): 2D
game engine based on procrastination.
* [Rocky](https://github.com/pelicanmapping/rocky): 3D geospatial application
engine.
* [Donner](https://github.com/jwmcglynn/donner): a modern C++20 SVG2 rendering
API with CSS3.
* [Coral Engine](https://github.com/GuusKemperman/CoralEngine): open-source
student engine with the tools to make games in C++ and Visual scripting.
## Articles, videos and blog posts:

View File

@@ -1,16 +1,10 @@
# Crash Course: service locator
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [Service locator](#service-locator)
* [Opaque handles](#opaque-handles)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction

View File

@@ -1,8 +1,5 @@
# Crash Course: runtime reflection system
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
@@ -18,12 +15,11 @@
* [From void to any](#from-void-to-any)
* [Policies: the more, the less](#policies-the-more-the-less)
* [Named constants and enums](#named-constants-and-enums)
* [Properties and meta objects](#properties-and-meta-objects)
* [User defined data](#user-defined-data)
* [Traits](#traits)
* [Custom data](#custom-data)
* [Unregister types](#unregister-types)
* [Meta context](#meta-context)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
@@ -841,48 +837,91 @@ auto max = entt::resolve<int>().data("max_int"_hs).get({}).cast<int>();
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
## User defined data
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:
to attach _traits_ or arbitrary _custom data_ to the meta objects created.
The main difference between them is that:
* Traits are simple user-defined flags with much higher access performance. The
library reserves up to 16 bits for traits, that is 16 flags for a bitmask or
2^16 values otherwise.
* Custom data are stored in a generic quick access area reserved for the user
and which the library will never use under any circumstances.
In all cases, this support is currently available only for meta types, meta data
and meta functions.
### Traits
User-defined traits are set via a meta factory:
```cpp
entt::meta<my_type>().type("reflected_type"_hs).prop("tooltip"_hs, "message");
entt::meta<my_type>().traits(my_traits::required | my_traits::hidden);
```
Properties are always in the key/value form. The key is a numeric identifier,
mostly similar to the identifier used to register meta objects. There are no
restrictions on the type of the value instead, as long as it's movable.<br/>
Key only properties are also supported out of the box:
In the example above, `EnTT` bitmask enum support is used but any integral value
is fine, as long as it doesn't exceed 16 bits.<br/>
It's not possible to assign traits at different times. Therefore, multiple calls
to the `traits` function overwrite previous values. However, traits can be read
from meta objects and used to update existing data with a factory, effectively
extending them as needed.<br/>
Likewise, users can also set traits on meta objects later if needed, as long as
the factory is reset to the meta object of interest:
```cpp
entt::meta<my_type>().type("reflected_type"_hs).prop(my_enum::key_only);
entt::meta<my_type>()
.data<&my_type::data_member, entt::as_ref_t>("member"_hs)
.traits(my_traits::internal);
```
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/>
These types also offer a couple of member functions named `prop` to iterate all
properties at once or to search a specific property by key:
Once created, all meta objects offer a member function named `traits` to get the
currently set value:
```cpp
// iterate all properties of a meta type
for(auto &&[id, prop]: entt::resolve<my_type>().prop()) {
// ...
}
// search for a given property by name
auto prop = entt::resolve<my_type>().prop("tooltip"_hs);
auto value = entt::resolve<my_type>().traits<my_traits>();
```
Meta properties are objects having a fairly poor interface, all in all. They
only provide the `value` member function to retrieve the contained value in the
form of a `meta_any` object.
Note that the type is erased upon registration and must therefore be repeated
when traits are _extracted_, so as to allow the library to _reconstruct_ them
correctly.
### Custom data
Custom arbitrary data are set via a meta factory:
```cpp
entt::meta<my_type>().custom<type_data>("name");
```
The way to do this is by specifying the data type to the `custom` function and
passing the necessary arguments to construct it correctly.<br/>
It's not possible to assign custom data at different times. Therefore, multiple
calls to the `custom` function overwrite previous values. However, this value
can be read from meta objects and used to update existing data with a factory,
effectively updating them as needed.<br/>
Likewise, users can also set custom data on meta objects later if needed, as
long as the factory is reset to the meta object of interest:
```cpp
entt::meta<my_type>()
.func<&my_type::member_function>("member"_hs)
.custom<function_data>("tooltip");
```
Once created, all meta objects offer a member function named `custom` to get the
currently set value as a reference or as a pointer to an element:
```cpp
const type_data &value = entt::resolve<my_type>().custom();
```
Note that the returned object performs an extra check in debug before converting
to the requested type, so as to avoid subtle bugs.<br/>
Only in the case of conversion to a pointer is this check safe and such that a
null pointer is returned to inform the user of the failed attempt.
## Unregister types

View File

@@ -1,8 +1,5 @@
# Crash Course: poly
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
@@ -14,9 +11,6 @@
* [Inheritance](#inheritance)
* [Static polymorphism in the wild](#static-polymorphism-in-the-wild)
* [Storage size and alignment requirement](#storage-size-and-alignment-requirement)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
@@ -236,8 +230,8 @@ For a deduced concept, inheritance is achieved in a few steps:
```cpp
struct DrawableAndErasable: entt::type_list<> {
template<typename Base>
struct type: typename Drawable::template type<Base> {
static constexpr auto base = std::tuple_size_v<typename entt::poly_vtable<Drawable>::type>;
struct type: typename Drawable::type<Base> {
static constexpr auto base = Drawable::impl<Drawable::type<entt::poly_inspector>>::size;
void erase() { entt::poly_call<base + 0>(*this); }
};
@@ -273,8 +267,7 @@ a `decltype` as it follows:
```cpp
struct DrawableAndErasable: entt::type_list_cat_t<
decltype(as_type_list(std::declval<Drawable>())),
entt::type_list<void()>
> {
entt::type_list<void()>> {
// ...
};
```

View File

@@ -1,17 +1,11 @@
# Crash Course: cooperative scheduler
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [The process](#the-process)
* [Adaptor](#adaptor)
* [The scheduler](#the-scheduler)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction

View File

@@ -1,15 +1,9 @@
# Similar projects
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [Similar projects](#similar-projects)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
@@ -55,8 +49,12 @@ details.
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.
* [Friflo Engine ECS](https://github.com/friflo/Friflo.Json.Fliox/blob/main/Engine/README.md):
an archetype ECS with focus on performance, cache locality and DX.
* [LeoECS](https://github.com/Leopotam/ecs): simple lightweight C# Entity
Component System framework.
* [Massive ECS](https://github.com/nilpunch/massive): sparse set based ECS
featuring rollbacks.
* [Svelto.ECS](https://github.com/sebas77/Svelto.ECS): a very interesting
platform agnostic and table based ECS framework.
@@ -79,7 +77,6 @@ details.
entity registry for ECS designs inspired by `EnTT`.
* Rust:
* [Legion](https://github.com/TomGillen/legion): a chunk based archetype ECS.
* [Shipyard](https://github.com/leudz/shipyard): it borrows some ideas from
`EnTT` and offers a sparse sets based ECS with grouping functionalities.
* [Sparsey](https://github.com/LechintanTudor/sparsey): sparse set based ECS

View File

@@ -1,18 +1,12 @@
# Crash Course: resource management
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [The resource, the loader and the cache](#the-resource-the-loader-and-the-cache)
* [Resource handle](#resource-handle)
* [Loaders](#loader)
* [The cache class](#the-cache)
<!--
@endcond TURN_OFF_DOXYGEN
-->
* [Loaders](#loaders)
* [The cache class](#the-cache-class)
# Introduction

View File

@@ -1,8 +1,5 @@
# Crash Course: events, signals and everything in between
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
@@ -14,9 +11,6 @@
* [Event dispatcher](#event-dispatcher)
* [Named queues](#named-queues)
* [Event emitter](#event-emitter)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction

View File

@@ -1,16 +1,10 @@
# EnTT and Unreal Engine
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Enable Cpp17](#enable-cpp17)
* [EnTT as a third party module](#entt-as-a-third-party-module)
* [Include EnTT](#include-entt)
<!--
@endcond TURN_OFF_DOXYGEN
-->
## Enable Cpp17

View File

@@ -5,7 +5,9 @@
# 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": [ "@[\"<].*/container/fwd\\.hpp[\">]", "private", "<entt/container/table.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/any.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/compressed_pair.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" ] },
@@ -17,8 +19,10 @@
{ "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/mixin.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/ranges.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" ] },
@@ -38,5 +42,12 @@
{ "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" ] }
{ "include": [ "@[\"<].*/signal/fwd\\.hpp[\">]", "private", "<entt/signal/sigh.hpp>", "public" ] },
# symbols
{ symbol: [ "std::allocator", private, "<entt/container/fwd.hpp>", public ] },
{ symbol: [ "std::allocator", private, "<entt/entity/fwd.hpp>", public ] },
{ symbol: [ "std::allocator", private, "<entt/graph/fwd.hpp>", public ] },
{ symbol: [ "std::allocator", private, "<entt/process/fwd.hpp>", public ] },
{ symbol: [ "std::allocator", private, "<entt/resource/fwd.hpp>", public ] },
{ symbol: [ "std::allocator", private, "<entt/signal/fwd.hpp>", public ] }
]

View File

@@ -30,4 +30,10 @@
</IndexListItems>
</Expand>
</Type>
<Type Name="entt::basic_table&lt;*&gt;">
<DisplayString>{ payload }</DisplayString>
<Expand>
<ExpandedItem>payload</ExpandedItem>
</Expand>
</Type>
</AutoVisualizer>

View File

@@ -8,7 +8,7 @@
<Intrinsic Name="first" Optional="true" Expression="*(first_base::base_type*)this"/>
<Intrinsic Name="second" Optional="true" Expression="((second_base*)this)->value"/>
<Intrinsic Name="second" Optional="true" Expression="*(second_base::base_type*)this"/>
<DisplayString >({ first() }, { second() })</DisplayString>
<DisplayString>({ first() }, { second() })</DisplayString>
<Expand>
<Item Name="[first]">first()</Item>
<Item Name="[second]">second()</Item>

View File

@@ -7,40 +7,55 @@
<Synthetic Name="[pools]">
<DisplayString>{ pools.size() }</DisplayString>
<Expand>
<IndexListItems ExcludeView="simple">
<Size>pools.size()</Size>
<ValueNode>*pools.packed.first_base::value[$i].element.second</ValueNode>
</IndexListItems>
<IndexListItems IncludeView="simple">
<Size>pools.size()</Size>
<ValueNode>*pools.packed.first_base::value[$i].element.second,view(simple)</ValueNode>
</IndexListItems>
<CustomListItems>
<Variable Name="pos" InitialValue="0" />
<Variable Name="last" InitialValue="pools.size()"/>
<Loop>
<Break Condition="pos == last"/>
<Item Name="[{ pools.packed.first_base::value[pos].element.first }]">
*pools.packed.first_base::value[pos].element.second,view(simple)
</Item>
<Exec>++pos</Exec>
</Loop>
</CustomListItems>
</Expand>
</Synthetic>
<Item Name="[groups]" ExcludeView="simple">groups.size()</Item>
<Item Name="[groups]">groups.size()</Item>
<Synthetic Name="[vars]">
<DisplayString>{ vars.ctx.size() }</DisplayString>
<Expand>
<IndexListItems>
<Size>vars.ctx.size()</Size>
<ValueNode>vars.ctx.packed.first_base::value[$i].element.second</ValueNode>
</IndexListItems>
<CustomListItems>
<Variable Name="pos" InitialValue="0" />
<Variable Name="last" InitialValue="vars.ctx.size()"/>
<Loop>
<Break Condition="pos == last"/>
<Item Name="[{ vars.ctx.packed.first_base::value[pos].element.first }]">
vars.ctx.packed.first_base::value[pos].element.second
</Item>
<Exec>++pos</Exec>
</Loop>
</CustomListItems>
</Expand>
</Synthetic>
</Expand>
</Type>
<Type Name="entt::basic_sparse_set&lt;*&gt;">
<Intrinsic Name="cap" Expression="(traits_type::version_mask &lt;&lt; traits_type::length)"/>
<Intrinsic Name="is_valid_position" Expression="sparse[page] &amp;&amp; ((*((traits_type::entity_type *)&amp;sparse[page][offset]) &amp; traits_type::entity_mask) != traits_type::entity_mask)">
<Parameter Name="page" Type="traits_type::entity_type"/>
<Parameter Name="offset" Type="traits_type::entity_type"/>
</Intrinsic>
<Intrinsic Name="is_valid_entity" Expression="!traits_type::version_mask || (*((traits_type::entity_type *)&amp;entity) &lt; (traits_type::version_mask &lt;&lt; traits_type::length))">
<Parameter Name="entity" Type="const traits_type::value_type &amp;"/>
</Intrinsic>
<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>
<Item Name="[policy]" ExcludeView="simple">mode,en</Item>
<Item Name="[free_list]" ExcludeView="simple">head</Item>
<Synthetic Name="[sparse]">
<DisplayString>{ sparse.size() * traits_type::page_size }</DisplayString>
<Expand>
<ExpandedItem IncludeView="simple">sparse,view(simple)</ExpandedItem>
<CustomListItems ExcludeView="simple">
<CustomListItems>
<Variable Name="pos" InitialValue="0"/>
<Variable Name="page" InitialValue="0"/>
<Variable Name="offset" InitialValue="0"/>
@@ -49,7 +64,7 @@
<Break Condition="pos == last"/>
<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())">
<If Condition="is_valid_position(page, offset)">
<Item Name="[{ pos }]">*((traits_type::entity_type *)&amp;sparse[page][offset]) &amp; traits_type::entity_mask</Item>
</If>
<Exec>++pos</Exec>
@@ -60,13 +75,12 @@
<Synthetic Name="[packed]">
<DisplayString>{ packed.size() }</DisplayString>
<Expand>
<ExpandedItem IncludeView="simple">packed,view(simple)</ExpandedItem>
<CustomListItems ExcludeView="simple">
<CustomListItems>
<Variable Name="pos" InitialValue="0"/>
<Variable Name="last" InitialValue="packed.size()"/>
<Loop>
<Break Condition="pos == last"/>
<If Condition="*((traits_type::entity_type *)&amp;packed[pos]) &lt; cap()">
<If Condition="is_valid_entity(packed[pos])">
<Item Name="[{ pos }]">packed[pos]</Item>
</If>
<Exec>++pos</Exec>
@@ -77,20 +91,22 @@
</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)"/>
<Intrinsic Name="is_valid_entity" Expression="!base_type::traits_type::version_mask || (*((base_type::traits_type::entity_type *)&amp;entity) &lt; (base_type::traits_type::version_mask &lt;&lt; base_type::traits_type::length))">
<Parameter Name="entity" Type="const base_type::traits_type::value_type &amp;"/>
</Intrinsic>
<DisplayString>{{ size={ base_type::packed.size() }, type={ base_type::info->alias,na } }}</DisplayString>
<Expand>
<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="[placeholder]" Optional="true" ExcludeView="simple">placeholder</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="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::traits_type::entity_type *)&amp;base_type::packed[pos]) &lt; cap()">
<If Condition="is_valid_entity(base_type::packed[pos])">
<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>
@@ -98,15 +114,28 @@
</CustomListItems>
</Expand>
</Type>
<Type Name="entt::basic_view&lt;*&gt;">
<DisplayString Condition="leading != nullptr">{{ size_hint={ leading->packed.size() } }}</DisplayString>
<Type Name="entt::basic_common_view&lt;*,*,*&gt;">
<DisplayString Condition="index != $T2">{{ size_hint={ pools[index]->packed.size() } }}</DisplayString>
<DisplayString>{{ size_hint=0 }}</DisplayString>
<Expand>
<Item Name="[pools]" Optional="true">pools,na</Item>
<Item Name="[filter]" Optional="true">filter,na</Item>
<Item Name="[pools]">pools,na</Item>
<Item Name="[filter]">filter,na</Item>
<Item Name="[handle]" Condition="index != $T2">pools[index],na</Item>
</Expand>
</Type>
<Type Name="entt::basic_storage_view&lt;*&gt;">
<DisplayString Condition="leading != nullptr">{{ size={ leading->packed.size() } }}</DisplayString>
<DisplayString>{{ size=0 }}</DisplayString>
<Expand>
<Item Name="[handle]" Condition="leading != nullptr">leading,na</Item>
</Expand>
</Type>
<Type Name="entt::basic_view&lt;*&gt;">
<DisplayString>{ *(base_type*)this }</DisplayString>
<Expand>
<ExpandedItem>*(base_type*)this</ExpandedItem>
</Expand>
</Type>
<Type Name="entt::basic_runtime_view&lt;*&gt;">
<DisplayString Condition="pools.size() != 0u">{{ size_hint={ pools[0]->packed.size() } }}</DisplayString>
<DisplayString>{{ size_hint=0 }}</DisplayString>
@@ -115,6 +144,34 @@
<Item Name="[filter]">filter,na</Item>
</Expand>
</Type>
<Type Name="entt::basic_handle&lt;*&gt;">
<Intrinsic Name="pool_at" Expression="owner->pools.packed.first_base::value[index].element.second._Ptr">
<Parameter Name="index" Type="unsigned int"/>
</Intrinsic>
<DisplayString>{{ entity={ entt } }}</DisplayString>
<Expand>
<Item Name="[entity]">entt</Item>
<Item Name="[registry]" Condition="owner != nullptr">owner,na</Item>
<Synthetic Name="[components]" Condition="owner != nullptr">
<Expand>
<CustomListItems>
<Variable Name="entity_mask" InitialValue="traits_type::entity_mask"/>
<Variable Name="page" InitialValue="((*((traits_type::entity_type *)&amp;entt)) &amp; entity_mask) / traits_type::page_size"/>
<Variable Name="offset" InitialValue="(*((traits_type::entity_type *)&amp;entt)) &amp; (traits_type::page_size - 1u)"/>
<Variable Name="last" InitialValue="owner->pools.packed.first_base::value.size()"/>
<Variable Name="pos" InitialValue="0u"/>
<Loop>
<Break Condition="pos == last"/>
<If Condition="pool_at(pos)->sparse.size() &gt; page &amp;&amp; pool_at(pos)->sparse[page] != nullptr &amp;&amp; ((*((traits_type::entity_type *)&amp;pool_at(pos)->sparse[page][offset])) &amp; entity_mask) != entity_mask">
<Item Name="[{ pool_at(pos)->info->alias,na }:{ ((*((traits_type::entity_type *)&amp;pool_at(pos)->sparse[page][offset])) &amp; entity_mask) != entity_mask }]">pool_at(pos),view(simple)nanr</Item>
</If>
<Exec>++pos</Exec>
</Loop>
</CustomListItems>
</Expand>
</Synthetic>
</Expand>
</Type>
<Type Name="entt::null_t">
<DisplayString>&lt;null&gt;</DisplayString>
</Type>

View File

@@ -1,46 +1,98 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="entt::internal::meta_base_node">
<DisplayString Condition="resolve != nullptr">{{ type={ type } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
</Type>
<Type Name="entt::internal::meta_conv_node">
<DisplayString>{{}}</DisplayString>
</Type>
<Type Name="entt::internal::meta_ctor_node">
<DisplayString>{{ arity={ arity } }}</DisplayString>
</Type>
<Type Name="entt::internal::meta_data_node">
<Intrinsic Name="has_property" Expression="!!(traits &amp; property)">
<Parameter Name="property" Type="int"/>
</Intrinsic>
<DisplayString>{{ arity={ arity } }}</DisplayString>
<Expand>
<Item Name="[arity]">arity</Item>
<Item Name="[is_const]">has_property(entt::internal::meta_traits::is_const)</Item>
<Item Name="[is_static]">has_property(entt::internal::meta_traits::is_static)</Item>
<Item Name="[prop]">prop</Item>
<Item Name="[type]">type</Item>
</Expand>
</Type>
<Type Name="entt::internal::meta_func_node" >
<Intrinsic Name="has_property" Expression="!!(traits &amp; property)">
<Type Name="entt::internal::meta_conv_node">
<DisplayString Condition="conv != nullptr">{{ type={ type } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[type]">type</Item>
</Expand>
</Type>
<Type Name="entt::internal::meta_ctor_node">
<DisplayString Condition="invoke != nullptr">{{ id={ id } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[id]">id</Item>
<Item Name="[arity]">arity</Item>
</Expand>
</Type>
<Type Name="entt::internal::meta_custom_node">
<DisplayString Condition="value != nullptr">{{ type={ type } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[type]">type</Item>
<Item Name="[value]">value</Item>
</Expand>
</Type>
<Type Name="entt::internal::meta_data_node">
<Intrinsic Name="has_trait" Expression="!!(traits &amp; property)">
<Parameter Name="property" Type="int"/>
</Intrinsic>
<DisplayString>{{ arity={ arity } }}</DisplayString>
<DisplayString Condition="get != nullptr">{{ id={ id } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[is_const]">has_property(entt::internal::meta_traits::is_const)</Item>
<Item Name="[is_static]">has_property(entt::internal::meta_traits::is_static)</Item>
<Item Name="[id]">id</Item>
<Item Name="[arity]">arity</Item>
<Item Name="[is_const]">has_trait(entt::internal::meta_traits::is_const)</Item>
<Item Name="[is_static]">has_trait(entt::internal::meta_traits::is_static)</Item>
<Item Name="[prop]">prop,view(simple)</Item>
<Item Name="[custom]">custom</Item>
</Expand>
</Type>
<Type Name="entt::internal::meta_dtor_node">
<DisplayString>{{}}</DisplayString>
<Expand/>
</Type>
<Type Name="entt::internal::meta_func_node" >
<Intrinsic Name="has_trait" Expression="!!(traits &amp; property)">
<Parameter Name="property" Type="int"/>
</Intrinsic>
<DisplayString Condition="invoke != nullptr">{{ id={ id } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[id]">id</Item>
<Item Name="[arity]">arity</Item>
<Item Name="[is_const]">has_trait(entt::internal::meta_traits::is_const)</Item>
<Item Name="[is_static]">has_trait(entt::internal::meta_traits::is_static)</Item>
<Item Name="[next]" Condition="next != nullptr">*next</Item>
<Item Name="[prop]">prop</Item>
<Item Name="[prop]">prop,view(simple)</Item>
<Item Name="[custom]">custom</Item>
</Expand>
</Type>
<Type Name="entt::internal::meta_prop_node">
<DisplayString>{ value }</DisplayString>
<DisplayString Condition="value != nullptr">{{ key={ id } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[key]">id</Item>
<Item Name="[value]">value</Item>
</Expand>
</Type>
<Type Name="entt::internal::meta_template_node">
<DisplayString>{{ arity={ arity } }}</DisplayString>
<DisplayString Condition="arity != 0u">{{ arity={ arity } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[arity]">arity</Item>
</Expand>
</Type>
<Type Name="entt::internal::meta_type_descriptor">
<DisplayString/>
<Expand>
<Item Name="[ctor]">ctor,view(simple)</Item>
<Item Name="[base]">base,view(simple)</Item>
<Item Name="[conv]">conv,view(simple)</Item>
<Item Name="[data]">data,view(simple)</Item>
<Item Name="[func]">func,view(simple)</Item>
<Item Name="[prop]">prop,view(simple)</Item>
</Expand>
</Type>
<Type Name="entt::internal::meta_type_node">
<Intrinsic Name="has_property" Expression="!!(traits &amp; property)">
<Intrinsic Name="has_trait" Expression="!!(traits &amp; property)">
<Parameter Name="property" Type="int"/>
</Intrinsic>
<DisplayString Condition="info != nullptr">{{ type={ info->alias,na } }}</DisplayString>
@@ -48,20 +100,22 @@
<Expand>
<Item Name="[id]">id</Item>
<Item Name="[sizeof]">size_of</Item>
<Item Name="[is_arithmetic]">has_property(entt::internal::meta_traits::is_arithmetic)</Item>
<Item Name="[is_integral]">has_property(entt::internal::meta_traits::is_integral)</Item>
<Item Name="[is_signed]">has_property(entt::internal::meta_traits::is_signed)</Item>
<Item Name="[is_array]">has_property(entt::internal::meta_traits::is_array)</Item>
<Item Name="[is_enum]">has_property(entt::internal::meta_traits::is_enum)</Item>
<Item Name="[is_class]">has_property(entt::internal::meta_traits::is_class)</Item>
<Item Name="[is_meta_pointer_like]">has_property(entt::internal::meta_traits::is_meta_pointer_like)</Item>
<Item Name="[is_meta_sequence_container]">has_property(entt::internal::meta_traits::is_meta_sequence_container)</Item>
<Item Name="[is_meta_associative_container]">has_property(entt::internal::meta_traits::is_meta_associative_container)</Item>
<Item Name="[is_arithmetic]">has_trait(entt::internal::meta_traits::is_arithmetic)</Item>
<Item Name="[is_integral]">has_trait(entt::internal::meta_traits::is_integral)</Item>
<Item Name="[is_signed]">has_trait(entt::internal::meta_traits::is_signed)</Item>
<Item Name="[is_array]">has_trait(entt::internal::meta_traits::is_array)</Item>
<Item Name="[is_enum]">has_trait(entt::internal::meta_traits::is_enum)</Item>
<Item Name="[is_class]">has_trait(entt::internal::meta_traits::is_class)</Item>
<Item Name="[is_pointer]">has_trait(entt::internal::meta_traits::is_pointer)</Item>
<Item Name="[is_meta_pointer_like]">has_trait(entt::internal::meta_traits::is_meta_pointer_like)</Item>
<Item Name="[is_meta_sequence_container]">has_trait(entt::internal::meta_traits::is_meta_sequence_container)</Item>
<Item Name="[is_meta_associative_container]">has_trait(entt::internal::meta_traits::is_meta_associative_container)</Item>
<Item Name="[default_constructor]">default_constructor != nullptr</Item>
<Item Name="[conversion_helper]">conversion_helper != nullptr</Item>
<Item Name="[from_void]">from_void != nullptr</Item>
<Item Name="[template_info]">templ</Item>
<Item Name="[details]" Condition="details != nullptr">*details</Item>
<Item Name="[custom]">custom</Item>
<Item Name="[details]" Condition="!(details == nullptr)">*details</Item>
</Expand>
</Type>
<Type Name="entt::meta_any">
@@ -74,17 +128,26 @@
</Type>
<Type Name="entt::meta_handle">
<DisplayString>{ any }</DisplayString>
<Expand>
<ExpandedItem>any</ExpandedItem>
</Expand>
</Type>
<Type Name="entt::meta_associative_container">
<DisplayString>{ storage }</DisplayString>
<DisplayString Condition="data != nullptr">{{ const={ const_only } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
<Item Name="[const]">const_only</Item>
<Item Name="[data]">data</Item>
</Expand>
</Type>
<Type Name="entt::meta_sequence_container">
<DisplayString>{ storage }</DisplayString>
<DisplayString Condition="data != nullptr">{{ const={ const_only } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
<Item Name="[const]">const_only</Item>
<Item Name="[data]">data</Item>
</Expand>
</Type>
<Type Name="entt::meta_data">
@@ -104,10 +167,10 @@
</Expand>
</Type>
<Type Name="entt::meta_prop">
<DisplayString Condition="node != nullptr">{ *node }</DisplayString>
<DisplayString Condition="node.type != nullptr">{ node }</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
<ExpandedItem Condition="node.type != nullptr">node</ExpandedItem>
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
</Expand>
</Type>
@@ -118,4 +181,21 @@
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
</Expand>
</Type>
<Type Name="entt::meta_ctx">
<Intrinsic Name="element_at" Expression="value.packed.first_base::value[pos].element">
<Parameter Name="pos" Type="int"/>
</Intrinsic>
<DisplayString>{ value }</DisplayString>
<Expand>
<CustomListItems>
<Variable Name="pos" InitialValue="0"/>
<Variable Name="last" InitialValue="value.size()"/>
<Loop>
<Break Condition="pos == last"/>
<Item Name="[{ element_at(pos).first }]">element_at(pos).second</Item>
<Exec>++pos</Exec>
</Loop>
</CustomListItems>
</Expand>
</Type>
</AutoVisualizer>

View File

@@ -1,3 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
</AutoVisualizer>

View File

@@ -7,9 +7,20 @@
</Expand>
</Type>
<Type Name="entt::resource_cache&lt;*&gt;">
<DisplayString>{ pool.first_base::value }</DisplayString>
<Intrinsic Name="size" Expression="pool.first_base::value.size()"/>
<DisplayString>{{ size={ size() } }}</DisplayString>
<Expand>
<ExpandedItem>pool.first_base::value</ExpandedItem>
<CustomListItems>
<Variable Name="pos" InitialValue="0" />
<Variable Name="last" InitialValue="size()"/>
<Loop>
<Break Condition="pos == last"/>
<Item Name="[{ pool.first_base::value.packed.first_base::value[pos].element.first }]">
*pool.first_base::value.packed.first_base::value[pos].element.second
</Item>
<Exec>++pos</Exec>
</Loop>
</CustomListItems>
</Expand>
</Type>
</AutoVisualizer>

View File

@@ -11,15 +11,10 @@
<Intrinsic Name="size" Expression="pools.first_base::value.size()"/>
<DisplayString>{{ size={ size() } }}</DisplayString>
<Expand>
<Synthetic Name="[pools]">
<DisplayString>{ size() }</DisplayString>
<Expand>
<IndexListItems>
<Size>size()</Size>
<ValueNode>*pools.first_base::value.packed.first_base::value[$i].element.second</ValueNode>
</IndexListItems>
</Expand>
</Synthetic>
<IndexListItems>
<Size>size()</Size>
<ValueNode>*pools.first_base::value.packed.first_base::value[$i].element.second</ValueNode>
</IndexListItems>
</Expand>
</Type>
<Type Name="entt::internal::dispatcher_handler&lt;*&gt;">
@@ -29,7 +24,7 @@
</Expand>
</Type>
<Type Name="entt::emitter&lt;*&gt;">
<DisplayString>{{ size={ handlers.first_base::value.packed.first_base::value.size() } }}</DisplayString>
<DisplayString>{{ size={ handlers.first_base::value.size() } }}</DisplayString>
</Type>
<Type Name="entt::connection">
<DisplayString>{{ bound={ signal != nullptr } }}</DisplayString>

View File

@@ -0,0 +1,28 @@
#!/usr/bin/env bash
set -e
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
VERSION_HEADER=$(realpath "$SCRIPT_DIR/../src/entt/config/version.h" --relative-to=$(pwd))
BAZEL_MODULE=$(realpath "$SCRIPT_DIR/../MODULE.bazel" --relative-to=$(pwd))
if [[ -z "${VERSION_HEADER}" ]]; then
echo "Cannot find version header"
exit 1
fi
echo "Getting version from $VERSION_HEADER ..."
ENTT_MAJOR_VERSION=$(sed -nr 's/#define ENTT_VERSION_MAJOR ([0-9]+)/\1/p' $VERSION_HEADER)
ENTT_MINOR_VERSION=$(sed -nr 's/#define ENTT_VERSION_MINOR ([0-9]+)/\1/p' $VERSION_HEADER)
ENTT_PATCH_VERSION=$(sed -nr 's/#define ENTT_VERSION_PATCH ([0-9]+)/\1/p' $VERSION_HEADER)
VERSION="$ENTT_MAJOR_VERSION.$ENTT_MINOR_VERSION.$ENTT_PATCH_VERSION"
echo "Found $VERSION"
buildozer "set version $VERSION" //MODULE.bazel:%module
# a commit is needed for 'git archive'
git add $BAZEL_MODULE
git commit -m "chore: update MODULE.bazel version to $VERSION"

View File

@@ -19,7 +19,7 @@
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -127,7 +127,7 @@
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -169,7 +169,7 @@
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -296,7 +296,7 @@
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -5248,7 +5248,7 @@ struct radix_sort {
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -10297,7 +10297,7 @@ private:
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -10419,7 +10419,7 @@ private:
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -18188,27 +18188,28 @@ class group_handler<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> fin
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;
void swap_elements(const std::size_t pos, const entity_type entt) {
std::apply([pos, entt](auto *...cpool) { (cpool->swap_elements(cpool->data()[pos], entt), ...); }, pools);
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);
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);
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);
swap_elements(--len, entt, std::index_sequence_for<Owned...>{});
}
}
@@ -18242,13 +18243,11 @@ public:
return len;
}
template<typename Type>
Type pools_as() const noexcept {
auto pools_as_tuple() const noexcept {
return pools;
}
template<typename Type>
Type filter_as() const noexcept {
auto filter_as_tuple() const noexcept {
return filter;
}
@@ -18310,13 +18309,11 @@ public:
return elem;
}
template<typename Type>
Type pools_as() const noexcept {
auto pools_as_tuple() const noexcept {
return pools;
}
template<typename Type>
Type filter_as() const noexcept {
auto filter_as_tuple() const noexcept {
return filter;
}
@@ -18370,12 +18367,12 @@ class basic_group<owned_t<>, get_t<Get...>, exclude_t<Exclude...>> {
auto pools() const noexcept {
using return_type = std::tuple<Get *...>;
return descriptor ? descriptor->template pools_as<return_type>() : return_type{};
return descriptor ? descriptor->pools_as_tuple() : return_type{};
}
auto filter() const noexcept {
using return_type = std::tuple<Exclude *...>;
return descriptor ? descriptor->template filter_as<return_type>() : return_type{};
return descriptor ? descriptor->filter_as_tuple() : return_type{};
}
public:
@@ -18791,12 +18788,12 @@ class basic_group<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> {
auto pools() const noexcept {
using return_type = std::tuple<Owned *..., Get *...>;
return descriptor ? descriptor->template pools_as<return_type>() : return_type{};
return descriptor ? descriptor->pools_as_tuple() : return_type{};
}
auto filter() const noexcept {
using return_type = std::tuple<Exclude *...>;
return descriptor ? descriptor->template filter_as<return_type>() : return_type{};
return descriptor ? descriptor->filter_as_tuple() : return_type{};
}
public:
@@ -19569,7 +19566,7 @@ template<typename... Args, typename... Other>
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -19688,7 +19685,7 @@ template<typename... Args, typename... Other>
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -21214,27 +21211,28 @@ class group_handler<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> fin
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;
void swap_elements(const std::size_t pos, const entity_type entt) {
std::apply([pos, entt](auto *...cpool) { (cpool->swap_elements(cpool->data()[pos], entt), ...); }, pools);
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);
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);
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);
swap_elements(--len, entt, std::index_sequence_for<Owned...>{});
}
}
@@ -21268,13 +21266,11 @@ public:
return len;
}
template<typename Type>
Type pools_as() const noexcept {
auto pools_as_tuple() const noexcept {
return pools;
}
template<typename Type>
Type filter_as() const noexcept {
auto filter_as_tuple() const noexcept {
return filter;
}
@@ -21336,13 +21332,11 @@ public:
return elem;
}
template<typename Type>
Type pools_as() const noexcept {
auto pools_as_tuple() const noexcept {
return pools;
}
template<typename Type>
Type filter_as() const noexcept {
auto filter_as_tuple() const noexcept {
return filter;
}
@@ -21396,12 +21390,12 @@ class basic_group<owned_t<>, get_t<Get...>, exclude_t<Exclude...>> {
auto pools() const noexcept {
using return_type = std::tuple<Get *...>;
return descriptor ? descriptor->template pools_as<return_type>() : return_type{};
return descriptor ? descriptor->pools_as_tuple() : return_type{};
}
auto filter() const noexcept {
using return_type = std::tuple<Exclude *...>;
return descriptor ? descriptor->template filter_as<return_type>() : return_type{};
return descriptor ? descriptor->filter_as_tuple() : return_type{};
}
public:
@@ -21817,12 +21811,12 @@ class basic_group<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> {
auto pools() const noexcept {
using return_type = std::tuple<Owned *..., Get *...>;
return descriptor ? descriptor->template pools_as<return_type>() : return_type{};
return descriptor ? descriptor->pools_as_tuple() : return_type{};
}
auto filter() const noexcept {
using return_type = std::tuple<Exclude *...>;
return descriptor ? descriptor->template filter_as<return_type>() : return_type{};
return descriptor ? descriptor->filter_as_tuple() : return_type{};
}
public:
@@ -24509,11 +24503,12 @@ public:
*/
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(; first != last; ++first) {
construction.publish(reg, *first);
for(const auto to = underlying_type::size(); from != to; ++from) {
construction.publish(reg, underlying_type::operator[](from));
}
}
}
@@ -25126,7 +25121,7 @@ private:
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -25446,7 +25441,7 @@ private:
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -25952,7 +25947,7 @@ private:
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -26079,7 +26074,7 @@ private:
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -33480,7 +33475,7 @@ private:
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -33607,7 +33602,7 @@ private:
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -37658,11 +37653,12 @@ public:
*/
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(; first != last; ++first) {
construction.publish(reg, *first);
for(const auto to = underlying_type::size(); from != to; ++from) {
construction.publish(reg, underlying_type::operator[](from));
}
}
}
@@ -39272,6 +39268,17 @@ public:
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);
@@ -39350,7 +39357,7 @@ public:
basic_snapshot_loader(registry_type &source) noexcept
: reg{&source} {
// restoring a snapshot as a whole requires a clean registry
ENTT_ASSERT(reg->template storage<entity_type>().empty() && (reg->storage().begin() == reg->storage().end()), "Registry must be empty");
ENTT_ASSERT(reg->template storage<entity_type>().free_list() == 0u, "Registry must be empty");
}
/*! @brief Default move constructor. */
@@ -42964,7 +42971,7 @@ basic_view(std::tuple<Get &...>, std::tuple<Exclude &...> = {}) -> basic_view<ge
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -43284,7 +43291,7 @@ private:
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -43851,7 +43858,7 @@ void dot(std::ostream &out, const Graph &graph) {
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -43978,7 +43985,7 @@ void dot(std::ostream &out, const Graph &graph) {
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -50681,7 +50688,7 @@ private:
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -50998,7 +51005,7 @@ struct adl_meta_pointer_like {
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -51125,7 +51132,7 @@ struct adl_meta_pointer_like {
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -55838,7 +55845,7 @@ private:
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -56122,7 +56129,7 @@ class meta_ctx: private internal::meta_context {
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -59830,7 +59837,7 @@ struct std::tuple_element<Index, entt::value_list<Value...>>: entt::value_list_e
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -67805,7 +67812,7 @@ using invoke_result_t = typename std::invoke_result<Func, Args...>::type;
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -71981,7 +71988,7 @@ struct process_adaptor: process<process_adaptor<Func, Delta>, Delta>, private Fu
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -72108,7 +72115,7 @@ struct process_adaptor: process<process_adaptor<Func, Delta>, Delta>, private Fu
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -74120,7 +74127,7 @@ private:
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -74247,7 +74254,7 @@ private:
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -78034,7 +78041,7 @@ struct uses_allocator<entt::internal::dense_map_node<Key, Value>, Allocator>
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -80659,7 +80666,7 @@ template<typename Lhs, typename Rhs>
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -80778,7 +80785,7 @@ template<typename Lhs, typename Rhs>
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -82225,7 +82232,7 @@ delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate<Ret
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
@@ -82352,7 +82359,7 @@ delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate<Ret
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \

View File

@@ -3,6 +3,8 @@
#include "version.h"
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
# define ENTT_CONSTEXPR
# define ENTT_THROW throw
@@ -42,7 +44,7 @@
# define ENTT_ASSERT(condition, msg) (void(0))
#elif !defined ENTT_ASSERT
# include <cassert>
# define ENTT_ASSERT(condition, msg) assert(condition)
# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg)))
#endif
#ifdef ENTT_DISABLE_ASSERT
@@ -60,6 +62,12 @@
# define ENTT_ETO_TYPE(Type) Type
#endif
#ifdef ENTT_NO_MIXIN
# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__
#else
# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__>
#endif
#ifdef ENTT_STANDARD_CPP
# define ENTT_NONSTD false
#else
@@ -82,4 +90,6 @@
# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
#endif
// NOLINTEND(cppcoreguidelines-macro-usage)
#endif

View File

@@ -1,7 +1,11 @@
#ifndef ENTT_CONFIG_MACRO_H
#define ENTT_CONFIG_MACRO_H
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
#define ENTT_STR(arg) #arg
#define ENTT_XSTR(arg) ENTT_STR(arg)
// NOLINTEND(cppcoreguidelines-macro-usage)
#endif

View File

@@ -3,12 +3,16 @@
#include "macro.h"
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_MINOR 14
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
"." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
// NOLINTEND(cppcoreguidelines-macro-usage)
#endif

View File

@@ -12,6 +12,7 @@
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../core/bit.hpp"
#include "../core/compressed_pair.hpp"
#include "../core/iterator.hpp"
#include "../core/memory.hpp"
@@ -122,7 +123,7 @@ public:
}
[[nodiscard]] constexpr reference operator*() const noexcept {
return {it->element.first, it->element.second};
return operator[](0);
}
template<typename Lhs, typename Rhs>
@@ -267,6 +268,7 @@ class dense_map {
template<typename Other>
[[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept {
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
return fast_mod(static_cast<size_type>(sparse.second()(key)), bucket_count());
}
@@ -325,7 +327,7 @@ class dense_map {
void move_and_pop(const std::size_t pos) {
if(const auto last = size() - 1u; pos != last) {
size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first);
size_type *curr = &sparse.first()[key_to_bucket(packed.first().back().element.first)];
packed.first()[pos] = std::move(packed.first().back());
for(; *curr != last; curr = &packed.first()[*curr].next) {}
*curr = pos;
@@ -341,6 +343,8 @@ class dense_map {
}
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Key type of the container. */
using key_type = Key;
/*! @brief Mapped type of the container. */
@@ -353,8 +357,6 @@ public:
using hasher = Hash;
/*! @brief Type of function to use to compare the keys for equality. */
using key_equal = KeyEqual;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Input iterator type. */
using iterator = internal::dense_map_iterator<typename packed_container_type::iterator>;
/*! @brief Constant input iterator type. */
@@ -404,8 +406,7 @@ public:
*/
explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{})
: sparse{allocator, hash},
packed{allocator, equal},
threshold{default_threshold} {
packed{allocator, equal} {
rehash(cnt);
}
@@ -423,7 +424,7 @@ public:
threshold{other.threshold} {}
/*! @brief Default move constructor. */
dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v<compressed_pair<sparse_container_type, hasher>> &&std::is_nothrow_move_constructible_v<compressed_pair<packed_container_type, key_equal>>) = default;
dense_map(dense_map &&) noexcept = default;
/**
* @brief Allocator-extended move constructor.
@@ -435,6 +436,9 @@ public:
packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))},
threshold{other.threshold} {}
/*! @brief Default destructor. */
~dense_map() = default;
/**
* @brief Default copy assignment operator.
* @return This container.
@@ -445,7 +449,7 @@ public:
* @brief Default move assignment operator.
* @return This container.
*/
dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v<compressed_pair<sparse_container_type, hasher>> &&std::is_nothrow_move_assignable_v<compressed_pair<packed_container_type, key_equal>>) = default;
dense_map &operator=(dense_map &&) noexcept = default;
/**
* @brief Returns the associated allocator.
@@ -677,7 +681,7 @@ public:
* @return Number of elements removed (either 0 or 1).
*/
size_type erase(const key_type &key) {
for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].next) {
for(size_type *curr = &sparse.first()[key_to_bucket(key)]; *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].next) {
if(packed.second()(packed.first()[*curr].element.first, key)) {
const auto index = *curr;
*curr = packed.first()[*curr].next;
@@ -693,7 +697,7 @@ public:
* @brief Exchanges the contents with those of a given container.
* @param other Container to exchange the content with.
*/
void swap(dense_map &other) {
void swap(dense_map &other) noexcept {
using std::swap;
swap(sparse, other.sparse);
swap(packed, other.packed);
@@ -1022,7 +1026,7 @@ public:
private:
compressed_pair<sparse_container_type, hasher> sparse;
compressed_pair<packed_container_type, key_equal> packed;
float threshold;
float threshold{default_threshold};
};
} // namespace entt

View File

@@ -12,8 +12,8 @@
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../core/bit.hpp"
#include "../core/compressed_pair.hpp"
#include "../core/memory.hpp"
#include "../core/type_traits.hpp"
#include "fwd.hpp"
@@ -85,11 +85,11 @@ public:
}
[[nodiscard]] constexpr pointer operator->() const noexcept {
return std::addressof(it->second);
return std::addressof(operator[](0));
}
[[nodiscard]] constexpr reference operator*() const noexcept {
return *operator->();
return operator[](0);
}
template<typename Lhs, typename Rhs>
@@ -271,7 +271,7 @@ class dense_set {
void move_and_pop(const std::size_t pos) {
if(const auto last = size() - 1u; pos != last) {
size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second);
size_type *curr = &sparse.first()[value_to_bucket(packed.first().back().second)];
packed.first()[pos] = std::move(packed.first().back());
for(; *curr != last; curr = &packed.first()[*curr].first) {}
*curr = pos;
@@ -287,6 +287,8 @@ class dense_set {
}
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Key type of the container. */
using key_type = Type;
/*! @brief Value type of the container. */
@@ -297,8 +299,6 @@ public:
using hasher = Hash;
/*! @brief Type of function to use to compare the elements for equality. */
using key_equal = KeyEqual;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Random access iterator type. */
using iterator = internal::dense_set_iterator<typename packed_container_type::iterator>;
/*! @brief Constant random access iterator type. */
@@ -352,8 +352,7 @@ public:
*/
explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{})
: sparse{allocator, hash},
packed{allocator, equal},
threshold{default_threshold} {
packed{allocator, equal} {
rehash(cnt);
}
@@ -371,7 +370,7 @@ public:
threshold{other.threshold} {}
/*! @brief Default move constructor. */
dense_set(dense_set &&) noexcept(std::is_nothrow_move_constructible_v<compressed_pair<sparse_container_type, hasher>> &&std::is_nothrow_move_constructible_v<compressed_pair<packed_container_type, key_equal>>) = default;
dense_set(dense_set &&) noexcept = default;
/**
* @brief Allocator-extended move constructor.
@@ -383,6 +382,9 @@ public:
packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))},
threshold{other.threshold} {}
/*! @brief Default destructor. */
~dense_set() = default;
/**
* @brief Default copy assignment operator.
* @return This container.
@@ -393,7 +395,7 @@ public:
* @brief Default move assignment operator.
* @return This container.
*/
dense_set &operator=(dense_set &&) noexcept(std::is_nothrow_move_assignable_v<compressed_pair<sparse_container_type, hasher>> &&std::is_nothrow_move_assignable_v<compressed_pair<packed_container_type, key_equal>>) = default;
dense_set &operator=(dense_set &&) noexcept = default;
/**
* @brief Returns the associated allocator.
@@ -609,7 +611,7 @@ public:
* @return Number of elements removed (either 0 or 1).
*/
size_type erase(const value_type &value) {
for(size_type *curr = sparse.first().data() + value_to_bucket(value); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].first) {
for(size_type *curr = &sparse.first()[value_to_bucket(value)]; *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].first) {
if(packed.second()(packed.first()[*curr].second, value)) {
const auto index = *curr;
*curr = packed.first()[*curr].first;
@@ -625,7 +627,7 @@ public:
* @brief Exchanges the contents with those of a given container.
* @param other Container to exchange the content with.
*/
void swap(dense_set &other) {
void swap(dense_set &other) noexcept {
using std::swap;
swap(sparse, other.sparse);
swap(packed, other.packed);
@@ -917,7 +919,7 @@ public:
private:
compressed_pair<sparse_container_type, hasher> sparse;
compressed_pair<packed_container_type, key_equal> packed;
float threshold;
float threshold{default_threshold};
};
} // namespace entt

View File

@@ -4,6 +4,7 @@
#include <functional>
#include <memory>
#include <utility>
#include <vector>
namespace entt {
@@ -11,17 +12,27 @@ template<
typename Key,
typename Type,
typename = std::hash<Key>,
typename = std::equal_to<Key>,
typename = std::equal_to<>,
typename = std::allocator<std::pair<const Key, Type>>>
class dense_map;
template<
typename Type,
typename = std::hash<Type>,
typename = std::equal_to<Type>,
typename = std::equal_to<>,
typename = std::allocator<Type>>
class dense_set;
template<typename...>
class basic_table;
/**
* @brief Alias declaration for the most common use case.
* @tparam Type Element types.
*/
template<typename... Type>
using table = basic_table<std::vector<Type>...>;
} // namespace entt
#endif

View File

@@ -0,0 +1,460 @@
#ifndef ENTT_CONTAINER_TABLE_HPP
#define ENTT_CONTAINER_TABLE_HPP
#include <cstddef>
#include <iterator>
#include <tuple>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/iterator.hpp"
#include "fwd.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename... It>
class table_iterator {
template<typename...>
friend class table_iterator;
public:
using value_type = decltype(std::forward_as_tuple(*std::declval<It>()...));
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
using iterator_concept = std::random_access_iterator_tag;
constexpr table_iterator() noexcept
: it{} {}
constexpr table_iterator(It... from) noexcept
: it{from...} {}
template<typename... Other, typename = std::enable_if_t<(std::is_constructible_v<It, Other> && ...)>>
constexpr table_iterator(const table_iterator<Other...> &other) noexcept
: table_iterator{std::get<Other>(other.it)...} {}
constexpr table_iterator &operator++() noexcept {
return (++std::get<It>(it), ...), *this;
}
constexpr table_iterator operator++(int) noexcept {
table_iterator orig = *this;
return ++(*this), orig;
}
constexpr table_iterator &operator--() noexcept {
return (--std::get<It>(it), ...), *this;
}
constexpr table_iterator operator--(int) noexcept {
table_iterator orig = *this;
return operator--(), orig;
}
constexpr table_iterator &operator+=(const difference_type value) noexcept {
return ((std::get<It>(it) += value), ...), *this;
}
constexpr table_iterator operator+(const difference_type value) const noexcept {
table_iterator copy = *this;
return (copy += value);
}
constexpr table_iterator &operator-=(const difference_type value) noexcept {
return (*this += -value);
}
constexpr table_iterator operator-(const difference_type value) const noexcept {
return (*this + -value);
}
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
return std::forward_as_tuple(std::get<It>(it)[value]...);
}
[[nodiscard]] constexpr pointer operator->() const noexcept {
return {operator[](0)};
}
[[nodiscard]] constexpr reference operator*() const noexcept {
return operator[](0);
}
template<typename... Lhs, typename... Rhs>
friend constexpr std::ptrdiff_t operator-(const table_iterator<Lhs...> &, const table_iterator<Rhs...> &) noexcept;
template<typename... Lhs, typename... Rhs>
friend constexpr bool operator==(const table_iterator<Lhs...> &, const table_iterator<Rhs...> &) noexcept;
template<typename... Lhs, typename... Rhs>
friend constexpr bool operator<(const table_iterator<Lhs...> &, const table_iterator<Rhs...> &) noexcept;
private:
std::tuple<It...> it;
};
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
return std::get<0>(lhs.it) - std::get<0>(rhs.it);
}
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator==(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
return std::get<0>(lhs.it) == std::get<0>(rhs.it);
}
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator!=(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
return !(lhs == rhs);
}
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator<(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
return std::get<0>(lhs.it) < std::get<0>(rhs.it);
}
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator>(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
return rhs < lhs;
}
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator<=(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
return !(lhs > rhs);
}
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator>=(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
return !(lhs < rhs);
}
} // namespace internal
/*! @endcond */
/**
* @brief Basic table implementation.
*
* Internal data structures arrange elements to maximize performance. There are
* no guarantees that objects are returned in the insertion order when iterate
* a table. Do not make assumption on the order in any case.
*
* @tparam Container Sequence container row types.
*/
template<typename... Container>
class basic_table {
using container_type = std::tuple<Container...>;
public:
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Input iterator type. */
using iterator = internal::table_iterator<typename Container::iterator...>;
/*! @brief Constant input iterator type. */
using const_iterator = internal::table_iterator<typename Container::const_iterator...>;
/*! @brief Reverse iterator type. */
using reverse_iterator = internal::table_iterator<typename Container::reverse_iterator...>;
/*! @brief Constant reverse iterator type. */
using const_reverse_iterator = internal::table_iterator<typename Container::const_reverse_iterator...>;
/*! @brief Default constructor. */
basic_table()
: payload{} {
}
/**
* @brief Copy constructs the underlying containers.
* @param container The containers to copy from.
*/
explicit basic_table(const Container &...container) noexcept
: payload{container...} {
ENTT_ASSERT((((std::get<Container>(payload).size() * sizeof...(Container)) == (std::get<Container>(payload).size() + ...)) && ...), "Unexpected container size");
}
/**
* @brief Move constructs the underlying containers.
* @param container The containers to move from.
*/
explicit basic_table(Container &&...container) noexcept
: payload{std::move(container)...} {
ENTT_ASSERT((((std::get<Container>(payload).size() * sizeof...(Container)) == (std::get<Container>(payload).size() + ...)) && ...), "Unexpected container size");
}
/*! @brief Default copy constructor, deleted on purpose. */
basic_table(const basic_table &) = delete;
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
basic_table(basic_table &&other) noexcept
: payload{std::move(other.payload)} {}
/**
* @brief Constructs the underlying containers using a given allocator.
* @tparam Allocator Type of allocator.
* @param allocator A valid allocator.
*/
template<typename Allocator>
explicit basic_table(const Allocator &allocator)
: payload{Container{allocator}...} {}
/**
* @brief Copy constructs the underlying containers using a given allocator.
* @tparam Allocator Type of allocator.
* @param container The containers to copy from.
* @param allocator A valid allocator.
*/
template<class Allocator>
basic_table(const Container &...container, const Allocator &allocator) noexcept
: payload{Container{container, allocator}...} {
ENTT_ASSERT((((std::get<Container>(payload).size() * sizeof...(Container)) == (std::get<Container>(payload).size() + ...)) && ...), "Unexpected container size");
}
/**
* @brief Move constructs the underlying containers using a given allocator.
* @tparam Allocator Type of allocator.
* @param container The containers to move from.
* @param allocator A valid allocator.
*/
template<class Allocator>
basic_table(Container &&...container, const Allocator &allocator) noexcept
: payload{Container{std::move(container), allocator}...} {
ENTT_ASSERT((((std::get<Container>(payload).size() * sizeof...(Container)) == (std::get<Container>(payload).size() + ...)) && ...), "Unexpected container size");
}
/**
* @brief Allocator-extended move constructor.
* @tparam Allocator Type of allocator.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
template<class Allocator>
basic_table(basic_table &&other, const Allocator &allocator)
: payload{Container{std::move(std::get<Container>(other.payload)), allocator}...} {}
/*! @brief Default destructor. */
~basic_table() = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This container.
*/
basic_table &operator=(const basic_table &) = delete;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This container.
*/
basic_table &operator=(basic_table &&other) noexcept {
swap(other);
return *this;
}
/**
* @brief Exchanges the contents with those of a given table.
* @param other Table to exchange the content with.
*/
void swap(basic_table &other) noexcept {
using std::swap;
swap(payload, other.payload);
}
/**
* @brief Increases the capacity of a table.
*
* If the new capacity is greater than the current capacity, new storage is
* allocated, otherwise the method does nothing.
*
* @param cap Desired capacity.
*/
void reserve(const size_type cap) {
(std::get<Container>(payload).reserve(cap), ...);
}
/**
* @brief Returns the number of rows that a table has currently allocated
* space for.
* @return Capacity of the table.
*/
[[nodiscard]] size_type capacity() const noexcept {
return std::get<0>(payload).capacity();
}
/*! @brief Requests the removal of unused capacity. */
void shrink_to_fit() {
(std::get<Container>(payload).shrink_to_fit(), ...);
}
/**
* @brief Returns the number of rows in a table.
* @return Number of rows.
*/
[[nodiscard]] size_type size() const noexcept {
return std::get<0>(payload).size();
}
/**
* @brief Checks whether a table is empty.
* @return True if the table is empty, false otherwise.
*/
[[nodiscard]] bool empty() const noexcept {
return std::get<0>(payload).empty();
}
/**
* @brief Returns an iterator to the beginning.
*
* If the table is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first row of the table.
*/
[[nodiscard]] const_iterator cbegin() const noexcept {
return {std::get<Container>(payload).cbegin()...};
}
/*! @copydoc cbegin */
[[nodiscard]] const_iterator begin() const noexcept {
return cbegin();
}
/*! @copydoc begin */
[[nodiscard]] iterator begin() noexcept {
return {std::get<Container>(payload).begin()...};
}
/**
* @brief Returns an iterator to the end.
* @return An iterator to the element following the last row of the table.
*/
[[nodiscard]] const_iterator cend() const noexcept {
return {std::get<Container>(payload).cend()...};
}
/*! @copydoc cend */
[[nodiscard]] const_iterator end() const noexcept {
return cend();
}
/*! @copydoc end */
[[nodiscard]] iterator end() noexcept {
return {std::get<Container>(payload).end()...};
}
/**
* @brief Returns a reverse iterator to the beginning.
*
* If the table is empty, the returned iterator will be equal to `rend()`.
*
* @return An iterator to the first row of the reversed table.
*/
[[nodiscard]] const_reverse_iterator crbegin() const noexcept {
return {std::get<Container>(payload).crbegin()...};
}
/*! @copydoc crbegin */
[[nodiscard]] const_reverse_iterator rbegin() const noexcept {
return crbegin();
}
/*! @copydoc rbegin */
[[nodiscard]] reverse_iterator rbegin() noexcept {
return {std::get<Container>(payload).rbegin()...};
}
/**
* @brief Returns a reverse iterator to the end.
* @return An iterator to the element following the last row of the reversed
* table.
*/
[[nodiscard]] const_reverse_iterator crend() const noexcept {
return {std::get<Container>(payload).crend()...};
}
/*! @copydoc crend */
[[nodiscard]] const_reverse_iterator rend() const noexcept {
return crend();
}
/*! @copydoc rend */
[[nodiscard]] reverse_iterator rend() noexcept {
return {std::get<Container>(payload).rend()...};
}
/**
* @brief Appends a row to the end of a table.
* @tparam Args Types of arguments to use to construct the row data.
* @param args Parameters to use to construct the row data.
* @return A reference to the newly created row data.
*/
template<typename... Args>
std::tuple<typename Container::value_type &...> emplace(Args &&...args) {
if constexpr(sizeof...(Args) == 0u) {
return std::forward_as_tuple(std::get<Container>(payload).emplace_back()...);
} else {
return std::forward_as_tuple(std::get<Container>(payload).emplace_back(std::forward<Args>(args))...);
}
}
/**
* @brief Removes a row from a table.
* @param pos An iterator to the row to remove.
* @return An iterator following the removed row.
*/
iterator erase(const_iterator pos) {
const auto diff = pos - begin();
return {std::get<Container>(payload).erase(std::get<Container>(payload).begin() + diff)...};
}
/**
* @brief Removes a row from a table.
* @param pos Index of the row to remove.
*/
void erase(const size_type pos) {
ENTT_ASSERT(pos < size(), "Index out of bounds");
erase(begin() + static_cast<typename const_iterator::difference_type>(pos));
}
/**
* @brief Returns the row data at specified location.
* @param pos The row for which to return the data.
* @return The row data at specified location.
*/
[[nodiscard]] std::tuple<const typename Container::value_type &...> operator[](const size_type pos) const {
ENTT_ASSERT(pos < size(), "Index out of bounds");
return std::forward_as_tuple(std::get<Container>(payload)[pos]...);
}
/*! @copydoc operator[] */
[[nodiscard]] std::tuple<typename Container::value_type &...> operator[](const size_type pos) {
ENTT_ASSERT(pos < size(), "Index out of bounds");
return std::forward_as_tuple(std::get<Container>(payload)[pos]...);
}
/*! @brief Clears a table. */
void clear() {
(std::get<Container>(payload).clear(), ...);
}
private:
container_type payload;
};
} // namespace entt
/*! @cond TURN_OFF_DOXYGEN */
namespace std {
template<typename... Container, typename Allocator>
struct uses_allocator<entt::basic_table<Container...>, Allocator>
: std::bool_constant<(std::uses_allocator_v<Container, Allocator> && ...)> {};
} // namespace std
/*! @endcond */
#endif

View File

@@ -58,9 +58,11 @@ struct insertion_sort {
auto value = std::move(*it);
auto pre = it;
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic)
for(; pre > first && compare(value, *(pre - 1)); --pre) {
*pre = std::move(*(pre - 1));
}
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
*pre = std::move(value);
}
@@ -104,13 +106,16 @@ struct radix_sort {
constexpr auto mask = (1 << Bit) - 1;
constexpr auto buckets = 1 << Bit;
std::size_t index[buckets]{};
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
std::size_t count[buckets]{};
for(auto it = from; it != to; ++it) {
++count[(getter(*it) >> start) & mask];
}
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
std::size_t index[buckets]{};
for(std::size_t pos{}, end = buckets - 1u; pos < end; ++pos) {
index[pos + 1u] = index[pos] + count[pos];
}

View File

@@ -50,66 +50,67 @@ class basic_any {
using vtable_type = const void *(const operation, const basic_any &, const void *);
struct storage_type {
alignas(Align) std::byte data[Len + !Len];
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
alignas(Align) std::byte data[Len + static_cast<std::size_t>(Len == 0u)];
};
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 != 0u) && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v<Type>;
template<typename Type>
static const void *basic_vtable(const operation op, const basic_any &value, const void *other) {
static_assert(!std::is_void_v<Type> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
const Type *element = nullptr;
const Type *elem = nullptr;
if constexpr(in_situ<Type>) {
element = (value.mode == any_policy::owner) ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance);
elem = (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);
elem = static_cast<const Type *>(value.instance);
}
switch(op) {
case operation::copy:
if constexpr(std::is_copy_constructible_v<Type>) {
static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*element);
static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*elem);
}
break;
case operation::move:
if constexpr(in_situ<Type>) {
if(value.mode == any_policy::owner) {
return new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(element))};
return ::new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(elem))};
}
}
return (static_cast<basic_any *>(const_cast<void *>(other))->instance = std::exchange(const_cast<basic_any &>(value).instance, nullptr));
case operation::transfer:
if constexpr(std::is_move_assignable_v<Type>) {
*const_cast<Type *>(element) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
*const_cast<Type *>(elem) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
return other;
}
[[fallthrough]];
case operation::assign:
if constexpr(std::is_copy_assignable_v<Type>) {
*const_cast<Type *>(element) = *static_cast<const Type *>(other);
*const_cast<Type *>(elem) = *static_cast<const Type *>(other);
return other;
}
break;
case operation::destroy:
if constexpr(in_situ<Type>) {
element->~Type();
elem->~Type();
} else if constexpr(std::is_array_v<Type>) {
delete[] element;
delete[] elem;
} else {
delete element;
delete elem;
}
break;
case operation::compare:
if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) {
return *element == *static_cast<const Type *>(other) ? other : nullptr;
return *elem == *static_cast<const Type *>(other) ? other : nullptr;
} else {
return (element == other) ? other : nullptr;
return (elem == other) ? other : nullptr;
}
case operation::get:
return element;
return elem;
}
return nullptr;
@@ -117,26 +118,31 @@ class basic_any {
template<typename Type, typename... Args>
void initialize([[maybe_unused]] Args &&...args) {
info = &type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
using plain_type = std::remove_cv_t<std::remove_reference_t<Type>>;
info = &type_id<plain_type>();
if constexpr(!std::is_void_v<Type>) {
vtable = basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>;
vtable = basic_vtable<plain_type>;
if constexpr(std::is_lvalue_reference_v<Type>) {
static_assert((std::is_lvalue_reference_v<Args> && ...) && (sizeof...(Args) == 1u), "Invalid arguments");
mode = std::is_const_v<std::remove_reference_t<Type>> ? any_policy::cref : any_policy::ref;
instance = (std::addressof(args), ...);
} else if constexpr(in_situ<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 if constexpr(in_situ<plain_type>) {
if constexpr(std::is_aggregate_v<plain_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<plain_type>)) {
::new(&storage) plain_type{std::forward<Args>(args)...};
} else {
new(&storage) std::remove_cv_t<std::remove_reference_t<Type>>(std::forward<Args>(args)...);
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
::new(&storage) plain_type(std::forward<Args>(args)...);
}
} else {
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)...};
if constexpr(std::is_aggregate_v<plain_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<plain_type>)) {
instance = new plain_type{std::forward<Args>(args)...};
} else if constexpr(std::is_array_v<plain_type>) {
static_assert(sizeof...(Args) == 0u, "Invalid arguments");
instance = new plain_type[std::extent_v<plain_type>]();
} else {
instance = new std::remove_cv_t<std::remove_reference_t<Type>>(std::forward<Args>(args)...);
instance = new plain_type(std::forward<Args>(args)...);
}
}
}
@@ -220,10 +226,12 @@ public:
* @return This any object.
*/
basic_any &operator=(const basic_any &other) {
reset();
if(this != &other) {
reset();
if(other.vtable) {
other.vtable(operation::copy, other, this);
if(other.vtable) {
other.vtable(operation::copy, other, this);
}
}
return *this;
@@ -235,6 +243,8 @@ public:
* @return This any object.
*/
basic_any &operator=(basic_any &&other) noexcept {
ENTT_ASSERT(this != &other, "Self move assignment");
reset();
if(other.vtable) {
@@ -253,9 +263,8 @@ public:
* @param value An instance of an object to use to initialize the wrapper.
* @return This any object.
*/
template<typename Type>
std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>, basic_any &>
operator=(Type &&value) {
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
basic_any &operator=(Type &&value) {
emplace<std::decay_t<Type>>(std::forward<Type>(value));
return *this;
}
@@ -332,9 +341,9 @@ public:
if(vtable && mode != any_policy::cref && *info == *other.info) {
if(auto *val = other.data(); val) {
return (vtable(operation::transfer, *this, val) != nullptr);
} else {
return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr);
}
return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr);
}
return false;
@@ -396,14 +405,6 @@ public:
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.
*/
[[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.
@@ -431,7 +432,7 @@ private:
* @return The element converted to the requested type.
*/
template<typename Type, std::size_t Len, std::size_t Align>
[[nodiscard]] Type any_cast(const basic_any<Len, Align> &data) noexcept {
[[nodiscard]] std::remove_const_t<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);
@@ -439,7 +440,7 @@ template<typename Type, std::size_t Len, std::size_t Align>
/*! @copydoc any_cast */
template<typename Type, std::size_t Len, std::size_t Align>
[[nodiscard]] Type any_cast(basic_any<Len, Align> &data) noexcept {
[[nodiscard]] std::remove_const_t<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");
@@ -448,13 +449,13 @@ template<typename Type, std::size_t Len, std::size_t Align>
/*! @copydoc any_cast */
template<typename Type, std::size_t Len, std::size_t Align>
[[nodiscard]] Type any_cast(basic_any<Len, Align> &&data) noexcept {
[[nodiscard]] std::remove_const_t<Type> any_cast(basic_any<Len, Align> &&data) noexcept {
if constexpr(std::is_copy_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
if(auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); instance) {
return static_cast<Type>(std::move(*instance));
} else {
return any_cast<Type>(data);
}
return any_cast<Type>(data);
} else {
auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
ENTT_ASSERT(instance, "Invalid instance");

69
src/entt/core/bit.hpp Normal file
View File

@@ -0,0 +1,69 @@
#ifndef ENTT_CORE_BIT_HPP
#define ENTT_CORE_BIT_HPP
#include <cstddef>
#include <limits>
#include <type_traits>
#include "../config/config.h"
namespace entt {
/**
* @brief Returns the number of set bits in a value (waiting for C++20 and
* `std::popcount`).
* @tparam Type Unsigned integer type.
* @param value A value of unsigned integer type.
* @return The number of set bits in the value.
*/
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, int> popcount(const Type value) noexcept {
return value ? (int(value & 1) + popcount(static_cast<Type>(value >> 1))) : 0;
}
/**
* @brief Checks whether a value is a power of two or not (waiting for C++20 and
* `std::has_single_bit`).
* @tparam Type Unsigned integer type.
* @param value A value of unsigned integer type.
* @return True if the value is a power of two, false otherwise.
*/
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, bool> has_single_bit(const Type value) noexcept {
return value && ((value & (value - 1)) == 0);
}
/**
* @brief Computes the smallest power of two greater than or equal to a value
* (waiting for C++20 and `std::bit_ceil`).
* @tparam Type Unsigned integer type.
* @param value A value of unsigned integer type.
* @return The smallest power of two greater than or equal to the given value.
*/
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, Type> next_power_of_two(const Type value) noexcept {
ENTT_ASSERT_CONSTEXPR(value < (Type{1u} << (std::numeric_limits<Type>::digits - 1)), "Numeric limits exceeded");
Type curr = value - (value != 0u);
for(int next = 1; next < std::numeric_limits<Type>::digits; next = next * 2) {
curr |= (curr >> next);
}
return ++curr;
}
/**
* @brief Fast module utility function (powers of two only).
* @tparam Type Unsigned integer type.
* @param value A value of unsigned integer type.
* @param mod _Modulus_, it must be a power of two.
* @return The common remainder.
*/
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, Type> fast_mod(const Type value, const std::size_t mod) noexcept {
ENTT_ASSERT_CONSTEXPR(has_single_bit(mod), "Value must be a power of two");
return value & (mod - 1u);
}
} // namespace entt
#endif

View File

@@ -5,6 +5,7 @@
#include <tuple>
#include <type_traits>
#include <utility>
#include "fwd.hpp"
#include "type_traits.hpp"
namespace entt {
@@ -17,9 +18,9 @@ struct compressed_pair_element {
using reference = Type &;
using const_reference = const Type &;
template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>>
constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<Type>)
: value{} {}
template<typename Dummy = Type, typename = std::enable_if_t<std::is_default_constructible_v<Dummy>>>
// NOLINTNEXTLINE(modernize-use-equals-default)
constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<Type>) {}
template<typename Arg, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Arg>>, compressed_pair_element>>>
constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v<Type, Arg>)
@@ -38,7 +39,7 @@ struct compressed_pair_element {
}
private:
Type value;
Type value{};
};
template<typename Type, std::size_t Tag>
@@ -47,7 +48,7 @@ struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Ty
using const_reference = const Type &;
using base_type = Type;
template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>>
template<typename Dummy = Type, typename = std::enable_if_t<std::is_default_constructible_v<Dummy>>>
constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<base_type>)
: base_type{} {}
@@ -102,7 +103,7 @@ public:
* @tparam Dummy Dummy template parameter used for internal purposes.
*/
template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>>
constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v<first_base> &&std::is_nothrow_default_constructible_v<second_base>)
constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v<first_base> && std::is_nothrow_default_constructible_v<second_base>)
: first_base{},
second_base{} {}
@@ -110,13 +111,13 @@ public:
* @brief Copy constructor.
* @param other The instance to copy from.
*/
constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v<first_base> &&std::is_nothrow_copy_constructible_v<second_base>) = default;
constexpr compressed_pair(const compressed_pair &other) = default;
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v<first_base> &&std::is_nothrow_move_constructible_v<second_base>) = default;
constexpr compressed_pair(compressed_pair &&other) noexcept = default;
/**
* @brief Constructs a pair from its values.
@@ -126,7 +127,7 @@ public:
* @param other Value to use to initialize the second element.
*/
template<typename Arg, typename Other>
constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v<first_base, Arg> &&std::is_nothrow_constructible_v<second_base, Other>)
constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v<first_base, Arg> && std::is_nothrow_constructible_v<second_base, Other>)
: first_base{std::forward<Arg>(arg)},
second_base{std::forward<Other>(other)} {}
@@ -138,23 +139,26 @@ public:
* @param other Arguments to use to initialize the second element.
*/
template<typename... Args, typename... Other>
constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other) noexcept(std::is_nothrow_constructible_v<first_base, Args...> &&std::is_nothrow_constructible_v<second_base, Other...>)
constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other) noexcept(std::is_nothrow_constructible_v<first_base, Args...> && std::is_nothrow_constructible_v<second_base, Other...>)
: first_base{std::move(args), std::index_sequence_for<Args...>{}},
second_base{std::move(other), std::index_sequence_for<Other...>{}} {}
/*! @brief Default destructor. */
~compressed_pair() = default;
/**
* @brief Copy assignment operator.
* @param other The instance to copy from.
* @return This compressed pair object.
*/
constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v<first_base> &&std::is_nothrow_copy_assignable_v<second_base>) = default;
constexpr compressed_pair &operator=(const compressed_pair &other) = default;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This compressed pair object.
*/
constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v<first_base> &&std::is_nothrow_move_assignable_v<second_base>) = default;
constexpr compressed_pair &operator=(compressed_pair &&other) noexcept = default;
/**
* @brief Returns the first element that a pair stores.
@@ -186,7 +190,7 @@ public:
* @brief Swaps two compressed pair objects.
* @param other The compressed pair to swap with.
*/
constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v<first_type> &&std::is_nothrow_swappable_v<second_type>) {
constexpr void swap(compressed_pair &other) noexcept {
using std::swap;
swap(first(), other.first());
swap(second(), other.second());
@@ -199,7 +203,7 @@ public:
* reference to the second element if `Index` is 1.
*/
template<std::size_t Index>
constexpr decltype(auto) get() noexcept {
[[nodiscard]] constexpr decltype(auto) get() noexcept {
if constexpr(Index == 0u) {
return first();
} else {
@@ -210,7 +214,7 @@ public:
/*! @copydoc get */
template<std::size_t Index>
constexpr decltype(auto) get() const noexcept {
[[nodiscard]] constexpr decltype(auto) get() const noexcept {
if constexpr(Index == 0u) {
return first();
} else {
@@ -242,8 +246,6 @@ inline constexpr void swap(compressed_pair<First, Second> &lhs, compressed_pair<
} // namespace entt
// disable structured binding support for clang 6, it messes when specializing tuple_size
#if !defined __clang_major__ || __clang_major__ > 6
namespace std {
/**
@@ -266,6 +268,5 @@ struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<I
};
} // namespace std
#endif
#endif

View File

@@ -15,6 +15,7 @@ namespace entt {
*/
template<typename...>
class family {
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
inline static ENTT_MAYBE_ATOMIC(id_type) identifier{};
public:

View File

@@ -6,6 +6,7 @@
namespace entt {
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(double[2])>
class basic_any;
@@ -15,6 +16,21 @@ using id_type = ENTT_ID_TYPE;
/*! @brief Alias declaration for the most common use case. */
using any = basic_any<>;
template<typename, typename>
class compressed_pair;
template<typename>
class basic_hashed_string;
/*! @brief Aliases for common character types. */
using hashed_string = basic_hashed_string<char>;
/*! @brief Aliases for common character types. */
using hashed_wstring = basic_hashed_string<wchar_t>;
// NOLINTNEXTLINE(bugprone-forward-declaration-namespace)
struct type_info;
} // namespace entt
#endif

View File

@@ -3,6 +3,7 @@
#include <cstddef>
#include <cstdint>
#include <string_view>
#include "fwd.hpp"
namespace entt {
@@ -10,21 +11,19 @@ namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename>
struct fnv1a_traits;
template<typename = id_type>
struct fnv_1a_params;
template<>
struct fnv1a_traits<std::uint32_t> {
using type = std::uint32_t;
static constexpr std::uint32_t offset = 2166136261;
static constexpr std::uint32_t prime = 16777619;
struct fnv_1a_params<std::uint32_t> {
static constexpr auto offset = 2166136261;
static constexpr auto prime = 16777619;
};
template<>
struct fnv1a_traits<std::uint64_t> {
using type = std::uint64_t;
static constexpr std::uint64_t offset = 14695981039346656037ull;
static constexpr std::uint64_t prime = 1099511628211ull;
struct fnv_1a_params<std::uint64_t> {
static constexpr auto offset = 14695981039346656037ull;
static constexpr auto prime = 1099511628211ull;
};
template<typename Char>
@@ -59,7 +58,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 traits_type = internal::fnv1a_traits<id_type>;
using params = internal::fnv_1a_params<>;
struct const_wrapper {
// non-explicit constructor on purpose
@@ -70,22 +69,11 @@ 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, traits_type::offset};
[[nodiscard]] static constexpr auto helper(const std::basic_string_view<Char> view) noexcept {
base_type base{view.data(), view.size(), params::offset};
for(; str[base.length]; ++base.length) {
base.hash = (base.hash ^ static_cast<traits_type::type>(str[base.length])) * traits_type::prime;
}
return base;
}
// 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, traits_type::offset};
for(size_type pos{}; pos < len; ++pos) {
base.hash = (base.hash ^ static_cast<traits_type::type>(str[pos])) * traits_type::prime;
for(auto &&curr: view) {
base.hash = (base.hash ^ static_cast<id_type>(curr)) * params::prime;
}
return base;
@@ -116,6 +104,7 @@ public:
* @return The numeric representation of the string.
*/
template<std::size_t N>
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
[[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept {
return basic_hashed_string{str};
}
@@ -131,7 +120,7 @@ public:
/*! @brief Constructs an empty hashed string. */
constexpr basic_hashed_string() noexcept
: base_type{} {}
: basic_hashed_string{nullptr, 0u} {}
/**
* @brief Constructs a hashed string from a string view.
@@ -139,7 +128,7 @@ public:
* @param len Length of the string to hash.
*/
constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept
: base_type{helper(str, len)} {}
: base_type{helper({str, len})} {}
/**
* @brief Constructs a hashed string from an array of const characters.
@@ -147,8 +136,9 @@ public:
* @param str Human-readable identifier.
*/
template<std::size_t N>
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
constexpr basic_hashed_string(const value_type (&str)[N]) noexcept
: base_type{helper(str)} {}
: base_type{helper({static_cast<const value_type *>(str)})} {}
/**
* @brief Explicit constructor on purpose to avoid constructing a hashed
@@ -160,14 +150,14 @@ public:
* @param wrapper Helps achieving the purpose by relying on overloading.
*/
explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept
: base_type{helper(wrapper.repr)} {}
: base_type{helper({wrapper.repr})} {}
/**
* @brief Returns the size a hashed string.
* @return The size of the hashed string.
*/
[[nodiscard]] constexpr size_type size() const noexcept {
return base_type::length; // NOLINT
return base_type::length;
}
/**
@@ -207,7 +197,7 @@ public:
* @param len Length of the string to hash.
*/
template<typename Char>
basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string<Char>;
basic_hashed_string(const Char *str, std::size_t len) -> basic_hashed_string<Char>;
/**
* @brief Deduction guide.
@@ -216,6 +206,7 @@ basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_stri
* @param str Human-readable identifier.
*/
template<typename Char, std::size_t N>
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string<Char>;
/**
@@ -293,12 +284,6 @@ template<typename Char>
return !(lhs < rhs);
}
/*! @brief Aliases for common character types. */
using hashed_string = basic_hashed_string<char>;
/*! @brief Aliases for common character types. */
using hashed_wstring = basic_hashed_string<wchar_t>;
inline namespace literals {
/**

View File

@@ -147,7 +147,7 @@ struct iterable_adaptor final {
using sentinel = Sentinel;
/*! @brief Default constructor. */
constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v<iterator> &&std::is_nothrow_default_constructible_v<sentinel>)
constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v<iterator> && std::is_nothrow_default_constructible_v<sentinel>)
: first{},
last{} {}
@@ -156,7 +156,7 @@ struct iterable_adaptor final {
* @param from Begin iterator.
* @param to End iterator.
*/
constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v<iterator> &&std::is_nothrow_move_constructible_v<sentinel>)
constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v<iterator> && std::is_nothrow_move_constructible_v<sentinel>)
: first{std::move(from)},
last{std::move(to)} {}

View File

@@ -2,7 +2,6 @@
#define ENTT_CORE_MEMORY_HPP
#include <cstddef>
#include <limits>
#include <memory>
#include <tuple>
#include <type_traits>
@@ -11,44 +10,6 @@
namespace entt {
/**
* @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.
*/
[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept {
return value && ((value & (value - 1)) == 0);
}
/**
* @brief Computes the smallest power of two greater than or equal to a value
* (waiting for C++20 and `std::bit_ceil`).
* @param value The value to use.
* @return The smallest power of two greater than or equal to the given value.
*/
[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept {
ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded");
std::size_t curr = value - (value != 0u);
for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) {
curr |= curr >> next;
}
return ++curr;
}
/**
* @brief Fast module utility function (powers of two only).
* @param value A value for which to calculate the modulus.
* @param mod _Modulus_, it must be a power of two.
* @return The common remainder.
*/
[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept {
ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two");
return value & (mod - 1u);
}
/**
* @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20).
* @tparam Type Pointer type.
@@ -275,7 +236,7 @@ constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...ar
*/
template<typename Type, typename Allocator, typename... Args>
constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) {
return std::apply([value](auto &&...curr) { return new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
return std::apply([value](auto &&...curr) { return ::new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
}
} // namespace entt

View File

@@ -23,10 +23,12 @@ struct monostate {
* @brief Assigns a value of a specific type to a given key.
* @tparam Type Type of the value to assign.
* @param val User data to assign to the given key.
* @return This monostate object.
*/
template<typename Type>
void operator=(Type val) const noexcept {
monostate &operator=(Type val) noexcept {
value<Type> = val;
return *this;
}
/**
@@ -41,6 +43,7 @@ struct monostate {
private:
template<typename Type>
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
inline static ENTT_MAYBE_ATOMIC(Type) value{};
};
@@ -49,7 +52,8 @@ private:
* @tparam Value Value used to differentiate between different variables.
*/
template<id_type Value>
inline monostate<Value> monostate_v = {};
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
inline monostate<Value> monostate_v{};
} // namespace entt

20
src/entt/core/ranges.hpp Normal file
View File

@@ -0,0 +1,20 @@
#ifndef ENTT_CORE_RANGES_HPP
#define ENTT_CORE_RANGES_HPP
#if __has_include(<version>)
# include <version>
#
# if defined(__cpp_lib_ranges)
# include <ranges>
# include "iterator.hpp"
template<class... Args>
inline constexpr bool std::ranges::enable_borrowed_range<entt::iterable_adaptor<Args...>>{true};
template<class... Args>
inline constexpr bool std::ranges::enable_view<entt::iterable_adaptor<Args...>>{true};
# endif
#endif
#endif

View File

@@ -24,7 +24,7 @@ struct ENTT_API type_index final {
template<typename Type>
[[nodiscard]] constexpr auto stripped_type_name() noexcept {
#if defined ENTT_PRETTY_FUNCTION
std::string_view pretty_function{ENTT_PRETTY_FUNCTION};
std::string_view pretty_function{static_cast<const char *>(ENTT_PRETTY_FUNCTION)};
auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1);
auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first);
return value;
@@ -136,10 +136,12 @@ struct type_info final {
* @tparam Type Type for which to construct a type info object.
*/
template<typename Type>
// NOLINTBEGIN(modernize-use-transparent-functors)
constexpr type_info(std::in_place_type_t<Type>) noexcept
: seq{type_index<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
identifier{type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
alias{type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()} {}
// NOLINTEND(modernize-use-transparent-functors)
/**
* @brief Type index.
@@ -197,7 +199,7 @@ private:
* @param rhs A valid type info object.
* @return True if the first element is less than the second, false otherwise.
*/
[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept {
[[nodiscard]] inline constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept {
return lhs.index() < rhs.index();
}
@@ -208,7 +210,7 @@ private:
* @return True if the first element is less than or equal to the second, false
* otherwise.
*/
[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept {
[[nodiscard]] inline constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept {
return !(rhs < lhs);
}
@@ -219,7 +221,7 @@ private:
* @return True if the first element is greater than the second, false
* otherwise.
*/
[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept {
[[nodiscard]] inline constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept {
return rhs < lhs;
}
@@ -230,7 +232,7 @@ private:
* @return True if the first element is greater than or equal to the second,
* false otherwise.
*/
[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept {
[[nodiscard]] inline constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept {
return !(lhs < rhs);
}

View File

@@ -574,7 +574,7 @@ inline constexpr bool value_list_contains_v = value_list_contains<List, Value>::
/*! @brief Primary template isn't defined on purpose. */
template<typename...>
class value_list_diff;
struct value_list_diff;
/**
* @brief Computes the difference between two value lists.
@@ -582,12 +582,9 @@ class value_list_diff;
* @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:
struct value_list_diff<value_list<Value...>, value_list<Other...>> {
/*! @brief A value list that is the difference between the two value lists. */
using type = value_list_cat_t<std::conditional_t<value_list_contains_v<v141_toolset_workaround, Value>, value_list<>, value_list<Value>>...>;
using type = value_list_cat_t<std::conditional_t<value_list_contains_v<value_list<Other...>, Value>, value_list<>, value_list<Value>>...>;
};
/**
@@ -772,14 +769,11 @@ template<typename Type>
template<typename Type>
[[nodiscard]] constexpr bool dispatch_is_equality_comparable() {
// NOLINTBEGIN(modernize-use-transparent-functors)
if constexpr(std::is_array_v<Type>) {
return false;
} else if constexpr(is_iterator_v<Type>) {
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>()) {
} else if constexpr(!is_iterator_v<Type> && has_value_type<Type>::value) {
if constexpr(std::is_same_v<typename Type::value_type, Type> || dispatch_is_equality_comparable<typename Type::value_type>()) {
return maybe_equality_comparable<Type>(0);
} else {
return false;
@@ -793,6 +787,7 @@ template<typename Type>
} else {
return maybe_equality_comparable<Type>(0);
}
// NOLINTEND(modernize-use-transparent-functors)
}
} // namespace internal
@@ -873,9 +868,9 @@ template<typename Member>
using member_class_t = typename member_class<Member>::type;
/**
* @brief Extracts the n-th argument of a given function or member function.
* @brief Extracts the n-th argument of a _callable_ type.
* @tparam Index The index of the argument to extract.
* @tparam Candidate A valid function, member function or data member type.
* @tparam Candidate A valid _callable_ type.
*/
template<std::size_t Index, typename Candidate>
class nth_argument {
@@ -891,8 +886,11 @@ class nth_argument {
template<typename Type, typename Class>
static constexpr type_list<Type> pick_up(Type Class ::*);
template<typename Type>
static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&);
public:
/*! @brief N-th argument of the given function or member function. */
/*! @brief N-th argument of the _callable_ type. */
using type = type_list_element_t<Index, decltype(pick_up(std::declval<Candidate>()))>;
};

View File

@@ -5,6 +5,7 @@
#include <cstdint>
#include <type_traits>
#include "../config/config.h"
#include "../core/bit.hpp"
#include "fwd.hpp"
namespace entt {
@@ -12,12 +13,6 @@ namespace entt {
/*! @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;
@@ -64,10 +59,10 @@ struct entt_traits<std::uint64_t> {
*/
template<typename Traits>
class basic_entt_traits {
static constexpr auto length = internal::popcount(Traits::entity_mask);
static constexpr auto length = popcount(Traits::entity_mask);
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");
static_assert(Traits::entity_mask && ((Traits::entity_mask & (Traits::entity_mask + 1)) == 0), "Invalid entity mask");
static_assert((Traits::version_mask & (Traits::version_mask + 1)) == 0, "Invalid version mask");
public:
/*! @brief Value type. */
@@ -106,7 +101,11 @@ public:
* @return The integral representation of the version part.
*/
[[nodiscard]] static constexpr version_type to_version(const value_type value) noexcept {
return (static_cast<version_type>(to_integral(value) >> length) & version_mask);
if constexpr(Traits::version_mask == 0u) {
return version_type{};
} else {
return (static_cast<version_type>(to_integral(value) >> length) & version_mask);
}
}
/**
@@ -130,7 +129,11 @@ 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 & entity_mask) | (static_cast<entity_type>(version & version_mask) << length)};
if constexpr(Traits::version_mask == 0u) {
return value_type{entity & entity_mask};
} else {
return value_type{(entity & entity_mask) | (static_cast<entity_type>(version & version_mask) << length)};
}
}
/**
@@ -144,7 +147,11 @@ public:
* @return A properly constructed identifier.
*/
[[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept {
return value_type{(lhs & entity_mask) | (rhs & (version_mask << length))};
if constexpr(Traits::version_mask == 0u) {
return value_type{lhs & entity_mask};
} else {
return value_type{(lhs & entity_mask) | (rhs & (version_mask << length))};
}
}
};
@@ -252,25 +259,25 @@ struct null_t {
/**
* @brief Compares a null object and an identifier of any type.
* @tparam Entity Type of identifier.
* @param entity Identifier with which to compare.
* @param other A null object yet to be converted.
* @param lhs Identifier with which to compare.
* @param rhs A null object yet to be converted.
* @return False if the two elements differ, true otherwise.
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator==(const Entity entity, const null_t other) noexcept {
return other.operator==(entity);
[[nodiscard]] constexpr bool operator==(const Entity lhs, const null_t rhs) noexcept {
return rhs.operator==(lhs);
}
/**
* @brief Compares a null object and an identifier of any type.
* @tparam Entity Type of identifier.
* @param entity Identifier with which to compare.
* @param other A null object yet to be converted.
* @param lhs Identifier with which to compare.
* @param rhs A null object yet to be converted.
* @return True if the two elements differ, false otherwise.
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator!=(const Entity entity, const null_t other) noexcept {
return !(other == entity);
[[nodiscard]] constexpr bool operator!=(const Entity lhs, const null_t rhs) noexcept {
return !(rhs == lhs);
}
/*! @brief Tombstone object for all identifiers. */
@@ -314,7 +321,12 @@ struct tombstone_t {
template<typename Entity>
[[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept {
using traits_type = entt_traits<Entity>;
return traits_type::to_version(entity) == traits_type::to_version(*this);
if constexpr(traits_type::version_mask == 0u) {
return false;
} else {
return (traits_type::to_version(entity) == traits_type::to_version(*this));
}
}
/**
@@ -332,25 +344,25 @@ struct tombstone_t {
/**
* @brief Compares a tombstone object and an identifier of any type.
* @tparam Entity Type of identifier.
* @param entity Identifier with which to compare.
* @param other A tombstone object yet to be converted.
* @param lhs Identifier with which to compare.
* @param rhs A tombstone object yet to be converted.
* @return False if the two elements differ, true otherwise.
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator==(const Entity entity, const tombstone_t other) noexcept {
return other.operator==(entity);
[[nodiscard]] constexpr bool operator==(const Entity lhs, const tombstone_t rhs) noexcept {
return rhs.operator==(lhs);
}
/**
* @brief Compares a tombstone object and an identifier of any type.
* @tparam Entity Type of identifier.
* @param entity Identifier with which to compare.
* @param other A tombstone object yet to be converted.
* @param lhs Identifier with which to compare.
* @param rhs A tombstone object yet to be converted.
* @return True if the two elements differ, false otherwise.
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator!=(const Entity entity, const tombstone_t other) noexcept {
return !(other == entity);
[[nodiscard]] constexpr bool operator!=(const Entity lhs, const tombstone_t rhs) noexcept {
return !(rhs == lhs);
}
/**

View File

@@ -4,6 +4,7 @@
#include <cstdint>
#include <memory>
#include <type_traits>
#include "../config/config.h"
#include "../core/fwd.hpp"
#include "../core/type_traits.hpp"
@@ -31,6 +32,9 @@ class basic_storage;
template<typename, typename>
class basic_sigh_mixin;
template<typename, typename>
class basic_reactive_mixin;
template<typename Entity = entity, typename = std::allocator<Entity>>
class basic_registry;
@@ -43,14 +47,14 @@ class basic_runtime_view;
template<typename, typename, typename>
class basic_group;
template<typename, typename Mask = std::uint32_t, typename = std::allocator<Mask>>
template<typename, typename = std::allocator<void>>
class basic_observer;
template<typename>
class basic_organizer;
template<typename, typename...>
struct basic_handle;
class basic_handle;
template<typename>
class basic_snapshot;
@@ -66,7 +70,7 @@ using sparse_set = basic_sparse_set<>;
/**
* @brief Alias declaration for the most common use case.
* @tparam Type Type of objects assigned to the entities.
* @tparam Type Element type.
*/
template<typename Type>
using storage = basic_storage<Type>;
@@ -78,6 +82,13 @@ using storage = basic_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.
* @tparam Type Underlying storage type.
*/
template<typename Type>
using reactive_mixin = basic_reactive_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<>;
@@ -129,7 +140,7 @@ using const_runtime_view = basic_runtime_view<const sparse_set>;
template<typename... Type>
struct exclude_t final: type_list<Type...> {
/*! @brief Default constructor. */
explicit constexpr exclude_t() {}
explicit constexpr exclude_t() = default;
};
/**
@@ -140,34 +151,34 @@ template<typename... Type>
inline constexpr exclude_t<Type...> exclude{};
/**
* @brief Alias for lists of observed components.
* @brief Alias for lists of observed elements.
* @tparam Type List of types.
*/
template<typename... Type>
struct get_t final: type_list<Type...> {
/*! @brief Default constructor. */
explicit constexpr get_t() {}
explicit constexpr get_t() = default;
};
/**
* @brief Variable template for lists of observed components.
* @brief Variable template for lists of observed elements.
* @tparam Type List of types.
*/
template<typename... Type>
inline constexpr get_t<Type...> get{};
/**
* @brief Alias for lists of owned components.
* @brief Alias for lists of owned elements.
* @tparam Type List of types.
*/
template<typename... Type>
struct owned_t final: type_list<Type...> {
/*! @brief Default constructor. */
explicit constexpr owned_t() {}
explicit constexpr owned_t() = default;
};
/**
* @brief Variable template for lists of owned components.
* @brief Variable template for lists of owned elements.
* @tparam Type List of types.
*/
template<typename... Type>
@@ -215,7 +226,21 @@ struct type_list_transform<owned_t<Type...>, Op> {
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>>;
using type = ENTT_STORAGE(sigh_mixin, basic_storage<Type, Entity, Allocator>);
};
/*! @brief Empty value type for reactive storage types. */
struct reactive final {};
/**
* @ brief Partial specialization for reactive storage types.
* @tparam Entity A valid entity type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Entity, typename Allocator>
struct storage_type<reactive, Entity, Allocator> {
/*! @brief Type-to-storage conversion result. */
using type = ENTT_STORAGE(reactive_mixin, basic_storage<reactive, Entity, Allocator>);
};
/**

View File

@@ -1,18 +1,20 @@
#ifndef ENTT_ENTITY_GROUP_HPP
#define ENTT_ENTITY_GROUP_HPP
#include <array>
#include <cstddef>
#include <iterator>
#include <tuple>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/algorithm.hpp"
#include "../core/fwd.hpp"
#include "../core/iterator.hpp"
#include "../core/type_info.hpp"
#include "../core/type_traits.hpp"
#include "entity.hpp"
#include "fwd.hpp"
#include "sparse_set.hpp"
#include "storage.hpp"
namespace entt {
@@ -25,8 +27,8 @@ class extended_group_iterator;
template<typename It, typename... Owned, typename... Get>
class extended_group_iterator<It, owned_t<Owned...>, get_t<Get...>> {
template<typename Type>
auto index_to_element([[maybe_unused]] Type &cpool) const {
if constexpr(Type::traits_type::page_size == 0u) {
[[nodiscard]] auto index_to_element([[maybe_unused]] Type &cpool) const {
if constexpr(std::is_void_v<typename Type::value_type>) {
return std::make_tuple();
} else {
return std::forward_as_tuple(cpool.rbegin()[it.index()]);
@@ -35,10 +37,10 @@ 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 difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
using iterator_concept = std::forward_iterator_tag;
@@ -46,9 +48,9 @@ public:
: it{},
pools{} {}
extended_group_iterator(iterator_type from, const std::tuple<Owned *..., Get *...> &cpools)
extended_group_iterator(iterator_type from, std::tuple<Owned *..., Get *...> cpools)
: it{from},
pools{cpools} {}
pools{std::move(cpools)} {}
extended_group_iterator &operator++() noexcept {
return ++it, *this;
@@ -92,100 +94,93 @@ template<typename... Lhs, typename... Rhs>
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;
[[nodiscard]] virtual bool owned(const id_type) const noexcept {
return false;
}
};
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<typename Type, std::size_t Owned, std::size_t Get, std::size_t Exclude>
class group_handler final: public group_descriptor {
using entity_type = typename Type::entity_type;
void swap_elements(const std::size_t pos, const entity_type entt) {
std::apply([pos, entt](auto *...cpool) { (cpool->swap_elements(cpool->data()[pos], entt), ...); }, pools);
for(size_type next{}; next < Owned; ++next) {
pools[next]->swap_elements((*pools[next])[pos], entt);
}
}
void push_on_construct(const entity_type entt) {
if(std::apply([entt, len = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < len) && (other->contains(entt) && ...); }, pools)
if(std::apply([entt, pos = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < pos) && (other->contains(entt) && ...); }, pools)
&& std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) {
swap_elements(len++, entt);
}
}
void push_on_destroy(const entity_type entt) {
if(std::apply([entt, len = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < len) && (other->contains(entt) && ...); }, pools)
if(std::apply([entt, pos = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < pos) && (other->contains(entt) && ...); }, pools)
&& std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) {
swap_elements(len++, entt);
}
}
void remove_if(const entity_type entt) {
if(std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < len)) {
if(pools[0u]->contains(entt) && (pools[0u]->index(entt) < len)) {
swap_elements(--len, entt);
}
}
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);
void common_setup() {
// 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) {
for(auto first = pools[0u]->rbegin(), last = first + pools[0u]->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;
public:
using common_type = Type;
using size_type = typename Type::size_type;
for(auto pos = 0u; pos < length; ++pos) {
cnt += ((elem[pos] == entt::type_hash<typename Owned::value_type>::value()) || ...);
template<typename... OGType, typename... EType>
group_handler(std::tuple<OGType &...> ogpool, std::tuple<EType &...> epool)
: pools{std::apply([](auto &&...cpool) { return std::array<common_type *, (Owned + Get)>{&cpool...}; }, ogpool)},
filter{std::apply([](auto &&...cpool) { return std::array<common_type *, Exclude>{&cpool...}; }, epool)} {
std::apply([this](auto &...cpool) { ((cpool.on_construct().template connect<&group_handler::push_on_construct>(*this), cpool.on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, ogpool);
std::apply([this](auto &...cpool) { ((cpool.on_construct().template connect<&group_handler::remove_if>(*this), cpool.on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, epool);
common_setup();
}
[[nodiscard]] bool owned(const id_type hash) const noexcept override {
for(size_type pos{}; pos < Owned; ++pos) {
if(pools[pos]->type().hash() == hash) {
return true;
}
}
return cnt;
return false;
}
[[nodiscard]] size_type length() const noexcept {
return len;
}
template<typename Type>
Type pools_as() const noexcept {
return pools;
}
template<typename Type>
Type filter_as() const noexcept {
return filter;
template<std::size_t Index>
[[nodiscard]] common_type *storage() const noexcept {
if constexpr(Index < (Owned + Get)) {
return pools[Index];
} else {
return filter[Index - (Owned + Get)];
}
}
private:
std::tuple<Owned *..., Get *...> pools;
std::tuple<Exclude *...> filter;
std::size_t len;
std::array<common_type *, (Owned + Get)> pools;
std::array<common_type *, 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;
template<typename Type, std::size_t Get, std::size_t Exclude>
class group_handler<Type, 0u, Get, Exclude> final: public group_descriptor {
using entity_type = typename Type::entity_type;
void push_on_construct(const entity_type entt) {
if(!elem.contains(entt)
@@ -207,44 +202,46 @@ class group_handler<owned_t<>, get_t<Get...>, exclude_t<Exclude...>> final: publ
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))) {
void common_setup() {
for(const auto entity: *pools[0u]) {
push_on_construct(entity);
}
}
common_type &handle() noexcept {
public:
using common_type = Type;
template<typename Allocator, typename... GType, typename... EType>
group_handler(const Allocator &allocator, std::tuple<GType &...> gpool, std::tuple<EType &...> epool)
: pools{std::apply([](auto &&...cpool) { return std::array<common_type *, Get>{&cpool...}; }, gpool)},
filter{std::apply([](auto &&...cpool) { return std::array<common_type *, Exclude>{&cpool...}; }, epool)},
elem{allocator} {
std::apply([this](auto &...cpool) { ((cpool.on_construct().template connect<&group_handler::push_on_construct>(*this), cpool.on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, gpool);
std::apply([this](auto &...cpool) { ((cpool.on_construct().template connect<&group_handler::remove_if>(*this), cpool.on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, epool);
common_setup();
}
[[nodiscard]] common_type &handle() noexcept {
return elem;
}
const common_type &handle() const noexcept {
[[nodiscard]] const common_type &handle() const noexcept {
return elem;
}
template<typename Type>
Type pools_as() const noexcept {
return pools;
}
template<typename Type>
Type filter_as() const noexcept {
return filter;
template<std::size_t Index>
[[nodiscard]] common_type *storage() const noexcept {
if constexpr(Index < Get) {
return pools[Index];
} else {
return filter[Index - Get];
}
}
private:
std::tuple<Get *...> pools;
std::tuple<Exclude *...> filter;
base_type elem;
std::array<common_type *, Get> pools;
std::array<common_type *, Exclude> filter;
common_type elem;
};
} // namespace internal
@@ -271,7 +268,7 @@ class basic_group;
* Iterators aren't invalidated if:
*
* * New elements are added to the storage.
* * The entity currently pointed is modified (for example, components are added
* * The entity currently pointed is modified (for example, elements are added
* or removed from it).
* * The entity currently pointed is destroyed.
*
@@ -287,16 +284,12 @@ class basic_group<owned_t<>, get_t<Get...>, exclude_t<Exclude...>> {
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..., typename Exclude::value_type...>>;
static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Get::element_type..., typename Exclude::element_type...>>;
auto pools() const noexcept {
template<std::size_t... Index>
[[nodiscard]] auto pools_for(std::index_sequence<Index...>) const noexcept {
using return_type = std::tuple<Get *...>;
return descriptor ? descriptor->template pools_as<return_type>() : return_type{};
}
auto filter() const noexcept {
using return_type = std::tuple<Exclude *...>;
return descriptor ? descriptor->template filter_as<return_type>() : return_type{};
return descriptor ? return_type{static_cast<Get *>(descriptor->template storage<Index>())...} : return_type{};
}
public:
@@ -308,12 +301,20 @@ public:
using common_type = base_type;
/*! @brief Random access iterator type. */
using iterator = typename common_type::iterator;
/*! @brief Reversed iterator type. */
/*! @brief Reverse iterator type. */
using reverse_iterator = typename common_type::reverse_iterator;
/*! @brief Iterable group type. */
using iterable = iterable_adaptor<internal::extended_group_iterator<iterator, owned_t<>, get_t<Get...>>>;
/*! @brief Group handler type. */
using handler = internal::group_handler<owned_t<>, get_t<std::remove_const_t<Get>...>, exclude_t<std::remove_const_t<Exclude>...>>;
using handler = internal::group_handler<common_type, 0u, sizeof...(Get), sizeof...(Exclude)>;
/**
* @brief Group opaque identifier.
* @return Group opaque identifier.
*/
static id_type group_id() noexcept {
return type_hash<basic_group<owned_t<>, get_t<std::remove_const_t<Get>...>, exclude_t<std::remove_const_t<Exclude>...>>>::value();
}
/*! @brief Default constructor to use to create empty, invalid groups. */
basic_group() noexcept
@@ -335,9 +336,9 @@ public:
}
/**
* @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.
* @brief Returns the storage for a given element type, if any.
* @tparam Type Type of element of which to return the storage.
* @return The storage for the given element type.
*/
template<typename Type>
[[nodiscard]] auto *storage() const noexcept {
@@ -351,13 +352,8 @@ public:
*/
template<std::size_t Index>
[[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());
}
using type = type_list_element_t<Index, type_list<Get..., Exclude...>>;
return *this ? static_cast<type *>(descriptor->template storage<Index>()) : nullptr;
}
/**
@@ -490,11 +486,11 @@ public:
}
/**
* @brief Returns the components assigned to the given entity.
* @tparam Type Type of the component to get.
* @tparam Other Other types of components to get.
* @brief Returns the elements assigned to the given entity.
* @tparam Type Type of the element to get.
* @tparam Other Other types of elements to get.
* @param entt A valid identifier.
* @return The components assigned to the entity.
* @return The elements assigned to the entity.
*/
template<typename Type, typename... Other>
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
@@ -502,14 +498,14 @@ public:
}
/**
* @brief Returns the components assigned to the given entity.
* @tparam Index Indexes of the components to get.
* @brief Returns the elements assigned to the given entity.
* @tparam Index Indexes of the elements to get.
* @param entt A valid identifier.
* @return The components assigned to the entity.
* @return The elements assigned to the entity.
*/
template<std::size_t... Index>
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
const auto cpools = pools();
const auto cpools = pools_for(std::index_sequence_for<Get...>{});
if constexpr(sizeof...(Index) == 0) {
return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools);
@@ -521,12 +517,12 @@ public:
}
/**
* @brief Iterates entities and components and applies the given function
* @brief Iterates entities and elements and applies the given function
* object to them.
*
* The function object is invoked for each entity. It is provided with the
* entity itself and a set of references to non-empty components. The
* _constness_ of the components is as requested.<br/>
* entity itself and a set of references to non-empty elements. The
* _constness_ of the elements is as requested.<br/>
* The signature of the function must be equivalent to one of the following
* forms:
*
@@ -557,8 +553,8 @@ public:
* @brief Returns an iterable object to use to _visit_ a group.
*
* The iterable object returns tuples that contain the current entity and a
* set of references to its non-empty components. The _constness_ of the
* components is as requested.
* set of references to its non-empty elements. The _constness_ of the
* elements is as requested.
*
* @note
* Empty types aren't explicitly instantiated and therefore they are never
@@ -567,7 +563,7 @@ public:
* @return An iterable object to use to _visit_ the group.
*/
[[nodiscard]] iterable each() const noexcept {
const auto cpools = pools();
const auto cpools = pools_for(std::index_sequence_for<Get...>{});
return iterable{{begin(), cpools}, {end(), cpools}};
}
@@ -595,8 +591,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 type of component to compare.
* @tparam Other Other optional types of components to compare.
* @tparam Type Optional type of element to compare.
* @tparam Other Other optional types of elements to compare.
* @tparam Compare Type of comparison function object.
* @tparam Sort Type of sort function object.
* @tparam Args Types of arguments to forward to the sort function object.
@@ -614,7 +610,7 @@ public:
*
* @sa sort
*
* @tparam Index Optional indexes of components to compare.
* @tparam Index Optional indexes of elements to compare.
* @tparam Compare Type of comparison function object.
* @tparam Sort Type of sort function object.
* @tparam Args Types of arguments to forward to the sort function object.
@@ -629,7 +625,7 @@ public:
static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function");
descriptor->handle().sort(std::move(compare), std::move(algo), std::forward<Args>(args)...);
} else {
auto comp = [&compare, cpools = pools()](const entity_type lhs, const entity_type rhs) {
auto comp = [&compare, cpools = pools_for(std::index_sequence_for<Get...>{})](const entity_type lhs, const entity_type rhs) {
if constexpr(sizeof...(Index) == 1) {
return compare((std::get<Index>(cpools)->get(lhs), ...), (std::get<Index>(cpools)->get(rhs), ...));
} else {
@@ -659,14 +655,6 @@ public:
}
}
/**
* @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:
handler *descriptor;
};
@@ -679,7 +667,7 @@ private:
*
* * It's guaranteed that the entity list is tightly packed in memory for fast
* iterations.
* * It's guaranteed that all components in the owned storage are tightly packed
* * It's guaranteed that all elements in the owned storage are tightly packed
* in memory for even faster iterations and to allow direct access.
* * They stay true to the order of the owned storage and all instances have the
* same order in memory.
@@ -691,7 +679,7 @@ private:
* Iterators aren't invalidated if:
*
* * New elements are added to the storage.
* * The entity currently pointed is modified (for example, components are added
* * The entity currently pointed is modified (for example, elements are added
* or removed from it).
* * The entity currently pointed is destroyed.
*
@@ -704,20 +692,18 @@ private:
*/
template<typename... Owned, typename... Get, typename... Exclude>
class basic_group<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> {
static_assert(((Owned::storage_policy != deletion_policy::in_place) && ...), "Groups do not support in-place delete");
using base_type = std::common_type_t<typename Owned::base_type..., typename Get::base_type..., typename Exclude::base_type...>;
using underlying_type = typename base_type::entity_type;
template<typename Type>
static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Owned::value_type..., typename Get::value_type..., typename Exclude::value_type...>>;
static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Owned::element_type..., typename Get::element_type..., typename Exclude::element_type...>>;
auto pools() const noexcept {
template<std::size_t... Index, std::size_t... Other>
[[nodiscard]] auto pools_for(std::index_sequence<Index...>, std::index_sequence<Other...>) const noexcept {
using return_type = std::tuple<Owned *..., Get *...>;
return descriptor ? descriptor->template pools_as<return_type>() : return_type{};
}
auto filter() const noexcept {
using return_type = std::tuple<Exclude *...>;
return descriptor ? descriptor->template filter_as<return_type>() : return_type{};
return descriptor ? return_type{static_cast<Owned *>(descriptor->template storage<Index>())..., static_cast<Get *>(descriptor->template storage<sizeof...(Owned) + Other>())...} : return_type{};
}
public:
@@ -729,12 +715,20 @@ public:
using common_type = base_type;
/*! @brief Random access iterator type. */
using iterator = typename common_type::iterator;
/*! @brief Reversed iterator type. */
/*! @brief Reverse iterator type. */
using reverse_iterator = typename common_type::reverse_iterator;
/*! @brief Iterable group type. */
using iterable = iterable_adaptor<internal::extended_group_iterator<iterator, owned_t<Owned...>, get_t<Get...>>>;
/*! @brief Group handler type. */
using handler = internal::group_handler<owned_t<std::remove_const_t<Owned>...>, get_t<std::remove_const_t<Get>...>, exclude_t<std::remove_const_t<Exclude>...>>;
using handler = internal::group_handler<common_type, sizeof...(Owned), sizeof...(Get), sizeof...(Exclude)>;
/**
* @brief Group opaque identifier.
* @return Group opaque identifier.
*/
static id_type group_id() noexcept {
return type_hash<basic_group<owned_t<std::remove_const_t<Owned>...>, get_t<std::remove_const_t<Get>...>, exclude_t<std::remove_const_t<Exclude>...>>>::value();
}
/*! @brief Default constructor to use to create empty, invalid groups. */
basic_group() noexcept
@@ -756,9 +750,9 @@ public:
}
/**
* @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.
* @brief Returns the storage for a given element type, if any.
* @tparam Type Type of element of which to return the storage.
* @return The storage for the given element type.
*/
template<typename Type>
[[nodiscard]] auto *storage() const noexcept {
@@ -772,13 +766,8 @@ public:
*/
template<std::size_t Index>
[[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());
}
using type = type_list_element_t<Index, type_list<Owned..., Get..., Exclude...>>;
return *this ? static_cast<type *>(descriptor->template storage<Index>()) : nullptr;
}
/**
@@ -896,11 +885,11 @@ public:
}
/**
* @brief Returns the components assigned to the given entity.
* @tparam Type Type of the component to get.
* @tparam Other Other types of components to get.
* @brief Returns the elements assigned to the given entity.
* @tparam Type Type of the element to get.
* @tparam Other Other types of elements to get.
* @param entt A valid identifier.
* @return The components assigned to the entity.
* @return The elements assigned to the entity.
*/
template<typename Type, typename... Other>
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
@@ -908,14 +897,14 @@ public:
}
/**
* @brief Returns the components assigned to the given entity.
* @tparam Index Indexes of the components to get.
* @brief Returns the elements assigned to the given entity.
* @tparam Index Indexes of the elements to get.
* @param entt A valid identifier.
* @return The components assigned to the entity.
* @return The elements assigned to the entity.
*/
template<std::size_t... Index>
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
const auto cpools = pools();
const auto cpools = pools_for(std::index_sequence_for<Owned...>{}, std::index_sequence_for<Get...>{});
if constexpr(sizeof...(Index) == 0) {
return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools);
@@ -927,12 +916,12 @@ public:
}
/**
* @brief Iterates entities and components and applies the given function
* @brief Iterates entities and elements and applies the given function
* object to them.
*
* The function object is invoked for each entity. It is provided with the
* entity itself and a set of references to non-empty components. The
* _constness_ of the components is as requested.<br/>
* entity itself and a set of references to non-empty elements. The
* _constness_ of the elements is as requested.<br/>
* The signature of the function must be equivalent to one of the following
* forms:
*
@@ -963,8 +952,8 @@ public:
* @brief Returns an iterable object to use to _visit_ a group.
*
* The iterable object returns tuples that contain the current entity and a
* set of references to its non-empty components. The _constness_ of the
* components is as requested.
* set of references to its non-empty elements. The _constness_ of the
* elements is as requested.
*
* @note
* Empty types aren't explicitly instantiated and therefore they are never
@@ -973,8 +962,8 @@ public:
* @return An iterable object to use to _visit_ the group.
*/
[[nodiscard]] iterable each() const noexcept {
const auto cpools = pools();
return {{begin(), cpools}, {end(), cpools}};
const auto cpools = pools_for(std::index_sequence_for<Owned...>{}, std::index_sequence_for<Get...>{});
return iterable{{begin(), cpools}, {end(), cpools}};
}
/**
@@ -1002,8 +991,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 type of component to compare.
* @tparam Other Other optional types of components to compare.
* @tparam Type Optional type of element to compare.
* @tparam Other Other optional types of elements to compare.
* @tparam Compare Type of comparison function object.
* @tparam Sort Type of sort function object.
* @tparam Args Types of arguments to forward to the sort function object.
@@ -1021,7 +1010,7 @@ public:
*
* @sa sort
*
* @tparam Index Optional indexes of components to compare.
* @tparam Index Optional indexes of elements to compare.
* @tparam Compare Type of comparison function object.
* @tparam Sort Type of sort function object.
* @tparam Args Types of arguments to forward to the sort function object.
@@ -1031,7 +1020,7 @@ public:
*/
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();
const auto cpools = pools_for(std::index_sequence_for<Owned...>{}, std::index_sequence_for<Get...>{});
if constexpr(sizeof...(Index) == 0) {
static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function");

View File

@@ -5,6 +5,7 @@
#include <tuple>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/iterator.hpp"
#include "../core/type_traits.hpp"
#include "entity.hpp"
@@ -94,19 +95,29 @@ template<typename ILhs, typename IRhs>
* @tparam Scope Types to which to restrict the scope of a handle.
*/
template<typename Registry, typename... Scope>
struct basic_handle {
class basic_handle {
using traits_type = entt_traits<typename Registry::entity_type>;
[[nodiscard]] auto &owner_or_assert() const noexcept {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
return static_cast<Registry &>(*owner);
}
public:
/*! @brief Type of registry accepted by the handle. */
using registry_type = Registry;
/*! @brief Underlying entity identifier. */
using entity_type = typename registry_type::entity_type;
using entity_type = typename traits_type::value_type;
/*! @brief Underlying version type. */
using version_type = typename registry_type::version_type;
using version_type = typename traits_type::version_type;
/*! @brief Unsigned integer type. */
using size_type = typename registry_type::size_type;
using size_type = std::size_t;
/*! @brief Iterable handle type. */
using iterable = iterable_adaptor<internal::handle_storage_iterator<typename decltype(std::declval<registry_type>().storage())::iterator>>;
/*! @brief Constructs an invalid handle. */
basic_handle() noexcept
: reg{},
: owner{},
entt{null} {}
/**
@@ -115,7 +126,7 @@ struct basic_handle {
* @param value A valid identifier.
*/
basic_handle(registry_type &ref, entity_type value) noexcept
: reg{&ref},
: owner{&ref},
entt{value} {}
/**
@@ -128,49 +139,23 @@ struct basic_handle {
*
* @return An iterable object to use to _visit_ the handle.
*/
[[nodiscard]] auto storage() const noexcept {
auto iterable = reg->storage();
using iterator_type = internal::handle_storage_iterator<typename decltype(iterable)::iterator>;
return iterable_adaptor{iterator_type{entt, iterable.begin(), iterable.end()}, iterator_type{entt, iterable.end(), iterable.end()}};
[[nodiscard]] iterable storage() const noexcept {
auto underlying = owner_or_assert().storage();
return iterable{{entt, underlying.begin(), underlying.end()}, {entt, underlying.end(), underlying.end()}};
}
/**
* @brief Constructs a const handle from a non-const one.
* @tparam Other A valid entity type.
* @tparam Args Scope of the handle to construct.
* @return A const handle referring to the same registry and the same
* entity.
*/
template<typename Other, typename... Args>
operator basic_handle<Other, Args...>() const noexcept {
static_assert(std::is_same_v<Other, Registry> || std::is_same_v<std::remove_const_t<Other>, Registry>, "Invalid conversion between different handles");
static_assert((sizeof...(Scope) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Scope)) && ... && (type_list_contains_v<type_list<Scope...>, Args>))), "Invalid conversion between different handles");
return reg ? basic_handle<Other, Args...>{*reg, entt} : basic_handle<Other, Args...>{};
}
/**
* @brief Converts a handle to its underlying entity.
* @return The contained identifier.
*/
[[nodiscard]] operator entity_type() const noexcept {
return entity();
}
/**
* @brief Checks if a handle refers to non-null registry pointer and entity.
* @return True if the handle refers to non-null registry and entity, false otherwise.
*/
/*! @copydoc valid */
[[nodiscard]] explicit operator bool() const noexcept {
return reg && reg->valid(entt);
return owner && owner->valid(entt);
}
/**
* @brief Checks if a handle refers to a valid entity or not.
* @return True if the handle refers to a valid entity, false otherwise.
* @brief Checks if a handle refers to a valid registry and entity.
* @return True if the handle refers to a valid registry and entity, false
* otherwise.
*/
[[nodiscard]] bool valid() const {
return reg->valid(entt);
return static_cast<bool>(*this);
}
/**
@@ -178,7 +163,7 @@ struct basic_handle {
* @return A pointer to the underlying registry, if any.
*/
[[nodiscard]] registry_type *registry() const noexcept {
return reg;
return owner;
}
/**
@@ -189,9 +174,14 @@ struct basic_handle {
return entt;
}
/*! @copydoc entity */
[[nodiscard]] operator entity_type() const noexcept {
return entity();
}
/*! @brief Destroys the entity associated with a handle. */
void destroy() {
reg->destroy(std::exchange(entt, null));
owner_or_assert().destroy(std::exchange(entt, null));
}
/**
@@ -199,148 +189,164 @@ struct basic_handle {
* @param version A desired version upon destruction.
*/
void destroy(const version_type version) {
reg->destroy(std::exchange(entt, null), version);
owner_or_assert().destroy(std::exchange(entt, null), version);
}
/**
* @brief Assigns the given component to a handle.
* @tparam Component Type of component to create.
* @tparam Args Types of arguments to use to construct the component.
* @param args Parameters to use to initialize the component.
* @return A reference to the newly created component.
* @brief Assigns the given element to a handle.
* @tparam Type Type of element to create.
* @tparam Args Types of arguments to use to construct the element.
* @param args Parameters to use to initialize the element.
* @return A reference to the newly created element.
*/
template<typename Component, typename... Args>
template<typename Type, typename... Args>
// NOLINTNEXTLINE(modernize-use-nodiscard)
decltype(auto) emplace(Args &&...args) const {
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
return reg->template emplace<Component>(entt, std::forward<Args>(args)...);
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type");
return owner_or_assert().template emplace<Type>(entt, std::forward<Args>(args)...);
}
/**
* @brief Assigns or replaces the given component for a handle.
* @tparam Component Type of component to assign or replace.
* @tparam Args Types of arguments to use to construct the component.
* @param args Parameters to use to initialize the component.
* @return A reference to the newly created component.
* @brief Assigns or replaces the given element for a handle.
* @tparam Type Type of element to assign or replace.
* @tparam Args Types of arguments to use to construct the element.
* @param args Parameters to use to initialize the element.
* @return A reference to the newly created element.
*/
template<typename Component, typename... Args>
template<typename Type, typename... Args>
decltype(auto) emplace_or_replace(Args &&...args) const {
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
return reg->template emplace_or_replace<Component>(entt, std::forward<Args>(args)...);
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type");
return owner_or_assert().template emplace_or_replace<Type>(entt, std::forward<Args>(args)...);
}
/**
* @brief Patches the given component for a handle.
* @tparam Component Type of component to patch.
* @brief Patches the given element for a handle.
* @tparam Type Type of element to patch.
* @tparam Func Types of the function objects to invoke.
* @param func Valid function objects.
* @return A reference to the patched component.
* @return A reference to the patched element.
*/
template<typename Component, typename... Func>
template<typename Type, typename... Func>
decltype(auto) patch(Func &&...func) const {
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
return reg->template patch<Component>(entt, std::forward<Func>(func)...);
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type");
return owner_or_assert().template patch<Type>(entt, std::forward<Func>(func)...);
}
/**
* @brief Replaces the given component for a handle.
* @tparam Component Type of component to replace.
* @tparam Args Types of arguments to use to construct the component.
* @param args Parameters to use to initialize the component.
* @return A reference to the component being replaced.
* @brief Replaces the given element for a handle.
* @tparam Type Type of element to replace.
* @tparam Args Types of arguments to use to construct the element.
* @param args Parameters to use to initialize the element.
* @return A reference to the element being replaced.
*/
template<typename Component, typename... Args>
template<typename Type, typename... Args>
decltype(auto) replace(Args &&...args) const {
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
return reg->template replace<Component>(entt, std::forward<Args>(args)...);
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type");
return owner_or_assert().template replace<Type>(entt, std::forward<Args>(args)...);
}
/**
* @brief Removes the given components from a handle.
* @tparam Component Types of components to remove.
* @return The number of components actually removed.
* @brief Removes the given elements from a handle.
* @tparam Type Types of elements to remove.
* @return The number of elements actually removed.
*/
template<typename... Component>
template<typename... Type>
// NOLINTNEXTLINE(modernize-use-nodiscard)
size_type remove() const {
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Component> && ...), "Invalid type");
return reg->template remove<Component...>(entt);
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Type> && ...), "Invalid type");
return owner_or_assert().template remove<Type...>(entt);
}
/**
* @brief Erases the given components from a handle.
* @tparam Component Types of components to erase.
* @brief Erases the given elements from a handle.
* @tparam Type Types of elements to erase.
*/
template<typename... Component>
template<typename... Type>
void erase() const {
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Component> && ...), "Invalid type");
reg->template erase<Component...>(entt);
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Type> && ...), "Invalid type");
owner_or_assert().template erase<Type...>(entt);
}
/**
* @brief Checks if a handle has all the given components.
* @tparam Component Components for which to perform the check.
* @return True if the handle has all the components, false otherwise.
* @brief Checks if a handle has all the given elements.
* @tparam Type Elements for which to perform the check.
* @return True if the handle has all the elements, false otherwise.
*/
template<typename... Component>
template<typename... Type>
[[nodiscard]] decltype(auto) all_of() const {
return reg->template all_of<Component...>(entt);
return owner_or_assert().template all_of<Type...>(entt);
}
/**
* @brief Checks if a handle has at least one of the given components.
* @tparam Component Components for which to perform the check.
* @return True if the handle has at least one of the given components,
* @brief Checks if a handle has at least one of the given elements.
* @tparam Type Elements for which to perform the check.
* @return True if the handle has at least one of the given elements,
* false otherwise.
*/
template<typename... Component>
template<typename... Type>
[[nodiscard]] decltype(auto) any_of() const {
return reg->template any_of<Component...>(entt);
return owner_or_assert().template any_of<Type...>(entt);
}
/**
* @brief Returns references to the given components for a handle.
* @tparam Component Types of components to get.
* @return References to the components owned by the handle.
* @brief Returns references to the given elements for a handle.
* @tparam Type Types of elements to get.
* @return References to the elements owned by the handle.
*/
template<typename... Component>
template<typename... Type>
[[nodiscard]] decltype(auto) get() const {
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Component> && ...), "Invalid type");
return reg->template get<Component...>(entt);
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Type> && ...), "Invalid type");
return owner_or_assert().template get<Type...>(entt);
}
/**
* @brief Returns a reference to the given component for a handle.
* @tparam Component Type of component to get.
* @tparam Args Types of arguments to use to construct the component.
* @param args Parameters to use to initialize the component.
* @return Reference to the component owned by the handle.
* @brief Returns a reference to the given element for a handle.
* @tparam Type Type of element to get.
* @tparam Args Types of arguments to use to construct the element.
* @param args Parameters to use to initialize the element.
* @return Reference to the element owned by the handle.
*/
template<typename Component, typename... Args>
template<typename Type, typename... Args>
[[nodiscard]] decltype(auto) get_or_emplace(Args &&...args) const {
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
return reg->template get_or_emplace<Component>(entt, std::forward<Args>(args)...);
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type");
return owner_or_assert().template get_or_emplace<Type>(entt, std::forward<Args>(args)...);
}
/**
* @brief Returns pointers to the given components for a handle.
* @tparam Component Types of components to get.
* @return Pointers to the components owned by the handle.
* @brief Returns pointers to the given elements for a handle.
* @tparam Type Types of elements to get.
* @return Pointers to the elements owned by the handle.
*/
template<typename... Component>
template<typename... Type>
[[nodiscard]] auto try_get() const {
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Component> && ...), "Invalid type");
return reg->template try_get<Component...>(entt);
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Type> && ...), "Invalid type");
return owner_or_assert().template try_get<Type...>(entt);
}
/**
* @brief Checks if a handle has components assigned.
* @return True if the handle has no components assigned, false otherwise.
* @brief Checks if a handle has elements assigned.
* @return True if the handle has no elements assigned, false otherwise.
*/
[[nodiscard]] bool orphan() const {
return reg->orphan(entt);
return owner_or_assert().orphan(entt);
}
/**
* @brief Returns a const handle from a non-const one.
* @tparam Other A valid entity type.
* @tparam Args Scope of the handle to construct.
* @return A const handle referring to the same registry and the same
* entity.
*/
template<typename Other, typename... Args>
operator basic_handle<Other, Args...>() const noexcept {
static_assert(std::is_same_v<Other, Registry> || std::is_same_v<std::remove_const_t<Other>, Registry>, "Invalid conversion between different handles");
static_assert((sizeof...(Scope) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Scope)) && ... && (type_list_contains_v<type_list<Scope...>, Args>))), "Invalid conversion between different handles");
return owner ? basic_handle<Other, Args...>{*owner, entt} : basic_handle<Other, Args...>{};
}
private:
registry_type *reg;
registry_type *owner;
entity_type entt;
};
@@ -372,6 +378,54 @@ template<typename... Args, typename... Other>
return !(lhs == rhs);
}
/**
* @brief Compares a handle with the null object.
* @tparam Args Scope of the handle.
* @param lhs A valid handle.
* @param rhs A null object yet to be converted.
* @return False if the two elements differ, true otherwise.
*/
template<typename... Args>
[[nodiscard]] constexpr bool operator==(const basic_handle<Args...> &lhs, const null_t rhs) noexcept {
return (lhs.entity() == rhs);
}
/**
* @brief Compares a handle with the null object.
* @tparam Args Scope of the handle.
* @param lhs A null object yet to be converted.
* @param rhs A valid handle.
* @return False if the two elements differ, true otherwise.
*/
template<typename... Args>
[[nodiscard]] constexpr bool operator==(const null_t lhs, const basic_handle<Args...> &rhs) noexcept {
return (rhs == lhs);
}
/**
* @brief Compares a handle with the null object.
* @tparam Args Scope of the handle.
* @param lhs A valid handle.
* @param rhs A null object yet to be converted.
* @return True if the two elements differ, false otherwise.
*/
template<typename... Args>
[[nodiscard]] constexpr bool operator!=(const basic_handle<Args...> &lhs, const null_t rhs) noexcept {
return (lhs.entity() != rhs);
}
/**
* @brief Compares a handle with the null object.
* @tparam Args Scope of the handle.
* @param lhs A null object yet to be converted.
* @param rhs A valid handle.
* @return True if the two elements differ, false otherwise.
*/
template<typename... Args>
[[nodiscard]] constexpr bool operator!=(const null_t lhs, const basic_handle<Args...> &rhs) noexcept {
return (rhs != lhs);
}
} // namespace entt
#endif

View File

@@ -6,7 +6,7 @@
#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"
@@ -21,8 +21,8 @@ namespace entt {
template<typename Registry>
class as_view {
template<typename... Get, typename... Exclude>
auto dispatch(get_t<Get...>, exclude_t<Exclude...>) const {
return reg.template view<constness_as_t<typename Get::value_type, Get>...>(exclude_t<constness_as_t<typename Exclude::value_type, Exclude>...>{});
[[nodiscard]] auto dispatch(get_t<Get...>, exclude_t<Exclude...>) const {
return reg.template view<constness_as_t<typename Get::element_type, Get>...>(exclude_t<constness_as_t<typename Exclude::element_type, Exclude>...>{});
}
public:
@@ -60,11 +60,11 @@ private:
template<typename Registry>
class as_group {
template<typename... Owned, typename... Get, typename... Exclude>
auto dispatch(owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>) const {
[[nodiscard]] auto dispatch(owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>) const {
if constexpr(std::is_const_v<registry_type>) {
return reg.template group_if_exists<typename Owned::value_type...>(get_t<typename Get::value_type...>{}, exclude_t<typename Exclude::value_type...>{});
return reg.template group_if_exists<typename Owned::element_type...>(get_t<typename Get::element_type...>{}, exclude_t<typename Exclude::element_type...>{});
} else {
return reg.template group<constness_as_t<typename Owned::value_type, Owned>...>(get_t<constness_as_t<typename Get::value_type, Get>...>{}, exclude_t<constness_as_t<typename Exclude::value_type, Exclude>...>{});
return reg.template group<constness_as_t<typename Owned::element_type, Owned>...>(get_t<constness_as_t<typename Get::element_type, Get>...>{}, exclude_t<constness_as_t<typename Exclude::element_type, Exclude>...>{});
}
}
@@ -99,39 +99,38 @@ private:
/**
* @brief Helper to create a listener that directly invokes a member function.
* @tparam Member Member function to invoke on a component of the given type.
* @tparam Member Member function to invoke on an element of the given type.
* @tparam Registry Basic registry type.
* @param reg A registry that contains the given entity and its components.
* @param entt Entity from which to get the component.
* @param reg A registry that contains the given entity and its elements.
* @param entt Entity from which to get the element.
*/
template<auto Member, typename Registry = std::decay_t<nth_argument_t<0u, decltype(Member)>>>
void invoke(Registry &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;
func.template connect<Member>(reg.template get<member_class_t<decltype(Member)>>(entt));
func(reg, entt);
(reg.template get<member_class_t<decltype(Member)>>(entt).*Member)(reg, entt);
}
/**
* @brief Returns the entity associated with a given component.
* @brief Returns the entity associated with a given element.
*
* @warning
* Currently, this function only works correctly with the default storage as it
* makes assumptions about how the components are laid out.
* makes assumptions about how the elements are laid out.
*
* @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.
* @param storage A storage that contains the given element.
* @param instance A valid element instance.
* @return The entity associated with the given element.
*/
template<typename... Args>
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;
typename basic_storage<Args...>::entity_type to_entity(const basic_storage<Args...> &storage, const typename basic_storage<Args...>::value_type &instance) {
using traits_type = component_traits<typename basic_storage<Args...>::value_type>;
static_assert(traits_type::page_size != 0u, "Unexpected page size");
const typename basic_storage<Args...>::base_type &base = storage;
const auto *addr = std::addressof(instance);
for(auto it = base.rbegin(), last = base.rend(); it < last; it += page_size) {
if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < static_cast<decltype(dist)>(page_size)) {
for(auto it = base.rbegin(), last = base.rend(); it < last; it += traits_type::page_size) {
if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < static_cast<decltype(dist)>(traits_type::page_size)) {
return *(it + dist);
}
}
@@ -139,23 +138,6 @@ auto to_entity(const basic_storage<Args...> &storage, const typename basic_stora
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;

View File

@@ -5,12 +5,54 @@
#include <utility>
#include "../config/config.h"
#include "../core/any.hpp"
#include "../core/type_info.hpp"
#include "../signal/sigh.hpp"
#include "entity.hpp"
#include "fwd.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename, typename, typename = void>
struct has_on_construct final: std::false_type {};
template<typename Type, typename Registry>
struct has_on_construct<Type, Registry, std::void_t<decltype(Type::on_construct(std::declval<Registry &>(), std::declval<Registry>().create()))>>
: std::true_type {};
template<typename, typename, typename = void>
struct has_on_update final: std::false_type {};
template<typename Type, typename Registry>
struct has_on_update<Type, Registry, std::void_t<decltype(Type::on_update(std::declval<Registry &>(), std::declval<Registry>().create()))>>
: std::true_type {};
template<typename, typename, typename = void>
struct has_on_destroy final: std::false_type {};
template<typename Type, typename Registry>
struct has_on_destroy<Type, Registry, std::void_t<decltype(Type::on_destroy(std::declval<Registry &>(), std::declval<Registry>().create()))>>
: std::true_type {};
template<typename Type>
auto *any_to_owner(any &value) noexcept {
using base_type = basic_registry<typename Type::entity_type, typename Type::allocator_type>;
auto *reg = any_cast<base_type>(&value);
if constexpr(!std::is_same_v<Type, base_type>) {
if(!reg) {
reg = any_cast<Type>(&value);
}
}
return reg;
}
} // namespace internal
/*! @endcond */
/**
* @brief Mixin type used to add signal support to storage types.
*
@@ -30,17 +72,18 @@ 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 basic_registry_type = basic_registry<typename owner_type::entity_type, typename owner_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 {
[[nodiscard]] auto &owner_or_assert() const noexcept {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
return static_cast<owner_type &>(*owner);
}
private:
void pop(underlying_iterator first, underlying_iterator last) final {
if(auto &reg = owner_or_assert(); destruction.empty()) {
underlying_type::pop(first, last);
@@ -56,16 +99,18 @@ class basic_sigh_mixin final: public Type {
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) {
if constexpr(std::is_same_v<typename underlying_type::element_type, entity_type>) {
for(typename underlying_type::size_type pos{}, last = underlying_type::free_list(); pos < last; ++pos) {
destruction.publish(reg, underlying_type::base_type::operator[](pos));
}
} else {
for(auto entt: static_cast<typename underlying_type::base_type &>(*this)) {
if constexpr(underlying_type::storage_policy == deletion_policy::in_place) {
if(entt != tombstone) {
destruction.publish(reg, entt);
}
} else {
destruction.publish(reg, *it);
destruction.publish(reg, entt);
}
}
}
@@ -84,6 +129,11 @@ class basic_sigh_mixin final: public Type {
return it;
}
void bind_any(any value) noexcept final {
owner = internal::any_to_owner<registry_type>(value);
underlying_type::bind_any(std::move(value));
}
public:
/*! @brief Allocator type. */
using allocator_type = typename underlying_type::allocator_type;
@@ -105,7 +155,22 @@ public:
owner{},
construction{allocator},
destruction{allocator},
update{allocator} {}
update{allocator} {
if constexpr(internal::has_on_construct<typename underlying_type::element_type, Registry>::value) {
entt::sink{construction}.template connect<&underlying_type::element_type::on_construct>();
}
if constexpr(internal::has_on_update<typename underlying_type::element_type, Registry>::value) {
entt::sink{update}.template connect<&underlying_type::element_type::on_update>();
}
if constexpr(internal::has_on_destroy<typename underlying_type::element_type, Registry>::value) {
entt::sink{destruction}.template connect<&underlying_type::element_type::on_destroy>();
}
}
/*! @brief Default copy constructor, deleted on purpose. */
basic_sigh_mixin(const basic_sigh_mixin &) = delete;
/**
* @brief Move constructor.
@@ -123,24 +188,29 @@ public:
* @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
basic_sigh_mixin(basic_sigh_mixin &&other, const allocator_type &allocator)
: underlying_type{std::move(other), allocator},
owner{other.owner},
construction{std::move(other.construction), allocator},
destruction{std::move(other.destruction), allocator},
update{std::move(other.update), allocator} {}
/*! @brief Default destructor. */
~basic_sigh_mixin() override = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This mixin.
*/
basic_sigh_mixin &operator=(const basic_sigh_mixin &) = delete;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This storage.
* @return This mixin.
*/
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);
swap(other);
return *this;
}
@@ -148,13 +218,13 @@ public:
* @brief Exchanges the contents with those of a given storage.
* @param other Storage to exchange the content with.
*/
void swap(basic_sigh_mixin &other) {
void swap(basic_sigh_mixin &other) noexcept {
using std::swap;
underlying_type::swap(other);
swap(owner, other.owner);
swap(construction, other.construction);
swap(destruction, other.destruction);
swap(update, other.update);
underlying_type::swap(other);
}
/**
@@ -202,6 +272,27 @@ public:
return sink{destruction};
}
/**
* @brief Checks if a mixin refers to a valid registry.
* @return True if the mixin refers to a valid registry, false otherwise.
*/
[[nodiscard]] explicit operator bool() const noexcept {
return (owner != nullptr);
}
/**
* @brief Returns a pointer to the underlying registry, if any.
* @return A pointer to the underlying registry, if any.
*/
[[nodiscard]] const registry_type &registry() const noexcept {
return owner_or_assert();
}
/*! @copydoc registry */
[[nodiscard]] registry_type &registry() noexcept {
return owner_or_assert();
}
/**
* @brief Emplace elements into a storage.
*
@@ -209,7 +300,7 @@ public:
* (for example, components vs entities).<br/>
* Refer to the specific documentation for more details.
*
* @return A return value as returned by the underlying storage.
* @return Whatever the underlying storage returns.
*/
auto emplace() {
const auto entt = underlying_type::emplace();
@@ -227,11 +318,12 @@ public:
* @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.
* @return Whatever the underlying storage returns.
*/
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>) {
std::conditional_t<std::is_same_v<typename underlying_type::element_type, entity_type>, entity_type, decltype(std::declval<underlying_type>().get({}))>
emplace(const entity_type hint, Args &&...args) {
if constexpr(std::is_same_v<typename underlying_type::element_type, entity_type>) {
const auto entt = underlying_type::emplace(hint, std::forward<Args>(args)...);
construction.publish(owner_or_assert(), entt);
return entt;
@@ -271,25 +363,16 @@ public:
*/
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(; first != last; ++first) {
construction.publish(reg, *first);
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;
@@ -297,6 +380,194 @@ private:
sigh_type update;
};
/**
* @brief Mixin type used to add _reactive_ support to storage types.
* @tparam Type Underlying storage type.
* @tparam Registry Basic registry type.
*/
template<typename Type, typename Registry>
class basic_reactive_mixin final: public Type {
using underlying_type = Type;
using owner_type = Registry;
using basic_registry_type = basic_registry<typename owner_type::entity_type, typename owner_type::allocator_type>;
static_assert(std::is_base_of_v<basic_registry_type, owner_type>, "Invalid registry type");
[[nodiscard]] auto &owner_or_assert() const noexcept {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
return static_cast<owner_type &>(*owner);
}
void emplace_element(const Registry &, typename underlying_type::entity_type entity) {
if(!underlying_type::contains(entity)) {
underlying_type::emplace(entity);
}
}
private:
void bind_any(any value) noexcept final {
owner = internal::any_to_owner<registry_type>(value);
underlying_type::bind_any(std::move(value));
}
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_reactive_mixin()
: basic_reactive_mixin{allocator_type{}} {}
/**
* @brief Constructs an empty storage with a given allocator.
* @param allocator The allocator to use.
*/
explicit basic_reactive_mixin(const allocator_type &allocator)
: underlying_type{allocator},
owner{} {
}
/*! @brief Default copy constructor, deleted on purpose. */
basic_reactive_mixin(const basic_reactive_mixin &) = delete;
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
basic_reactive_mixin(basic_reactive_mixin &&other) noexcept
: underlying_type{std::move(other)},
owner{other.owner} {}
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
basic_reactive_mixin(basic_reactive_mixin &&other, const allocator_type &allocator)
: underlying_type{std::move(other), allocator},
owner{other.owner} {}
/*! @brief Default destructor. */
~basic_reactive_mixin() override = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This mixin.
*/
basic_reactive_mixin &operator=(const basic_reactive_mixin &) = delete;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This mixin.
*/
basic_reactive_mixin &operator=(basic_reactive_mixin &&other) noexcept {
swap(other);
return *this;
}
/**
* @brief Exchanges the contents with those of a given storage.
* @param other Storage to exchange the content with.
*/
void swap(basic_reactive_mixin &other) noexcept {
using std::swap;
swap(owner, other.owner);
underlying_type::swap(other);
}
/**
* @brief Makes storage _react_ to creation of objects of the given type.
* @tparam Clazz Type of element to _react_ to.
* @tparam Candidate Function to use to _react_ to the event.
* @param id Optional name used to map the storage within the registry.
* @return This mixin.
*/
template<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
basic_reactive_mixin &on_construct(const id_type id = type_hash<Clazz>::value()) {
owner_or_assert().template storage<Clazz>(id).on_construct().template connect<Candidate>(*this);
return *this;
}
/**
* @brief Makes storage _react_ to update of objects of the given type.
* @tparam Clazz Type of element to _react_ to.
* @tparam Candidate Function to use to _react_ to the event.
* @param id Optional name used to map the storage within the registry.
* @return This mixin.
*/
template<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
basic_reactive_mixin &on_update(const id_type id = type_hash<Clazz>::value()) {
owner_or_assert().template storage<Clazz>(id).on_update().template connect<Candidate>(*this);
return *this;
}
/**
* @brief Makes storage _react_ to destruction of objects of the given type.
* @tparam Clazz Type of element to _react_ to.
* @tparam Candidate Function to use to _react_ to the event.
* @param id Optional name used to map the storage within the registry.
* @return This mixin.
*/
template<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
basic_reactive_mixin &on_destroy(const id_type id = type_hash<Clazz>::value()) {
owner_or_assert().template storage<Clazz>(id).on_destroy().template connect<Candidate>(*this);
return *this;
}
/**
* @brief Checks if a mixin refers to a valid registry.
* @return True if the mixin refers to a valid registry, false otherwise.
*/
[[nodiscard]] explicit operator bool() const noexcept {
return (owner != nullptr);
}
/**
* @brief Returns a pointer to the underlying registry, if any.
* @return A pointer to the underlying registry, if any.
*/
[[nodiscard]] const registry_type &registry() const noexcept {
return owner_or_assert();
}
/*! @copydoc registry */
[[nodiscard]] registry_type &registry() noexcept {
return owner_or_assert();
}
/**
* @brief Returns a view that is filtered by the underlying storage.
* @tparam Get Types of elements used to construct the view.
* @tparam Exclude Types of elements used to filter the view.
* @return A newly created view.
*/
template<typename... Get, typename... Exclude>
[[nodiscard]] basic_view<get_t<const basic_reactive_mixin, typename basic_registry_type::template storage_for_type<const Get>...>, exclude_t<typename basic_registry_type::template storage_for_type<const Exclude>...>>
view(exclude_t<Exclude...> = exclude_t{}) const {
const owner_type &parent = owner_or_assert();
basic_view<get_t<const basic_reactive_mixin, typename basic_registry_type::template storage_for_type<const Get>...>, exclude_t<typename basic_registry_type::template storage_for_type<const Exclude>...>> elem{};
[&elem](const auto *...curr) { ((curr ? elem.storage(*curr) : void()), ...); }(parent.template storage<std::remove_const_t<Exclude>>()..., parent.template storage<std::remove_const_t<Get>>()..., this);
return elem;
}
/*! @copydoc view */
template<typename... Get, typename... Exclude>
[[nodiscard]] basic_view<get_t<const basic_reactive_mixin, typename basic_registry_type::template storage_for_type<Get>...>, exclude_t<typename basic_registry_type::template storage_for_type<Exclude>...>>
view(exclude_t<Exclude...> = exclude_t{}) {
owner_type &parent = owner_or_assert();
return {*this, parent.template storage<std::remove_const_t<Get>>()..., parent.template storage<std::remove_const_t<Exclude>>()...};
}
private:
basic_registry_type *owner;
};
} // namespace entt
#endif

View File

@@ -7,7 +7,6 @@
#include <type_traits>
#include <utility>
#include "../core/type_traits.hpp"
#include "../signal/delegate.hpp"
#include "fwd.hpp"
#include "storage.hpp"
@@ -38,8 +37,8 @@ template<>
struct basic_collector<> {
/**
* @brief Adds a grouping matcher to the collector.
* @tparam AllOf Types of components tracked by the matcher.
* @tparam NoneOf Types of components used to filter out entities.
* @tparam AllOf Types of elements tracked by the matcher.
* @tparam NoneOf Types of elements used to filter out entities.
* @return The updated collector.
*/
template<typename... AllOf, typename... NoneOf>
@@ -49,7 +48,7 @@ struct basic_collector<> {
/**
* @brief Adds an observing matcher to the collector.
* @tparam AnyOf Type of component for which changes should be detected.
* @tparam AnyOf Type of element for which changes should be detected.
* @return The updated collector.
*/
template<typename AnyOf>
@@ -67,14 +66,14 @@ struct basic_collector<> {
* @tparam Other Other matchers.
*/
template<typename... Reject, typename... Require, typename... Rule, typename... Other>
struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule...>, Other...> {
struct [[deprecated("use reactive mixin instead")]] basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule...>, Other...> {
/*! @brief Current matcher. */
using current_type = matcher<type_list<Reject...>, type_list<Require...>, Rule...>;
/**
* @brief Adds a grouping matcher to the collector.
* @tparam AllOf Types of components tracked by the matcher.
* @tparam NoneOf Types of components used to filter out entities.
* @tparam AllOf Types of elements tracked by the matcher.
* @tparam NoneOf Types of elements used to filter out entities.
* @return The updated collector.
*/
template<typename... AllOf, typename... NoneOf>
@@ -84,7 +83,7 @@ struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule
/**
* @brief Adds an observing matcher to the collector.
* @tparam AnyOf Type of component for which changes should be detected.
* @tparam AnyOf Type of element for which changes should be detected.
* @return The updated collector.
*/
template<typename AnyOf>
@@ -94,8 +93,8 @@ struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule
/**
* @brief Updates the filter of the last added matcher.
* @tparam AllOf Types of components required by the matcher.
* @tparam NoneOf Types of components used to filter out entities.
* @tparam AllOf Types of elements required by the matcher.
* @tparam NoneOf Types of elements used to filter out entities.
* @return The updated collector.
*/
template<typename... AllOf, typename... NoneOf>
@@ -114,13 +113,13 @@ inline constexpr basic_collector<> collector{};
* An observer returns all the entities and only the entities that fit the
* requirements of at least one matcher. Moreover, it's guaranteed that the
* entity list is tightly packed in memory for fast iterations.<br/>
* In general, observers don't stay true to the order of any set of components.
* In general, observers don't stay true to the order of any set of elements.
*
* Observers work mainly with two types of matchers, provided through a
* collector:
*
* * Observing matcher: an observer will return at least all the living entities
* for which one or more of the given components have been updated and not yet
* for which one or more of the given elements have been updated and not yet
* destroyed.
* * Grouping matcher: an observer will return at least all the living entities
* that would have entered the given group if it existed and that would have
@@ -140,13 +139,13 @@ inline constexpr basic_collector<> collector{};
*
* Iterators aren't invalidated if:
*
* * New instances of the given components are created and assigned to entities.
* * New instances of the given elements are created and assigned to entities.
* * The entity currently pointed is modified (as an example, if one of the
* given components is removed from the entity to which the iterator points).
* given elements is removed from the entity to which the iterator points).
* * The entity currently pointed is destroyed.
*
* In all the other cases, modifying the pools of the given components in any
* way invalidates all the iterators.
* In all the other cases, modifying the pools of the given elements in any way
* invalidates all the iterators.
*
* @warning
* Lifetime of an observer doesn't necessarily have to overcome that of the
@@ -155,12 +154,19 @@ 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, 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 Registry, typename Allocator>
class basic_observer {
using mask_type = std::uint64_t;
using storage_type = basic_storage<mask_type, typename Registry::entity_type, typename std::allocator_traits<Allocator>::template rebind_alloc<mask_type>>;
template<std::size_t Index>
static void discard_if(storage_type &storage, Registry &, const typename Registry::entity_type entt) {
if(storage.contains(entt) && !(storage.get(entt) &= (~(1 << Index)))) {
storage.erase(entt);
}
}
template<typename>
struct matcher_handler;
@@ -168,108 +174,93 @@ class basic_observer: private basic_storage<Mask, typename Registry::entity_type
template<typename... Reject, typename... Require, typename AnyOf>
struct matcher_handler<matcher<type_list<Reject...>, type_list<Require...>, AnyOf>> {
template<std::size_t Index>
static void maybe_valid_if(basic_observer &obs, Registry &reg, const typename Registry::entity_type entt) {
if(reg.template all_of<Require...>(entt) && !reg.template any_of<Reject...>(entt)) {
if(!obs.contains(entt)) {
obs.emplace(entt);
static void maybe_valid_if(storage_type &storage, Registry &parent, const typename Registry::entity_type entt) {
if(parent.template all_of<Require...>(entt) && !parent.template any_of<Reject...>(entt)) {
if(!storage.contains(entt)) {
storage.emplace(entt);
}
obs.get(entt) |= (1 << Index);
storage.get(entt) |= (1 << Index);
}
}
template<std::size_t Index>
static void discard_if(basic_observer &obs, Registry &, const typename Registry::entity_type entt) {
if(obs.contains(entt) && !(obs.get(entt) &= (~(1 << Index)))) {
obs.erase(entt);
}
static void connect(storage_type &storage, Registry &parent) {
(parent.template on_destroy<Require>().template connect<&discard_if<Index>>(storage), ...);
(parent.template on_construct<Reject>().template connect<&discard_if<Index>>(storage), ...);
parent.template on_update<AnyOf>().template connect<&maybe_valid_if<Index>>(storage);
parent.template on_destroy<AnyOf>().template connect<&discard_if<Index>>(storage);
}
template<std::size_t Index>
static void connect(basic_observer &obs, Registry &reg) {
(reg.template on_destroy<Require>().template connect<&discard_if<Index>>(obs), ...);
(reg.template on_construct<Reject>().template connect<&discard_if<Index>>(obs), ...);
reg.template on_update<AnyOf>().template connect<&maybe_valid_if<Index>>(obs);
reg.template on_destroy<AnyOf>().template connect<&discard_if<Index>>(obs);
}
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);
static void disconnect(storage_type &storage, Registry &parent) {
(parent.template on_destroy<Require>().disconnect(&storage), ...);
(parent.template on_construct<Reject>().disconnect(&storage), ...);
parent.template on_update<AnyOf>().disconnect(&storage);
parent.template on_destroy<AnyOf>().disconnect(&storage);
}
};
template<typename... Reject, typename... Require, typename... NoneOf, typename... AllOf>
struct matcher_handler<matcher<type_list<Reject...>, type_list<Require...>, type_list<NoneOf...>, AllOf...>> {
template<std::size_t Index, typename... Ignore>
static void maybe_valid_if(basic_observer &obs, Registry &reg, const typename Registry::entity_type entt) {
auto condition = [&reg, entt]() {
if constexpr(sizeof...(Ignore) == 0) {
return reg.template all_of<AllOf..., Require...>(entt) && !reg.template any_of<NoneOf..., Reject...>(entt);
} else {
return reg.template all_of<AllOf..., Require...>(entt) && ((std::is_same_v<Ignore..., NoneOf> || !reg.template any_of<NoneOf>(entt)) && ...) && !reg.template any_of<Reject...>(entt);
}
};
static void maybe_valid_if(storage_type &storage, Registry &parent, const typename Registry::entity_type entt) {
bool guard{};
if(condition()) {
if(!obs.contains(entt)) {
obs.emplace(entt);
if constexpr(sizeof...(Ignore) == 0) {
guard = parent.template all_of<AllOf..., Require...>(entt) && !parent.template any_of<NoneOf..., Reject...>(entt);
} else {
guard = parent.template all_of<AllOf..., Require...>(entt) && ((std::is_same_v<Ignore..., NoneOf> || !parent.template any_of<NoneOf>(entt)) && ...) && !parent.template any_of<Reject...>(entt);
}
if(guard) {
if(!storage.contains(entt)) {
storage.emplace(entt);
}
obs.get(entt) |= (1 << Index);
storage.get(entt) |= (1 << Index);
}
}
template<std::size_t Index>
static void discard_if(basic_observer &obs, Registry &, const typename Registry::entity_type entt) {
if(obs.contains(entt) && !(obs.get(entt) &= (~(1 << Index)))) {
obs.erase(entt);
}
static void connect(storage_type &storage, Registry &parent) {
(parent.template on_destroy<Require>().template connect<&discard_if<Index>>(storage), ...);
(parent.template on_construct<Reject>().template connect<&discard_if<Index>>(storage), ...);
(parent.template on_construct<AllOf>().template connect<&maybe_valid_if<Index>>(storage), ...);
(parent.template on_destroy<NoneOf>().template connect<&maybe_valid_if<Index, NoneOf>>(storage), ...);
(parent.template on_destroy<AllOf>().template connect<&discard_if<Index>>(storage), ...);
(parent.template on_construct<NoneOf>().template connect<&discard_if<Index>>(storage), ...);
}
template<std::size_t Index>
static void connect(basic_observer &obs, Registry &reg) {
(reg.template on_destroy<Require>().template connect<&discard_if<Index>>(obs), ...);
(reg.template on_construct<Reject>().template connect<&discard_if<Index>>(obs), ...);
(reg.template on_construct<AllOf>().template connect<&maybe_valid_if<Index>>(obs), ...);
(reg.template on_destroy<NoneOf>().template connect<&maybe_valid_if<Index, NoneOf>>(obs), ...);
(reg.template on_destroy<AllOf>().template connect<&discard_if<Index>>(obs), ...);
(reg.template on_construct<NoneOf>().template connect<&discard_if<Index>>(obs), ...);
}
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), ...);
static void disconnect(storage_type &storage, Registry &parent) {
(parent.template on_destroy<Require>().disconnect(&storage), ...);
(parent.template on_construct<Reject>().disconnect(&storage), ...);
(parent.template on_construct<AllOf>().disconnect(&storage), ...);
(parent.template on_destroy<NoneOf>().disconnect(&storage), ...);
(parent.template on_destroy<AllOf>().disconnect(&storage), ...);
(parent.template on_construct<NoneOf>().disconnect(&storage), ...);
}
};
template<typename... Matcher>
static void disconnect(Registry &reg, basic_observer &obs) {
(matcher_handler<Matcher>::disconnect(obs, reg), ...);
static void disconnect(Registry &parent, storage_type &storage) {
(matcher_handler<Matcher>::disconnect(storage, parent), ...);
}
template<typename... Matcher, std::size_t... Index>
void connect(Registry &reg, std::index_sequence<Index...>) {
static_assert(sizeof...(Matcher) < std::numeric_limits<typename base_type::value_type>::digits, "Too many matchers");
(matcher_handler<Matcher>::template connect<Index>(*this, reg), ...);
release.template connect<&basic_observer::disconnect<Matcher...>>(reg);
static void connect(Registry &parent, storage_type &storage, std::index_sequence<Index...>) {
static_assert(sizeof...(Matcher) < std::numeric_limits<mask_type>::digits, "Too many matchers");
(matcher_handler<Matcher>::template connect<Index>(storage, parent), ...);
}
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! Basic registry type. */
using registry_type = Registry;
/*! @brief Underlying entity identifier. */
using entity_type = typename registry_type::entity_type;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Random access iterator type. */
using iterator = typename registry_type::common_type::iterator;
@@ -282,8 +273,9 @@ public:
* @param allocator The allocator to use.
*/
explicit basic_observer(const allocator_type &allocator)
: base_type{allocator},
release{} {}
: release{},
parent{},
storage{allocator} {}
/*! @brief Default copy constructor, deleted on purpose. */
basic_observer(const basic_observer &) = delete;
@@ -299,10 +291,15 @@ public:
*/
template<typename... Matcher>
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...>{});
: release{&basic_observer::disconnect<Matcher...>},
parent{&reg},
storage{allocator} {
connect<Matcher...>(reg, storage, std::index_sequence_for<Matcher...>{});
}
/*! @brief Default destructor. */
~basic_observer() = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This observer.
@@ -323,15 +320,18 @@ public:
template<typename... Matcher>
void connect(registry_type &reg, basic_collector<Matcher...>) {
disconnect();
connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{});
base_type::clear();
storage.clear();
parent = &reg;
release = &basic_observer::disconnect<Matcher...>;
connect<Matcher...>(reg, storage, std::index_sequence_for<Matcher...>{});
}
/*! @brief Disconnects an observer from the registry it keeps track of. */
void disconnect() {
if(release) {
release(*this);
release.reset();
release(*parent, storage);
release = nullptr;
}
}
@@ -340,7 +340,7 @@ public:
* @return Number of elements.
*/
[[nodiscard]] size_type size() const noexcept {
return base_type::size();
return storage.size();
}
/**
@@ -348,7 +348,7 @@ public:
* @return True if the observer is empty, false otherwise.
*/
[[nodiscard]] bool empty() const noexcept {
return base_type::empty();
return storage.empty();
}
/**
@@ -364,7 +364,7 @@ public:
* @return A pointer to the array of entities.
*/
[[nodiscard]] const entity_type *data() const noexcept {
return base_type::data();
return storage.data();
}
/**
@@ -375,7 +375,7 @@ public:
* @return An iterator to the first entity of the observer.
*/
[[nodiscard]] iterator begin() const noexcept {
return base_type::base_type::begin();
return storage.storage_type::base_type::begin();
}
/**
@@ -384,12 +384,12 @@ public:
* observer.
*/
[[nodiscard]] iterator end() const noexcept {
return base_type::base_type::end();
return storage.storage_type::base_type::end();
}
/*! @brief Clears the underlying container. */
void clear() noexcept {
base_type::clear();
storage.clear();
}
/**
@@ -428,7 +428,9 @@ public:
}
private:
delegate<void(basic_observer &)> release;
void (*release)(registry_type &, storage_type &);
registry_type *parent;
storage_type storage;
};
} // namespace entt

View File

@@ -52,8 +52,8 @@ struct unpack_type<const basic_registry<Args...>, type_list<Override...>>
template<typename... Get, typename... Exclude, typename... Override>
struct unpack_type<basic_view<get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>> {
using ro = type_list_cat_t<type_list<typename Exclude::value_type...>, typename unpack_type<constness_as_t<typename Get::value_type, Get>, type_list<Override...>>::ro...>;
using rw = type_list_cat_t<typename unpack_type<constness_as_t<typename Get::value_type, Get>, type_list<Override...>>::rw...>;
using ro = type_list_cat_t<type_list<typename Exclude::element_type...>, typename unpack_type<constness_as_t<typename Get::element_type, Get>, type_list<Override...>>::ro...>;
using rw = type_list_cat_t<typename unpack_type<constness_as_t<typename Get::element_type, Get>, type_list<Override...>>::rw...>;
};
template<typename... Get, typename... Exclude, typename... Override>
@@ -108,7 +108,7 @@ class basic_organizer final {
const char *name{};
const void *payload{};
callback_type *callback{};
dependency_type *dependency;
dependency_type *dependency{};
prepare_type *prepare{};
const type_info *info{};
};
@@ -130,14 +130,16 @@ class basic_organizer final {
}
template<typename... Type>
static std::size_t fill_dependencies(type_list<Type...>, [[maybe_unused]] const type_info **buffer, [[maybe_unused]] const std::size_t count) {
[[nodiscard]] static std::size_t fill_dependencies(type_list<Type...>, [[maybe_unused]] const type_info **buffer, [[maybe_unused]] const std::size_t count) {
if constexpr(sizeof...(Type) == 0u) {
return {};
} else {
const type_info *info[sizeof...(Type)]{&type_id<Type>()...};
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
const type_info *info[]{&type_id<Type>()...};
const auto length = count < sizeof...(Type) ? count : sizeof...(Type);
for(std::size_t pos{}; pos < length; ++pos) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
buffer[pos] = info[pos];
}
@@ -167,14 +169,14 @@ public:
struct vertex {
/**
* @brief Constructs a vertex of the task graph.
* @param vtype True if the vertex is a top-level one, false otherwise.
* @param data The data associated with the vertex.
* @param edges The indices of the children in the adjacency list.
* @param from List of in-edges of the vertex.
* @param to List of out-edges of the vertex.
*/
vertex(const bool vtype, vertex_data data, std::vector<std::size_t> edges)
: is_top_level{vtype},
node{std::move(data)},
reachable{std::move(edges)} {}
vertex(vertex_data data, std::vector<std::size_t> from, std::vector<std::size_t> to)
: node{std::move(data)},
in{std::move(from)},
out{std::move(to)} {}
/**
* @brief Fills a buffer with the type info objects for the writable
@@ -183,7 +185,7 @@ public:
* @param length The length of the user-supplied buffer.
* @return The number of type info objects written to the buffer.
*/
size_type ro_dependency(const type_info **buffer, const std::size_t length) const noexcept {
[[nodiscard]] size_type ro_dependency(const type_info **buffer, const std::size_t length) const noexcept {
return node.dependency(false, buffer, length);
}
@@ -194,7 +196,7 @@ public:
* @param length The length of the user-supplied buffer.
* @return The number of type info objects written to the buffer.
*/
size_type rw_dependency(const type_info **buffer, const std::size_t length) const noexcept {
[[nodiscard]] size_type rw_dependency(const type_info **buffer, const std::size_t length) const noexcept {
return node.dependency(true, buffer, length);
}
@@ -202,7 +204,7 @@ public:
* @brief Returns the number of read-only resources of a vertex.
* @return The number of read-only resources of the vertex.
*/
size_type ro_count() const noexcept {
[[nodiscard]] size_type ro_count() const noexcept {
return node.ro_count;
}
@@ -210,7 +212,7 @@ public:
* @brief Returns the number of writable resources of a vertex.
* @return The number of writable resources of the vertex.
*/
size_type rw_count() const noexcept {
[[nodiscard]] size_type rw_count() const noexcept {
return node.rw_count;
}
@@ -218,15 +220,15 @@ public:
* @brief Checks if a vertex is also a top-level one.
* @return True if the vertex is a top-level one, false otherwise.
*/
bool top_level() const noexcept {
return is_top_level;
[[nodiscard]] bool top_level() const noexcept {
return in.empty();
}
/**
* @brief Returns a type info object associated with a vertex.
* @return A properly initialized type info object.
*/
const type_info &info() const noexcept {
[[nodiscard]] const type_info &info() const noexcept {
return *node.info;
}
@@ -234,7 +236,7 @@ public:
* @brief Returns a user defined name associated with a vertex, if any.
* @return The user defined name associated with the vertex, if any.
*/
const char *name() const noexcept {
[[nodiscard]] const char *name() const noexcept {
return node.name;
}
@@ -242,7 +244,7 @@ public:
* @brief Returns the function associated with a vertex.
* @return The function associated with the vertex.
*/
function_type *callback() const noexcept {
[[nodiscard]] function_type *callback() const noexcept {
return node.callback;
}
@@ -250,16 +252,32 @@ public:
* @brief Returns the payload associated with a vertex, if any.
* @return The payload associated with the vertex, if any.
*/
const void *data() const noexcept {
[[nodiscard]] const void *data() const noexcept {
return node.payload;
}
/**
* @brief Returns the list of in-edges of a vertex.
* @return The list of in-edges of a vertex.
*/
[[nodiscard]] const std::vector<std::size_t> &in_edges() const noexcept {
return in;
}
/**
* @brief Returns the list of out-edges of a vertex.
* @return The list of out-edges of a vertex.
*/
[[nodiscard]] const std::vector<std::size_t> &out_edges() const noexcept {
return out;
}
/**
* @brief Returns the list of nodes reachable from a given vertex.
* @return The list of nodes reachable from the vertex.
*/
const std::vector<std::size_t> &children() const noexcept {
return reachable;
[[deprecated("use ::out_edges")]] [[nodiscard]] const std::vector<std::size_t> &children() const noexcept {
return out_edges();
}
/**
@@ -272,9 +290,9 @@ public:
}
private:
bool is_top_level;
vertex_data node;
std::vector<std::size_t> reachable;
std::vector<std::size_t> in;
std::vector<std::size_t> out;
};
/**
@@ -369,20 +387,24 @@ public:
* @brief Generates a task graph for the current content.
* @return The adjacency list of the task graph.
*/
std::vector<vertex> graph() {
[[nodiscard]] std::vector<vertex> graph() {
std::vector<vertex> adjacency_list{};
adjacency_list.reserve(vertices.size());
auto adjacency_matrix = builder.graph();
for(auto curr: adjacency_matrix.vertices()) {
const auto iterable = adjacency_matrix.in_edges(curr);
std::vector<std::size_t> reachable{};
std::vector<std::size_t> in{};
std::vector<std::size_t> out{};
for(auto &&edge: adjacency_matrix.out_edges(curr)) {
reachable.push_back(edge.second);
for(auto &&edge: adjacency_matrix.in_edges(curr)) {
in.push_back(edge.first);
}
adjacency_list.emplace_back(iterable.cbegin() == iterable.cend(), vertices[curr], std::move(reachable));
for(auto &&edge: adjacency_matrix.out_edges(curr)) {
out.push_back(edge.second);
}
adjacency_list.emplace_back(vertices[curr], std::move(in), std::move(out));
}
return adjacency_list;

View File

@@ -0,0 +1,26 @@
#ifndef ENTT_ENTITY_RANGES_HPP
#define ENTT_ENTITY_RANGES_HPP
#if __has_include(<version>)
# include <version>
#
# if defined(__cpp_lib_ranges)
# include <ranges>
# include "fwd.hpp"
template<class... Args>
inline constexpr bool std::ranges::enable_borrowed_range<entt::basic_view<Args...>>{true};
template<class... Args>
inline constexpr bool std::ranges::enable_borrowed_range<entt::basic_group<Args...>>{true};
template<class... Args>
inline constexpr bool std::ranges::enable_view<entt::basic_view<Args...>>{true};
template<class... Args>
inline constexpr bool std::ranges::enable_view<entt::basic_group<Args...>>{true};
# endif
#endif
#endif

View File

@@ -2,6 +2,7 @@
#define ENTT_ENTITY_REGISTRY_HPP
#include <algorithm>
#include <array>
#include <cstddef>
#include <functional>
#include <iterator>
@@ -99,7 +100,7 @@ public:
}
[[nodiscard]] constexpr reference operator*() const noexcept {
return {it->first, *it->second};
return operator[](0);
}
[[nodiscard]] constexpr pointer operator->() const noexcept {
@@ -218,7 +219,7 @@ public:
}
private:
dense_map<id_type, basic_any<0u>, identity, std::equal_to<id_type>, allocator_type> ctx;
dense_map<id_type, basic_any<0u>, identity, std::equal_to<>, allocator_type> ctx;
};
} // namespace internal
@@ -232,39 +233,42 @@ private:
template<typename Entity, typename Allocator>
class basic_registry {
using base_type = basic_sparse_set<Entity, Allocator>;
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, Entity>, "Invalid value type");
// std::shared_ptr because of its type erased allocator which is useful here
using pool_container_type = dense_map<id_type, std::shared_ptr<base_type>, identity, std::equal_to<id_type>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, std::shared_ptr<base_type>>>>;
using group_container_type = dense_map<id_type, std::shared_ptr<internal::group_descriptor>, identity, std::equal_to<id_type>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, std::shared_ptr<internal::group_descriptor>>>>;
using pool_container_type = dense_map<id_type, std::shared_ptr<base_type>, identity, std::equal_to<>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, std::shared_ptr<base_type>>>>;
using group_container_type = dense_map<id_type, std::shared_ptr<internal::group_descriptor>, identity, std::equal_to<>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, std::shared_ptr<internal::group_descriptor>>>>;
using traits_type = entt_traits<Entity>;
template<typename Type>
[[nodiscard]] auto &assure([[maybe_unused]] const id_type id = type_hash<Type>::value()) {
static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Non-decayed types not allowed");
if constexpr(std::is_same_v<Type, entity_type>) {
ENTT_ASSERT(id == type_hash<Type>::value(), "User entity storage not allowed");
return entities;
} else {
auto &cpool = pools[id];
using storage_type = storage_for_type<Type>;
if(!cpool) {
using storage_type = storage_for_type<Type>;
using alloc_type = typename storage_type::allocator_type;
if constexpr(std::is_void_v<Type> && !std::is_constructible_v<alloc_type, allocator_type>) {
// std::allocator<void> has no cross constructors (waiting for C++20)
cpool = std::allocate_shared<storage_type>(get_allocator(), alloc_type{});
} else {
cpool = std::allocate_shared<storage_type>(get_allocator(), get_allocator());
}
cpool->bind(forward_as_any(*this));
if(auto it = pools.find(id); it != pools.cend()) {
ENTT_ASSERT(it->second->type() == type_id<Type>(), "Unexpected type");
return static_cast<storage_type &>(*it->second);
}
ENTT_ASSERT(cpool->type() == type_id<Type>(), "Unexpected type");
return static_cast<storage_for_type<Type> &>(*cpool);
using alloc_type = typename storage_type::allocator_type;
typename pool_container_type::mapped_type cpool{};
if constexpr(std::is_void_v<Type> && !std::is_constructible_v<alloc_type, allocator_type>) {
// std::allocator<void> has no cross constructors (waiting for C++20)
cpool = std::allocate_shared<storage_type>(get_allocator(), alloc_type{});
} else {
cpool = std::allocate_shared<storage_type>(get_allocator(), get_allocator());
}
pools.emplace(id, cpool);
cpool->bind(*this);
return static_cast<storage_type &>(*cpool);
}
}
@@ -273,6 +277,7 @@ class basic_registry {
static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Non-decayed types not allowed");
if constexpr(std::is_same_v<Type, entity_type>) {
ENTT_ASSERT(id == type_hash<Type>::value(), "User entity storage not allowed");
return &entities;
} else {
if(const auto it = pools.find(id); it != pools.cend()) {
@@ -285,16 +290,14 @@ class basic_registry {
}
void rebind() {
entities.bind(forward_as_any(*this));
entities.bind(*this);
for(auto &&curr: pools) {
curr.second->bind(forward_as_any(*this));
curr.second->bind(*this);
}
}
public:
/*! @brief Entity traits. */
using traits_type = typename base_type::traits_type;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Underlying entity identifier. */
@@ -344,6 +347,9 @@ public:
rebind();
}
/*! @brief Default copy constructor, deleted on purpose. */
basic_registry(const basic_registry &) = delete;
/**
* @brief Move constructor.
* @param other The instance to move from.
@@ -356,19 +362,22 @@ public:
rebind();
}
/*! @brief Default destructor. */
~basic_registry() = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This mixin.
*/
basic_registry &operator=(const basic_registry &) = delete;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This registry.
*/
basic_registry &operator=(basic_registry &&other) noexcept {
vars = std::move(other.vars);
pools = std::move(other.pools);
groups = std::move(other.groups);
entities = std::move(other.entities);
rebind();
swap(other);
return *this;
}
@@ -376,7 +385,7 @@ public:
* @brief Exchanges the contents with those of a given registry.
* @param other Registry to exchange the content with.
*/
void swap(basic_registry &other) {
void swap(basic_registry &other) noexcept {
using std::swap;
swap(vars, other.vars);
@@ -405,12 +414,12 @@ public:
* @return An iterable object to use to _visit_ the registry.
*/
[[nodiscard]] iterable storage() noexcept {
return iterable_adaptor{internal::registry_storage_iterator{pools.begin()}, internal::registry_storage_iterator{pools.end()}};
return iterable{pools.begin(), pools.end()};
}
/*! @copydoc storage */
[[nodiscard]] const_iterable storage() const noexcept {
return iterable_adaptor{internal::registry_storage_iterator{pools.cbegin()}, internal::registry_storage_iterator{pools.cend()}};
return const_iterable{pools.cbegin(), pools.cend()};
}
/**
@@ -433,10 +442,10 @@ public:
}
/**
* @brief Returns the storage for a given component type.
* @tparam Type Type of component of which to return the storage.
* @brief Returns the storage for a given element type.
* @tparam Type Type of element of which to return the storage.
* @param id Optional name used to map the storage within the registry.
* @return The storage for the given component type.
* @return The storage for the given element type.
*/
template<typename Type>
storage_for_type<Type> &storage(const id_type id = type_hash<Type>::value()) {
@@ -444,23 +453,33 @@ public:
}
/**
* @brief Returns the storage for a given component type, if any.
* @tparam Type Type of component of which to return the storage.
* @brief Returns the storage for a given element type, if any.
* @tparam Type Type of element of which to return the storage.
* @param id Optional name used to map the storage within the registry.
* @return The storage for the given component type.
* @return The storage for the given element type.
*/
template<typename Type>
const storage_for_type<Type> *storage(const id_type id = type_hash<Type>::value()) const {
[[nodiscard]] const storage_for_type<Type> *storage(const id_type id = type_hash<Type>::value()) const {
return assure<Type>(id);
}
/**
* @brief Discards the storage associated with a given name, if any.
* @param id Name used to map the storage within the registry.
* @return True in case of success, false otherwise.
*/
bool reset(const id_type id) {
ENTT_ASSERT(id != type_hash<entity_type>::value(), "Cannot reset entity storage");
return !(pools.erase(id) == 0u);
}
/**
* @brief Checks if an identifier refers to a valid entity.
* @param entt An identifier, either valid or not.
* @return True if the identifier is valid, false otherwise.
*/
[[nodiscard]] bool valid(const entity_type entt) const {
return entities.contains(entt) && (entities.index(entt) < entities.free_list());
return static_cast<size_type>(entities.find(entt).index()) < entities.free_list();
}
/**
@@ -512,14 +531,14 @@ public:
* @brief Destroys an entity and releases its identifier.
*
* @warning
* Adding or removing components to an entity that is being destroyed can
* Adding or removing elements to an entity that is being destroyed can
* result in undefined behavior.
*
* @param entt A valid identifier.
* @return The version of the recycled entity.
*/
version_type destroy(const entity_type entt) {
for(size_type pos = pools.size(); pos; --pos) {
for(size_type pos = pools.size(); pos != 0u; --pos) {
pools.begin()[pos - 1u].second->remove(entt);
}
@@ -556,10 +575,8 @@ public:
*/
template<typename It>
void destroy(It first, It last) {
entities.sort_as(first, last);
const auto from = entities.cbegin(0);
const auto to = from + std::distance(first, last);
const auto to = entities.sort_as(first, last);
const auto from = entities.cend() - entities.free_list();
for(auto &&curr: pools) {
curr.second->remove(from, to);
@@ -569,81 +586,82 @@ public:
}
/**
* @brief Assigns the given component to an entity.
* @brief Assigns the given element to an entity.
*
* The component must have a proper constructor or be of aggregate type.
* The element must have a proper constructor or be of aggregate type.
*
* @warning
* Attempting to assign a component to an entity that already owns it
* results in undefined behavior.
* Attempting to assign an element to an entity that already owns it results
* in undefined behavior.
*
* @tparam Type Type of component to create.
* @tparam Args Types of arguments to use to construct the component.
* @tparam Type Type of element to create.
* @tparam Args Types of arguments to use to construct the element.
* @param entt A valid identifier.
* @param args Parameters to use to initialize the component.
* @return A reference to the newly created component.
* @param args Parameters to use to initialize the element.
* @return A reference to the newly created element.
*/
template<typename Type, typename... Args>
decltype(auto) emplace(const entity_type entt, Args &&...args) {
ENTT_ASSERT(valid(entt), "Invalid entity");
return assure<Type>().emplace(entt, std::forward<Args>(args)...);
}
/**
* @brief Assigns each entity in a range the given component.
* @brief Assigns each entity in a range the given element.
*
* @sa emplace
*
* @tparam Type Type of component to create.
* @tparam Type Type of element to create.
* @tparam It Type of input iterator.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @param value An instance of the component to assign.
* @param value An instance of the element to assign.
*/
template<typename Type, typename It>
void insert(It first, It last, const Type &value = {}) {
ENTT_ASSERT(std::all_of(first, last, [this](const auto entt) { return valid(entt); }), "Invalid entity");
assure<Type>().insert(std::move(first), std::move(last), value);
}
/**
* @brief Assigns each entity in a range the given components.
* @brief Assigns each entity in a range the given elements.
*
* @sa emplace
*
* @tparam Type Type of component to create.
* @tparam Type Type of element to create.
* @tparam EIt Type of input iterator.
* @tparam CIt Type of input iterator.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @param from An iterator to the first element of the range of components.
* @param from An iterator to the first element of the range of elements.
*/
template<typename Type, typename EIt, typename CIt, typename = std::enable_if_t<std::is_same_v<typename std::iterator_traits<CIt>::value_type, Type>>>
void insert(EIt first, EIt last, CIt from) {
ENTT_ASSERT(std::all_of(first, last, [this](const auto entt) { return valid(entt); }), "Invalid entity");
assure<Type>().insert(first, last, from);
}
/**
* @brief Assigns or replaces the given component for an entity.
* @brief Assigns or replaces the given element for an entity.
*
* @sa emplace
* @sa replace
*
* @tparam Type Type of component to assign or replace.
* @tparam Args Types of arguments to use to construct the component.
* @tparam Type Type of element to assign or replace.
* @tparam Args Types of arguments to use to construct the element.
* @param entt A valid identifier.
* @param args Parameters to use to initialize the component.
* @return A reference to the newly created component.
* @param args Parameters to use to initialize the element.
* @return A reference to the newly created element.
*/
template<typename Type, typename... Args>
decltype(auto) emplace_or_replace(const entity_type entt, Args &&...args) {
if(auto &cpool = assure<Type>(); cpool.contains(entt)) {
return cpool.patch(entt, [&args...](auto &...curr) { ((curr = Type{std::forward<Args>(args)...}), ...); });
} else {
return cpool.emplace(entt, std::forward<Args>(args)...);
}
auto &cpool = assure<Type>();
ENTT_ASSERT(valid(entt), "Invalid entity");
return cpool.contains(entt) ? cpool.patch(entt, [&args...](auto &...curr) { ((curr = Type{std::forward<Args>(args)...}), ...); }) : cpool.emplace(entt, std::forward<Args>(args)...);
}
/**
* @brief Patches the given component for an entity.
* @brief Patches the given element for an entity.
*
* The signature of the function should be equivalent to the following:
*
@@ -652,14 +670,14 @@ public:
* @endcode
*
* @warning
* Attempting to patch a component of an entity that doesn't own it
* results in undefined behavior.
* Attempting to patch an element of an entity that doesn't own it results
* in undefined behavior.
*
* @tparam Type Type of component to patch.
* @tparam Type Type of element to patch.
* @tparam Func Types of the function objects to invoke.
* @param entt A valid identifier.
* @param func Valid function objects.
* @return A reference to the patched component.
* @return A reference to the patched element.
*/
template<typename Type, typename... Func>
decltype(auto) patch(const entity_type entt, Func &&...func) {
@@ -667,19 +685,19 @@ public:
}
/**
* @brief Replaces the given component for an entity.
* @brief Replaces the given element for an entity.
*
* The component must have a proper constructor or be of aggregate type.
* The element must have a proper constructor or be of aggregate type.
*
* @warning
* Attempting to replace a component of an entity that doesn't own it
* results in undefined behavior.
* Attempting to replace an element of an entity that doesn't own it results
* in undefined behavior.
*
* @tparam Type Type of component to replace.
* @tparam Args Types of arguments to use to construct the component.
* @tparam Type Type of element to replace.
* @tparam Args Types of arguments to use to construct the element.
* @param entt A valid identifier.
* @param args Parameters to use to initialize the component.
* @return A reference to the component being replaced.
* @param args Parameters to use to initialize the element.
* @return A reference to the element being replaced.
*/
template<typename Type, typename... Args>
decltype(auto) replace(const entity_type entt, Args &&...args) {
@@ -687,11 +705,11 @@ public:
}
/**
* @brief Removes the given components from an entity.
* @tparam Type Type of component to remove.
* @tparam Other Other types of components to remove.
* @brief Removes the given elements from an entity.
* @tparam Type Type of element to remove.
* @tparam Other Other types of elements to remove.
* @param entt A valid identifier.
* @return The number of components actually removed.
* @return The number of elements actually removed.
*/
template<typename Type, typename... Other>
size_type remove(const entity_type entt) {
@@ -699,33 +717,34 @@ public:
}
/**
* @brief Removes the given components from all the entities in a range.
* @brief Removes the given elements from all the entities in a range.
*
* @sa remove
*
* @tparam Type Type of component to remove.
* @tparam Other Other types of components to remove.
* @tparam Type Type of element to remove.
* @tparam Other Other types of elements to remove.
* @tparam It Type of input iterator.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @return The number of components actually removed.
* @return The number of elements actually removed.
*/
template<typename Type, typename... Other, typename It>
size_type remove(It first, It last) {
size_type count{};
if constexpr(std::is_same_v<It, typename common_type::iterator>) {
common_type *cpools[sizeof...(Other) + 1u]{&assure<Type>(), &assure<Other>()...};
std::array cpools{static_cast<common_type *>(&assure<Type>()), static_cast<common_type *>(&assure<Other>())...};
for(size_type pos{}, len = sizeof...(Other) + 1u; pos < len; ++pos) {
for(auto from = cpools.begin(), to = cpools.end(); from != to; ++from) {
if constexpr(sizeof...(Other) != 0u) {
if(cpools[pos]->data() == first.data()) {
std::swap(cpools[pos], cpools[sizeof...(Other)]);
if((*from)->data() == first.data()) {
std::swap((*from), cpools.back());
}
}
count += cpools[pos]->remove(first, last);
count += (*from)->remove(first, last);
}
} else {
for(auto cpools = std::forward_as_tuple(assure<Type>(), assure<Other>()...); first != last; ++first) {
count += std::apply([entt = *first](auto &...curr) { return (curr.remove(entt) + ... + 0u); }, cpools);
@@ -736,14 +755,14 @@ public:
}
/**
* @brief Erases the given components from an entity.
* @brief Erases the given elements from an entity.
*
* @warning
* Attempting to erase a component from an entity that doesn't own it
* results in undefined behavior.
* Attempting to erase an element from an entity that doesn't own it results
* in undefined behavior.
*
* @tparam Type Types of components to erase.
* @tparam Other Other types of components to erase.
* @tparam Type Types of elements to erase.
* @tparam Other Other types of elements to erase.
* @param entt A valid identifier.
*/
template<typename Type, typename... Other>
@@ -752,12 +771,12 @@ public:
}
/**
* @brief Erases the given components from all the entities in a range.
* @brief Erases the given elements from all the entities in a range.
*
* @sa erase
*
* @tparam Type Types of components to erase.
* @tparam Other Other types of components to erase.
* @tparam Type Types of elements to erase.
* @tparam Other Other types of elements to erase.
* @tparam It Type of input iterator.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
@@ -765,16 +784,16 @@ public:
template<typename Type, typename... Other, typename It>
void erase(It first, It last) {
if constexpr(std::is_same_v<It, typename common_type::iterator>) {
common_type *cpools[sizeof...(Other) + 1u]{&assure<Type>(), &assure<Other>()...};
std::array cpools{static_cast<common_type *>(&assure<Type>()), static_cast<common_type *>(&assure<Other>())...};
for(size_type pos{}, len = sizeof...(Other) + 1u; pos < len; ++pos) {
for(auto from = cpools.begin(), to = cpools.end(); from != to; ++from) {
if constexpr(sizeof...(Other) != 0u) {
if(cpools[pos]->data() == first.data()) {
std::swap(cpools[pos], cpools[sizeof...(Other)]);
if((*from)->data() == first.data()) {
std::swap(*from, cpools.back());
}
}
cpools[pos]->erase(first, last);
(*from)->erase(first, last);
}
} else {
for(auto cpools = std::forward_as_tuple(assure<Type>(), assure<Other>()...); first != last; ++first) {
@@ -784,12 +803,12 @@ public:
}
/**
* @brief Erases components satisfying specific criteria from an entity.
* @brief Erases elements satisfying specific criteria from an entity.
*
* The function type is equivalent to:
*
* @code{.cpp}
* void(const id_type, typename basic_registry<Entity>::base_type &);
* void(const id_type, typename basic_registry<Entity>::common_type &);
* @endcode
*
* Only storage where the entity exists are passed to the function.
@@ -809,8 +828,8 @@ public:
/**
* @brief Removes all tombstones from a registry or only the pools for the
* given components.
* @tparam Type Types of components for which to clear all tombstones.
* given elements.
* @tparam Type Types of elements for which to clear all tombstones.
*/
template<typename... Type>
void compact() {
@@ -852,15 +871,15 @@ public:
}
/**
* @brief Returns references to the given components for an entity.
* @brief Returns references to the given elements for an entity.
*
* @warning
* Attempting to get a component from an entity that doesn't own it results
* Attempting to get an element from an entity that doesn't own it results
* in undefined behavior.
*
* @tparam Type Types of components to get.
* @tparam Type Types of elements to get.
* @param entt A valid identifier.
* @return References to the components owned by the entity.
* @return References to the elements owned by the entity.
*/
template<typename... Type>
[[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entt) const {
@@ -882,38 +901,36 @@ public:
}
/**
* @brief Returns a reference to the given component for an entity.
* @brief Returns a reference to the given element for an entity.
*
* In case the entity doesn't own the component, the parameters provided are
* In case the entity doesn't own the element, the parameters provided are
* used to construct it.
*
* @sa get
* @sa emplace
*
* @tparam Type Type of component to get.
* @tparam Args Types of arguments to use to construct the component.
* @tparam Type Type of element to get.
* @tparam Args Types of arguments to use to construct the element.
* @param entt A valid identifier.
* @param args Parameters to use to initialize the component.
* @return Reference to the component owned by the entity.
* @param args Parameters to use to initialize the element.
* @return Reference to the element owned by the entity.
*/
template<typename Type, typename... Args>
[[nodiscard]] decltype(auto) get_or_emplace(const entity_type entt, Args &&...args) {
if(auto &cpool = assure<Type>(); cpool.contains(entt)) {
return cpool.get(entt);
} else {
return cpool.emplace(entt, std::forward<Args>(args)...);
}
auto &cpool = assure<Type>();
ENTT_ASSERT(valid(entt), "Invalid entity");
return cpool.contains(entt) ? cpool.get(entt) : cpool.emplace(entt, std::forward<Args>(args)...);
}
/**
* @brief Returns pointers to the given components for an entity.
* @brief Returns pointers to the given elements for an entity.
*
* @note
* The registry retains ownership of the pointed-to components.
* The registry retains ownership of the pointed-to elements.
*
* @tparam Type Types of components to get.
* @tparam Type Types of elements to get.
* @param entt A valid identifier.
* @return Pointers to the components owned by the entity.
* @return Pointers to the elements owned by the entity.
*/
template<typename... Type>
[[nodiscard]] auto try_get([[maybe_unused]] const entity_type entt) const {
@@ -936,8 +953,8 @@ public:
}
/**
* @brief Clears a whole registry or the pools for the given components.
* @tparam Type Types of components to remove from their entities.
* @brief Clears a whole registry or the pools for the given elements.
* @tparam Type Types of elements to remove from their entities.
*/
template<typename... Type>
void clear() {
@@ -954,30 +971,30 @@ public:
}
/**
* @brief Checks if an entity has components assigned.
* @brief Checks if an entity has elements assigned.
* @param entt A valid identifier.
* @return True if the entity has no components assigned, false otherwise.
* @return True if the entity has no elements assigned, false otherwise.
*/
[[nodiscard]] bool orphan(const entity_type entt) const {
return std::none_of(pools.cbegin(), pools.cend(), [entt](auto &&curr) { return curr.second->contains(entt); });
}
/**
* @brief Returns a sink object for the given component.
* @brief Returns a sink object for the given element.
*
* Use this function to receive notifications whenever a new instance of the
* given component is created and assigned to an entity.<br/>
* given element is created and assigned to an entity.<br/>
* The function type for a listener is equivalent to:
*
* @code{.cpp}
* void(basic_registry<Entity> &, Entity);
* @endcode
*
* Listeners are invoked **after** assigning the component to the entity.
* Listeners are invoked **after** assigning the element to the entity.
*
* @sa sink
*
* @tparam Type Type of component of which to get the sink.
* @tparam Type Type of element of which to get the sink.
* @param id Optional name used to map the storage within the registry.
* @return A temporary sink object.
*/
@@ -987,21 +1004,21 @@ public:
}
/**
* @brief Returns a sink object for the given component.
* @brief Returns a sink object for the given element.
*
* Use this function to receive notifications whenever an instance of the
* given component is explicitly updated.<br/>
* given element is explicitly updated.<br/>
* The function type for a listener is equivalent to:
*
* @code{.cpp}
* void(basic_registry<Entity> &, Entity);
* @endcode
*
* Listeners are invoked **after** updating the component.
* Listeners are invoked **after** updating the element.
*
* @sa sink
*
* @tparam Type Type of component of which to get the sink.
* @tparam Type Type of element of which to get the sink.
* @param id Optional name used to map the storage within the registry.
* @return A temporary sink object.
*/
@@ -1011,21 +1028,21 @@ public:
}
/**
* @brief Returns a sink object for the given component.
* @brief Returns a sink object for the given element.
*
* Use this function to receive notifications whenever an instance of the
* given component is removed from an entity and thus destroyed.<br/>
* given element is removed from an entity and thus destroyed.<br/>
* The function type for a listener is equivalent to:
*
* @code{.cpp}
* void(basic_registry<Entity> &, Entity);
* @endcode
*
* Listeners are invoked **before** removing the component from the entity.
* Listeners are invoked **before** removing the element from the entity.
*
* @sa sink
*
* @tparam Type Type of component of which to get the sink.
* @tparam Type Type of element of which to get the sink.
* @param id Optional name used to map the storage within the registry.
* @return A temporary sink object.
*/
@@ -1035,18 +1052,17 @@ public:
}
/**
* @brief Returns a view for the given components.
* @tparam Type Type of component used to construct the view.
* @tparam Other Other types of components used to construct the view.
* @tparam Exclude Types of components used to filter the view.
* @brief Returns a view for the given elements.
* @tparam Type Type of element used to construct the view.
* @tparam Other Other types of elements used to construct the view.
* @tparam Exclude Types of elements used to filter the view.
* @return A newly created view.
*/
template<typename Type, typename... Other, typename... Exclude>
[[nodiscard]] basic_view<get_t<storage_for_type<const Type>, storage_for_type<const Other>...>, exclude_t<storage_for_type<const Exclude>...>>
view(exclude_t<Exclude...> = exclude_t{}) const {
const auto cpools = std::make_tuple(assure<std::remove_const_t<Type>>(), assure<std::remove_const_t<Other>>()..., assure<std::remove_const_t<Exclude>>()...);
basic_view<get_t<storage_for_type<const Type>, storage_for_type<const Other>...>, exclude_t<storage_for_type<const Exclude>...>> elem{};
std::apply([&elem](const auto *...curr) { ((curr ? elem.storage(*curr) : void()), ...); }, cpools);
[&elem](const auto *...curr) { ((curr ? elem.storage(*curr) : void()), ...); }(assure<std::remove_const_t<Exclude>>()..., assure<std::remove_const_t<Other>>()..., assure<std::remove_const_t<Type>>());
return elem;
}
@@ -1058,7 +1074,7 @@ public:
}
/**
* @brief Returns a group for the given components.
* @brief Returns a group for the given elements.
* @tparam Owned Types of storage _owned_ by the group.
* @tparam Get Types of storage _observed_ by the group, if any.
* @tparam Exclude Types of storage used to filter the group, if any.
@@ -1067,33 +1083,34 @@ public:
template<typename... Owned, typename... Get, typename... Exclude>
basic_group<owned_t<storage_for_type<Owned>...>, get_t<storage_for_type<Get>...>, exclude_t<storage_for_type<Exclude>...>>
group(get_t<Get...> = get_t{}, exclude_t<Exclude...> = exclude_t{}) {
using handler_type = typename basic_group<owned_t<storage_for_type<Owned>...>, get_t<storage_for_type<Get>...>, exclude_t<storage_for_type<Exclude>...>>::handler;
using group_type = basic_group<owned_t<storage_for_type<Owned>...>, get_t<storage_for_type<Get>...>, exclude_t<storage_for_type<Exclude>...>>;
using handler_type = typename group_type::handler;
if(auto it = groups.find(type_hash<handler_type>::value()); it != groups.cend()) {
if(auto it = groups.find(group_type::group_id()); it != groups.cend()) {
return {*std::static_pointer_cast<handler_type>(it->second)};
}
std::shared_ptr<handler_type> handler{};
if constexpr(sizeof...(Owned) == 0u) {
handler = std::allocate_shared<handler_type>(get_allocator(), get_allocator(), assure<std::remove_const_t<Get>>()..., assure<std::remove_const_t<Exclude>>()...);
handler = std::allocate_shared<handler_type>(get_allocator(), get_allocator(), std::forward_as_tuple(assure<std::remove_const_t<Get>>()...), std::forward_as_tuple(assure<std::remove_const_t<Exclude>>()...));
} else {
handler = std::allocate_shared<handler_type>(get_allocator(), assure<std::remove_const_t<Owned>>()..., assure<std::remove_const_t<Get>>()..., assure<std::remove_const_t<Exclude>>()...);
[[maybe_unused]] const id_type elem[]{type_hash<std::remove_const_t<Owned>>::value()..., type_hash<std::remove_const_t<Get>>::value()..., type_hash<std::remove_const_t<Exclude>>::value()...};
ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), [&elem](const auto &data) { return data.second->owned(elem, sizeof...(Owned)) == 0u; }), "Conflicting groups");
handler = std::allocate_shared<handler_type>(get_allocator(), std::forward_as_tuple(assure<std::remove_const_t<Owned>>()..., assure<std::remove_const_t<Get>>()...), std::forward_as_tuple(assure<std::remove_const_t<Exclude>>()...));
ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), [](const auto &data) { return !(data.second->owned(type_id<Owned>().hash()) || ...); }), "Conflicting groups");
}
groups.emplace(type_hash<handler_type>::value(), handler);
groups.emplace(group_type::group_id(), handler);
return {*handler};
}
/*! @copydoc group */
template<typename... Owned, typename... Get, typename... Exclude>
basic_group<owned_t<storage_for_type<const Owned>...>, get_t<storage_for_type<const Get>...>, exclude_t<storage_for_type<const Exclude>...>>
[[nodiscard]] basic_group<owned_t<storage_for_type<const Owned>...>, get_t<storage_for_type<const Get>...>, exclude_t<storage_for_type<const Exclude>...>>
group_if_exists(get_t<Get...> = get_t{}, exclude_t<Exclude...> = exclude_t{}) const {
using handler_type = typename basic_group<owned_t<storage_for_type<const Owned>...>, get_t<storage_for_type<const Get>...>, exclude_t<storage_for_type<const Exclude>...>>::handler;
using group_type = basic_group<owned_t<storage_for_type<const Owned>...>, get_t<storage_for_type<const Get>...>, exclude_t<storage_for_type<const Exclude>...>>;
using handler_type = typename group_type::handler;
if(auto it = groups.find(type_hash<handler_type>::value()); it != groups.cend()) {
if(auto it = groups.find(group_type::group_id()); it != groups.cend()) {
return {*std::static_pointer_cast<handler_type>(it->second)};
}
@@ -1101,20 +1118,18 @@ public:
}
/**
* @brief Checks whether the given components belong to any group.
* @tparam Type Type of component in which one is interested.
* @tparam Other Other types of components in which one is interested.
* @return True if the pools of the given components are _free_, false
* @brief Checks whether the given elements belong to any group.
* @tparam Type Types of elements in which one is interested.
* @return True if the pools of the given elements are _free_, false
* otherwise.
*/
template<typename Type, typename... Other>
template<typename... Type>
[[nodiscard]] bool owned() const {
const id_type elem[]{type_hash<std::remove_const_t<Type>>::value(), type_hash<std::remove_const_t<Other>>::value()...};
return std::any_of(groups.cbegin(), groups.cend(), [&elem](auto &&data) { return data.second->owned(elem, 1u + sizeof...(Other)); });
return std::any_of(groups.cbegin(), groups.cend(), [](auto &&data) { return (data.second->owned(type_id<Type>().hash()) || ...); });
}
/**
* @brief Sorts the elements of a given component.
* @brief Sorts the elements of a given element.
*
* The comparison function object returns `true` if the first element is
* _less_ than the second one, `false` otherwise. Its signature is also
@@ -1136,9 +1151,9 @@ public:
* passed along with the other parameters to this member function.
*
* @warning
* Pools of components owned by a group cannot be sorted.
* Pools of elements owned by a group cannot be sorted.
*
* @tparam Type Type of components to sort.
* @tparam Type Type of elements to sort.
* @tparam Compare Type of comparison function object.
* @tparam Sort Type of sort function object.
* @tparam Args Types of arguments to forward to the sort function object.
@@ -1160,17 +1175,17 @@ public:
}
/**
* @brief Sorts two pools of components in the same way.
* @brief Sorts two pools of elements in the same way.
*
* Entities and components in `To` which are part of both storage are sorted
* Entities and elements in `To` which are part of both storage are sorted
* internally with the order they have in `From`. The others follow in no
* particular order.
*
* @warning
* Pools of components owned by a group cannot be sorted.
* Pools of elements owned by a group cannot be sorted.
*
* @tparam To Type of components to sort.
* @tparam From Type of components to use to sort.
* @tparam To Type of elements to sort.
* @tparam From Type of elements to use to sort.
*/
template<typename To, typename From>
void sort() {
@@ -1183,12 +1198,12 @@ public:
* @brief Returns the context object, that is, a general purpose container.
* @return The context object, that is, a general purpose container.
*/
context &ctx() noexcept {
[[nodiscard]] context &ctx() noexcept {
return vars;
}
/*! @copydoc ctx */
const context &ctx() const noexcept {
[[nodiscard]] const context &ctx() const noexcept {
return vars;
}

View File

@@ -17,6 +17,7 @@ namespace internal {
template<typename Set>
class runtime_view_iterator final {
using iterator_type = typename Set::iterator;
using iterator_traits = std::iterator_traits<iterator_type>;
[[nodiscard]] bool valid() const {
return (!tombstone_check || *it != tombstone)
@@ -25,10 +26,10 @@ class runtime_view_iterator final {
}
public:
using difference_type = typename iterator_type::difference_type;
using value_type = typename iterator_type::value_type;
using pointer = typename iterator_type::pointer;
using reference = typename iterator_type::reference;
using value_type = typename iterator_traits::value_type;
using pointer = typename iterator_traits::pointer;
using reference = typename iterator_traits::reference;
using difference_type = typename iterator_traits::difference_type;
using iterator_category = std::bidirectional_iterator_tag;
constexpr runtime_view_iterator() noexcept
@@ -37,6 +38,7 @@ public:
it{},
tombstone_check{} {}
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
runtime_view_iterator(const std::vector<Set *> &cpools, const std::vector<Set *> &ignore, iterator_type curr) noexcept
: pools{&cpools},
filter{&ignore},
@@ -48,7 +50,8 @@ public:
}
runtime_view_iterator &operator++() {
while(++it != (*pools)[0]->end() && !valid()) {}
++it;
for(const auto last = (*pools)[0]->end(); it != last && !valid(); ++it) {}
return *this;
}
@@ -58,7 +61,8 @@ public:
}
runtime_view_iterator &operator--() {
while(--it != (*pools)[0]->begin() && !valid()) {}
--it;
for(const auto first = (*pools)[0]->begin(); it != first && !valid(); --it) {}
return *this;
}
@@ -98,15 +102,15 @@ private:
*
* 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.
* entities available for each element and uses the smallest set in order to get
* a performance boost when iterating.
*
* @b Important
*
* Iterators aren't invalidated if:
*
* * New elements are added to the storage.
* * The entity currently pointed is modified (for example, components are added
* * The entity currently pointed is modified (for example, elements are added
* or removed from it).
* * The entity currently pointed is destroyed.
*
@@ -159,7 +163,7 @@ public:
filter{other.filter, allocator} {}
/*! @brief Default move constructor. */
basic_runtime_view(basic_runtime_view &&) noexcept(std::is_nothrow_move_constructible_v<container_type>) = default;
basic_runtime_view(basic_runtime_view &&) noexcept = default;
/**
* @brief Allocator-extended move constructor.
@@ -170,23 +174,26 @@ public:
: pools{std::move(other.pools), allocator},
filter{std::move(other.filter), allocator} {}
/*! @brief Default destructor. */
~basic_runtime_view() = default;
/**
* @brief Default copy assignment operator.
* @return This container.
* @return This runtime view.
*/
basic_runtime_view &operator=(const basic_runtime_view &) = default;
/**
* @brief Default move assignment operator.
* @return This container.
* @return This runtime view.
*/
basic_runtime_view &operator=(basic_runtime_view &&) noexcept(std::is_nothrow_move_assignable_v<container_type>) = default;
basic_runtime_view &operator=(basic_runtime_view &&) noexcept = default;
/**
* @brief Exchanges the contents with those of a given view.
* @param other View to exchange the content with.
*/
void swap(basic_runtime_view &other) {
void swap(basic_runtime_view &other) noexcept {
using std::swap;
swap(pools, other.pools);
swap(filter, other.filter);
@@ -241,11 +248,11 @@ public:
/**
* @brief Returns an iterator to the first entity that has the given
* components.
* elements.
*
* If the view is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first entity that has the given components.
* @return An iterator to the first entity that has the given elements.
*/
[[nodiscard]] iterator begin() const {
return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->begin()};
@@ -253,14 +260,22 @@ public:
/**
* @brief Returns an iterator that is past the last entity that has the
* given components.
* given elements.
* @return An iterator to the entity following the last entity that has the
* given components.
* given elements.
*/
[[nodiscard]] iterator end() const {
return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->end()};
}
/**
* @brief Checks whether a view is initialized or not.
* @return True if the view is initialized, false otherwise.
*/
[[nodiscard]] explicit operator bool() const noexcept {
return !(pools.empty() && filter.empty());
}
/**
* @brief Checks if a view contains an entity.
* @param entt A valid identifier.

View File

@@ -37,7 +37,7 @@ void orphans(Registry &registry) {
* @brief Utility class to create snapshots from a registry.
*
* A _snapshot_ can be either a dump of the entire registry or a narrower
* selection of components of interest.<br/>
* selection of elements of interest.<br/>
* This type can be used in both cases if provided with a correctly configured
* output archive.
*
@@ -46,7 +46,7 @@ void orphans(Registry &registry) {
template<typename Registry>
class basic_snapshot {
static_assert(!std::is_const_v<Registry>, "Non-const registry type required");
using traits_type = typename Registry::traits_type;
using traits_type = entt_traits<typename Registry::entity_type>;
public:
/*! Basic registry type. */
@@ -61,10 +61,25 @@ public:
basic_snapshot(const registry_type &source) noexcept
: reg{&source} {}
/*! @brief Default copy constructor, deleted on purpose. */
basic_snapshot(const basic_snapshot &) = delete;
/*! @brief Default move constructor. */
basic_snapshot(basic_snapshot &&) noexcept = default;
/*! @brief Default move assignment operator. @return This snapshot. */
/*! @brief Default destructor. */
~basic_snapshot() = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This snapshot.
*/
basic_snapshot &operator=(const basic_snapshot &) = delete;
/**
* @brief Default move assignment operator.
* @return This snapshot.
*/
basic_snapshot &operator=(basic_snapshot &&) noexcept = default;
/**
@@ -78,14 +93,25 @@ public:
template<typename Type, typename Archive>
const basic_snapshot &get(Archive &archive, const id_type id = type_hash<Type>::value()) const {
if(const auto *storage = reg->template storage<Type>(id); storage) {
const typename registry_type::common_type &base = *storage;
archive(static_cast<typename traits_type::entity_type>(storage->size()));
if constexpr(std::is_same_v<Type, entity_type>) {
archive(static_cast<typename traits_type::entity_type>(storage->free_list()));
for(auto first = storage->data(), last = first + storage->size(); first != last; ++first) {
for(auto first = base.rbegin(), last = base.rend(); first != last; ++first) {
archive(*first);
}
} else if constexpr(registry_type::template storage_for_type<Type>::storage_policy == deletion_policy::in_place) {
for(auto it = base.rbegin(), last = base.rend(); it != last; ++it) {
if(const auto entt = *it; entt == tombstone) {
archive(static_cast<entity_type>(null));
} else {
archive(entt);
std::apply([&archive](auto &&...args) { (archive(std::forward<decltype(args)>(args)), ...); }, storage->get_as_tuple(entt));
}
}
} else {
for(auto elem: storage->reach()) {
std::apply([&archive](auto &&...args) { (archive(std::forward<decltype(args)>(args)), ...); }, elem);
@@ -149,7 +175,7 @@ private:
template<typename Registry>
class basic_snapshot_loader {
static_assert(!std::is_const_v<Registry>, "Non-const registry type required");
using traits_type = typename Registry::traits_type;
using traits_type = entt_traits<typename Registry::entity_type>;
public:
/*! Basic registry type. */
@@ -164,13 +190,28 @@ public:
basic_snapshot_loader(registry_type &source) noexcept
: reg{&source} {
// restoring a snapshot as a whole requires a clean registry
ENTT_ASSERT(reg->template storage<entity_type>().empty() && (reg->storage().begin() == reg->storage().end()), "Registry must be empty");
ENTT_ASSERT(reg->template storage<entity_type>().free_list() == 0u, "Registry must be empty");
}
/*! @brief Default copy constructor, deleted on purpose. */
basic_snapshot_loader(const basic_snapshot_loader &) = delete;
/*! @brief Default move constructor. */
basic_snapshot_loader(basic_snapshot_loader &&) noexcept = default;
/*! @brief Default move assignment operator. @return This loader. */
/*! @brief Default destructor. */
~basic_snapshot_loader() = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This loader.
*/
basic_snapshot_loader &operator=(const basic_snapshot_loader &) = delete;
/**
* @brief Default move assignment operator.
* @return This loader.
*/
basic_snapshot_loader &operator=(basic_snapshot_loader &&) noexcept = default;
/**
@@ -224,10 +265,10 @@ public:
}
/**
* @brief Destroys those entities that have no components.
* @brief Destroys those entities that have no elements.
*
* 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
* In case all the entities were serialized but only part of the elements
* was saved, it could happen that some of the entities have no elements
* once restored.<br/>
* This function helps to identify and destroy those entities.
*
@@ -261,7 +302,7 @@ private:
template<typename Registry>
class basic_continuous_loader {
static_assert(!std::is_const_v<Registry>, "Non-const registry type required");
using traits_type = typename Registry::traits_type;
using traits_type = entt_traits<typename Registry::entity_type>;
void restore(typename Registry::entity_type entt) {
if(const auto entity = to_entity(entt); remloc.contains(entity) && remloc[entity].first == entt) {
@@ -332,11 +373,26 @@ public:
: remloc{source.get_allocator()},
reg{&source} {}
/*! @brief Default move constructor. */
basic_continuous_loader(basic_continuous_loader &&) = default;
/*! @brief Default copy constructor, deleted on purpose. */
basic_continuous_loader(const basic_continuous_loader &) = delete;
/*! @brief Default move assignment operator. @return This loader. */
basic_continuous_loader &operator=(basic_continuous_loader &&) = default;
/*! @brief Default move constructor. */
basic_continuous_loader(basic_continuous_loader &&) noexcept = default;
/*! @brief Default destructor. */
~basic_continuous_loader() = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This loader.
*/
basic_continuous_loader &operator=(const basic_continuous_loader &) = delete;
/**
* @brief Default move assignment operator.
* @return This loader.
*/
basic_continuous_loader &operator=(basic_continuous_loader &&) noexcept = default;
/**
* @brief Restores all elements of a type with associated identifiers.
@@ -406,10 +462,10 @@ public:
}
/**
* @brief Destroys those entities that have no components.
* @brief Destroys those entities that have no elements.
*
* 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
* In case all the entities were serialized but only part of the elements
* was saved, it could happen that some of the entities have no elements
* once restored.<br/>
* This function helps to identify and destroy those entities.
*

View File

@@ -10,7 +10,7 @@
#include "../config/config.h"
#include "../core/algorithm.hpp"
#include "../core/any.hpp"
#include "../core/memory.hpp"
#include "../core/bit.hpp"
#include "../core/type_info.hpp"
#include "entity.hpp"
#include "fwd.hpp"
@@ -33,7 +33,7 @@ struct sparse_set_iterator final {
offset{} {}
constexpr sparse_set_iterator(const Container &ref, const difference_type idx) noexcept
: packed{std::addressof(ref)},
: packed{&ref},
offset{idx} {}
constexpr sparse_set_iterator &operator++() noexcept {
@@ -73,15 +73,15 @@ struct sparse_set_iterator final {
}
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
return packed->data()[index() - value];
return (*packed)[index() - value];
}
[[nodiscard]] constexpr pointer operator->() const noexcept {
return packed->data() + index();
return std::addressof(operator[](0));
}
[[nodiscard]] constexpr reference operator*() const noexcept {
return *operator->();
return operator[](0);
}
[[nodiscard]] constexpr pointer data() const noexcept {
@@ -136,7 +136,7 @@ template<typename Container>
/*! @endcond */
/**
* @brief Basic sparse set implementation.
* @brief Sparse set implementation.
*
* Sparse set or packed array or whatever is the name users give it.<br/>
* Two arrays: an _external_ one and an _internal_ one; a _sparse_ one and a
@@ -160,7 +160,13 @@ 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 underlying_type = typename entt_traits<Entity>::entity_type;
using traits_type = entt_traits<Entity>;
static constexpr auto max_size = static_cast<std::size_t>(traits_type::to_entity(null));
[[nodiscard]] auto policy_to_head() const noexcept {
return static_cast<size_type>(max_size * static_cast<decltype(max_size)>(mode != deletion_policy::swap_only));
}
[[nodiscard]] auto sparse_ptr(const Entity entt) const {
const auto pos = static_cast<size_type>(traits_type::to_entity(entt));
@@ -208,27 +214,23 @@ class basic_sparse_set {
}
}
void swap_at(const std::size_t from, const std::size_t to) {
auto &lhs = packed[from];
auto &rhs = packed[to];
void swap_at(const std::size_t lhs, const std::size_t rhs) {
auto &from = packed[lhs];
auto &to = packed[rhs];
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));
sparse_ref(from) = traits_type::combine(static_cast<typename traits_type::entity_type>(rhs), traits_type::to_integral(from));
sparse_ref(to) = traits_type::combine(static_cast<typename traits_type::entity_type>(lhs), traits_type::to_integral(to));
std::swap(lhs, rhs);
}
underlying_type policy_to_head() {
return traits_type::entity_mask * (mode != deletion_policy::swap_only);
std::swap(from, to);
}
private:
virtual const void *get_at(const std::size_t) const {
[[nodiscard]] virtual const void *get_at(const std::size_t) const {
return nullptr;
}
virtual void swap_or_move([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) {
ENTT_ASSERT((mode != deletion_policy::swap_only) || (((lhs < free_list()) + (rhs < free_list())) != 1u), "Cross swapping is not supported");
ENTT_ASSERT((mode != deletion_policy::swap_only) || ((lhs < head) == (rhs < head)), "Cross swapping is not supported");
}
protected:
@@ -241,9 +243,9 @@ protected:
*/
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));
const auto pos = index(*it);
bump(traits_type::next(*it));
swap_at(pos, static_cast<size_type>(head -= (pos < head)));
swap_at(pos, head -= (pos < head));
}
/**
@@ -269,11 +271,10 @@ protected:
*/
void in_place_pop(const basic_iterator it) {
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);
const auto pos = static_cast<size_type>(traits_type::to_entity(std::exchange(sparse_ref(*it), null)));
packed[pos] = traits_type::combine(static_cast<typename traits_type::entity_type>(std::exchange(head, pos)), tombstone);
}
protected:
/**
* @brief Erases entities from a sparse set.
* @param first An iterator to the first element of the range of entities.
@@ -303,7 +304,7 @@ protected:
virtual void pop_all() {
switch(mode) {
case deletion_policy::in_place:
if(head != traits_type::to_entity(null)) {
if(head != max_size) {
for(auto first = begin(); !(first.index() < 0); ++first) {
if(*first != tombstone) {
sparse_ref(*first) = null;
@@ -331,16 +332,17 @@ 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(entt != null && entt != tombstone, "Invalid element");
auto &elem = assure_at_least(entt);
auto pos = size();
switch(mode) {
case deletion_policy::in_place:
if(head != traits_type::to_entity(null) && !force_back) {
pos = static_cast<size_type>(head);
if(head != max_size && !force_back) {
pos = 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));
elem = traits_type::combine(static_cast<typename traits_type::entity_type>(head), traits_type::to_integral(entt));
head = static_cast<size_type>(traits_type::to_entity(std::exchange(packed[pos], entt)));
break;
}
[[fallthrough]];
@@ -354,32 +356,31 @@ protected:
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");
ENTT_ASSERT(!(static_cast<size_type>(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);
}
pos = head++;
swap_at(static_cast<size_type>(traits_type::to_entity(elem)), pos);
break;
}
return --(end() - pos);
return --(end() - static_cast<typename iterator::difference_type>(pos));
}
/*! @brief Forwards variables to derived classes, if any. */
// NOLINTNEXTLINE(performance-unnecessary-value-param)
virtual void bind_any(any) noexcept {}
public:
/*! @brief Entity traits. */
using traits_type = entt_traits<Entity>;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Underlying entity identifier. */
using entity_type = typename traits_type::value_type;
/*! @brief Underlying version type. */
using version_type = typename traits_type::version_type;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief 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. */
@@ -400,7 +401,7 @@ public:
* @param allocator The allocator to use.
*/
explicit basic_sparse_set(const allocator_type &allocator)
: basic_sparse_set{type_id<void>(), deletion_policy::swap_and_pop, allocator} {}
: basic_sparse_set{deletion_policy::swap_and_pop, allocator} {}
/**
* @brief Constructs an empty container with the given policy and allocator.
@@ -422,7 +423,12 @@ public:
packed{allocator},
info{&elem},
mode{pol},
head{policy_to_head()} {}
head{policy_to_head()} {
ENTT_ASSERT(traits_type::version_mask || mode != deletion_policy::in_place, "Policy does not support zero-sized versions");
}
/*! @brief Default copy constructor, deleted on purpose. */
basic_sparse_set(const basic_sparse_set &) = delete;
/**
* @brief Move constructor.
@@ -440,13 +446,13 @@ public:
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) noexcept
basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator)
: sparse{std::move(other.sparse), allocator},
packed{std::move(other.packed), allocator},
info{other.info},
mode{other.mode},
head{std::exchange(other.head, policy_to_head())} {
ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed");
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a sparse set is not allowed");
}
/*! @brief Default destructor. */
@@ -454,20 +460,20 @@ public:
release_sparse_pages();
}
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This sparse set.
*/
basic_sparse_set &operator=(const basic_sparse_set &) = delete;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This sparse set.
*/
basic_sparse_set &operator=(basic_sparse_set &&other) noexcept {
ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed");
release_sparse_pages();
sparse = std::move(other.sparse);
packed = std::move(other.packed);
info = other.info;
mode = other.mode;
head = std::exchange(other.head, policy_to_head());
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a sparse set is not allowed");
swap(other);
return *this;
}
@@ -475,7 +481,7 @@ public:
* @brief Exchanges the contents with those of a given sparse set.
* @param other Sparse set to exchange the content with.
*/
void swap(basic_sparse_set &other) {
void swap(basic_sparse_set &other) noexcept {
using std::swap;
swap(sparse, other.sparse);
swap(packed, other.packed);
@@ -501,20 +507,20 @@ public:
}
/**
* @brief Returns the head of the free list, if any.
* @return The head of the free list.
* @brief Returns data on the free list whose meaning depends on the mode.
* @return Free list information that is mode dependent.
*/
[[nodiscard]] size_type free_list() const noexcept {
return static_cast<size_type>(head);
return 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.
* @brief Sets data on the free list whose meaning depends on the mode.
* @param value Free list information that is mode dependent.
*/
void free_list(const size_type len) noexcept {
ENTT_ASSERT((mode == deletion_policy::swap_only) && !(len > packed.size()), "Invalid value");
head = static_cast<underlying_type>(len);
void free_list(const size_type value) noexcept {
ENTT_ASSERT((mode == deletion_policy::swap_only) && !(value > packed.size()), "Invalid value");
head = value;
}
/**
@@ -584,7 +590,7 @@ public:
* @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));
return (mode != deletion_policy::in_place) || (head == max_size);
}
/**
@@ -659,46 +665,6 @@ 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.
@@ -715,7 +681,7 @@ public:
* @return True if the sparse set contains the entity, false otherwise.
*/
[[nodiscard]] bool contains(const entity_type entt) const noexcept {
const auto elem = sparse_ptr(entt);
const auto *elem = sparse_ptr(entt);
constexpr auto cap = traits_type::entity_mask;
constexpr auto mask = traits_type::to_integral(null) & ~cap;
// testing versions permits to avoid accessing the packed array
@@ -729,7 +695,7 @@ public:
* version otherwise.
*/
[[nodiscard]] version_type current(const entity_type entt) const noexcept {
const auto elem = sparse_ptr(entt);
const auto *elem = sparse_ptr(entt);
constexpr auto fallback = traits_type::to_version(tombstone);
return elem ? traits_type::to_version(*elem) : fallback;
}
@@ -750,21 +716,12 @@ public:
}
/**
* @brief Returns the entity at specified location, with bounds checking.
* @param pos The position for which to return the entity.
* @return The entity at specified location if any, a null entity otherwise.
*/
[[deprecated("use .begin()[pos] instead")]] [[nodiscard]] entity_type at(const size_type pos) const noexcept {
return pos < packed.size() ? packed[pos] : null;
}
/**
* @brief Returns the entity at specified location, without bounds checking.
* @brief Returns the entity at specified location.
* @param pos The position for which to return the entity.
* @return The entity at specified location.
*/
[[nodiscard]] entity_type operator[](const size_type pos) const noexcept {
ENTT_ASSERT(pos < packed.size(), "Position is out of bounds");
ENTT_ASSERT(pos < packed.size(), "Index out of bounds");
return packed[pos];
}
@@ -800,7 +757,7 @@ public:
* `end()` iterator otherwise.
*/
iterator push(const entity_type entt, const void *elem = nullptr) {
return try_emplace(entt, (mode == deletion_policy::swap_only), elem);
return try_emplace(entt, false, elem);
}
/**
@@ -818,11 +775,13 @@ public:
*/
template<typename It>
iterator push(It first, It last) {
for(auto it = first; it != last; ++it) {
try_emplace(*it, true);
auto curr = end();
for(; first != last; ++first) {
curr = try_emplace(*first, true);
}
return first == last ? end() : find(*first);
return curr;
}
/**
@@ -836,10 +795,10 @@ public:
* @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;
auto &elem = sparse_ref(entt);
ENTT_ASSERT(entt != null && elem != tombstone, "Cannot set the required version");
elem = traits_type::combine(traits_type::to_integral(elem), traits_type::to_integral(entt));
packed[static_cast<size_type>(traits_type::to_entity(elem))] = entt;
return traits_type::to_version(entt);
}
@@ -925,17 +884,18 @@ public:
void compact() {
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);
size_type pos = std::exchange(head, max_size);
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) {
for(; from && packed[from - 1u] == tombstone; --from) {}
while(pos != max_size) {
if(const auto to = std::exchange(pos, static_cast<size_type>(traits_type::to_entity(packed[pos]))); to < from) {
--from;
swap_or_move(from, to);
packed[to] = packed[from];
const auto 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 elem = static_cast<typename traits_type::entity_type>(to);
sparse_ref(packed[to]) = traits_type::combine(elem, traits_type::to_integral(packed[to]));
for(; from && packed[from - 1u] == tombstone; --from) {}
}
@@ -999,7 +959,7 @@ 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((mode != deletion_policy::in_place) || (head == max_size), "Sorting with tombstones not allowed");
ENTT_ASSERT(!(length > packed.size()), "Length exceeds the number of elements");
algo(packed.rend() - length, packed.rend(), std::move(compare), std::forward<Args>(args)...);
@@ -1013,8 +973,8 @@ public:
const auto entt = 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]));
const auto elem = static_cast<typename traits_type::entity_type>(curr);
sparse_ref(entt) = traits_type::combine(elem, traits_type::to_integral(packed[curr]));
curr = std::exchange(next, idx);
}
}
@@ -1034,7 +994,8 @@ public:
*/
template<typename Compare, typename Sort = std_sort, typename... Args>
void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
sort_n(static_cast<size_type>(end(0) - begin(0)), std::move(compare), std::move(algo), std::forward<Args>(args)...);
const size_type len = (mode == deletion_policy::swap_only) ? head : packed.size();
sort_n(len, std::move(compare), std::move(algo), std::forward<Args>(args)...);
}
/**
@@ -1048,12 +1009,15 @@ public:
* @tparam It Type of input iterator.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @return An iterator past the last of the elements actually shared.
*/
template<typename It>
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");
iterator sort_as(It first, It last) {
ENTT_ASSERT((mode != deletion_policy::in_place) || (head == max_size), "Sorting with tombstones not allowed");
const size_type len = (mode == deletion_policy::swap_only) ? head : packed.size();
auto it = end() - static_cast<typename iterator::difference_type>(len);
for(auto it = begin(0); it.index() && first != last; ++first) {
for(const auto other = end(); (it != other) && (first != last); ++first) {
if(const auto curr = *first; contains(curr)) {
if(const auto entt = *it; entt != curr) {
// basic no-leak guarantee (with invalid state) if swapping throws
@@ -1063,14 +1027,8 @@ public:
++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());
return it;
}
/*! @brief Clears a sparse set. */
@@ -1086,19 +1044,41 @@ public:
* @brief Returned value type, if any.
* @return Returned value type, if any.
*/
const type_info &type() const noexcept {
[[nodiscard]] const type_info &type() const noexcept {
return *info;
}
/*! @brief Forwards variables to derived classes, if any. */
virtual void bind(any) noexcept {}
/**
* @brief Forwards variables to derived classes, if any.
* @tparam Type Type of the element to forward.
* @param value The element to forward.
* @return Nothing.
*/
template<typename Type>
[[deprecated("avoid wrapping elements with basic_any")]] std::enable_if_t<std::is_same_v<std::remove_const_t<std::remove_reference_t<Type>>, basic_any<>>>
bind(Type &&value) noexcept {
// backward compatibility
bind_any(std::forward<Type>(value));
}
/**
* @brief Forwards variables to derived classes, if any.
* @tparam Type Type of the element to forward.
* @param value The element to forward.
* @return Nothing.
*/
template<typename Type>
std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_reference_t<Type>>, basic_any<>>>
bind(Type &&value) noexcept {
bind_any(forward_as_any(std::forward<Type>(value)));
}
private:
sparse_container_type sparse;
packed_container_type packed;
const type_info *info;
deletion_policy mode;
underlying_type head;
size_type head;
};
} // namespace entt

View File

@@ -9,6 +9,7 @@
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../core/bit.hpp"
#include "../core/iterator.hpp"
#include "../core/memory.hpp"
#include "../core/type_info.hpp"
@@ -22,9 +23,9 @@ namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename Container, std::size_t Size>
template<typename Container>
class storage_iterator final {
friend storage_iterator<const Container, Size>;
friend storage_iterator<const Container>;
using container_type = std::remove_const_t<Container>;
using alloc_traits = std::allocator_traits<typename container_type::allocator_type>;
@@ -48,7 +49,7 @@ public:
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>, Size> &other) noexcept
constexpr storage_iterator(const storage_iterator<std::remove_const_t<Container>> &other) noexcept
: storage_iterator{other.payload, other.offset} {}
constexpr storage_iterator &operator++() noexcept {
@@ -89,16 +90,16 @@ public:
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
const auto pos = index() - value;
return (*payload)[pos / Size][fast_mod(pos, Size)];
constexpr auto page_size = component_traits<value_type>::page_size;
return (*payload)[pos / page_size][fast_mod(static_cast<std::size_t>(pos), page_size)];
}
[[nodiscard]] constexpr pointer operator->() const noexcept {
const auto pos = index();
return (*payload)[pos / Size] + fast_mod(pos, Size);
return std::addressof(operator[](0));
}
[[nodiscard]] constexpr reference operator*() const noexcept {
return *operator->();
return operator[](0);
}
[[nodiscard]] constexpr difference_type index() const noexcept {
@@ -110,38 +111,38 @@ private:
difference_type offset;
};
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 {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &rhs) noexcept {
return rhs.index() - lhs.index();
}
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator==(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator==(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &rhs) noexcept {
return lhs.index() == rhs.index();
}
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator!=(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator!=(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &rhs) noexcept {
return !(lhs == rhs);
}
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator<(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator<(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &rhs) noexcept {
return lhs.index() > rhs.index();
}
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator>(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator>(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &rhs) noexcept {
return rhs < lhs;
}
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator<=(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator<=(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &rhs) noexcept {
return !(lhs > rhs);
}
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator>=(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator>=(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &rhs) noexcept {
return !(lhs < rhs);
}
@@ -152,10 +153,10 @@ class extended_storage_iterator final {
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;
@@ -211,7 +212,7 @@ template<typename... Lhs, typename... Rhs>
/*! @endcond */
/**
* @brief Basic storage implementation.
* @brief Storage implementation.
*
* Internal data structures arrange elements to maximize performance. There are
* no guarantees that objects are returned in the insertion order when iterate
@@ -221,7 +222,7 @@ template<typename... Lhs, typename... Rhs>
* Empty types aren't explicitly instantiated. Therefore, many of the functions
* normally available for non-empty types will not be available for empty ones.
*
* @tparam Type Type of objects assigned to the entities.
* @tparam Type Element type.
* @tparam Entity A valid entity type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
@@ -232,6 +233,7 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
using container_type = std::vector<typename alloc_traits::pointer, typename alloc_traits::template rebind_alloc<typename alloc_traits::pointer>>;
using underlying_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
using underlying_iterator = typename underlying_type::basic_iterator;
using traits_type = component_traits<Type>;
[[nodiscard]] auto &element_at(const std::size_t pos) const {
return payload[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)];
@@ -264,8 +266,8 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
const auto it = base_type::try_emplace(entt, force_back);
ENTT_TRY {
auto elem = assure_at_least(static_cast<size_type>(it.index()));
entt::uninitialized_construct_using_allocator(to_address(elem), get_allocator(), std::forward<Args>(args)...);
auto *elem = to_address(assure_at_least(static_cast<size_type>(it.index())));
entt::uninitialized_construct_using_allocator(elem, get_allocator(), std::forward<Args>(args)...);
}
ENTT_CATCH {
base_type::pop(it, it + 1u);
@@ -297,7 +299,7 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
}
private:
const void *get_at(const std::size_t pos) const final {
[[nodiscard]] const void *get_at(const std::size_t pos) const final {
return std::addressof(element_at(pos));
}
@@ -372,14 +374,14 @@ protected:
* @return Iterator pointing to the emplaced element.
*/
underlying_iterator try_emplace([[maybe_unused]] const Entity entt, [[maybe_unused]] const bool force_back, const void *value) override {
if(value) {
if constexpr(std::is_copy_constructible_v<value_type>) {
return emplace_element(entt, force_back, *static_cast<const value_type *>(value));
if(value != nullptr) {
if constexpr(std::is_copy_constructible_v<element_type>) {
return emplace_element(entt, force_back, *static_cast<const element_type *>(value));
} else {
return base_type::end();
}
} else {
if constexpr(std::is_default_constructible_v<value_type>) {
if constexpr(std::is_default_constructible_v<element_type>) {
return emplace_element(entt, force_back);
} else {
return base_type::end();
@@ -388,26 +390,26 @@ protected:
}
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Base type. */
using base_type = underlying_type;
/*! @brief Element type. */
using element_type = Type;
/*! @brief Type of the objects assigned to entities. */
using value_type = Type;
/*! @brief Component traits. */
using traits_type = component_traits<value_type>;
using value_type = element_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, traits_type::page_size>;
using iterator = internal::storage_iterator<container_type>;
/*! @brief Constant random access iterator type. */
using const_iterator = internal::storage_iterator<const container_type, traits_type::page_size>;
using const_iterator = internal::storage_iterator<const container_type>;
/*! @brief Reverse iterator type. */
using reverse_iterator = std::reverse_iterator<iterator>;
/*! @brief Constant reverse iterator type. */
@@ -420,6 +422,8 @@ public:
using reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::reverse_iterator, reverse_iterator>>;
/*! @brief Constant extended reverse iterable storage proxy. */
using const_reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_reverse_iterator, const_reverse_iterator>>;
/*! @brief Storage deletion policy. */
static constexpr deletion_policy storage_policy{traits_type::in_place_delete};
/*! @brief Default constructor. */
basic_storage()
@@ -430,9 +434,12 @@ public:
* @param allocator The allocator to use.
*/
explicit basic_storage(const allocator_type &allocator)
: base_type{type_id<value_type>(), deletion_policy{traits_type::in_place_delete}, allocator},
: base_type{type_id<element_type>(), storage_policy, allocator},
payload{allocator} {}
/*! @brief Default copy constructor, deleted on purpose. */
basic_storage(const basic_storage &) = delete;
/**
* @brief Move constructor.
* @param other The instance to move from.
@@ -446,28 +453,33 @@ public:
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept
basic_storage(basic_storage &&other, const allocator_type &allocator)
: base_type{std::move(other), allocator},
payload{std::move(other.payload), allocator} {
ENTT_ASSERT(alloc_traits::is_always_equal::value || payload.get_allocator() == other.payload.get_allocator(), "Copying a storage is not allowed");
// NOLINTNEXTLINE(bugprone-use-after-move)
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a storage is not allowed");
}
/*! @brief Default destructor. */
// NOLINTNEXTLINE(bugprone-exception-escape)
~basic_storage() override {
shrink_to_size(0u);
}
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This storage.
*/
basic_storage &operator=(const basic_storage &) = delete;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This storage.
*/
basic_storage &operator=(basic_storage &&other) noexcept {
ENTT_ASSERT(alloc_traits::is_always_equal::value || payload.get_allocator() == other.payload.get_allocator(), "Copying a storage is not allowed");
shrink_to_size(0u);
base_type::operator=(std::move(other));
payload = std::move(other.payload);
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a storage is not allowed");
swap(other);
return *this;
}
@@ -475,10 +487,10 @@ public:
* @brief Exchanges the contents with those of a given storage.
* @param other Storage to exchange the content with.
*/
void swap(basic_storage &other) {
void swap(basic_storage &other) noexcept {
using std::swap;
base_type::swap(other);
swap(payload, other.payload);
base_type::swap(other);
}
/**
@@ -697,7 +709,7 @@ 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.
* @return Iterator pointing to the first element inserted, if any.
*/
template<typename It>
iterator insert(It first, It last, const value_type &value = {}) {
@@ -734,17 +746,17 @@ public:
* @brief Returns an iterable object to use to _visit_ a storage.
*
* The iterable object returns a tuple that contains the current entity and
* a reference to its component.
* a reference to its element.
*
* @return An iterable object to use to _visit_ the storage.
*/
[[nodiscard]] iterable each() noexcept {
return {internal::extended_storage_iterator{base_type::begin(), begin()}, internal::extended_storage_iterator{base_type::end(), end()}};
return iterable{{base_type::begin(), begin()}, {base_type::end(), end()}};
}
/*! @copydoc each */
[[nodiscard]] const_iterable each() const noexcept {
return {internal::extended_storage_iterator{base_type::cbegin(), cbegin()}, internal::extended_storage_iterator{base_type::cend(), cend()}};
return const_iterable{{base_type::cbegin(), cbegin()}, {base_type::cend(), cend()}};
}
/**
@@ -755,12 +767,12 @@ public:
* @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()}};
return reverse_iterable{{base_type::rbegin(), rbegin()}, {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()}};
return const_reverse_iterable{{base_type::crbegin(), crbegin()}, {base_type::crend(), crend()}};
}
private:
@@ -773,20 +785,21 @@ class basic_storage<Type, Entity, Allocator, std::enable_if_t<component_traits<T
: public basic_sparse_set<Entity, typename std::allocator_traits<Allocator>::template rebind_alloc<Entity>> {
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
using traits_type = component_traits<Type>;
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Base type. */
using base_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
/*! @brief Element type. */
using element_type = Type;
/*! @brief Type of the objects assigned to entities. */
using value_type = Type;
/*! @brief Component traits. */
using traits_type = component_traits<value_type>;
using value_type = void;
/*! @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. */
@@ -795,6 +808,8 @@ public:
using reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::reverse_iterator>>;
/*! @brief Constant extended reverse iterable storage proxy. */
using const_reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_reverse_iterator>>;
/*! @brief Storage deletion policy. */
static constexpr deletion_policy storage_policy{traits_type::in_place_delete};
/*! @brief Default constructor. */
basic_storage()
@@ -805,7 +820,10 @@ public:
* @param allocator The allocator to use.
*/
explicit basic_storage(const allocator_type &allocator)
: base_type{type_id<value_type>(), deletion_policy{traits_type::in_place_delete}, allocator} {}
: base_type{type_id<element_type>(), storage_policy, allocator} {}
/*! @brief Default copy constructor, deleted on purpose. */
basic_storage(const basic_storage &) = delete;
/**
* @brief Move constructor.
@@ -818,9 +836,18 @@ public:
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept
basic_storage(basic_storage &&other, const allocator_type &allocator)
: base_type{std::move(other), allocator} {}
/*! @brief Default destructor. */
~basic_storage() override = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This storage.
*/
basic_storage &operator=(const basic_storage &) = delete;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
@@ -834,7 +861,7 @@ public:
*/
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
// std::allocator<void> has no cross constructors (waiting for C++20)
if constexpr(std::is_void_v<value_type> && !std::is_constructible_v<allocator_type, typename base_type::allocator_type>) {
if constexpr(std::is_void_v<element_type> && !std::is_constructible_v<allocator_type, typename base_type::allocator_type>) {
return allocator_type{};
} else {
return allocator_type{base_type::get_allocator()};
@@ -851,21 +878,16 @@ public:
* @param entt A valid identifier.
*/
void get([[maybe_unused]] const entity_type entt) const noexcept {
ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity");
ENTT_ASSERT(base_type::contains(entt), "Invalid entity");
}
/**
* @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::contains(entt), "Storage does not contain entity");
ENTT_ASSERT(base_type::contains(entt), "Invalid entity");
return std::tuple{};
}
@@ -892,7 +914,7 @@ public:
*/
template<typename... Func>
void patch([[maybe_unused]] const entity_type entt, Func &&...func) {
ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity");
ENTT_ASSERT(base_type::contains(entt), "Invalid entity");
(std::forward<Func>(func)(), ...);
}
@@ -918,12 +940,12 @@ public:
* @return An iterable object to use to _visit_ the storage.
*/
[[nodiscard]] iterable each() noexcept {
return {internal::extended_storage_iterator{base_type::begin()}, internal::extended_storage_iterator{base_type::end()}};
return iterable{base_type::begin(), base_type::end()};
}
/*! @copydoc each */
[[nodiscard]] const_iterable each() const noexcept {
return {internal::extended_storage_iterator{base_type::cbegin()}, internal::extended_storage_iterator{base_type::cend()}};
return const_iterable{base_type::cbegin(), base_type::cend()};
}
/**
@@ -934,12 +956,12 @@ public:
* @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()}};
return reverse_iterable{{base_type::rbegin()}, {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()}};
return const_reverse_iterable{{base_type::crbegin()}, {base_type::crend()}};
}
};
@@ -953,15 +975,27 @@ 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;
using underlying_iterator = typename basic_sparse_set<Entity, Allocator>::basic_iterator;
using traits_type = entt_traits<Entity>;
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), {});
auto next() noexcept {
entity_type entt = null;
do {
ENTT_ASSERT(placeholder < traits_type::to_entity(null), "No more entities available");
entt = traits_type::combine(static_cast<typename traits_type::entity_type>(placeholder++), {});
} while(base_type::current(entt) != traits_type::to_version(tombstone) && entt != null);
return entt;
}
protected:
/*! @brief Erases all entities of a storage. */
void pop_all() override {
base_type::pop_all();
placeholder = {};
}
/**
* @brief Assigns an entity to a storage.
* @param hint A valid identifier.
@@ -972,16 +1006,18 @@ protected:
}
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Base type. */
using base_type = basic_sparse_set<Entity, Allocator>;
/*! @brief Element type. */
using element_type = Entity;
/*! @brief Type of the objects assigned to entities. */
using value_type = Entity;
using value_type = void;
/*! @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. */
@@ -990,6 +1026,8 @@ public:
using reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::reverse_iterator>>;
/*! @brief Constant extended reverse iterable storage proxy. */
using const_reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_reverse_iterator>>;
/*! @brief Storage deletion policy. */
static constexpr deletion_policy storage_policy = deletion_policy::swap_only;
/*! @brief Default constructor. */
basic_storage()
@@ -1001,22 +1039,36 @@ public:
* @param allocator The allocator to use.
*/
explicit basic_storage(const allocator_type &allocator)
: base_type{type_id<void>(), deletion_policy::swap_only, allocator} {}
: base_type{type_id<void>(), storage_policy, allocator} {}
/*! @brief Default copy constructor, deleted on purpose. */
basic_storage(const basic_storage &) = delete;
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
basic_storage(basic_storage &&other) noexcept
: base_type{std::move(other)} {}
: base_type{std::move(other)},
placeholder{other.placeholder} {}
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept
: base_type{std::move(other), allocator} {}
basic_storage(basic_storage &&other, const allocator_type &allocator)
: base_type{std::move(other), allocator},
placeholder{other.placeholder} {}
/*! @brief Default destructor. */
~basic_storage() override = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This storage.
*/
basic_storage &operator=(const basic_storage &) = delete;
/**
* @brief Move assignment operator.
@@ -1024,6 +1076,7 @@ public:
* @return This storage.
*/
basic_storage &operator=(basic_storage &&other) noexcept {
placeholder = other.placeholder;
base_type::operator=(std::move(other));
return *this;
}
@@ -1043,11 +1096,6 @@ public:
/**
* @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.
*/
@@ -1062,7 +1110,7 @@ public:
*/
entity_type emplace() {
const auto len = base_type::free_list();
const auto entt = (len == base_type::size()) ? entity_at(len) : base_type::data()[len];
const auto entt = (len == base_type::size()) ? next() : base_type::data()[len];
return *base_type::try_emplace(entt, true);
}
@@ -1076,22 +1124,13 @@ public:
* @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);
if(hint != null && hint != tombstone) {
if(const auto curr = traits_type::construct(traits_type::to_entity(hint), base_type::current(hint)); curr == tombstone || !(base_type::index(curr) < base_type::free_list())) {
return *base_type::try_emplace(hint, true);
}
return 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);
}
return emplace();
}
/**
@@ -1119,39 +1158,10 @@ public:
}
for(; first != last; ++first) {
*first = *base_type::try_emplace(entity_at(base_type::free_list()), true);
*first = *base_type::try_emplace(next(), 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.
*
@@ -1160,12 +1170,13 @@ public:
* @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)}};
return std::as_const(*this).each();
}
/*! @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)}};
const auto it = base_type::cend();
return const_iterable{it - base_type::free_list(), it};
}
/**
@@ -1176,13 +1187,17 @@ public:
* @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)}};
return std::as_const(*this).reach();
}
/*! @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)}};
const auto it = base_type::crbegin();
return const_reverse_iterable{it, it + base_type::free_list()};
}
private:
size_type placeholder{};
};
} // namespace entt

File diff suppressed because it is too large Load Diff

View File

@@ -4,9 +4,11 @@
#include "config/version.h"
#include "container/dense_map.hpp"
#include "container/dense_set.hpp"
#include "container/table.hpp"
#include "core/algorithm.hpp"
#include "core/any.hpp"
#include "core/attribute.h"
#include "core/bit.hpp"
#include "core/compressed_pair.hpp"
#include "core/enum.hpp"
#include "core/family.hpp"
@@ -15,6 +17,7 @@
#include "core/iterator.hpp"
#include "core/memory.hpp"
#include "core/monostate.hpp"
#include "core/ranges.hpp"
#include "core/tuple.hpp"
#include "core/type_info.hpp"
#include "core/type_traits.hpp"
@@ -27,6 +30,7 @@
#include "entity/mixin.hpp"
#include "entity/observer.hpp"
#include "entity/organizer.hpp"
#include "entity/ranges.hpp"
#include "entity/registry.hpp"
#include "entity/runtime_view.hpp"
#include "entity/snapshot.hpp"
@@ -50,7 +54,6 @@
#include "meta/template.hpp"
#include "meta/type_traits.hpp"
#include "meta/utility.hpp"
#include "platform/android-ndk-r17.hpp"
#include "poly/poly.hpp"
#include "process/process.hpp"
#include "process/scheduler.hpp"

View File

@@ -28,13 +28,9 @@ public:
using iterator_category = std::input_iterator_tag;
using iterator_concept = std::forward_iterator_tag;
constexpr edge_iterator() noexcept
: it{},
vert{},
pos{},
last{},
offset{} {}
constexpr edge_iterator() noexcept = default;
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept
: it{std::move(base)},
vert{vertices},
@@ -66,10 +62,10 @@ public:
friend constexpr bool operator==(const edge_iterator<Type> &, const edge_iterator<Type> &) noexcept;
private:
It it;
size_type vert;
size_type pos;
size_type last;
It it{};
size_type vert{};
size_type pos{};
size_type last{};
size_type offset{};
};
@@ -111,16 +107,17 @@ public:
using vertex_iterator = iota_iterator<vertex_type>;
/*! @brief Edge iterator type. */
using edge_iterator = internal::edge_iterator<typename container_type::const_iterator>;
/*! @brief Out edge iterator type. */
/*! @brief Out-edge iterator type. */
using out_edge_iterator = edge_iterator;
/*! @brief In edge iterator type. */
/*! @brief In-edge iterator type. */
using in_edge_iterator = edge_iterator;
/*! @brief Graph category tag. */
using graph_category = Category;
/*! @brief Default constructor. */
adjacency_matrix() noexcept(noexcept(allocator_type{}))
: adjacency_matrix{0u} {}
: adjacency_matrix{0u} {
}
/**
* @brief Constructs an empty container with a given allocator.
@@ -139,12 +136,8 @@ public:
: matrix{vertices * vertices, allocator},
vert{vertices} {}
/**
* @brief Copy constructor.
* @param other The instance to copy from.
*/
adjacency_matrix(const adjacency_matrix &other)
: adjacency_matrix{other, other.get_allocator()} {}
/*! @brief Default copy constructor. */
adjacency_matrix(const adjacency_matrix &) = default;
/**
* @brief Allocator-extended copy constructor.
@@ -155,12 +148,8 @@ public:
: matrix{other.matrix, allocator},
vert{other.vert} {}
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
adjacency_matrix(adjacency_matrix &&other) noexcept
: adjacency_matrix{std::move(other), other.get_allocator()} {}
/*! @brief Default move constructor. */
adjacency_matrix(adjacency_matrix &&) noexcept = default;
/**
* @brief Allocator-extended move constructor.
@@ -169,29 +158,22 @@ public:
*/
adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator)
: matrix{std::move(other.matrix), allocator},
vert{std::exchange(other.vert, 0u)} {}
vert{other.vert} {}
/*! @brief Default destructor. */
~adjacency_matrix() = default;
/**
* @brief Default copy assignment operator.
* @param other The instance to copy from.
* @return This container.
*/
adjacency_matrix &operator=(const adjacency_matrix &other) {
matrix = other.matrix;
vert = other.vert;
return *this;
}
adjacency_matrix &operator=(const adjacency_matrix &) = default;
/**
* @brief Default move assignment operator.
* @param other The instance to move from.
* @return This container.
*/
adjacency_matrix &operator=(adjacency_matrix &&other) noexcept {
matrix = std::move(other.matrix);
vert = std::exchange(other.vert, 0u);
return *this;
}
adjacency_matrix &operator=(adjacency_matrix &&) noexcept = default;
/**
* @brief Returns the associated allocator.
@@ -211,12 +193,25 @@ public:
* @brief Exchanges the contents with those of a given adjacency matrix.
* @param other Adjacency matrix to exchange the content with.
*/
void swap(adjacency_matrix &other) {
void swap(adjacency_matrix &other) noexcept {
using std::swap;
swap(matrix, other.matrix);
swap(vert, other.vert);
}
/**
* @brief Returns true if an adjacency matrix is empty, false otherwise.
*
* @warning
* Potentially expensive, try to avoid it on hot paths.
*
* @return True if the adjacency matrix is empty, false otherwise.
*/
[[nodiscard]] bool empty() const noexcept {
const auto iterable = edges();
return (iterable.begin() == iterable.end());
}
/**
* @brief Returns the number of vertices.
* @return The number of vertices.
@@ -244,9 +239,9 @@ public:
}
/**
* @brief Returns an iterable object to visit all out edges of a vertex.
* @param vertex The vertex of which to return all out edges.
* @return An iterable object to visit all out edges of a vertex.
* @brief Returns an iterable object to visit all out-edges of a vertex.
* @param vertex The vertex of which to return all out-edges.
* @return An iterable object to visit all out-edges of a vertex.
*/
[[nodiscard]] iterable_adaptor<out_edge_iterator> out_edges(const vertex_type vertex) const noexcept {
const auto it = matrix.cbegin();
@@ -256,9 +251,9 @@ public:
}
/**
* @brief Returns an iterable object to visit all in edges of a vertex.
* @param vertex The vertex of which to return all in edges.
* @return An iterable object to visit all in edges of a vertex.
* @brief Returns an iterable object to visit all in-edges of a vertex.
* @param vertex The vertex of which to return all in-edges.
* @return An iterable object to visit all in-edges of a vertex.
*/
[[nodiscard]] iterable_adaptor<in_edge_iterator> in_edges(const vertex_type vertex) const noexcept {
const auto it = matrix.cbegin();

View File

@@ -29,9 +29,9 @@ template<typename Allocator>
class basic_flow {
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, id_type>, "Invalid value type");
using task_container_type = dense_set<id_type, identity, std::equal_to<id_type>, typename alloc_traits::template rebind_alloc<id_type>>;
using task_container_type = dense_set<id_type, identity, std::equal_to<>, typename alloc_traits::template rebind_alloc<id_type>>;
using ro_rw_container_type = std::vector<std::pair<std::size_t, bool>, typename alloc_traits::template rebind_alloc<std::pair<std::size_t, bool>>>;
using deps_container_type = dense_map<id_type, ro_rw_container_type, identity, std::equal_to<id_type>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, ro_rw_container_type>>>;
using deps_container_type = dense_map<id_type, ro_rw_container_type, identity, std::equal_to<>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, ro_rw_container_type>>>;
using adjacency_matrix_type = adjacency_matrix<directed_tag, typename alloc_traits::template rebind_alloc<std::size_t>>;
void emplace(const id_type res, const bool is_rw) {
@@ -135,8 +135,7 @@ public:
explicit basic_flow(const allocator_type &allocator)
: index{0u, allocator},
vertices{allocator},
deps{allocator},
sync_on{} {}
deps{allocator} {}
/*! @brief Default copy constructor. */
basic_flow(const basic_flow &) = default;
@@ -166,6 +165,9 @@ public:
deps{std::move(other.deps), allocator},
sync_on{other.sync_on} {}
/*! @brief Default destructor. */
~basic_flow() = default;
/**
* @brief Default copy assignment operator.
* @return This flow builder.
@@ -207,7 +209,7 @@ public:
* @brief Exchanges the contents with those of a given flow builder.
* @param other Flow builder to exchange the content with.
*/
void swap(basic_flow &other) {
void swap(basic_flow &other) noexcept {
using std::swap;
std::swap(index, other.index);
std::swap(vertices, other.vertices);
@@ -215,6 +217,14 @@ public:
std::swap(sync_on, other.sync_on);
}
/**
* @brief Returns true if a flow builder contains no tasks, false otherwise.
* @return True if the flow builder contains no tasks, false otherwise.
*/
[[nodiscard]] bool empty() const noexcept {
return vertices.empty();
}
/**
* @brief Returns the number of tasks.
* @return The number of tasks.
@@ -333,7 +343,7 @@ private:
compressed_pair<size_type, allocator_type> index;
task_container_type vertices;
deps_container_type deps;
size_type sync_on;
size_type sync_on{};
};
} // namespace entt

View File

@@ -38,9 +38,19 @@ public:
/*! @brief Default constructor, deleted on purpose. */
locator() = delete;
/*! @brief Default copy constructor, deleted on purpose. */
locator(const locator &) = delete;
/*! @brief Default destructor, deleted on purpose. */
~locator() = delete;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This locator.
*/
locator &operator=(const locator &) = delete;
/**
* @brief Checks whether a service locator contains a value.
* @return True if the service locator contains a value, false otherwise.
@@ -139,6 +149,7 @@ public:
private:
// std::shared_ptr because of its type erased allocator which is useful here
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
inline static std::shared_ptr<Service> service{};
};

View File

@@ -1,3 +1,5 @@
// IWYU pragma: always_keep
#ifndef ENTT_META_CONTAINER_HPP
#define ENTT_META_CONTAINER_HPP
@@ -60,14 +62,14 @@ 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 True in case of key-only containers, false otherwise. */
static constexpr bool fixed_size = internal::fixed_size_sequence_container_v<Type>;
/**
* @brief Returns the number of elements in a container.
* @param container Opaque pointer to a container of the given type.
@@ -129,8 +131,8 @@ struct basic_meta_sequence_container_traits {
* @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()};
return (container != nullptr) ? iterator{area, static_cast<Type *>(container)->begin()}
: iterator{area, static_cast<const Type *>(as_const)->begin()};
}
/**
@@ -141,8 +143,8 @@ struct basic_meta_sequence_container_traits {
* @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()};
return (container != nullptr) ? iterator{area, static_cast<Type *>(container)->end()}
: iterator{area, static_cast<const Type *>(as_const)->end()};
}
/**
@@ -157,14 +159,14 @@ struct basic_meta_sequence_container_traits {
* @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) {
[[nodiscard]] static iterator insert([[maybe_unused]] const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const void *value, [[maybe_unused]] const void *cref, [[maybe_unused]] const iterator &it) {
if constexpr(fixed_size) {
return iterator{area};
return iterator{};
} else {
auto *const non_const = any_cast<typename Type::iterator>(&it.base());
return {area, static_cast<Type *>(container)->insert(
non_const ? *non_const : any_cast<const typename Type::const_iterator &>(it.base()),
value ? *static_cast<const typename Type::value_type *>(value) : *static_cast<const std::remove_reference_t<typename Type::const_reference> *>(cref))};
(value != nullptr) ? *static_cast<const typename Type::value_type *>(value) : *static_cast<const std::remove_reference_t<typename Type::const_reference> *>(cref))};
}
}
@@ -175,9 +177,9 @@ struct basic_meta_sequence_container_traits {
* @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) {
[[nodiscard]] static iterator erase([[maybe_unused]] const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const iterator &it) {
if constexpr(fixed_size) {
return iterator{area};
return iterator{};
} else {
auto *const non_const = any_cast<typename Type::iterator>(&it.base());
return {area, static_cast<Type *>(container)->erase(non_const ? *non_const : any_cast<const typename Type::const_iterator &>(it.base()))};
@@ -193,14 +195,14 @@ 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 True in case of key-only containers, false otherwise. */
static constexpr bool key_only = internal::key_only_associative_container_v<Type>;
/**
* @brief Returns the number of elements in a container.
* @param container Opaque pointer to a container of the given type.
@@ -243,8 +245,8 @@ struct basic_meta_associative_container_traits {
* @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()};
return (container != nullptr) ? 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()};
}
/**
@@ -255,8 +257,8 @@ struct basic_meta_associative_container_traits {
* @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()};
return (container != nullptr) ? 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()};
}
/**
@@ -293,8 +295,8 @@ struct basic_meta_associative_container_traits {
* @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))};
return (container != nullptr) ? 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))};
}
};

View File

@@ -2,12 +2,14 @@
#define ENTT_META_FACTORY_HPP
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/bit.hpp"
#include "../core/fwd.hpp"
#include "../core/type_info.hpp"
#include "../core/type_traits.hpp"
@@ -25,52 +27,154 @@ namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
[[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()];
}
class basic_meta_factory {
using invoke_type = std::remove_pointer_t<decltype(meta_func_node::invoke)>;
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;
}
inline meta_func_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_func_node node) {
if(auto it = parent.details->func.find(id); it != parent.details->func.end()) {
for(auto *curr = &it->second; curr; curr = curr->next.get()) {
if(curr->invoke == node.invoke) {
node.next = std::move(curr->next);
*curr = std::move(node);
return *curr;
}
}
// locally overloaded function
node.next = std::make_shared<meta_func_node>(std::move(parent.details->func[id]));
auto *find_member_or_assert() {
auto *member = find_member<&meta_data_node::id>(details->data, bucket);
ENTT_ASSERT(member != nullptr, "Cannot find member");
return member;
}
return parent.details->func.insert_or_assign(id, std::move(node)).first->second;
}
auto *find_overload_or_assert() {
auto *overload = find_overload(find_member<&meta_func_node::id>(details->func, bucket), invoke);
ENTT_ASSERT(overload != nullptr, "Cannot find overload");
return overload;
}
void reset_bucket(const id_type id, invoke_type *const ref = nullptr) {
invoke = ref;
bucket = id;
}
protected:
void type(const id_type id) noexcept {
reset_bucket(parent);
auto &&elem = meta_context::from(*ctx).value[parent];
ENTT_ASSERT(elem.id == id || !resolve(*ctx, id), "Duplicate identifier");
elem.id = id;
}
template<typename Type>
void insert_or_assign(Type node) {
reset_bucket(parent);
if constexpr(std::is_same_v<Type, meta_base_node>) {
auto *member = find_member<&meta_base_node::type>(details->base, node.type);
member ? (*member = node) : details->base.emplace_back(node);
} else if constexpr(std::is_same_v<Type, meta_conv_node>) {
auto *member = find_member<&meta_conv_node::type>(details->conv, node.type);
member ? (*member = node) : details->conv.emplace_back(node);
} else {
static_assert(std::is_same_v<Type, meta_ctor_node>, "Unexpected type");
auto *member = find_member<&meta_ctor_node::id>(details->ctor, node.id);
member ? (*member = node) : details->ctor.emplace_back(node);
}
}
void dtor(meta_dtor_node node) {
reset_bucket(parent);
meta_context::from(*ctx).value[parent].dtor = node;
}
void data(meta_data_node node) {
reset_bucket(node.id);
if(auto *member = find_member<&meta_data_node::id>(details->data, node.id); member == nullptr) {
details->data.emplace_back(std::move(node));
} else if(member->set != node.set || member->get != node.get) {
*member = std::move(node);
}
}
void func(meta_func_node node) {
reset_bucket(node.id, node.invoke);
if(auto *member = find_member<&meta_func_node::id>(details->func, node.id); member == nullptr) {
details->func.emplace_back(std::move(node));
} else if(auto *overload = find_overload(member, node.invoke); overload == nullptr) {
while(member->next != nullptr) { member = member->next.get(); }
member->next = std::make_shared<meta_func_node>(std::move(node));
}
}
void prop(meta_prop_node node) {
std::vector<meta_prop_node> *container = nullptr;
if(bucket == parent) {
container = &details->prop;
} else if(invoke == nullptr) {
container = &find_member_or_assert()->prop;
} else {
container = &find_overload_or_assert()->prop;
}
auto *member = find_member<&meta_prop_node::id>(*container, node.id);
(member != nullptr) ? (*member = std::move(node)) : container->emplace_back(std::move(node));
}
void traits(const meta_traits value) {
if(bucket == parent) {
meta_context::from(*ctx).value[bucket].traits |= value;
} else if(invoke == nullptr) {
find_member_or_assert()->traits |= value;
} else {
find_overload_or_assert()->traits |= value;
}
}
void custom(meta_custom_node node) {
if(bucket == parent) {
meta_context::from(*ctx).value[bucket].custom = std::move(node);
} else if(invoke == nullptr) {
find_member_or_assert()->custom = std::move(node);
} else {
find_overload_or_assert()->custom = std::move(node);
}
}
public:
basic_meta_factory(const id_type id, meta_ctx &area)
: ctx{&area},
parent{id},
bucket{id} {
auto &&elem = meta_context::from(*ctx).value[parent];
if(!elem.details) {
elem.details = std::make_shared<meta_type_descriptor>();
}
details = elem.details.get();
}
private:
meta_ctx *ctx{};
id_type parent{};
id_type bucket{};
invoke_type *invoke{};
meta_type_descriptor *details{};
};
} // namespace internal
/*! @endcond */
/**
* @brief Basic meta factory to be used for reflection purposes.
* @brief Meta factory to be used for reflection purposes.
* @tparam Type Reflected type for which the factory was created.
*/
template<typename Type>
class meta_factory {
class meta_factory: private internal::basic_meta_factory {
using base_type = internal::basic_meta_factory;
template<typename Setter, auto Getter, typename Policy, std::size_t... Index>
void data(const id_type id, std::index_sequence<Index...>) noexcept {
using data_type = std::invoke_result_t<decltype(Getter), Type &>;
using args_type = type_list<typename meta_function_helper_t<Type, decltype(value_list_element_v<Index, Setter>)>::args_type...>;
static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
auto &&elem = internal::meta_extend(
internal::owner(*ctx, *info),
id,
base_type::data(
internal::meta_data_node{
id,
/* this is never static */
(std::is_member_object_pointer_v<decltype(value_list_element_v<Index, Setter>)> && ... && std::is_const_v<std::remove_reference_t<data_type>>) ? internal::meta_traits::is_const : internal::meta_traits::is_none,
Setter::size,
@@ -78,42 +182,27 @@ class meta_factory {
&meta_arg<type_list<type_list_element_t<type_list_element_t<Index, args_type>::size != 1u, type_list_element_t<Index, args_type>>...>>,
+[](meta_handle instance, meta_any value) { return (meta_setter<Type, value_list_element_v<Index, Setter>>(*instance.operator->(), value.as_ref()) || ...); },
&meta_getter<Type, Getter, Policy>});
bucket = &elem.prop;
}
public:
/*! @brief Default constructor. */
meta_factory() noexcept
: meta_factory{locator<meta_ctx>::value_or()} {}
: internal::basic_meta_factory{type_id<Type>(), locator<meta_ctx>::value_or()} {}
/**
* @brief Context aware constructor.
* @param area The context into which to construct meta types.
*/
meta_factory(meta_ctx &area) noexcept
: ctx{&area},
bucket{},
info{&type_id<Type>()} {
auto &&elem = internal::owner(*ctx, *info);
if(!elem.details) {
elem.details = std::make_shared<internal::meta_type_descriptor>();
}
bucket = &elem.details->prop;
}
: internal::basic_meta_factory{type_id<Type>().hash(), area} {}
/**
* @brief Assigns a custom unique identifier to a meta type.
* @param id A custom unique identifier.
* @return A meta factory for the given type.
*/
auto type(const id_type id) noexcept {
auto &&elem = internal::owner(*ctx, *info);
ENTT_ASSERT(elem.id == id || !resolve(*ctx, id), "Duplicate identifier");
bucket = &elem.details->prop;
elem.id = id;
meta_factory type(const id_type id) noexcept {
base_type::type(id);
return *this;
}
@@ -126,11 +215,10 @@ public:
* @return A meta factory for the parent type.
*/
template<typename Base>
auto base() noexcept {
meta_factory base() noexcept {
static_assert(!std::is_same_v<Type, Base> && std::is_base_of_v<Base, Type>, "Invalid base type");
auto *const op = +[](const void *instance) noexcept { return static_cast<const void *>(static_cast<const Base *>(static_cast<const Type *>(instance))); };
internal::owner(*ctx, *info).details->base.insert_or_assign(type_id<Base>().hash(), internal::meta_base_node{&internal::resolve<Base>, op});
bucket = nullptr;
base_type::insert_or_assign(internal::meta_base_node{type_id<Base>().hash(), &internal::resolve<Base>, op});
return *this;
}
@@ -150,8 +238,7 @@ public:
auto conv() noexcept {
using conv_type = std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(Candidate), Type &>>>;
auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, std::invoke(Candidate, *static_cast<const Type *>(instance))); };
internal::owner(*ctx, *info).details->conv.insert_or_assign(type_id<conv_type>().hash(), internal::meta_conv_node{op});
bucket = nullptr;
base_type::insert_or_assign(internal::meta_conv_node{type_id<conv_type>().hash(), op});
return *this;
}
@@ -165,11 +252,10 @@ public:
* @return A meta factory for the parent type.
*/
template<typename To>
auto conv() noexcept {
meta_factory conv() noexcept {
using conv_type = std::remove_cv_t<std::remove_reference_t<To>>;
auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, static_cast<To>(*static_cast<const Type *>(instance))); };
internal::owner(*ctx, *info).details->conv.insert_or_assign(type_id<conv_type>().hash(), internal::meta_conv_node{op});
bucket = nullptr;
base_type::insert_or_assign(internal::meta_conv_node{type_id<conv_type>().hash(), op});
return *this;
}
@@ -187,12 +273,11 @@ public:
* @return A meta factory for the parent type.
*/
template<auto Candidate, typename Policy = as_is_t>
auto ctor() noexcept {
meta_factory ctor() noexcept {
using descriptor = meta_function_helper_t<Type, decltype(Candidate)>;
static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy");
static_assert(std::is_same_v<std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>, Type>, "The function doesn't return an object of the required type");
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;
base_type::insert_or_assign(internal::meta_ctor_node{type_id<typename descriptor::args_type>().hash(), descriptor::args_type::size, &meta_arg<typename descriptor::args_type>, &meta_construct<Type, Candidate, Policy>});
return *this;
}
@@ -207,14 +292,13 @@ public:
* @return A meta factory for the parent type.
*/
template<typename... Args>
auto ctor() noexcept {
meta_factory ctor() noexcept {
// default constructor is already implicitly generated, no need for redundancy
if constexpr(sizeof...(Args) != 0u) {
using descriptor = meta_function_helper_t<Type, Type (*)(Args...)>;
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...>});
base_type::insert_or_assign(internal::meta_ctor_node{type_id<typename descriptor::args_type>().hash(), descriptor::args_type::size, &meta_arg<typename descriptor::args_type>, &meta_construct<Type, Args...>});
}
bucket = nullptr;
return *this;
}
@@ -237,11 +321,10 @@ public:
* @return A meta factory for the parent type.
*/
template<auto Func>
auto dtor() noexcept {
meta_factory dtor() noexcept {
static_assert(std::is_invocable_v<decltype(Func), Type &>, "The function doesn't accept an object of the type provided");
auto *const op = +[](void *instance) { std::invoke(Func, *static_cast<Type *>(instance)); };
internal::owner(*ctx, *info).dtor = internal::meta_dtor_node{op};
bucket = nullptr;
base_type::dtor(internal::meta_dtor_node{op});
return *this;
}
@@ -259,15 +342,14 @@ public:
* @return A meta factory for the parent type.
*/
template<auto Data, typename Policy = as_is_t>
auto data(const id_type id) noexcept {
meta_factory data(const id_type id) noexcept {
if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
using data_type = std::invoke_result_t<decltype(Data), Type &>;
static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
auto &&elem = internal::meta_extend(
internal::owner(*ctx, *info),
id,
base_type::data(
internal::meta_data_node{
id,
/* this is never static */
std::is_const_v<std::remove_reference_t<data_type>> ? internal::meta_traits::is_const : internal::meta_traits::is_none,
1u,
@@ -275,8 +357,6 @@ public:
&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_pointer_t<decltype(Data)>;
@@ -286,18 +366,15 @@ public:
static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
}
auto &&elem = internal::meta_extend(
internal::owner(*ctx, *info),
id,
base_type::data(
internal::meta_data_node{
id,
((std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<data_type>>> || std::is_const_v<std::remove_reference_t<data_type>>) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static,
1u,
&internal::resolve<std::remove_cv_t<std::remove_reference_t<data_type>>>,
&meta_arg<type_list<std::remove_cv_t<std::remove_reference_t<data_type>>>>,
&meta_setter<Type, Data>,
&meta_getter<Type, Data, Policy>});
bucket = &elem.prop;
}
return *this;
@@ -324,15 +401,14 @@ public:
* @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 {
meta_factory data(const id_type id) noexcept {
using data_type = std::invoke_result_t<decltype(Getter), Type &>;
static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
if constexpr(std::is_same_v<decltype(Setter), std::nullptr_t>) {
auto &&elem = internal::meta_extend(
internal::owner(*ctx, *info),
id,
base_type::data(
internal::meta_data_node{
id,
/* this is never static */
internal::meta_traits::is_const,
0u,
@@ -340,15 +416,12 @@ public:
&meta_arg<type_list<>>,
&meta_setter<Type, Setter>,
&meta_getter<Type, Getter, Policy>});
bucket = &elem.prop;
} else {
using args_type = typename meta_function_helper_t<Type, decltype(Setter)>::args_type;
auto &&elem = internal::meta_extend(
internal::owner(*ctx, *info),
id,
base_type::data(
internal::meta_data_node{
id,
/* this is never static nor const */
internal::meta_traits::is_none,
1u,
@@ -356,8 +429,6 @@ public:
&meta_arg<type_list<type_list_element_t<args_type::size != 1u, args_type>>>,
&meta_setter<Type, Setter>,
&meta_getter<Type, Getter, Policy>});
bucket = &elem.prop;
}
return *this;
@@ -381,7 +452,7 @@ public:
* @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 {
meta_factory data(const id_type id) noexcept {
data<Setter, Getter, Policy>(id, std::make_index_sequence<Setter::size>{});
return *this;
}
@@ -400,26 +471,24 @@ public:
* @return A meta factory for the parent type.
*/
template<auto Candidate, typename Policy = as_is_t>
auto func(const id_type id) noexcept {
meta_factory func(const id_type id) noexcept {
using descriptor = meta_function_helper_t<Type, decltype(Candidate)>;
static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy");
auto &&elem = internal::meta_extend(
internal::owner(*ctx, *info),
id,
base_type::func(
internal::meta_func_node{
id,
(descriptor::is_const ? internal::meta_traits::is_const : internal::meta_traits::is_none) | (descriptor::is_static ? internal::meta_traits::is_static : internal::meta_traits::is_none),
descriptor::args_type::size,
&internal::resolve<std::conditional_t<std::is_same_v<Policy, as_void_t>, void, std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>>>,
&meta_arg<typename descriptor::args_type>,
&meta_invoke<Type, Candidate, Policy>});
bucket = &elem.prop;
return *this;
}
/**
* @brief Assigns a property to the last meta object created.
* @brief Assigns a property to the last created meta object.
*
* Both the key and the value (if any) must be at least copy constructible.
*
@@ -429,24 +498,44 @@ public:
* @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");
[[deprecated("use ::custom() instead")]] meta_factory prop(id_type id, [[maybe_unused]] Value &&...value) {
if constexpr(sizeof...(Value) == 0u) {
(*bucket)[id] = internal::meta_prop_node{&internal::resolve<void>};
base_type::prop(internal::meta_prop_node{id, &internal::resolve<void>});
} else {
(*bucket)[id] = internal::meta_prop_node{
&internal::resolve<std::decay_t<Value>>...,
std::make_shared<std::decay_t<Value>>(std::forward<Value>(value))...};
base_type::prop(internal::meta_prop_node{id, &internal::resolve<std::decay_t<Value>>..., std::make_shared<std::decay_t<Value>>(std::forward<Value>(value))...});
}
return *this;
}
private:
meta_ctx *ctx;
dense_map<id_type, internal::meta_prop_node, identity> *bucket;
const type_info *info;
/**
* @brief Sets traits on the last created meta object.
*
* The assigned value must be an enum and intended as a bitmask.
*
* @tparam Value Type of the traits value.
* @param value Traits value.
* @return A meta factory for the parent type.
*/
template<typename Value>
meta_factory traits(const Value value) {
static_assert(std::is_enum_v<Value>, "Invalid enum type");
base_type::traits(internal::user_to_meta_traits(value));
return *this;
}
/**
* @brief Sets user defined data that will never be used by the library.
* @tparam Value Type of user defined data to store.
* @tparam Args Types of arguments to use to construct the user data.
* @param args Parameters to use to initialize the user data.
* @return A meta factory for the parent type.
*/
template<typename Value, typename... Args>
meta_factory custom(Args &&...args) {
base_type::custom(internal::meta_custom_node{type_id<Value>().hash(), std::make_shared<Value>(std::forward<Args>(args)...)});
return *this;
}
};
/**

View File

@@ -13,6 +13,8 @@ struct meta_handle;
struct meta_prop;
struct meta_custom;
struct meta_data;
struct meta_func;

View File

@@ -1,6 +1,7 @@
#ifndef ENTT_META_META_HPP
#define ENTT_META_META_HPP
#include <array>
#include <cstddef>
#include <iterator>
#include <memory>
@@ -37,8 +38,7 @@ public:
using iterator = meta_iterator;
/*! @brief Default constructor. */
meta_sequence_container() noexcept
: meta_sequence_container{locator<meta_ctx>::value_or()} {}
meta_sequence_container() = default;
/**
* @brief Context aware constructor.
@@ -70,18 +70,18 @@ public:
[[nodiscard]] inline meta_type value_type() const noexcept;
[[nodiscard]] inline size_type size() const noexcept;
inline bool resize(const size_type);
inline bool resize(size_type);
inline bool clear();
inline bool reserve(const size_type);
inline bool reserve(size_type);
[[nodiscard]] inline iterator begin();
[[nodiscard]] inline iterator end();
inline iterator insert(iterator, meta_any);
inline iterator erase(iterator);
[[nodiscard]] inline meta_any operator[](const size_type);
inline iterator insert(const iterator &, meta_any);
inline iterator erase(const iterator &);
[[nodiscard]] inline meta_any operator[](size_type);
[[nodiscard]] inline explicit operator bool() const noexcept;
private:
const meta_ctx *ctx{};
const meta_ctx *ctx{&locator<meta_ctx>::value_or()};
internal::meta_type_node (*value_type_node)(const internal::meta_context &){};
internal::meta_type_node (*const_reference_node)(const internal::meta_context &){};
size_type (*size_fn)(const void *){};
@@ -107,8 +107,7 @@ public:
using iterator = meta_iterator;
/*! @brief Default constructor. */
meta_associative_container() noexcept
: meta_associative_container{locator<meta_ctx>::value_or()} {}
meta_associative_container() = default;
/**
* @brief Context aware constructor.
@@ -143,13 +142,12 @@ public:
data = &instance;
}
[[nodiscard]] inline bool key_only() const noexcept;
[[nodiscard]] inline meta_type key_type() const noexcept;
[[nodiscard]] inline meta_type mapped_type() const noexcept;
[[nodiscard]] inline meta_type value_type() const noexcept;
[[nodiscard]] inline size_type size() const noexcept;
inline bool clear();
inline bool reserve(const size_type);
inline bool reserve(size_type);
[[nodiscard]] inline iterator begin();
[[nodiscard]] inline iterator end();
inline bool insert(meta_any, meta_any);
@@ -158,7 +156,7 @@ public:
[[nodiscard]] inline explicit operator bool() const noexcept;
private:
const meta_ctx *ctx{};
const meta_ctx *ctx{&locator<meta_ctx>::value_or()};
internal::meta_type_node (*key_type_node)(const internal::meta_context &){};
internal::meta_type_node (*mapped_type_node)(const internal::meta_context &){};
internal::meta_type_node (*value_type_node)(const internal::meta_context &){};
@@ -215,31 +213,27 @@ class meta_any {
}
void release() {
if(node.dtor.dtor && (storage.policy() == any_policy::owner)) {
if((node.dtor.dtor != nullptr) && (storage.policy() == any_policy::owner)) {
node.dtor.dtor(storage.data());
}
}
meta_any(const meta_ctx &area, const meta_any &other, any ref) noexcept
meta_any(const meta_any &other, any ref) noexcept
: storage{std::move(ref)},
ctx{&area},
ctx{other.ctx},
node{storage ? other.node : internal::meta_type_node{}},
vtable{storage ? other.vtable : &basic_vtable<void>} {}
public:
/*! Default constructor. */
meta_any() noexcept
: meta_any{meta_ctx_arg, locator<meta_ctx>::value_or()} {}
meta_any() = default;
/**
* @brief Context aware constructor.
* @param area The context from which to search for meta types.
*/
meta_any(meta_ctx_arg_t, const meta_ctx &area) noexcept
: storage{},
ctx{&area},
node{},
vtable{&basic_vtable<void>} {}
meta_any(meta_ctx_arg_t, const meta_ctx &area)
: ctx{&area} {}
/**
* @brief Constructs a wrapper by directly initializing the new object.
@@ -290,10 +284,10 @@ public:
* @param other The instance to copy from.
*/
meta_any(const meta_ctx &area, const meta_any &other)
: meta_any{other} {
ctx = &area;
node = node.resolve ? node.resolve(internal::meta_context::from(*ctx)) : node;
}
: storage{other.storage},
ctx{&area},
node{(other.node.resolve != nullptr) ? other.node.resolve(internal::meta_context::from(*ctx)) : other.node},
vtable{other.vtable} {}
/**
* @brief Context aware move constructor.
@@ -301,10 +295,10 @@ public:
* @param other The instance to move from.
*/
meta_any(const meta_ctx &area, meta_any &&other)
: meta_any{std::move(other)} {
ctx = &area;
node = node.resolve ? node.resolve(internal::meta_context::from(*ctx)) : node;
}
: storage{std::move(other.storage)},
ctx{&area},
node{(other.node.resolve != nullptr) ? std::exchange(other.node, internal::meta_type_node{}).resolve(internal::meta_context::from(*ctx)) : std::exchange(other.node, internal::meta_type_node{})},
vtable{std::exchange(other.vtable, &basic_vtable<void>)} {}
/**
* @brief Copy constructor.
@@ -333,11 +327,14 @@ public:
* @return This meta any object.
*/
meta_any &operator=(const meta_any &other) {
release();
storage = other.storage;
ctx = other.ctx;
node = other.node;
vtable = other.vtable;
if(this != &other) {
release();
storage = other.storage;
ctx = other.ctx;
node = other.node;
vtable = other.vtable;
}
return *this;
}
@@ -347,6 +344,8 @@ public:
* @return This meta any object.
*/
meta_any &operator=(meta_any &&other) noexcept {
ENTT_ASSERT(this != &other, "Self move assignment");
release();
storage = std::move(other.storage);
ctx = other.ctx;
@@ -361,9 +360,8 @@ public:
* @param value An instance of an object to use to initialize the wrapper.
* @return This meta any object.
*/
template<typename Type>
std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_any>, meta_any &>
operator=(Type &&value) {
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_any>>>
meta_any &operator=(Type &&value) {
emplace<std::decay_t<Type>>(std::forward<Type>(value));
return *this;
}
@@ -389,11 +387,11 @@ public:
* @return A wrapper containing the returned value, if any.
*/
template<typename... Args>
meta_any invoke(const id_type id, Args &&...args) const;
meta_any invoke(id_type id, Args &&...args) const;
/*! @copydoc invoke */
template<typename... Args>
meta_any invoke(const id_type id, Args &&...args);
meta_any invoke(id_type id, Args &&...args);
/**
* @brief Sets the value of a given variable.
@@ -403,17 +401,17 @@ public:
* @return True in case of success, false otherwise.
*/
template<typename Type>
bool set(const id_type id, Type &&value);
bool set(id_type id, Type &&value);
/**
* @brief Gets the value of a given variable.
* @param id Unique identifier.
* @return A wrapper containing the value of the underlying variable.
*/
[[nodiscard]] meta_any get(const id_type id) const;
[[nodiscard]] meta_any get(id_type id) const;
/*! @copydoc get */
[[nodiscard]] meta_any get(const id_type id);
[[nodiscard]] meta_any get(id_type id);
/**
* @brief Tries to cast an instance to a given type.
@@ -443,7 +441,7 @@ public:
* @return A reference to the contained instance.
*/
template<typename Type>
[[nodiscard]] Type cast() const {
[[nodiscard]] std::remove_const_t<Type> cast() const {
auto *const instance = try_cast<std::remove_reference_t<Type>>();
ENTT_ASSERT(instance, "Invalid instance");
return static_cast<Type>(*instance);
@@ -451,7 +449,7 @@ public:
/*! @copydoc cast */
template<typename Type>
[[nodiscard]] Type cast() {
[[nodiscard]] std::remove_const_t<Type> cast() {
// forces const on non-reference types to make them work also with wrappers for const references
auto *const instance = try_cast<std::remove_reference_t<const Type>>();
ENTT_ASSERT(instance, "Invalid instance");
@@ -588,7 +586,7 @@ public:
/*! @copydoc any::operator== */
[[nodiscard]] bool operator==(const meta_any &other) const noexcept {
return (ctx == other.ctx) && ((!node.info && !other.node.info) || (node.info && other.node.info && *node.info == *other.node.info && storage == other.storage));
return (ctx == other.ctx) && (((node.info == nullptr) && (other.node.info == nullptr)) || ((node.info != nullptr) && (other.node.info != nullptr) && *node.info == *other.node.info && storage == other.storage));
}
/*! @copydoc any::operator!= */
@@ -598,17 +596,12 @@ public:
/*! @copydoc any::as_ref */
[[nodiscard]] meta_any as_ref() noexcept {
return meta_any{*ctx, *this, storage.as_ref()};
return meta_any{*this, storage.as_ref()};
}
/*! @copydoc any::as_ref */
[[nodiscard]] meta_any as_ref() const noexcept {
return meta_any{*ctx, *this, storage.as_ref()};
}
/*! @copydoc any::owner */
[[deprecated("use policy() and meta_any_policy instead")]] [[nodiscard]] bool owner() const noexcept {
return (storage.policy() == any_policy::owner);
return meta_any{*this, storage.as_ref()};
}
/**
@@ -620,10 +613,10 @@ public:
}
private:
any storage;
const meta_ctx *ctx;
internal::meta_type_node node;
vtable_type *vtable;
any storage{};
const meta_ctx *ctx{&locator<meta_ctx>::value_or()};
internal::meta_type_node node{};
vtable_type *vtable{&basic_vtable<void>};
};
/**
@@ -657,28 +650,27 @@ template<typename Type>
*/
struct meta_handle {
/*! Default constructor. */
meta_handle() noexcept
: meta_handle{meta_ctx_arg, locator<meta_ctx>::value_or()} {}
meta_handle() = default;
/**
* @brief Context aware constructor.
* @param area The context from which to search for meta types.
*/
meta_handle(meta_ctx_arg_t, const meta_ctx &area) noexcept
meta_handle(meta_ctx_arg_t, const meta_ctx &area)
: any{meta_ctx_arg, area} {}
/**
* @brief Creates a handle that points to an unmanaged object.
* @param value An instance of an object to use to initialize the handle.
*/
meta_handle(meta_any &value) noexcept
meta_handle(meta_any &value)
: any{value.as_ref()} {}
/**
* @brief Creates a handle that points to an unmanaged object.
* @param value An instance of an object to use to initialize the handle.
*/
meta_handle(const meta_any &value) noexcept
meta_handle(const meta_any &value)
: any{value.as_ref()} {}
/**
@@ -688,7 +680,7 @@ struct meta_handle {
* @param value An instance of an object to use to initialize the handle.
*/
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_handle>>>
meta_handle(const meta_ctx &ctx, Type &value) noexcept
meta_handle(const meta_ctx &ctx, Type &value)
: any{ctx, std::in_place_type<Type &>, value} {}
/**
@@ -697,7 +689,7 @@ struct meta_handle {
* @param value An instance of an object to use to initialize the handle.
*/
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_handle>>>
meta_handle(Type &value) noexcept
meta_handle(Type &value)
: meta_handle{locator<meta_ctx>::value_or(), value} {}
/**
@@ -722,6 +714,9 @@ struct meta_handle {
/*! @brief Default move constructor. */
meta_handle(meta_handle &&) = default;
/*! @brief Default destructor. */
~meta_handle() = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This meta handle.
@@ -766,23 +761,21 @@ struct meta_handle {
}
private:
meta_any any;
meta_any any{meta_ctx_arg, locator<meta_ctx>::value_or()};
};
/*! @brief Opaque wrapper for properties of any type. */
struct meta_prop {
/*! @brief Default constructor. */
meta_prop() noexcept
: node{},
ctx{} {}
meta_prop() noexcept = default;
/**
* @brief Context aware constructor for meta objects.
* @param area The context from which to search for meta types.
* @param curr The underlying node with which to construct the instance.
*/
meta_prop(const meta_ctx &area, const internal::meta_prop_node &curr) noexcept
: node{&curr},
meta_prop(const meta_ctx &area, internal::meta_prop_node curr) noexcept
: node{std::move(curr)},
ctx{&area} {}
/**
@@ -790,7 +783,7 @@ struct meta_prop {
* @return A wrapper containing the value stored with the property.
*/
[[nodiscard]] meta_any value() const {
return node->value ? node->type(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, node->value.get()) : meta_any{meta_ctx_arg, *ctx};
return node.value ? node.type(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, node.value.get()) : meta_any{meta_ctx_arg, *ctx};
}
/**
@@ -798,7 +791,7 @@ struct meta_prop {
* @return A wrapper containing the value stored with the property.
*/
[[nodiscard]] meta_any value() {
return node->value ? node->type(internal::meta_context::from(*ctx)).from_void(*ctx, node->value.get(), nullptr) : meta_any{meta_ctx_arg, *ctx};
return node.value ? node.type(internal::meta_context::from(*ctx)).from_void(*ctx, node.value.get(), nullptr) : meta_any{meta_ctx_arg, *ctx};
}
/**
@@ -806,7 +799,7 @@ struct meta_prop {
* @return True if the object is valid, false otherwise.
*/
[[nodiscard]] explicit operator bool() const noexcept {
return (node != nullptr);
return static_cast<bool>(node.type);
}
/**
@@ -815,12 +808,12 @@ struct meta_prop {
* @return True if the objects refer to the same type, false otherwise.
*/
[[nodiscard]] bool operator==(const meta_prop &other) const noexcept {
return (ctx == other.ctx && node == other.node);
return (ctx == other.ctx && node.value == other.node.value);
}
private:
const internal::meta_prop_node *node;
const meta_ctx *ctx;
internal::meta_prop_node node{};
const meta_ctx *ctx{};
};
/**
@@ -833,15 +826,48 @@ private:
return !(lhs == rhs);
}
/*! @brief Opaque wrapper for user defined data of any type. */
struct meta_custom {
/*! @brief Default constructor. */
meta_custom() noexcept = default;
/**
* @brief Basic constructor for meta objects.
* @param curr The underlying node with which to construct the instance.
*/
meta_custom(internal::meta_custom_node curr) noexcept
: node{std::move(curr)} {}
/**
* @brief Generic conversion operator.
* @tparam Type Type to which conversion is requested.
*/
template<typename Type>
[[nodiscard]] operator Type *() const noexcept {
return (type_id<Type>().hash() == node.type) ? std::static_pointer_cast<Type>(node.value).get() : nullptr;
}
/**
* @brief Generic conversion operator.
* @tparam Type Type to which conversion is requested.
*/
template<typename Type>
[[nodiscard]] operator Type &() const noexcept {
ENTT_ASSERT(type_id<Type>().hash() == node.type, "Invalid type");
return *std::static_pointer_cast<Type>(node.value);
}
private:
internal::meta_custom_node node{};
};
/*! @brief Opaque wrapper for data members. */
struct meta_data {
/*! @brief Unsigned integer type. */
using size_type = typename internal::meta_data_node::size_type;
/*! @brief Default constructor. */
meta_data() noexcept
: node{},
ctx{} {}
meta_data() noexcept = default;
/**
* @brief Context aware constructor for meta objects.
@@ -887,6 +913,7 @@ struct meta_data {
* @return True in case of success, false otherwise.
*/
template<typename Type>
// NOLINTNEXTLINE(modernize-use-nodiscard)
bool set(meta_handle instance, Type &&value) const {
return node->set && node->set(meta_handle{*ctx, std::move(instance)}, meta_any{*ctx, std::forward<Type>(value)});
}
@@ -905,13 +932,13 @@ struct meta_data {
* @param index Index of the setter of which to return the accepted type.
* @return The type accepted by the i-th setter.
*/
[[nodiscard]] inline meta_type arg(const size_type index) const noexcept;
[[nodiscard]] inline meta_type arg(size_type index) const noexcept;
/**
* @brief Returns a range to visit registered meta properties.
* @return An iterable range to visit registered meta properties.
*/
[[nodiscard]] meta_range<meta_prop, typename decltype(internal::meta_data_node::prop)::const_iterator> prop() const noexcept {
[[nodiscard]] [[deprecated("use ::custom() instead")]] meta_range<meta_prop, typename decltype(internal::meta_data_node::prop)::const_iterator> prop() const noexcept {
return {{*ctx, node->prop.cbegin()}, {*ctx, node->prop.cend()}};
}
@@ -920,9 +947,32 @@ struct meta_data {
* @param key The key to use to search for a property.
* @return The registered meta property for the given key, if any.
*/
[[nodiscard]] meta_prop prop(const id_type key) const {
const auto it = node->prop.find(key);
return it != node->prop.cend() ? meta_prop{*ctx, it->second} : meta_prop{};
[[nodiscard]] [[deprecated("use ::custom() instead")]] meta_prop prop(const id_type key) const {
for(auto &&elem: node->prop) {
if(elem.id == key) {
return meta_prop{*ctx, elem};
}
}
return meta_prop{};
}
/**
* @brief Returns all meta traits for a given meta object.
* @tparam Type The type to convert the meta traits to.
* @return The registered meta traits, if any.
*/
template<typename Type>
[[nodiscard]] Type traits() const noexcept {
return internal::meta_to_user_traits<Type>(node->traits);
}
/**
* @brief Returns user defined data for a given meta object.
* @return User defined arbitrary data.
*/
[[nodiscard]] meta_custom custom() const noexcept {
return {node->custom};
}
/**
@@ -939,8 +989,8 @@ struct meta_data {
}
private:
const internal::meta_data_node *node;
const meta_ctx *ctx;
const internal::meta_data_node *node{};
const meta_ctx *ctx{};
};
/**
@@ -959,9 +1009,7 @@ struct meta_func {
using size_type = typename internal::meta_func_node::size_type;
/*! @brief Default constructor. */
meta_func() noexcept
: node{},
ctx{} {}
meta_func() noexcept = default;
/**
* @brief Context aware constructor for meta objects.
@@ -1007,14 +1055,10 @@ struct meta_func {
* @param index Index of the argument of which to return the type.
* @return The type of the i-th argument of a member function.
*/
[[nodiscard]] inline meta_type arg(const size_type index) const noexcept;
[[nodiscard]] inline meta_type arg(size_type index) const noexcept;
/**
* @brief Invokes the underlying function, if possible.
*
* @warning
* The context of the arguments is **never** changed.
*
* @param instance An opaque instance of the underlying type.
* @param args Parameters to use to invoke the function.
* @param sz Number of parameters to use to invoke the function.
@@ -1032,13 +1076,14 @@ struct meta_func {
* @return A wrapper containing the returned value, if any.
*/
template<typename... Args>
// NOLINTNEXTLINE(modernize-use-nodiscard)
meta_any invoke(meta_handle instance, Args &&...args) const {
meta_any arguments[sizeof...(Args) + !sizeof...(Args)]{{*ctx, std::forward<Args>(args)}...};
return invoke(std::move(instance), arguments, sizeof...(Args));
std::array<meta_any, sizeof...(Args)> arguments{meta_any{*ctx, std::forward<Args>(args)}...};
return invoke(std::move(instance), arguments.data(), sizeof...(Args));
}
/*! @copydoc meta_data::prop */
[[nodiscard]] meta_range<meta_prop, typename decltype(internal::meta_func_node::prop)::const_iterator> prop() const noexcept {
[[nodiscard]] [[deprecated("use ::custom() instead")]] meta_range<meta_prop, typename decltype(internal::meta_func_node::prop)::const_iterator> prop() const noexcept {
return {{*ctx, node->prop.cbegin()}, {*ctx, node->prop.cend()}};
}
@@ -1047,9 +1092,25 @@ struct meta_func {
* @param key The key to use to search for a property.
* @return The registered meta property for the given key, if any.
*/
[[nodiscard]] meta_prop prop(const id_type key) const {
const auto it = node->prop.find(key);
return it != node->prop.cend() ? meta_prop{*ctx, it->second} : meta_prop{};
[[nodiscard]] [[deprecated("use ::custom() instead")]] meta_prop prop(const id_type key) const {
for(auto &&elem: node->prop) {
if(elem.id == key) {
return meta_prop{*ctx, elem};
}
}
return meta_prop{};
}
/*! @copydoc meta_data::traits */
template<typename Type>
[[nodiscard]] Type traits() const noexcept {
return internal::meta_to_user_traits<Type>(node->traits);
}
/*! @copydoc meta_data::custom */
[[nodiscard]] meta_custom custom() const noexcept {
return {node->custom};
}
/**
@@ -1074,8 +1135,8 @@ struct meta_func {
}
private:
const internal::meta_func_node *node;
const meta_ctx *ctx;
const internal::meta_func_node *node{};
const meta_ctx *ctx{};
};
/**
@@ -1107,16 +1168,18 @@ class meta_type {
size_type match{};
size_type pos{};
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) - waiting for C++20 (and std::span)
for(; pos < sz && args[pos]; ++pos) {
const auto other = curr->arg(*ctx, pos);
const auto type = args[pos].type();
if(const auto &info = other.info(); info == type.info()) {
++match;
} else if(!((type.node.details && (type.node.details->base.contains(info.hash()) || type.node.details->conv.contains(info.hash()))) || (type.node.conversion_helper && other.node.conversion_helper))) {
} else if(!(type.node.conversion_helper && other.node.conversion_helper) && !(type.node.details && (internal::find_member<&internal::meta_base_node::type>(type.node.details->base, info.hash()) || internal::find_member<&internal::meta_conv_node::type>(type.node.details->conv, info.hash())))) {
break;
}
}
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
if(pos == sz) {
if(!candidate || match > same) {
@@ -1146,17 +1209,15 @@ public:
using size_type = typename internal::meta_type_node::size_type;
/*! @brief Default constructor. */
meta_type() noexcept
: node{},
ctx{} {}
meta_type() noexcept = default;
/**
* @brief Context aware constructor for meta objects.
* @param area The context from which to search for meta types.
* @param curr The underlying node with which to construct the instance.
*/
meta_type(const meta_ctx &area, const internal::meta_type_node &curr) noexcept
: node{curr},
meta_type(const meta_ctx &area, internal::meta_type_node curr) noexcept
: node{std::move(curr)},
ctx{&area} {}
/**
@@ -1165,7 +1226,7 @@ public:
* @param curr The underlying node with which to construct the instance.
*/
meta_type(const meta_ctx &area, const internal::meta_base_node &curr) noexcept
: meta_type{area, curr.type(internal::meta_context::from(area))} {}
: meta_type{area, curr.resolve(internal::meta_context::from(area))} {}
/**
* @brief Returns the type info object of the underlying type.
@@ -1245,7 +1306,7 @@ public:
* @return True if the underlying type is a pointer, false otherwise.
*/
[[nodiscard]] bool is_pointer() const noexcept {
return node.info && (node.info->hash() != remove_pointer().info().hash());
return static_cast<bool>(node.traits & internal::meta_traits::is_pointer);
}
/**
@@ -1254,7 +1315,7 @@ public:
* doesn't refer to a pointer type.
*/
[[nodiscard]] meta_type remove_pointer() const noexcept {
return {*ctx, node.remove_pointer(internal::meta_context::from(*ctx))}; // NOLINT
return {*ctx, node.remove_pointer(internal::meta_context::from(*ctx))};
}
/**
@@ -1304,7 +1365,7 @@ public:
* @return The tag for the class template of the underlying type.
*/
[[nodiscard]] inline meta_type template_type() const noexcept {
return node.templ.type ? meta_type{*ctx, node.templ.type(internal::meta_context::from(*ctx))} : meta_type{};
return (node.templ.resolve != nullptr) ? meta_type{*ctx, node.templ.resolve(internal::meta_context::from(*ctx))} : meta_type{};
}
/**
@@ -1360,7 +1421,7 @@ public:
*/
[[nodiscard]] meta_data data(const id_type id) const {
const auto *elem = internal::look_for<&internal::meta_type_descriptor::data>(internal::meta_context::from(*ctx), node, id);
return elem ? meta_data{*ctx, *elem} : meta_data{};
return (elem != nullptr) ? meta_data{*ctx, *elem} : meta_data{};
}
/**
@@ -1382,27 +1443,23 @@ public:
*/
[[nodiscard]] meta_func func(const id_type id) const {
const auto *elem = internal::look_for<&internal::meta_type_descriptor::func>(internal::meta_context::from(*ctx), node, id);
return elem ? meta_func{*ctx, *elem} : meta_func{};
return (elem != nullptr) ? meta_func{*ctx, *elem} : meta_func{};
}
/**
* @brief Creates an instance of the underlying type, if possible.
*
* @warning
* The context of the arguments is **never** changed.
*
* @param args Parameters to use to construct the instance.
* @param sz Number of parameters to use to construct the instance.
* @return A wrapper containing the new instance, if any.
*/
[[nodiscard]] meta_any construct(meta_any *const args, const size_type sz) const {
if(node.details) {
if(const auto *candidate = lookup(args, sz, false, [first = node.details->ctor.cbegin(), last = node.details->ctor.cend()]() mutable { return first == last ? nullptr : &(first++)->second; }); candidate) {
if(const auto *candidate = lookup(args, sz, false, [first = node.details->ctor.cbegin(), last = node.details->ctor.cend()]() mutable { return first == last ? nullptr : &*(first++); }); candidate) {
return candidate->invoke(*ctx, args);
}
}
if(sz == 0u && node.default_constructor) {
if(sz == 0u && (node.default_constructor != nullptr)) {
return node.default_constructor(*ctx);
}
@@ -1417,30 +1474,26 @@ public:
*/
template<typename... Args>
[[nodiscard]] meta_any construct(Args &&...args) const {
meta_any arguments[sizeof...(Args) + !sizeof...(Args)]{{*ctx, std::forward<Args>(args)}...};
return construct(arguments, sizeof...(Args));
std::array<meta_any, sizeof...(Args)> arguments{meta_any{*ctx, std::forward<Args>(args)}...};
return construct(arguments.data(), sizeof...(Args));
}
/**
* @brief Wraps an opaque element of the underlying type.
* @param element A valid pointer to an element of the underlying type.
* @param elem A valid pointer to an element of the underlying type.
* @return A wrapper that references the given instance.
*/
[[nodiscard]] meta_any from_void(void *element) const {
return (element && node.from_void) ? node.from_void(*ctx, element, nullptr) : meta_any{meta_ctx_arg, *ctx};
[[nodiscard]] meta_any from_void(void *elem) const {
return ((elem != nullptr) && (node.from_void != nullptr)) ? node.from_void(*ctx, elem, nullptr) : meta_any{meta_ctx_arg, *ctx};
}
/*! @copydoc from_void */
[[nodiscard]] meta_any from_void(const void *element) const {
return (element && node.from_void) ? node.from_void(*ctx, nullptr, element) : meta_any{meta_ctx_arg, *ctx};
[[nodiscard]] meta_any from_void(const void *elem) const {
return ((elem != nullptr) && (node.from_void != nullptr)) ? node.from_void(*ctx, nullptr, elem) : meta_any{meta_ctx_arg, *ctx};
}
/**
* @brief Invokes a function given an identifier, if possible.
*
* @warning
* The context of the arguments is **never** changed.
*
* @param id Unique identifier.
* @param instance An opaque instance of the underlying type.
* @param args Parameters to use to invoke the function.
@@ -1449,8 +1502,8 @@ public:
*/
meta_any invoke(const id_type id, meta_handle instance, meta_any *const args, const size_type sz) const {
if(node.details) {
if(auto it = node.details->func.find(id); it != node.details->func.cend()) {
if(const auto *candidate = lookup(args, sz, instance && (instance->data() == nullptr), [curr = &it->second]() mutable { return curr ? std::exchange(curr, curr->next.get()) : nullptr; }); candidate) {
if(auto *elem = internal::find_member<&internal::meta_func_node::id>(node.details->func, id); elem != nullptr) {
if(const auto *candidate = lookup(args, sz, instance && (instance->data() == nullptr), [curr = elem]() mutable { return (curr != nullptr) ? std::exchange(curr, curr->next.get()) : nullptr; }); candidate) {
return candidate->invoke(*ctx, meta_handle{*ctx, std::move(instance)}, args);
}
}
@@ -1474,9 +1527,10 @@ public:
* @return A wrapper containing the returned value, if any.
*/
template<typename... Args>
// NOLINTNEXTLINE(modernize-use-nodiscard)
meta_any invoke(const id_type id, meta_handle instance, Args &&...args) const {
meta_any arguments[sizeof...(Args) + !sizeof...(Args)]{{*ctx, std::forward<Args>(args)}...};
return invoke(id, std::move(instance), arguments, sizeof...(Args));
std::array<meta_any, sizeof...(Args)> arguments{meta_any{*ctx, std::forward<Args>(args)}...};
return invoke(id, std::move(instance), arguments.data(), sizeof...(Args));
}
/**
@@ -1508,7 +1562,7 @@ public:
* @brief Returns a range to visit registered top-level meta properties.
* @return An iterable range to visit registered top-level meta properties.
*/
[[nodiscard]] meta_range<meta_prop, typename decltype(internal::meta_type_descriptor::prop)::const_iterator> prop() const noexcept {
[[nodiscard]] [[deprecated("use ::custom() instead")]] meta_range<meta_prop, typename decltype(internal::meta_type_descriptor::prop)::const_iterator> prop() const noexcept {
using range_type = meta_range<meta_prop, typename decltype(internal::meta_type_descriptor::prop)::const_iterator>;
return node.details ? range_type{{*ctx, node.details->prop.cbegin()}, {*ctx, node.details->prop.cend()}} : range_type{};
}
@@ -1518,9 +1572,20 @@ public:
* @param key The key to use to search for a property.
* @return The registered meta property for the given key, if any.
*/
[[nodiscard]] meta_prop prop(const id_type key) const {
[[nodiscard]] [[deprecated("use ::custom() instead")]] meta_prop prop(const id_type key) const {
const auto *elem = internal::look_for<&internal::meta_type_descriptor::prop>(internal::meta_context::from(*ctx), node, key);
return elem ? meta_prop{*ctx, *elem} : meta_prop{};
return (elem != nullptr) ? meta_prop{*ctx, *elem} : meta_prop{};
}
/*! @copydoc meta_data::traits */
template<typename Type>
[[nodiscard]] Type traits() const noexcept {
return internal::meta_to_user_traits<Type>(node.traits);
}
/*! @copydoc meta_data::custom */
[[nodiscard]] meta_custom custom() const noexcept {
return {node.custom};
}
/**
@@ -1533,12 +1598,12 @@ public:
/*! @copydoc meta_prop::operator== */
[[nodiscard]] bool operator==(const meta_type &other) const noexcept {
return (ctx == other.ctx) && ((!node.info && !other.node.info) || (node.info && other.node.info && *node.info == *other.node.info));
return (ctx == other.ctx) && (((node.info == nullptr) && (other.node.info == nullptr)) || ((node.info != nullptr) && (other.node.info != nullptr) && *node.info == *other.node.info));
}
private:
internal::meta_type_node node;
const meta_ctx *ctx;
internal::meta_type_node node{};
const meta_ctx *ctx{};
};
/**
@@ -1552,10 +1617,11 @@ private:
}
[[nodiscard]] inline meta_type meta_any::type() const noexcept {
return node.info ? meta_type{*ctx, node} : meta_type{};
return (node.info != nullptr) ? meta_type{*ctx, node} : meta_type{};
}
template<typename... Args>
// NOLINTNEXTLINE(modernize-use-nodiscard)
meta_any meta_any::invoke(const id_type id, Args &&...args) const {
return type().invoke(id, *this, std::forward<Args>(args)...);
}
@@ -1637,18 +1703,14 @@ class meta_sequence_container::meta_iterator final {
}
public:
using difference_type = std::ptrdiff_t;
using value_type = meta_any;
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
using iterator_concept = std::bidirectional_iterator_tag;
meta_iterator() noexcept
: meta_iterator{locator<meta_ctx>::value_or()} {}
meta_iterator(const meta_ctx &area) noexcept
: ctx{&area} {}
meta_iterator() = default;
template<typename It>
meta_iterator(const meta_ctx &area, It iter) noexcept
@@ -1705,7 +1767,7 @@ public:
}
private:
const meta_ctx *ctx{};
const meta_ctx *ctx{&locator<meta_ctx>::value_or()};
vtable_type *vtable{};
any handle{};
};
@@ -1728,18 +1790,14 @@ class meta_associative_container::meta_iterator final {
}
public:
using difference_type = std::ptrdiff_t;
using value_type = std::pair<meta_any, meta_any>;
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
using iterator_concept = std::forward_iterator_tag;
meta_iterator() noexcept
: meta_iterator{locator<meta_ctx>::value_or()} {}
meta_iterator(const meta_ctx &area) noexcept
: ctx{&area} {}
meta_iterator() = default;
template<bool KeyOnly, typename It>
meta_iterator(const meta_ctx &area, std::bool_constant<KeyOnly>, It iter) noexcept
@@ -1781,7 +1839,7 @@ public:
}
private:
const meta_ctx *ctx{};
const meta_ctx *ctx{&locator<meta_ctx>::value_or()};
vtable_type *vtable{};
any handle{};
};
@@ -1792,7 +1850,7 @@ private:
* @return The meta value type of the container.
*/
[[nodiscard]] inline meta_type meta_sequence_container::value_type() const noexcept {
return value_type_node ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{};
return (value_type_node != nullptr) ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{};
}
/**
@@ -1851,14 +1909,14 @@ inline bool meta_sequence_container::reserve(const size_type sz) {
* @param value Element value to insert.
* @return A possibly invalid iterator to the inserted element.
*/
inline meta_sequence_container::iterator meta_sequence_container::insert(iterator it, meta_any value) {
inline meta_sequence_container::iterator meta_sequence_container::insert(const iterator &it, meta_any value) {
// this abomination is necessary because only on macos value_type and const_reference are different types for std::vector<bool>
if(const auto vtype = value_type_node(internal::meta_context::from(*ctx)); !const_only && (value.allow_cast({*ctx, vtype}) || value.allow_cast({*ctx, const_reference_node(internal::meta_context::from(*ctx))}))) {
const bool is_value_type = (value.type().info() == *vtype.info);
return insert_fn(*ctx, const_cast<void *>(data), is_value_type ? std::as_const(value).data() : nullptr, is_value_type ? nullptr : std::as_const(value).data(), it);
}
return iterator{*ctx};
return iterator{};
}
/**
@@ -1866,13 +1924,12 @@ inline meta_sequence_container::iterator meta_sequence_container::insert(iterato
* @param it Iterator to the element to remove.
* @return A possibly invalid iterator following the last removed element.
*/
inline meta_sequence_container::iterator meta_sequence_container::erase(iterator it) {
return const_only ? iterator{*ctx} : erase_fn(*ctx, const_cast<void *>(data), it);
inline meta_sequence_container::iterator meta_sequence_container::erase(const iterator &it) {
return const_only ? iterator{} : erase_fn(*ctx, const_cast<void *>(data), it);
}
/**
* @brief Returns a reference to the element at a given location of a container
* (no bounds checking is performed).
* @brief Returns a reference to the element at a given location of a container.
* @param pos The position of the element to return.
* @return A reference to the requested element properly wrapped.
*/
@@ -1890,20 +1947,12 @@ inline meta_sequence_container::iterator meta_sequence_container::erase(iterator
return (data != nullptr);
}
/**
* @brief Returns true if a container is also key-only, false otherwise.
* @return True if the associative container is also key-only, false otherwise.
*/
[[deprecated("use mapped_type() instead")]] [[nodiscard]] inline bool meta_associative_container::key_only() const noexcept {
return (mapped_type_node == nullptr);
}
/**
* @brief Returns the meta key type of a container.
* @return The meta key type of the a container.
*/
[[nodiscard]] inline meta_type meta_associative_container::key_type() const noexcept {
return key_type_node ? meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))} : meta_type{};
return (key_type_node != nullptr) ? meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))} : meta_type{};
}
/**
@@ -1911,12 +1960,12 @@ inline meta_sequence_container::iterator meta_sequence_container::erase(iterator
* @return The meta mapped type of the a container.
*/
[[nodiscard]] inline meta_type meta_associative_container::mapped_type() const noexcept {
return mapped_type_node ? meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))} : meta_type{};
return (mapped_type_node != nullptr) ? meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))} : meta_type{};
}
/*! @copydoc meta_sequence_container::value_type */
[[nodiscard]] inline meta_type meta_associative_container::value_type() const noexcept {
return value_type_node ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{};
return (value_type_node != nullptr) ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{};
}
/*! @copydoc meta_sequence_container::size */
@@ -1952,7 +2001,7 @@ inline bool meta_associative_container::reserve(const size_type sz) {
*/
inline bool meta_associative_container::insert(meta_any key, meta_any value = {}) {
return !const_only && key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))})
&& (!mapped_type_node || value.allow_cast(meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))}))
&& ((mapped_type_node == nullptr) || value.allow_cast(meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))}))
&& insert_fn(const_cast<void *>(data), std::as_const(key).data(), std::as_const(value).data());
}
@@ -1971,7 +2020,7 @@ inline meta_associative_container::size_type meta_associative_container::erase(m
* @return An iterator to the element with the given key, if any.
*/
[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::find(meta_any key) {
return key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))}) ? find_fn(*ctx, const_only ? nullptr : const_cast<void *>(data), data, std::as_const(key).data()) : iterator{*ctx};
return key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))}) ? find_fn(*ctx, const_only ? nullptr : const_cast<void *>(data), data, std::as_const(key).data()) : iterator{};
}
/**

View File

@@ -5,9 +5,10 @@
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../container/dense_map.hpp"
#include "../core/attribute.h"
#include "../core/bit.hpp"
#include "../core/enum.hpp"
#include "../core/fwd.hpp"
#include "../core/type_info.hpp"
@@ -35,31 +36,58 @@ enum class meta_traits : std::uint32_t {
is_array = 0x0020,
is_enum = 0x0040,
is_class = 0x0080,
is_meta_pointer_like = 0x0100,
is_meta_sequence_container = 0x0200,
is_meta_associative_container = 0x0400,
_entt_enum_as_bitmask
is_pointer = 0x0100,
is_meta_pointer_like = 0x0200,
is_meta_sequence_container = 0x0400,
is_meta_associative_container = 0x0800,
_user_defined_traits = 0xFFFF,
_entt_enum_as_bitmask = 0xFFFF
};
template<typename Type>
[[nodiscard]] auto meta_to_user_traits(const meta_traits traits) noexcept {
static_assert(std::is_enum_v<Type>, "Invalid enum type");
constexpr auto shift = popcount(static_cast<std::underlying_type_t<meta_traits>>(meta_traits::_user_defined_traits));
return Type{static_cast<std::underlying_type_t<Type>>(static_cast<std::underlying_type_t<meta_traits>>(traits) >> shift)};
}
template<typename Type>
[[nodiscard]] auto user_to_meta_traits(const Type value) noexcept {
static_assert(std::is_enum_v<Type>, "Invalid enum type");
constexpr auto shift = popcount(static_cast<std::underlying_type_t<meta_traits>>(meta_traits::_user_defined_traits));
const auto traits = static_cast<std::underlying_type_t<internal::meta_traits>>(static_cast<std::underlying_type_t<Type>>(value));
ENTT_ASSERT(traits < ((~static_cast<std::underlying_type_t<meta_traits>>(meta_traits::_user_defined_traits)) >> shift), "Invalid traits");
return meta_traits{traits << shift};
}
struct meta_type_node;
struct meta_custom_node {
id_type type{};
std::shared_ptr<void> value{};
};
struct meta_prop_node {
id_type id{};
meta_type_node (*type)(const meta_context &) noexcept {};
std::shared_ptr<void> value{};
};
struct meta_base_node {
meta_type_node (*type)(const meta_context &) noexcept {};
id_type type{};
meta_type_node (*resolve)(const meta_context &) noexcept {};
const void *(*cast)(const void *) noexcept {};
};
struct meta_conv_node {
id_type type{};
meta_any (*conv)(const meta_ctx &, const void *){};
};
struct meta_ctor_node {
using size_type = std::size_t;
id_type id{};
size_type arity{0u};
meta_type (*arg)(const meta_ctx &, const size_type) noexcept {};
meta_any (*invoke)(const meta_ctx &, meta_any *const){};
@@ -72,42 +100,46 @@ struct meta_dtor_node {
struct meta_data_node {
using size_type = std::size_t;
id_type id{};
meta_traits traits{meta_traits::is_none};
size_type arity{0u};
meta_type_node (*type)(const meta_context &) noexcept {};
meta_type (*arg)(const meta_ctx &, const size_type) noexcept {};
bool (*set)(meta_handle, meta_any){};
meta_any (*get)(const meta_ctx &, meta_handle){};
dense_map<id_type, meta_prop_node, identity> prop{};
meta_custom_node custom{};
std::vector<meta_prop_node> prop{};
};
struct meta_func_node {
using size_type = std::size_t;
id_type id{};
meta_traits traits{meta_traits::is_none};
size_type arity{0u};
meta_type_node (*ret)(const meta_context &) noexcept {};
meta_type (*arg)(const meta_ctx &, const size_type) noexcept {};
meta_any (*invoke)(const meta_ctx &, meta_handle, meta_any *const){};
std::shared_ptr<meta_func_node> next{};
dense_map<id_type, meta_prop_node, identity> prop{};
meta_custom_node custom{};
std::vector<meta_prop_node> prop{};
};
struct meta_template_node {
using size_type = std::size_t;
size_type arity{0u};
meta_type_node (*type)(const meta_context &) noexcept {};
meta_type_node (*resolve)(const meta_context &) noexcept {};
meta_type_node (*arg)(const meta_context &, const size_type) noexcept {};
};
struct meta_type_descriptor {
dense_map<id_type, meta_ctor_node, identity> ctor{};
dense_map<id_type, meta_base_node, identity> base{};
dense_map<id_type, meta_conv_node, identity> conv{};
dense_map<id_type, meta_data_node, identity> data{};
dense_map<id_type, meta_func_node, identity> func{};
dense_map<id_type, meta_prop_node, identity> prop{};
std::vector<meta_ctor_node> ctor{};
std::vector<meta_base_node> base{};
std::vector<meta_conv_node> conv{};
std::vector<meta_data_node> data{};
std::vector<meta_func_node> func{};
std::vector<meta_prop_node> prop{};
};
struct meta_type_node {
@@ -124,24 +156,43 @@ struct meta_type_node {
meta_any (*from_void)(const meta_ctx &, void *, const void *){};
meta_template_node templ{};
meta_dtor_node dtor{};
meta_custom_node custom{};
std::shared_ptr<meta_type_descriptor> details{};
};
template<auto Member, typename Type, typename Value>
[[nodiscard]] auto *find_member(Type &from, const Value value) {
for(auto &&elem: from) {
if((elem.*Member) == value) {
return &elem;
}
}
return static_cast<typename Type::value_type *>(nullptr);
}
[[nodiscard]] inline auto *find_overload(meta_func_node *curr, std::remove_pointer_t<decltype(meta_func_node::invoke)> *const ref) {
while((curr != nullptr) && (curr->invoke != ref)) { curr = curr->next.get(); }
return curr;
}
template<auto Member>
auto *look_for(const meta_context &context, const meta_type_node &node, const id_type id) {
[[nodiscard]] auto *look_for(const meta_context &context, const meta_type_node &node, const id_type id) {
using value_type = typename std::remove_reference_t<decltype((node.details.get()->*Member))>::value_type;
if(node.details) {
if(const auto it = (node.details.get()->*Member).find(id); it != (node.details.get()->*Member).cend()) {
return &it->second;
if(auto *member = find_member<&value_type::id>((node.details.get()->*Member), id); member != nullptr) {
return member;
}
for(auto &&curr: node.details->base) {
if(auto *elem = look_for<Member>(context, curr.second.type(context), id); elem) {
if(auto *elem = look_for<Member>(context, curr.resolve(context), id); elem) {
return elem;
}
}
}
return static_cast<typename std::remove_reference_t<decltype(node.details.get()->*Member)>::mapped_type *>(nullptr);
return static_cast<value_type *>(nullptr);
}
template<typename Type>
@@ -157,13 +208,13 @@ template<typename... Args>
}
[[nodiscard]] inline const void *try_cast(const meta_context &context, const meta_type_node &from, const meta_type_node &to, const void *instance) noexcept {
if(from.info && to.info && *from.info == *to.info) {
if((from.info != nullptr) && (to.info != nullptr) && *from.info == *to.info) {
return instance;
}
if(from.details) {
for(auto &&curr: from.details->base) {
if(const void *elem = try_cast(context, curr.second.type(context), to, curr.second.cast(instance)); elem) {
if(const void *elem = try_cast(context, curr.resolve(context), to, curr.cast(instance)); elem) {
return elem;
}
}
@@ -179,12 +230,14 @@ template<typename Func>
}
if(from.details) {
if(auto it = from.details->conv.find(to.hash()); it != from.details->conv.cend()) {
return func(instance, it->second);
for(auto &&elem: from.details->conv) {
if(elem.type == to.hash()) {
return func(instance, elem);
}
}
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) {
if(auto other = try_convert(context, curr.resolve(context), to, arithmetic_or_enum, curr.cast(instance), func); other) {
return other;
}
}
@@ -219,6 +272,7 @@ template<typename Type>
| (std::is_array_v<Type> ? meta_traits::is_array : meta_traits::is_none)
| (std::is_enum_v<Type> ? meta_traits::is_enum : meta_traits::is_none)
| (std::is_class_v<Type> ? meta_traits::is_class : meta_traits::is_none)
| (std::is_pointer_v<Type> ? meta_traits::is_pointer : meta_traits::is_none)
| (is_meta_pointer_like_v<Type> ? meta_traits::is_meta_pointer_like : meta_traits::is_none)
| (is_complete_v<meta_sequence_container_traits<Type>> ? meta_traits::is_meta_sequence_container : meta_traits::is_none)
| (is_complete_v<meta_associative_container_traits<Type>> ? meta_traits::is_meta_associative_container : meta_traits::is_none),
@@ -233,22 +287,22 @@ template<typename Type>
}
if constexpr(std::is_arithmetic_v<Type>) {
node.conversion_helper = +[](void *bin, const void *value) {
return bin ? static_cast<double>(*static_cast<Type *>(bin) = static_cast<Type>(*static_cast<const double *>(value))) : static_cast<double>(*static_cast<const Type *>(value));
node.conversion_helper = +[](void *lhs, const void *rhs) {
return lhs ? static_cast<double>(*static_cast<Type *>(lhs) = static_cast<Type>(*static_cast<const double *>(rhs))) : static_cast<double>(*static_cast<const Type *>(rhs));
};
} else if constexpr(std::is_enum_v<Type>) {
node.conversion_helper = +[](void *bin, const void *value) {
return bin ? static_cast<double>(*static_cast<Type *>(bin) = static_cast<Type>(static_cast<std::underlying_type_t<Type>>(*static_cast<const double *>(value)))) : static_cast<double>(*static_cast<const Type *>(value));
node.conversion_helper = +[](void *lhs, const void *rhs) {
return lhs ? static_cast<double>(*static_cast<Type *>(lhs) = static_cast<Type>(static_cast<std::underlying_type_t<Type>>(*static_cast<const double *>(rhs)))) : static_cast<double>(*static_cast<const Type *>(rhs));
};
}
if constexpr(!std::is_void_v<Type> && !std::is_function_v<Type>) {
node.from_void = +[](const meta_ctx &ctx, void *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)};
node.from_void = +[](const meta_ctx &ctx, void *elem, const void *celem) {
if(elem) {
return meta_any{ctx, std::in_place_type<std::decay_t<Type> &>, *static_cast<std::decay_t<Type> *>(elem)};
}
return meta_any{ctx, std::in_place_type<const std::decay_t<Type> &>, *static_cast<const std::decay_t<Type> *>(as_const)};
return meta_any{ctx, std::in_place_type<const std::decay_t<Type> &>, *static_cast<const std::decay_t<Type> *>(celem)};
};
}

View File

@@ -1,3 +1,5 @@
// IWYU pragma: always_keep
#ifndef ENTT_META_POINTER_HPP
#define ENTT_META_POINTER_HPP
@@ -21,6 +23,7 @@ struct is_meta_pointer_like<Type *>
* @tparam N Number of elements of the array.
*/
template<typename Type, std::size_t N>
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
struct is_meta_pointer_like<Type (*)[N]>
: std::false_type {};

View File

@@ -13,12 +13,14 @@ namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
struct meta_base_node;
template<typename Type, typename It>
struct meta_range_iterator final {
using difference_type = std::ptrdiff_t;
using value_type = std::pair<id_type, Type>;
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
using iterator_concept = std::random_access_iterator_tag;
@@ -67,7 +69,13 @@ struct meta_range_iterator final {
}
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
return {it[value].first, Type{*ctx, it[value].second}};
if constexpr(std::is_same_v<It, typename decltype(meta_context::value)::const_iterator>) {
return {it[value].first, Type{*ctx, it[value].second}};
} else if constexpr(std::is_same_v<typename std::iterator_traits<It>::value_type, meta_base_node>) {
return {it[value].type, Type{*ctx, it[value]}};
} else {
return {it[value].id, Type{*ctx, it[value]}};
}
}
[[nodiscard]] constexpr pointer operator->() const noexcept {
@@ -75,7 +83,7 @@ struct meta_range_iterator final {
}
[[nodiscard]] constexpr reference operator*() const noexcept {
return {it->first, Type{*ctx, it->second}};
return operator[](0);
}
template<typename... Args>

View File

@@ -85,7 +85,7 @@ template<typename Type>
[[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const type_info &info) noexcept {
auto &&context = internal::meta_context::from(ctx);
const auto *elem = internal::try_resolve(context, info);
return elem ? meta_type{ctx, *elem} : meta_type{};
return (elem != nullptr) ? meta_type{ctx, *elem} : meta_type{};
}
/**

View File

@@ -1,3 +1,5 @@
// IWYU pragma: always_keep
#ifndef ENTT_META_TEMPLATE_HPP
#define ENTT_META_TEMPLATE_HPP

View File

@@ -129,7 +129,7 @@ class meta_function_helper {
template<typename Ret, typename... Args, typename Class>
static constexpr meta_function_descriptor<Type, Ret (Class::*)(Args...)> get_rid_of_noexcept(Ret (Class::*)(Args...));
template<typename Ret, typename Class>
template<typename Ret, typename Class, typename = std::enable_if_t<std::is_member_object_pointer_v<Ret Class::*>>>
static constexpr meta_function_descriptor<Type, Ret Class::*> get_rid_of_noexcept(Ret Class::*);
template<typename Ret, typename... Args>
@@ -327,15 +327,16 @@ template<typename Policy, typename Candidate, typename... Args>
}
template<typename Type, typename Policy, typename Candidate, std::size_t... Index>
[[nodiscard]] meta_any meta_invoke(const meta_ctx &ctx, [[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *args, std::index_sequence<Index...>) {
[[nodiscard]] meta_any meta_invoke(const meta_ctx &ctx, [[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *const args, std::index_sequence<Index...>) {
using descriptor = meta_function_helper_t<Type, std::remove_reference_t<Candidate>>;
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) - waiting for C++20 (and std::span)
if constexpr(std::is_invocable_v<std::remove_reference_t<Candidate>, const Type &, type_list_element_t<Index, typename descriptor::args_type>...>) {
if(const auto *const clazz = instance->try_cast<const Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
return meta_invoke_with_args<Policy>(ctx, std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
}
} else if constexpr(std::is_invocable_v<std::remove_reference_t<Candidate>, Type &, type_list_element_t<Index, typename descriptor::args_type>...>) {
if(auto *const clazz = instance->try_cast<Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) { // NOLINT
if(auto *const clazz = instance->try_cast<Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
return meta_invoke_with_args<Policy>(ctx, std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
}
} else {
@@ -343,15 +344,18 @@ template<typename Type, typename Policy, typename Candidate, std::size_t... Inde
return meta_invoke_with_args<Policy>(ctx, std::forward<Candidate>(candidate), (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
}
}
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
return meta_any{meta_ctx_arg, ctx};
}
template<typename Type, typename... Args, std::size_t... Index>
[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, meta_any *const args, std::index_sequence<Index...>) {
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) - waiting for C++20 (and std::span)
if(((args + Index)->allow_cast<Args>() && ...)) {
return meta_any{ctx, std::in_place_type<Type>, (args + Index)->cast<Args>()...};
}
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
return meta_any{meta_ctx_arg, ctx};
}
@@ -479,6 +483,7 @@ template<typename Type, typename Policy = as_is_t, typename Candidate>
if constexpr(meta_function_helper_t<Type, Candidate>::is_static || std::is_class_v<std::remove_cv_t<std::remove_reference_t<Candidate>>>) {
return internal::meta_invoke<Type, Policy>(ctx, {}, std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
} else {
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - waiting for C++20 (and std::span)
return internal::meta_invoke<Type, Policy>(ctx, *args, std::forward<Candidate>(candidate), args + 1u, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
}
}

View File

@@ -1,59 +0,0 @@
#ifndef ENTT_PLATFORM_ANDROID_NDK_R17_HPP
#define ENTT_PLATFORM_ANDROID_NDK_R17_HPP
/*! @cond TURN_OFF_DOXYGEN */
#ifdef __ANDROID__
# include <android/ndk-version.h>
# if __NDK_MAJOR__ == 17
# include <functional>
# include <type_traits>
# include <utility>
namespace std {
namespace internal {
template<typename Func, typename... Args>
constexpr auto is_invocable(int) -> decltype(std::invoke(std::declval<Func>(), std::declval<Args>()...), std::true_type{});
template<typename, typename...>
constexpr std::false_type is_invocable(...);
template<typename Ret, typename Func, typename... Args>
constexpr auto is_invocable_r(int)
-> std::enable_if_t<decltype(std::is_convertible_v<decltype(std::invoke(std::declval<Func>(), std::declval<Args>()...)), Ret>, std::true_type>;
template<typename, typename, typename...>
constexpr std::false_type is_invocable_r(...);
} // namespace internal
template<typename Func, typename... Args>
struct is_invocable: decltype(internal::is_invocable<Func, Args...>(0)) {};
template<typename Func, typename... Argsv>
inline constexpr bool is_invocable_v = std::is_invocable<Func, Args...>::value;
template<typename Ret, typename Func, typename... Args>
struct is_invocable_r: decltype(internal::is_invocable_r<Ret, Func, Args...>(0)) {};
template<typename Ret, typename Func, typename... Args>
inline constexpr bool is_invocable_r_v = std::is_invocable_r<Ret, Func, Args...>::value;
template<typename Func, typename... Args>
struct invoke_result {
using type = decltype(std::invoke(std::declval<Func>(), std::declval<Args>()...));
};
template<typename Func, typename... Args>
using invoke_result_t = typename std::invoke_result<Func, Args...>::type;
} // namespace std
# endif
#endif
/*! @endcond */
#endif

View File

@@ -5,6 +5,7 @@
namespace entt {
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
template<typename, std::size_t Len = sizeof(double[2]), std::size_t = alignof(double[2])>
class basic_poly;

View File

@@ -30,11 +30,11 @@ struct poly_inspector {
* @return A poly inspector convertible to any type.
*/
template<std::size_t Member, typename... Args>
poly_inspector invoke(Args &&...args) const;
[[nodiscard]] poly_inspector invoke(Args &&...args) const;
/*! @copydoc invoke */
template<std::size_t Member, typename... Args>
poly_inspector invoke(Args &&...args);
[[nodiscard]] poly_inspector invoke(Args &&...args);
};
/**
@@ -47,20 +47,21 @@ template<typename Concept, std::size_t Len, std::size_t Align>
class poly_vtable {
using inspector = typename Concept::template type<poly_inspector>;
template<typename Ret, typename... Args>
static auto vtable_entry(Ret (*)(inspector &, Args...)) -> Ret (*)(basic_any<Len, Align> &, Args...);
template<typename Ret, typename Clazz, typename... Args>
static auto vtable_entry(Ret (*)(Clazz &, Args...))
-> std::enable_if_t<std::is_base_of_v<std::remove_const_t<Clazz>, inspector>, Ret (*)(constness_as_t<basic_any<Len, Align>, Clazz> &, Args...)>;
template<typename Ret, typename... Args>
static auto vtable_entry(Ret (*)(const inspector &, Args...)) -> Ret (*)(const basic_any<Len, Align> &, Args...);
static auto vtable_entry(Ret (*)(Args...))
-> Ret (*)(const basic_any<Len, Align> &, Args...);
template<typename Ret, typename... Args>
static auto vtable_entry(Ret (*)(Args...)) -> Ret (*)(const basic_any<Len, Align> &, Args...);
template<typename Ret, typename Clazz, typename... Args>
static auto vtable_entry(Ret (Clazz::*)(Args...))
-> std::enable_if_t<std::is_base_of_v<Clazz, inspector>, Ret (*)(basic_any<Len, Align> &, Args...)>;
template<typename Ret, typename... Args>
static auto vtable_entry(Ret (inspector::*)(Args...)) -> Ret (*)(basic_any<Len, Align> &, Args...);
template<typename Ret, typename... Args>
static auto vtable_entry(Ret (inspector::*)(Args...) const) -> Ret (*)(const basic_any<Len, Align> &, Args...);
template<typename Ret, typename Clazz, typename... Args>
static auto vtable_entry(Ret (Clazz::*)(Args...) const)
-> std::enable_if_t<std::is_base_of_v<Clazz, inspector>, Ret (*)(const basic_any<Len, Align> &, Args...)>;
template<auto... Candidate>
static auto make_vtable(value_list<Candidate...>) noexcept
@@ -199,9 +200,7 @@ public:
using vtable_type = typename poly_vtable<Concept, Len, Align>::type;
/*! @brief Default constructor. */
basic_poly() noexcept
: storage{},
vtable{} {}
basic_poly() noexcept = default;
/**
* @brief Constructs a poly by directly initializing the new object.
@@ -303,8 +302,8 @@ public:
}
private:
basic_any<Len, Align> storage;
vtable_type vtable;
basic_any<Len, Align> storage{};
vtable_type vtable{};
};
} // namespace entt

View File

@@ -110,7 +110,8 @@ class process {
static_cast<Target *>(this)->aborted();
}
void next(...) const noexcept {}
template<typename... Args>
void next(Args &&...) const noexcept {}
protected:
/**
@@ -165,8 +166,29 @@ public:
/*! @brief Type used to provide elapsed time. */
using delta_type = Delta;
/*! @brief Default constructor. */
constexpr process() = default;
/*! @brief Default copy constructor. */
process(const process &) = default;
/*! @brief Default move constructor. */
process(process &&) noexcept = default;
/**
* @brief Default copy assignment operator.
* @return This process.
*/
process &operator=(const process &) = default;
/**
* @brief Default move assignment operator.
* @return This process.
*/
process &operator=(process &&) noexcept = default;
/*! @brief Default destructor. */
virtual ~process() noexcept {
virtual ~process() {
static_assert(std::is_base_of_v<process, Derived>, "Incorrect use of the class template");
}

View File

@@ -20,8 +20,8 @@ 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;
virtual bool update(Delta, void *) = 0;
virtual void abort(bool) = 0;
// std::shared_ptr because of its type erased allocator which is useful here
std::shared_ptr<basic_process_handler> next;
@@ -109,6 +109,9 @@ public:
explicit basic_scheduler(const allocator_type &allocator)
: handlers{allocator, allocator} {}
/*! @brief Default copy constructor, deleted on purpose. */
basic_scheduler(const basic_scheduler &) = delete;
/**
* @brief Move constructor.
* @param other The instance to move from.
@@ -121,19 +124,28 @@ public:
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
basic_scheduler(basic_scheduler &&other, const allocator_type &allocator) noexcept
basic_scheduler(basic_scheduler &&other, const allocator_type &allocator)
: handlers{container_type{std::move(other.handlers.first()), allocator}, allocator} {
ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying a scheduler is not allowed");
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a scheduler is not allowed");
}
/*! @brief Default destructor. */
~basic_scheduler() = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This process scheduler.
*/
basic_scheduler &operator=(const basic_scheduler &) = delete;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This scheduler.
* @return This process 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);
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a scheduler is not allowed");
swap(other);
return *this;
}
@@ -141,7 +153,7 @@ public:
* @brief Exchanges the contents with those of a given scheduler.
* @param other Scheduler to exchange the content with.
*/
void swap(basic_scheduler &other) {
void swap(basic_scheduler &other) noexcept {
using std::swap;
swap(handlers, other.handlers);
}

View File

@@ -85,7 +85,7 @@ public:
}
[[nodiscard]] constexpr reference operator*() const noexcept {
return (*this)[0];
return operator[](0);
}
[[nodiscard]] constexpr pointer operator->() const noexcept {
@@ -154,17 +154,17 @@ class resource_cache {
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
using container_allocator = typename alloc_traits::template rebind_alloc<std::pair<const id_type, typename Loader::result_type>>;
using container_type = dense_map<id_type, typename Loader::result_type, identity, std::equal_to<id_type>, container_allocator>;
using container_type = dense_map<id_type, typename Loader::result_type, identity, std::equal_to<>, container_allocator>;
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Resource type. */
using value_type = Type;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Loader type. */
using loader_type = Loader;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Input iterator type. */
using iterator = internal::resource_cache_iterator<Type, typename container_type::iterator>;
/*! @brief Constant input iterator type. */
@@ -201,7 +201,7 @@ public:
: pool{std::piecewise_construct, std::forward_as_tuple(other.pool.first(), allocator), std::forward_as_tuple(other.pool.second())} {}
/*! @brief Default move constructor. */
resource_cache(resource_cache &&) = default;
resource_cache(resource_cache &&) noexcept = default;
/**
* @brief Allocator-extended move constructor.
@@ -211,6 +211,9 @@ public:
resource_cache(resource_cache &&other, const allocator_type &allocator)
: pool{std::piecewise_construct, std::forward_as_tuple(std::move(other.pool.first()), allocator), std::forward_as_tuple(std::move(other.pool.second()))} {}
/*! @brief Default destructor. */
~resource_cache() = default;
/**
* @brief Default copy assignment operator.
* @return This cache.
@@ -221,7 +224,7 @@ public:
* @brief Default move assignment operator.
* @return This cache.
*/
resource_cache &operator=(resource_cache &&) = default;
resource_cache &operator=(resource_cache &&) noexcept = default;
/**
* @brief Returns the associated allocator.

View File

@@ -37,8 +37,8 @@ public:
: value{} {}
/**
* @brief Creates a handle from a weak pointer, namely a resource.
* @param res A weak pointer to a resource.
* @brief Creates a new resource handle.
* @param res A handle to a resource.
*/
explicit resource(handle_type res) noexcept
: value{std::move(res)} {}
@@ -77,6 +77,9 @@ public:
resource(resource<Other> &&other) noexcept
: value{std::move(other.value)} {}
/*! @brief Default destructor. */
~resource() = default;
/**
* @brief Default copy assignment operator.
* @return This resource handle.
@@ -95,9 +98,8 @@ public:
* @param other The handle to copy from.
* @return This resource handle.
*/
template<typename Other>
std::enable_if_t<is_acceptable_v<Other>, resource &>
operator=(const resource<Other> &other) noexcept {
template<typename Other, typename = std::enable_if_t<is_acceptable_v<Other>>>
resource &operator=(const resource<Other> &other) noexcept {
value = other.value;
return *this;
}
@@ -108,13 +110,21 @@ public:
* @param other The handle to move from.
* @return This resource handle.
*/
template<typename Other>
std::enable_if_t<is_acceptable_v<Other>, resource &>
operator=(resource<Other> &&other) noexcept {
template<typename Other, typename = std::enable_if_t<is_acceptable_v<Other>>>
resource &operator=(resource<Other> &&other) noexcept {
value = std::move(other.value);
return *this;
}
/**
* @brief Exchanges the content with that of a given resource.
* @param other Resource to exchange the content with.
*/
void swap(resource &other) noexcept {
using std::swap;
swap(value, other.value);
}
/**
* @brief Returns a reference to the managed resource.
*
@@ -148,6 +158,19 @@ public:
return static_cast<bool>(value);
}
/*! @brief Releases the ownership of the managed resource. */
void reset() {
value.reset();
}
/**
* @brief Replaces the managed resource.
* @param other A handle to a resource.
*/
void reset(handle_type other) {
value = std::move(other);
}
/**
* @brief Returns the underlying resource handle.
* @return The underlying resource handle.

View File

@@ -27,7 +27,7 @@ constexpr auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)
template<typename Class, typename Ret, typename... Args, typename... Other>
constexpr auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...);
template<typename Class, typename Type, typename... Other>
template<typename Class, typename Type, typename... Other, typename = std::enable_if_t<std::is_member_object_pointer_v<Type Class::*>>>
constexpr auto function_pointer(Type Class::*, Other &&...) -> Type (*)();
template<typename... Type>
@@ -64,47 +64,35 @@ class delegate;
*/
template<typename Ret, typename... Args>
class delegate<Ret(Args...)> {
using return_type = std::remove_const_t<Ret>;
using delegate_type = return_type(const void *, Args...);
template<auto Candidate, std::size_t... Index>
[[nodiscard]] auto wrap(std::index_sequence<Index...>) noexcept {
return [](const void *, Args... args) -> Ret {
return [](const void *, Args... args) -> return_type {
[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
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))...));
}
[[maybe_unused]] constexpr auto offset = !std::is_invocable_r_v<Ret, decltype(Candidate), type_list_element_t<Index, type_list<Args...>>...> * (sizeof...(Args) - sizeof...(Index));
return static_cast<Ret>(std::invoke(Candidate, std::forward<type_list_element_t<Index + offset, type_list<Args...>>>(std::get<Index + offset>(arguments))...));
};
}
template<auto Candidate, typename Type, std::size_t... Index>
[[nodiscard]] auto wrap(Type &, std::index_sequence<Index...>) noexcept {
return [](const void *payload, Args... args) -> Ret {
[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
return [](const void *payload, Args... args) -> return_type {
Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
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))...));
}
[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
[[maybe_unused]] constexpr auto offset = !std::is_invocable_r_v<Ret, decltype(Candidate), Type &, type_list_element_t<Index, type_list<Args...>>...> * (sizeof...(Args) - sizeof...(Index));
return static_cast<Ret>(std::invoke(Candidate, *curr, std::forward<type_list_element_t<Index + offset, type_list<Args...>>>(std::get<Index + offset>(arguments))...));
};
}
template<auto Candidate, typename Type, std::size_t... Index>
[[nodiscard]] auto wrap(Type *, std::index_sequence<Index...>) noexcept {
return [](const void *payload, Args... args) -> Ret {
[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
return [](const void *payload, Args... args) -> return_type {
Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
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))...));
}
[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
[[maybe_unused]] constexpr auto offset = !std::is_invocable_r_v<Ret, decltype(Candidate), Type *, type_list_element_t<Index, type_list<Args...>>...> * (sizeof...(Args) - sizeof...(Index));
return static_cast<Ret>(std::invoke(Candidate, curr, std::forward<type_list_element_t<Index + offset, type_list<Args...>>>(std::get<Index + offset>(arguments))...));
};
}
@@ -117,9 +105,7 @@ public:
using result_type = Ret;
/*! @brief Default constructor. */
delegate() noexcept
: instance{nullptr},
fn{nullptr} {}
delegate() noexcept = default;
/**
* @brief Constructs a delegate with a given object or payload, if any.
@@ -151,7 +137,7 @@ public:
instance = nullptr;
if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Args...>) {
fn = [](const void *, Args... args) -> Ret {
fn = [](const void *, Args... args) -> return_type {
return Ret(std::invoke(Candidate, std::forward<Args>(args)...));
};
} else if constexpr(std::is_member_pointer_v<decltype(Candidate)>) {
@@ -181,7 +167,7 @@ public:
instance = &value_or_instance;
if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type &, Args...>) {
fn = [](const void *payload, Args... args) -> Ret {
fn = [](const void *payload, Args... args) -> return_type {
Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
return Ret(std::invoke(Candidate, *curr, std::forward<Args>(args)...));
};
@@ -205,7 +191,7 @@ public:
instance = value_or_instance;
if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type *, Args...>) {
fn = [](const void *payload, Args... args) -> Ret {
fn = [](const void *payload, Args... args) -> return_type {
Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
return Ret(std::invoke(Candidate, curr, std::forward<Args>(args)...));
};
@@ -295,8 +281,8 @@ public:
}
private:
const void *instance;
function_type *fn;
const void *instance{};
delegate_type *fn{};
};
/**

View File

@@ -25,7 +25,7 @@ struct basic_dispatcher_handler {
virtual void publish() = 0;
virtual void disconnect(void *) = 0;
virtual void clear() noexcept = 0;
virtual std::size_t size() const noexcept = 0;
[[nodiscard]] virtual std::size_t size() const noexcept = 0;
};
template<typename Type, typename Allocator>
@@ -115,7 +115,7 @@ class basic_dispatcher {
using alloc_traits = std::allocator_traits<Allocator>;
using container_allocator = typename alloc_traits::template rebind_alloc<std::pair<const key_type, mapped_type>>;
using container_type = dense_map<key_type, mapped_type, identity, std::equal_to<key_type>, container_allocator>;
using container_type = dense_map<key_type, mapped_type, identity, std::equal_to<>, container_allocator>;
template<typename Type>
[[nodiscard]] handler_type<Type> &assure(const id_type id) {
@@ -158,6 +158,9 @@ public:
explicit basic_dispatcher(const allocator_type &allocator)
: pools{allocator, allocator} {}
/*! @brief Default copy constructor, deleted on purpose. */
basic_dispatcher(const basic_dispatcher &) = delete;
/**
* @brief Move constructor.
* @param other The instance to move from.
@@ -170,19 +173,28 @@ public:
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
basic_dispatcher(basic_dispatcher &&other, const allocator_type &allocator) noexcept
basic_dispatcher(basic_dispatcher &&other, const allocator_type &allocator)
: pools{container_type{std::move(other.pools.first()), allocator}, allocator} {
ENTT_ASSERT(alloc_traits::is_always_equal::value || pools.second() == other.pools.second(), "Copying a dispatcher is not allowed");
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a dispatcher is not allowed");
}
/*! @brief Default destructor. */
~basic_dispatcher() = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This dispatcher.
*/
basic_dispatcher &operator=(const basic_dispatcher &) = delete;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This dispatcher.
*/
basic_dispatcher &operator=(basic_dispatcher &&other) noexcept {
ENTT_ASSERT(alloc_traits::is_always_equal::value || pools.second() == other.pools.second(), "Copying a dispatcher is not allowed");
pools = std::move(other.pools);
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a dispatcher is not allowed");
swap(other);
return *this;
}
@@ -190,7 +202,7 @@ public:
* @brief Exchanges the contents with those of a given dispatcher.
* @param other Dispatcher to exchange the content with.
*/
void swap(basic_dispatcher &other) {
void swap(basic_dispatcher &other) noexcept {
using std::swap;
swap(pools, other.pools);
}
@@ -210,7 +222,7 @@ public:
* @return The number of pending events for the given type.
*/
template<typename Type>
size_type size(const id_type id = type_hash<Type>::value()) const noexcept {
[[nodiscard]] size_type size(const id_type id = type_hash<Type>::value()) const noexcept {
const auto *cpool = assure<std::decay_t<Type>>(id);
return cpool ? cpool->size() : 0u;
}
@@ -219,7 +231,7 @@ public:
* @brief Returns the total number of pending events.
* @return The total number of pending events.
*/
size_type size() const noexcept {
[[nodiscard]] size_type size() const noexcept {
size_type count{};
for(auto &&cpool: pools.first()) {

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