Compare commits

..

884 Commits

Author SHA1 Message Date
skypjack
1333fa5312 entity: refine requires on entt_traits for enum 2026-03-27 15:41:04 +01:00
skypjack
171700153e stl: internal changes 2026-03-26 18:31:26 +01:00
skypjack
0ebf837e6a entt_traits: relax class constraints for backward compatibility 2026-03-26 17:42:05 +01:00
skypjack
093bb5bd89 entity: let the traits check the concept for us 2026-03-25 10:02:23 +01:00
skypjack
720dda4103 entity_like: move the concept to the file it belongs to, and fix it - see #1244 2026-03-25 10:01:15 +01:00
skypjack
05d11b7fa0 sparse_set: reduce impact of entity_like, and prepare to fix it 2026-03-25 00:03:59 +01:00
skypjack
9b94f09999 component_traits: reduce impact of entity_like, and prepare to fix it 2026-03-24 15:50:47 +01:00
skypjack
ca32a3668a test: reduce impact of entity_like, and prepare to fix it 2026-03-24 15:45:30 +01:00
skypjack
060eda7586 davey: reduce impact of entity_like, and prepare to fix it 2026-03-24 15:44:40 +01:00
skypjack
7c463516b1 storage_for 2026-03-24 14:16:05 +01:00
skypjack
f23f923608 storage_type: reduce impact of entity_like, and prepare to fix it 2026-03-24 14:15:12 +01:00
skypjack
8cc2eb43b3 storage: reduce impact of entity_like, and prepare to fix it 2026-03-24 14:12:43 +01:00
skypjack
6d31b33a10 view: minor changes on internal stuff 2026-03-24 14:11:26 +01:00
skypjack
fe517b0592 registry: reduce impact of entity_like, and prepare to fix it 2026-03-24 14:10:17 +01:00
skypjack
06bcc770f0 config: config injection 2026-03-23 16:46:24 +01:00
skypjack
75769cde27 build: refine SETUP_BASIC_TEST to also support include directories 2026-03-21 15:29:21 +01:00
skypjack
eec10a861e storage: minor change to suppress doc warning 2026-03-19 19:20:37 +01:00
skypjack
e099db8e15 storage: no fwd ref, no NOLINT 2026-03-19 19:09:37 +01:00
skypjack
9905ed6cff type_info: no fwd ref, no NOLINT 2026-03-19 18:33:12 +01:00
skypjack
6d6ba9b175 doc: a note about context variables - close #1326 2026-03-18 15:40:16 +01:00
skypjack
793ea69195 dispatcher: refine ::trigger API - close #1307 2026-03-17 15:27:10 +01:00
skypjack
ef088a298f updated TODO 2026-03-17 15:25:06 +01:00
skypjack
814a3e1404 cleanup 2026-03-16 14:29:01 +01:00
skypjack
f79c6dbde1 storage: constrain iterator types - close #1323 2026-03-16 14:15:33 +01:00
skypjack
6ca3db7de4 storage: reintroduce support to args for empty types - see #1323 2026-03-16 14:10:10 +01:00
skypjack
9c5281c79a sparse_set: make ::pop_all check pages before filling them 2026-03-12 09:50:27 +01:00
skypjack
136b137940 test: cover sparse_set::clear with empty pages (and make the sanitizer complain on the CI hopefully) 2026-03-12 09:29:25 +01:00
skypjack
1de7c8bbd2 view: avoid warnings and add a test to trigger them on the CI in future - close #1328 2026-03-11 16:38:01 +01:00
skypjack
93cb729322 test: use the mixin design a little more 2026-03-03 15:29:32 +01:00
skypjack
7d931a8df3 meta: avoid shadow warnings 2026-03-03 12:40:54 +01:00
skypjack
e3a736a2a5 test: use the mixin design a little more 2026-03-03 11:13:02 +01:00
skypjack
6b8fe4deff test: use the mixin design a little more 2026-03-03 10:10:46 +01:00
skypjack
3cf061201a test: use the mixin design a little more 2026-03-03 09:42:52 +01:00
skypjack
70d908d854 test: minor changes 2026-03-02 14:58:54 +01:00
skypjack
ca9a5f22d0 test: empty_mixin 2026-03-02 14:51:10 +01:00
skypjack
b7e8bb265f test: use the mixin design a little more 2026-03-02 14:30:43 +01:00
skypjack
b5797912d3 test: use the mixin design a little more 2026-03-02 14:09:26 +01:00
skypjack
c113dfe082 test: use the mixin design a little more 2026-03-02 13:54:46 +01:00
skypjack
24a7cc0deb sparse_set: review internal functions (perf) 2026-03-02 13:54:28 +01:00
skypjack
4d7fce0edc doc: review meta container support section 2026-02-27 17:36:34 +01:00
skypjack
79188ad748 storage: refine ::pop to improve perf for trivially destructible types - close #1311 2026-02-26 09:34:50 +01:00
skypjack
b1758e221b storage: small improvement for shrink_to_size 2026-02-26 09:19:52 +01:00
skypjack
017123be3e storage: skip ::clear for-loop for trivially destructible types - see #1311 2026-02-25 15:54:03 +01:00
skypjack
b43bbd5e25 updated TODO 2026-02-25 15:52:55 +01:00
skypjack
bb6a4abd6b sparse_set: ::pop_all early exit on empty sets 2026-02-24 15:08:01 +01:00
skypjack
3209cd854e test: meta dereference const correctness for non-pointer non-pointer-like types 2026-02-24 15:07:16 +01:00
skypjack
d20fc9551f meta: coding style 2026-02-23 11:24:59 +01:00
skypjack
9fdc43f6f8 meta: std::forward_iterator -> entt::stl::forward_iterator 2026-02-23 09:06:18 +01:00
skypjack
4e7833540c updated TODO 2026-02-23 08:37:02 +01:00
skypjack
1c456e2cb6 test: refine common classes to cover both trivially destructible and non trivially destructible types in the storage tests - see #1311 2026-02-23 08:36:47 +01:00
skypjack
17fc67de1f meta: revert last commit, the intermediate class is actually useful for user customizations 2026-02-23 08:29:19 +01:00
skypjack
3e47fb6bbd meta: cleanup 2026-02-22 18:16:50 +01:00
skypjack
8d4597ead0 meta: auto-detect meta containers, use traits to opt-out 2026-02-22 15:17:02 +01:00
skypjack
e14033b215 meta: get rid of reserve_aware_container 2026-02-20 11:55:40 +01:00
skypjack
41a8a7d921 test: cleanup 2026-02-20 10:31:54 +01:00
skypjack
3b3638ac8a test: non_trivially_destructible with storage (test coverage for future optimizations) 2026-02-20 10:27:59 +01:00
skypjack
34d2994cf3 test: non_trivially_destructible type 2026-02-20 10:26:54 +01:00
skypjack
011054e515 sparse_set: internal change (perf) - see #1311 2026-02-20 09:38:56 +01:00
skypjack
4321f5ece8 cleanup TODO 2026-02-20 09:38:28 +01:00
skypjack
0b3827366d davey: wrap imgui with __has_include - close #1312 2026-02-19 09:41:29 +01:00
skypjack
7cd612cc87 davey: walk up bases - close #1313 2026-02-18 15:18:04 +01:00
skypjack
657f2aade4 doc: more about the dispatcher - close #1299 2026-02-17 09:45:29 +01:00
skypjack
8cfb5534ab build: update bazel file 2026-02-16 10:58:51 +01:00
skypjack
737676f23f meta: support all dereferenceable types (i.e. optional) 2026-02-16 10:49:00 +01:00
skypjack
c6a8cf3de6 meta: pointer vs pointer-like rework (prepare to support all dereferenceable types) 2026-02-16 10:39:06 +01:00
skypjack
c18f1c3c07 test: internal changes 2026-02-16 10:25:59 +01:00
skypjack
fc4605a6b2 meta: non-const meta_any::operator* 2026-02-13 13:07:29 +01:00
skypjack
849852464e meta: prepare to support all dereferenceable types with meta_any 2026-02-12 19:06:14 +01:00
skypjack
071febbc0c any: as_ref() on empty returns empty 2026-02-12 09:18:26 +01:00
skypjack
0bb54130c9 meta: invalid meta_any when copying non-copyable types 2026-02-11 18:43:36 +01:00
skypjack
e7650e263f entt: cleanup 2026-02-10 16:37:49 +01:00
skypjack
f93d28981f entt: there is a reason why an allocator concept does not exist... 2026-02-10 16:29:46 +01:00
skypjack
106af0f6d8 container: cleanup 2026-02-09 17:01:10 +01:00
skypjack
3b71902777 entt: constrain allocator types 2026-02-09 16:57:59 +01:00
skypjack
7b46448cd9 test: please older compilers (like g++13) 2026-02-06 18:49:49 +01:00
skypjack
ed590b7ac8 concepts: allocator_like 2026-02-06 18:33:57 +01:00
skypjack
bde5d86690 test: concepts 2026-02-06 18:30:46 +01:00
skypjack
06491c6a0a updated TODO 2026-02-06 17:05:05 +01:00
skypjack
f22c7d931b test: iterator concepts workarounds 2026-02-05 18:43:22 +01:00
skypjack
2edc59d8bf cleanup TODO 2026-02-05 18:29:50 +01:00
skypjack
7fd8c1d2b1 sigh: minor changes 2026-02-04 09:59:05 +01:00
skypjack
4e191a95f4 test: cleanup 2026-02-04 09:56:08 +01:00
skypjack
a15a7653ee entity: more concepts, less sfinae 2026-02-04 09:14:49 +01:00
skypjack
a77b34f929 view: minor changes 2026-02-03 17:56:55 +01:00
skypjack
c5ae1fc8f5 doc: update to reflect the last changes 2026-02-03 10:36:10 +01:00
skypjack
39806a525f is_meta_pointer_like: refine definition 2026-02-03 10:35:58 +01:00
skypjack
8718ee3279 component_traits: refine definition 2026-02-03 10:35:38 +01:00
skypjack
b79d78350f type_info: refine definitions 2026-02-03 10:35:03 +01:00
skypjack
e08302e169 test: enclose expression in parentheses 2026-02-02 14:04:46 +01:00
skypjack
6ce08bc01d entt: enclose expression in parentheses 2026-02-02 14:04:39 +01:00
skypjack
8e5b05301b any: less sfinae, more concepts 2026-02-02 14:01:35 +01:00
skypjack
0e5537e4a9 meta: less sfinae, more concepts 2026-02-02 14:01:20 +01:00
skypjack
f28104bfac test: less sfinae, more concepts 2026-02-02 14:01:06 +01:00
skypjack
c7828b380d resource: less sfinae, more concepts 2026-02-02 14:00:26 +01:00
skypjack
101535b666 poly: less sfinae, more concepts 2026-02-02 13:59:34 +01:00
skypjack
efe67a700d container: less sfinae, more concepts 2026-02-02 13:59:13 +01:00
skypjack
7d2321efde type_traits: more concepts, less sfinae 2026-02-02 09:34:40 +01:00
skypjack
0891adb135 updated TODO 2026-02-02 09:34:24 +01:00
skypjack
222cb7b16e meta: refine concepts for the best 2026-01-30 18:47:48 +01:00
skypjack
924cb5f9e0 test: typo 2026-01-30 18:47:36 +01:00
skypjack
392e522b62 meta: more concepts, less sfinae 2026-01-30 18:31:08 +01:00
skypjack
3cc3a03c40 test: internal changes 2026-01-30 18:30:50 +01:00
skypjack
ebed6d8b7a any: more concept-ness :) 2026-01-30 18:30:36 +01:00
skypjack
e09593b44b entt: use cvref_unqualified as needed 2026-01-30 18:15:35 +01:00
skypjack
3971113c3c concepts: cvref_unqualified (QoL series) 2026-01-30 18:15:25 +01:00
skypjack
e74aac0626 entt: more concepts, less sfinae 2026-01-30 17:53:35 +01:00
skypjack
9509d37431 stl: refine iterator concepts 2026-01-29 16:59:28 +01:00
skypjack
cabdc1d1e3 stl: typo 2026-01-29 15:28:03 +01:00
skypjack
cf7be25ec9 workflow: try to enable C++23 2026-01-29 15:01:26 +01:00
skypjack
75d09d6f8d stl: cleanup 2026-01-29 15:01:04 +01:00
skypjack
413f1cba9e updated TODO 2026-01-29 14:29:32 +01:00
skypjack
20263bace1 meta_factory: refine internal logic 2026-01-29 14:28:11 +01:00
skypjack
f1708811e4 stl: typo 2026-01-29 14:19:54 +01:00
skypjack
a7f0371f9c stl: minor changes 2026-01-29 14:15:42 +01:00
skypjack
9c66e7219d stl: concepts.hpp -> iterator.hpp 2026-01-29 13:54:55 +01:00
skypjack
b00038a034 concepts: refine iterator concepts 2026-01-29 13:51:45 +01:00
skypjack
2a5f049de8 entity: replace std::output_iterator with stl::output_iterator 2026-01-29 13:42:13 +01:00
skypjack
c53b9c0b30 entt::stl -> stl:: 2026-01-29 13:41:20 +01:00
skypjack
84ba626acd dense_map: get rid of unnecessary template parameter 2026-01-29 12:49:59 +01:00
skypjack
9d0028e036 stl: fake iterator concepts to make up for the lack on some platforms 2026-01-29 12:36:02 +01:00
skypjack
ad330e4c94 meta: support name clashing between meta data/func and type 2026-01-29 09:43:38 +01:00
skypjack
6ac13b140f meta: avoid unnecessary allocations 2026-01-29 08:26:48 +01:00
skypjack
f6e22dc4ba meta: avoid looking up meta types again and again 2026-01-29 08:20:52 +01:00
skypjack
5c2326fa50 updated TODO 2026-01-28 16:17:23 +01:00
skypjack
1000562b1c container: more concepts, less sfinae 2026-01-28 12:31:29 +01:00
skypjack
22d39a0163 storage: update basic_storage definition 2026-01-28 12:30:41 +01:00
skypjack
272844ac90 view: use concepts to get rid of dummy template parameters 2026-01-28 12:29:50 +01:00
skypjack
fdbf30abc4 view: update basic_view definition 2026-01-28 12:29:00 +01:00
skypjack
5e92b9113f component_traits: refine internal checks 2026-01-28 12:27:55 +01:00
skypjack
4b43368116 is_iterator: more concepts, less sfinae 2026-01-28 12:27:18 +01:00
skypjack
c5743a6d6c compressed_pair: add missing parentheses 2026-01-27 19:11:30 +01:00
skypjack
d77d6fac48 compressed_pair: more concepts, less sfinae 2026-01-27 19:04:17 +01:00
skypjack
b16c5c3454 meta: more concepts, less sfinae 2026-01-27 18:51:06 +01:00
skypjack
ad69bec438 mixin: more concepts, less sfinae 2026-01-27 18:50:38 +01:00
skypjack
42c9e41c37 has_value_type: more concepts, less sfinae 2026-01-26 14:44:30 +01:00
skypjack
32dc7a37c2 is_transparent: more concepts, less sfinae 2026-01-26 14:44:08 +01:00
skypjack
c2e7139a70 test: internal changes 2026-01-26 14:43:29 +01:00
skypjack
778d78d98c compressed_pair: more concepts, less sfinae 2026-01-26 10:20:48 +01:00
skypjack
04bf3bcbfa meta: meta_policy concept 2026-01-26 09:19:57 +01:00
skypjack
9173c4d2f1 clang-format: refine config 2026-01-26 09:19:45 +01:00
skypjack
0d575c9a97 doc: typo 2026-01-23 19:00:07 +01:00
skypjack
4a2213ef4e tuple: fixed typo 2026-01-23 18:46:30 +01:00
skypjack
c6235fc047 ident: minor changes 2026-01-23 18:46:15 +01:00
skypjack
0cc794e921 clang-format: update config 2026-01-23 18:23:32 +01:00
skypjack
23dc5eb501 bit: refine fast_mod implementation 2026-01-23 18:06:06 +01:00
skypjack
100edffc96 enum: add missing include 2026-01-23 18:05:52 +01:00
skypjack
f7d1a7d849 doc: typo 2026-01-23 18:01:51 +01:00
skypjack
1bb590a57e enum: enum_bitmask concept 2026-01-23 18:01:16 +01:00
skypjack
80bb50c06d entity: coding style 2026-01-23 18:01:07 +01:00
skypjack
b8cb5de8dd dispatcher: try to also please g++12 2026-01-22 17:54:02 +01:00
skypjack
beda424de1 dispatcher: support enqueueing the same event type from listeners - close #1303 2026-01-22 17:19:16 +01:00
skypjack
7b456b9044 natvis: update dispatcher views 2026-01-22 15:46:25 +01:00
skypjack
6cf91dc0e0 updated TODO 2026-01-22 15:46:11 +01:00
skypjack
f69bfbb9f1 locator: oops :) 2026-01-21 16:43:54 +01:00
skypjack
5ea023c853 cleanup TODO 2026-01-21 16:43:40 +01:00
skypjack
9687efcaf6 locator: constrain a bit the interface 2026-01-21 16:25:17 +01:00
skypjack
5df5d165af stl: refine to_address impl 2026-01-21 16:24:56 +01:00
skypjack
c4cc708538 entity: clang-format only changes 2026-01-21 16:23:54 +01:00
skypjack
d552039b95 update clang-format config 2026-01-21 16:23:35 +01:00
skypjack
ee3738d5e7 doxy: TURN_OFF_DOXYGEN -> ENTT_INTERNAL 2026-01-21 15:27:41 +01:00
skypjack
80ffcda35d iota_iterator: constrain value type 2026-01-21 15:00:05 +01:00
skypjack
7d50da0463 iterable_adaptor: constrain iterator types 2026-01-21 14:58:31 +01:00
skypjack
47a6a27378 meta: internal changes/share common base 2026-01-21 14:56:09 +01:00
skypjack
a4315a9241 meta: export meta_base 2026-01-20 09:24:35 +01:00
skypjack
7ef8da9e87 natvis: meta_base view 2026-01-20 09:17:40 +01:00
skypjack
2aecdfe497 meta: added meta_base to further reduce implicit hidden lookups 2026-01-20 09:17:24 +01:00
skypjack
d43c5427e6 doc: minor changes 2026-01-20 09:05:35 +01:00
skypjack
9697549749 natvis: refine views for meta 2026-01-20 09:05:21 +01:00
skypjack
68e64d33ec meta: cleanup 2026-01-19 17:14:58 +01:00
skypjack
9cd81bcc37 natvis: meta 2026-01-19 17:14:23 +01:00
skypjack
72b2c5ac8e meta: update meta_custom_node for consistency 2026-01-19 17:14:17 +01:00
skypjack
94d05fab2f updated TODO 2026-01-19 17:10:41 +01:00
skypjack
be45cfafda meta: cleanup/prepare for meta_base first citizen type 2026-01-19 17:10:17 +01:00
skypjack
f0d2f0a415 meta: cleanup 2026-01-19 16:24:01 +01:00
skypjack
bba3722b2e algorithm: constrain iterator types 2026-01-19 15:48:31 +01:00
skypjack
5b9aa95a65 graph: cleanup 2026-01-19 15:48:17 +01:00
skypjack
818ddc4e3e entity: cleanup 2026-01-19 15:48:08 +01:00
skypjack
77a66812d4 container: cleanup 2026-01-19 15:47:59 +01:00
skypjack
5feca37a78 updated TODO 2026-01-19 15:47:47 +01:00
skypjack
682f6b2392 entity: constrain iterator types 2026-01-19 13:56:39 +01:00
skypjack
d447f8c14a meta: constrain iterator types 2026-01-19 12:00:11 +01:00
skypjack
ea5deaf3c9 updated TODO 2026-01-19 11:49:44 +01:00
skypjack
4793cbfd20 graph: constrain iterator types 2026-01-18 17:04:29 +01:00
skypjack
504768ffb3 container: constrain iterator types 2026-01-18 16:18:17 +01:00
skypjack
0ed7e3163e build: cxx standard update 2026-01-17 17:41:33 +01:00
skypjack
11deba942c doc: typo 2026-01-17 01:07:48 +01:00
skypjack
d13851f3b9 entity: add missing include 2026-01-17 01:02:19 +01:00
skypjack
00729e19b4 view: cleanup 2026-01-17 00:59:50 +01:00
skypjack
cbeda51f8d updated copyright :) 2026-01-16 09:43:20 +01:00
skypjack
c91b1c9155 entity: use entity_like as needed to constrain types in the ecs module 2026-01-16 08:28:24 +01:00
skypjack
cb499c5aea handle: cleanup operators 2026-01-16 08:12:15 +01:00
skypjack
f951d5d5a7 entity: review operators, and cleanup things 2026-01-15 18:24:24 +01:00
skypjack
5d9afc070e entity: constrained entt_traits, and null_t/tombstone_t functionalities 2026-01-15 18:23:19 +01:00
skypjack
6dfd760649 updated TODO 2026-01-15 18:21:48 +01:00
skypjack
439265c486 entity: introduce the entity_like concept 2026-01-15 18:21:37 +01:00
skypjack
f81f34e6fa entity: constrained type instead of sfinae based design for traits 2026-01-15 18:21:03 +01:00
skypjack
cc7d4aaeea handle: minor changes 2026-01-14 15:20:32 +01:00
skypjack
9ac749d7b6 hashed_string: operator==/operator<=> 2026-01-14 15:20:17 +01:00
skypjack
a2c2422fdf storage: avoid warnings/errors due to name conflicts 2026-01-13 16:01:25 +01:00
skypjack
dc6366d6d1 registry_storage_iterator: operator==/operator<=> 2026-01-13 15:32:13 +01:00
skypjack
5a3faa7256 view: review operators 2026-01-13 15:31:57 +01:00
skypjack
4df87a007d table: operator==/operator<=> 2026-01-13 15:31:39 +01:00
skypjack
5b5821ecc1 storage_iterator: operator==/operator<=> 2026-01-12 15:36:13 +01:00
skypjack
b3eef84102 sparse_set_iterator: operator==/operator<=> 2026-01-12 15:35:46 +01:00
skypjack
3066d0ed5c resource: operator==/operator<=> 2026-01-12 15:33:06 +01:00
skypjack
88e5fc2609 resource_cache_iterator: operator==/operator<=> 2026-01-12 15:32:50 +01:00
skypjack
20f395ba36 meta_range_iterator: operator==/operator<=> 2026-01-12 13:06:21 +01:00
skypjack
df10c0fcdc type_info: operator==/operator<=> 2026-01-12 12:59:02 +01:00
skypjack
f119bff9d5 container: use raw pointers to avoid issues with __wrap_iter 2026-01-12 12:01:37 +01:00
skypjack
1f41b86665 test: add missing include 2026-01-10 18:27:04 +01:00
skypjack
5ef5d31419 container: add missing include 2026-01-10 18:26:58 +01:00
skypjack
86148d12ee dense_set: operator==/operator<=> 2026-01-09 15:58:19 +01:00
skypjack
3cae3fbb2b dense_map: operator==/operator<=> 2026-01-09 15:58:09 +01:00
skypjack
40109e9ae4 group: internal changes (coding style) 2026-01-09 15:35:46 +01:00
skypjack
767013e0cb handle: cleanup 2026-01-09 15:35:17 +01:00
skypjack
9b47f44c48 registry: cleanup 2026-01-09 15:35:02 +01:00
skypjack
8a51b5950c iota_iterator: internal changes (coding style) 2026-01-09 15:34:29 +01:00
skypjack
e54add5eab adjacency_matrix: internal changes (coding style) 2026-01-09 15:33:44 +01:00
skypjack
7d4933790b test: internal changes to modernize the codebase 2026-01-09 15:32:42 +01:00
skypjack
ad147da839 entity: discard the no longer necessary operator!= 2026-01-08 12:12:56 +01:00
skypjack
dd5043fa81 handle: discard the no longer necessary operator!= 2026-01-08 12:12:29 +01:00
skypjack
656a2d2036 any: discard the no longer necessary operator!= 2026-01-08 11:53:10 +01:00
skypjack
e35b698f26 iota_iterator: discard the no longer necessary operator!= 2026-01-08 11:52:56 +01:00
skypjack
cfabb3b21c group: discard the no longer necessary operator!= 2026-01-08 11:47:08 +01:00
skypjack
ecb82e1555 resource: discard the no longer necessary operator!= 2026-01-08 11:46:50 +01:00
skypjack
9588c3cc5a cache: discard the no longer necessary operator!= 2026-01-08 11:46:15 +01:00
skypjack
f5983aa751 table: discard the no longer necessary operator!= 2026-01-08 11:44:00 +01:00
skypjack
dd8e28a734 dense_set: discard the no longer necessary operator!= 2026-01-08 11:43:52 +01:00
skypjack
42acf469cf dense_map: discard the no longer necessary operator!= 2026-01-08 11:43:34 +01:00
skypjack
7eca5e840f registry: discard the no longer necessary operator!= 2026-01-08 11:42:14 +01:00
skypjack
fa579eea59 meta: discard the no longer necessary operator!= 2026-01-08 11:36:14 +01:00
skypjack
9e8c10a443 meta_range_iterator: discard the no longer necessary operator!= 2026-01-08 11:35:46 +01:00
skypjack
60a8e9dc2e view: discard the no longer necessary operator!= 2026-01-08 11:35:23 +01:00
skypjack
c6dbcf6c5f sparse_set: discard the no longer necessary operator!= 2026-01-08 11:35:00 +01:00
skypjack
12ec00ac28 storage: discard the no longer necessary operator!= 2026-01-08 11:34:50 +01:00
skypjack
bc93bf2f79 runtime_view: discard the no longer necessary operator!= 2026-01-08 11:34:36 +01:00
skypjack
55a6fd65aa delegate: discard the no longer necessary operator!= 2026-01-08 11:31:40 +01:00
skypjack
16a8c25bd6 graph: discard the no longer necessary operator!= 2026-01-08 11:31:19 +01:00
skypjack
d5aa3973db type_info: discard the no longer necessary operator!= 2026-01-08 11:30:45 +01:00
skypjack
b3718b329d hashed_string: discard the no longer necessary operator!= 2026-01-08 11:25:50 +01:00
skypjack
8992822fe7 test: discard the no longer necessary operator!= 2026-01-08 11:14:37 +01:00
skypjack
32f030a346 natvis: meta_ctx view 2026-01-07 12:35:45 +01:00
skypjack
3961aff10c davey: updated to string_view names 2026-01-07 10:31:11 +01:00
skypjack
f4b6b513fa doc: added MonsterWar to the list 2026-01-07 10:13:22 +01:00
skypjack
deed237bbe meta: return a string_view from name() 2026-01-05 12:16:55 +01:00
skypjack
d26a9baa1c test: avoid shadow warning 2026-01-05 11:39:36 +01:00
skypjack
3e9bddff98 test: avoid shadow warnings 2026-01-05 11:38:32 +01:00
skypjack
e2044f34a8 test: avoid shadow warnings 2026-01-05 11:35:34 +01:00
skypjack
bfd7320609 meta: prepare to more perf improvements 2026-01-03 11:48:02 +01:00
skypjack
4756933039 meta: minor changes 2026-01-02 16:21:09 +01:00
skypjack
cfdffa64a1 entt: cleanup TODO file 2025-12-29 17:27:33 +01:00
skypjack
ed1fd2dc26 test: add missing template keywords 2025-12-29 17:27:02 +01:00
skypjack
3552f45b83 build: delete SETUP_BASIC_TEST_DEPRECATED 2025-12-28 20:07:50 +01:00
skypjack
8f6e1588e5 build: review entity tests 2025-12-28 20:07:13 +01:00
skypjack
1f7efe511d build: review example tests 2025-12-24 10:26:01 +01:00
skypjack
9bcd14f31f build: refine lib tests 2025-12-23 17:28:28 +01:00
skypjack
37b56a535d doc: stl is a no-doc area 2025-12-23 17:26:25 +01:00
skypjack
21a9525b44 build: refine meta tests 2025-12-23 17:24:01 +01:00
skypjack
374b612c15 build: refine signal tests 2025-12-23 15:31:41 +01:00
skypjack
741fdc3810 build: review core tests 2025-12-23 15:25:28 +01:00
skypjack
e9d4c05825 build: review container tests 2025-12-23 15:23:33 +01:00
skypjack
7b273db8b8 build: review config tests 2025-12-23 15:22:35 +01:00
skypjack
6f2ba58e21 build: review graph tests 2025-12-23 15:22:16 +01:00
skypjack
b3b794aef5 build: review stl tests 2025-12-23 15:20:01 +01:00
skypjack
f2fc45dd36 build: review resource tests 2025-12-23 15:17:06 +01:00
skypjack
c210375657 build: review process tests 2025-12-23 15:16:25 +01:00
skypjack
85503302cb build: review poly tests 2025-12-23 15:14:25 +01:00
skypjack
aaeeba2167 build: review locator tests 2025-12-23 15:13:55 +01:00
skypjack
e734078bb0 build: review benchmark test 2025-12-23 15:12:14 +01:00
skypjack
98cf280aae build: prepare test refactoring 2025-12-23 15:11:11 +01:00
skypjack
d14707ed3a test: prepare for refactoring 2025-12-22 16:40:11 +01:00
skypjack
b157dfbf86 test: prepare for refactoring 2025-12-22 11:33:00 +01:00
skypjack
ebb9cf017a entt: replaced identity with stl::identity 2025-12-22 10:24:34 +01:00
skypjack
09b9099b1c stl: functional.hpp for identity 2025-12-22 10:22:46 +01:00
skypjack
b469b2b353 doc: remove reference to identity 2025-12-22 08:56:25 +01:00
skypjack
93bfd66fc6 stl: cleanup 2025-12-19 18:19:35 +01:00
skypjack
66f42d358a doc: config.md 2025-12-19 18:09:59 +01:00
skypjack
036196bed3 config: ENTT_USE_STL (mainly for test purposes) 2025-12-19 17:47:17 +01:00
skypjack
5460315b6e *: updated TODO 2025-12-19 17:43:55 +01:00
skypjack
3ab0000b31 config: ENTT_NOEXCEPTION -> ENTT_NO_EXCEPTION 2025-12-19 17:16:43 +01:00
skypjack
fbffdaf176 stl: refine memory.hpp 2025-12-19 16:57:55 +01:00
skypjack
884f698fab core: make inclusion of custom to_address conditional 2025-12-19 15:37:54 +01:00
skypjack
5843c75b80 doc: cleanup 2025-12-19 15:35:43 +01:00
skypjack
0a6968f3a4 *: updated TODO 2025-12-19 15:35:30 +01:00
skypjack
c2d68e242a core: reintroduce to_address (it's not available everywhere yet, I need to find a solution to inject it when needed) 2025-12-19 10:03:33 +01:00
skypjack
b69d856ee3 core: entt::to_address -> std::to_address 2025-12-19 08:20:55 +01:00
skypjack
8dd00aea29 config: reintroduce ENTT_CONSTEVAL to make consteval easier for older compilers 2025-12-18 18:06:04 +01:00
skypjack
e40537afc2 test: try to make clang 16 happy again on the CI 2025-12-18 18:00:19 +01:00
skypjack
e50ee6a956 entity: rollback some consteval to constexpr to please all compilers 2025-12-18 17:48:35 +01:00
skypjack
06784b8d63 view: make it clear to clang that I'm using the this pointer 2025-12-18 17:45:00 +01:00
skypjack
ad9ef0320b test: more consteval-ness 2025-12-18 17:28:52 +01:00
skypjack
efe1e0830a signal: more consteval-ness 2025-12-18 17:28:45 +01:00
skypjack
16cf5b96a6 poly: more consteval-ness 2025-12-18 17:28:39 +01:00
skypjack
489bf2fbec meta: more consteval-ness 2025-12-18 17:28:30 +01:00
skypjack
b35045ac78 entity: more consteval-ness 2025-12-18 17:28:20 +01:00
skypjack
4f994cbec2 core: more consteval-ness 2025-12-18 17:27:52 +01:00
skypjack
8e7b810a3c doc: more consteval-ness 2025-12-18 17:27:30 +01:00
skypjack
afc94e62f2 config: drop ENTT_CONSTEVAL 2025-12-17 14:00:57 +01:00
skypjack
2f2fe11343 entt: std::remove_const_t<std::remove_reference_t<...>> -> std::remove_cvref_t<...> 2025-12-17 11:22:58 +01:00
skypjack
3390e3c563 doc: std::remove_const_t<std::remove_reference_t<...>> -> std::remove_cvref_t<...> 2025-12-17 11:22:38 +01:00
skypjack
df37ba35ac any: cleanup 2025-12-17 11:11:17 +01:00
skypjack
1232d7c285 test: cleanup 2025-12-17 11:11:11 +01:00
skypjack
3a32f18c78 view: missing template keywords 2025-12-16 23:19:12 +01:00
skypjack
e504310399 view: support converting views 2025-12-16 16:14:59 +01:00
skypjack
8d64106372 view: refine storage view conversion logic 2025-12-16 16:14:03 +01:00
skypjack
6b4365bec9 view: support multi-to-single view conversions 2025-12-16 12:31:24 +01:00
skypjack
fc936c3e6f view: support converting storage views 2025-12-15 11:43:15 +01:00
skypjack
ff13ce5f83 *: cleanup TODO 2025-12-15 11:06:48 +01:00
skypjack
cc4f7b3ab8 doc: updated links 2025-12-15 11:06:41 +01:00
skypjack
a6681e97c9 doc: update requirements 2025-12-12 13:40:27 +01:00
skypjack
6d6d60d2a7 build: update min cmake version (no patch value) 2025-12-12 13:40:07 +01:00
skypjack
60bdaacfdf build: update min doxy version 2025-12-12 13:39:46 +01:00
skypjack
3cfd6e1739 *: cleanup TODO 2025-12-12 11:57:56 +01:00
skypjack
b34131ef4b build: refine cereal config 2025-12-12 11:57:46 +01:00
skypjack
d2b00fc03a build: refine docs config 2025-12-12 11:09:59 +01:00
skypjack
66d02050a8 build: refine testbed config 2025-12-11 18:57:56 +01:00
skypjack
9a4ffa2142 build: bump cmake version 2025-12-11 18:57:31 +01:00
skypjack
9fc1245013 view: cleanup 2025-12-11 18:28:56 +01:00
skypjack
aeaf6f7304 davey: add missing typename 2025-12-10 15:52:17 +01:00
skypjack
140eeae80d *: updated TODO 2025-12-10 14:42:23 +01:00
skypjack
84646ad457 doc: updated links 2025-12-10 14:39:50 +01:00
skypjack
e54928794f davey: cleanup 2025-12-10 14:37:16 +01:00
skypjack
4c2ed7380f doc: minor changes 2025-12-10 14:36:57 +01:00
Starman
85a042b7c9 doc: add KODZA game engine link to documentation (#1298)
I made it btw, love this project
2025-12-10 14:36:04 +01:00
skypjack
06df4003e8 entity: cleanup 2025-12-09 15:32:20 +01:00
skypjack
a56e19993b meta: cleanup 2025-12-08 16:07:17 +01:00
skypjack
83dc6f4a97 signal: cleanup 2025-12-08 15:00:16 +01:00
skypjack
9e7a31cc74 core: cleanup 2025-12-08 14:59:48 +01:00
skypjack
c72a42d38d doc: update 2025-12-05 23:43:46 +01:00
skypjack
f1e59c4ea4 container: cleanup 2025-12-05 18:11:23 +01:00
skypjack
7f397f6aa0 process: cleanup 2025-12-05 18:10:57 +01:00
skypjack
48dbba6c70 poly: cleanup 2025-12-05 18:08:19 +01:00
skypjack
56b9486460 graph: cleanup 2025-12-05 18:08:04 +01:00
skypjack
f76191d27c resource: cleanup 2025-12-05 18:07:50 +01:00
skypjack
4a18647aa9 test: minor changes 2025-12-05 14:42:54 +01:00
skypjack
f4f6edbc73 *: updated TODO 2025-12-05 14:41:30 +01:00
skypjack
d878089ecc doc: cleanup 2025-12-05 14:38:47 +01:00
skypjack
29dba57433 test: cleanup entity 2025-12-05 14:38:26 +01:00
skypjack
46552b0663 *: updated TODO 2025-12-05 14:38:17 +01:00
skypjack
784ba1e9cd test: cleanup core 2025-12-05 14:38:09 +01:00
skypjack
b7040d940f test: cleanup meta 2025-12-04 11:53:42 +01:00
skypjack
494714504f test: cleanup container 2025-12-04 11:53:33 +01:00
skypjack
f6c688d9e3 test: cleanup resource 2025-12-04 11:53:21 +01:00
skypjack
6e68159b5e test: thanks msvc, as usual :) 2025-12-03 18:32:24 +01:00
skypjack
c8c5905913 test: cleanup process 2025-12-03 18:28:54 +01:00
skypjack
b90607f87b test: cleanup poly 2025-12-03 18:28:37 +01:00
skypjack
cfcee56b95 test: cleanup graph 2025-12-03 18:28:19 +01:00
skypjack
90d1a8b92d test: cleanup signal 2025-12-03 18:28:00 +01:00
skypjack
12d9660016 test: cleanup examples 2025-12-03 18:27:45 +01:00
skypjack
4cbbdf5ffb test: re-enable commented test 2025-12-03 18:10:07 +01:00
skypjack
da254e1552 registry: cleanup 2025-12-02 18:29:28 +01:00
skypjack
afad2972dd build: cmake does not support toolset 144 apparently 2025-12-02 18:25:40 +01:00
skypjack
05bf0aa505 build: update vs toolsets 2025-12-02 17:57:08 +01:00
skypjack
13a479eff0 storage: exploit std::allocator<void> cross constructors 2025-12-02 17:30:05 +01:00
skypjack
52b41cfab5 registry: exploit std::allocator<void> cross constructors 2025-12-02 17:29:59 +01:00
skypjack
be2b93549f core: drop type_identity[_t] 2025-12-02 17:29:10 +01:00
skypjack
a6638cd026 config: drop ENTT_CONSTEXPR 2025-12-02 17:28:22 +01:00
skypjack
ce2c8b3b91 core: reintroduce entt::identity (it's not available everywhere yet, I need to find a solution to inject it when needed) 2025-12-01 17:08:22 +01:00
skypjack
cd28e6269b core: next_power_of_two -> std::bit_ceil 2025-12-01 17:04:28 +01:00
skypjack
760b2aaccf *: entt::has_single_bit -> std::has_single_bit 2025-11-27 17:26:03 +01:00
skypjack
537ffb48a7 *: entt::popcount -> std::popcount 2025-11-26 14:37:26 +01:00
skypjack
7f0caeb7bc doc: cleanup the bit section 2025-11-26 14:37:04 +01:00
skypjack
13395a88f1 core: drop entt::identity 2025-11-25 15:29:30 +01:00
skypjack
9869ab7209 resource: use std::identity instead of entt::identity 2025-11-25 15:29:10 +01:00
skypjack
0503932abf test: use std::identity instead of entt::identity 2025-11-25 15:28:58 +01:00
skypjack
2df0073f53 signal: use std::identity instead of entt::identity 2025-11-25 15:28:39 +01:00
skypjack
311f6cac91 meta: use std::identity instead of entt::identity 2025-11-25 15:28:25 +01:00
skypjack
3bf839b89a flow: use std::identity instead of entt::identity 2025-11-25 15:27:52 +01:00
skypjack
eece255765 registry: use std::identity instead of entt::identity 2025-11-25 15:27:25 +01:00
skypjack
5ce5968d44 algorithm: use std::identity instead of entt::identity 2025-11-25 15:27:01 +01:00
skypjack
64982c34ad doc: remove reference to entt::identity 2025-11-25 15:25:01 +01:00
skypjack
024d478409 doc: minor changes 2025-11-25 15:23:55 +01:00
skypjack
c84ff3bcd8 build: C++20 2025-11-25 11:55:39 +01:00
skypjack
e9b64a21a2 doc: updated README 2025-11-24 17:06:36 +01:00
skypjack
ea3de5499e any: remove deprecated functions 2025-11-24 15:52:15 +01:00
skypjack
de32688d46 meta: remove deprecated functions/types 2025-11-24 15:52:03 +01:00
skypjack
98872fc1cc poly: remove deprecated functions 2025-11-24 14:53:02 +01:00
skypjack
75fa927f31 sparse set: remove deprecated functions 2025-11-24 14:52:36 +01:00
skypjack
dc784ba2e7 doc: remove unreal page, no longer applies 2025-11-24 09:49:46 +01:00
skypjack
57b52a25a4 bazel: switch to C++20 2025-11-22 19:30:31 +01:00
skypjack
d558d06dcf build: update conan test to C++20 2025-11-22 19:29:11 +01:00
skypjack
db024cff7d build: C++20 only workflows (but still ready for C++23) 2025-11-22 19:09:52 +01:00
skypjack
c4904e8bcf build: update docs custom target 2025-11-21 16:56:39 +01:00
skypjack
889d6217d1 now working on version 4.0.x 2025-11-21 16:55:45 +01:00
skypjack
b4e58bdd36 *: updated TODO 2025-11-19 10:10:18 +01:00
skypjack
8375ee0910 update single include file 2025-11-17 09:27:48 +01:00
skypjack
137339f48c build: refine clang-tidy setup for local use 2025-11-15 19:11:57 +01:00
skypjack
713bcd50a3 test: explicit nullptr check 2025-11-15 19:11:54 +01:00
skypjack
03a45d9cca test: clang-tidy friendly tests 2025-11-15 18:51:35 +01:00
skypjack
cffbf89d9b test: refine lib tests 2025-11-15 18:50:57 +01:00
skypjack
f48d16a6e6 meta: linter friendly extra pointer check 2025-11-15 17:52:29 +01:00
skypjack
4905da2637 test: internal changes to make clang-tidy happy 2025-11-15 16:02:36 +01:00
skypjack
558109bbe1 doc: add OneArc to the EnTT in Action list 2025-11-14 17:30:45 +01:00
skypjack
a505d41266 meta: explicit nullptr checks 2025-11-14 17:04:34 +01:00
skypjack
9381900b00 meta: get around bugprone assignment warning 2025-11-14 16:59:14 +01:00
skypjack
f6a3b09ec2 dense set: suppress conversion warning 2025-11-14 16:58:25 +01:00
skypjack
51c7533265 meta: use find_member to slightly reduce symbol size 2025-11-14 16:58:10 +01:00
skypjack
d502bf18da dense map: avoid implicit conversions 2025-11-14 16:10:50 +01:00
skypjack
db263d2ff9 any: reorder ctor warning 2025-11-14 16:10:30 +01:00
skypjack
16fae2d625 doc: cleanup 2025-11-14 15:14:52 +01:00
skypjack
4e3a1a8428 meta: internal changes 2025-11-14 15:14:42 +01:00
skypjack
aec8a6588c meta: cleanup 2025-11-13 18:41:31 +01:00
skypjack
3160d991f7 meta: drop unnecessary remove_reference_t 2025-11-13 18:34:45 +01:00
skypjack
7359018880 dense_map: refine constrained_find (perf) 2025-11-13 12:02:36 +01:00
skypjack
17fb301636 dense_set: refine constrained_find (perf) 2025-11-13 12:02:28 +01:00
skypjack
7dac5af05d meta: skip fetch_node if possible 2025-11-12 15:39:59 +01:00
skypjack
d15e514f10 meta: allow unsetting flags from meta factory 2025-11-12 10:08:31 +01:00
skypjack
fe8320c66f any: plugin-friendly, faster quick check 2025-11-10 14:34:39 +01:00
skypjack
7111549c7c meta:: minor changes 2025-11-10 00:01:54 +01:00
skypjack
e8e5f5ee2b dense_[map|set]: internal changes 2025-11-09 23:38:26 +01:00
skypjack
9caaf569e6 config: drop attribute.h, merge it with config.h 2025-11-07 10:21:02 +01:00
skypjack
03bf0f90b4 meta: minor changes 2025-11-06 00:16:15 +01:00
skypjack
5df7142f51 *: move attribute.h to config 2025-11-05 23:47:16 +01:00
skypjack
4d95793123 meta: minor changes 2025-11-05 12:12:10 +01:00
skypjack
0aa503e69e meta: use the right guard 2025-11-04 15:12:17 +01:00
skypjack
02323e814a meta: missing template keyword 2025-11-04 15:10:59 +01:00
skypjack
440fed990d doc: cleanup 2025-11-04 15:08:37 +01:00
skypjack
c2dcd7257f meta: deprecate const meta_handle::operator-> 2025-11-04 15:08:24 +01:00
skypjack
716e3257b1 meta: refine meta_any::as_ref 2025-11-04 15:01:39 +01:00
skypjack
f781441bcd meta: oops :) 2025-11-04 14:54:37 +01:00
skypjack
3c0aa4d300 meta: minor changes 2025-11-04 14:42:25 +01:00
skypjack
a57e1a971d meta: minor changes 2025-11-04 14:19:57 +01:00
skypjack
dc8da9e42c meta: minor changes 2025-11-04 14:11:44 +01:00
skypjack
7eeb828980 meta: further refine typed allow_cast 2025-11-04 14:06:53 +01:00
skypjack
b0d7490829 meta: replace try_convert to squeeze more perf from allow_cast 2025-11-04 13:34:22 +01:00
skypjack
0d678442a0 cmake: profile prop 2025-11-03 23:37:43 +01:00
skypjack
2dff7c9d55 container: prepare for some optimizations 2025-11-03 18:16:28 +01:00
skypjack
3fc5de2916 doc: meta 2025-11-03 14:32:12 +01:00
skypjack
a572671947 doc: meta 2025-11-03 08:30:04 +01:00
skypjack
2a6f769092 any: internal changes 2025-10-31 18:02:53 +01:00
skypjack
6a25c0df84 *: updated TODO 2025-10-31 17:50:13 +01:00
skypjack
72127d699a meta: avoid invoking twice the same function 2025-10-31 16:19:54 +01:00
skypjack
b52d0aa133 *: updated TODO 2025-10-31 15:35:09 +01:00
skypjack
5d006fefcc meta: drop redundant (wrong) const 2025-10-31 15:00:48 +01:00
skypjack
d7c58d00b4 test: make the linter happy again 2025-10-31 14:44:38 +01:00
skypjack
9198a27c25 meta: avoid resetting nodes on meta_any::emplace if not needed 2025-10-31 14:37:16 +01:00
skypjack
31d5d5ff1c meta: top-level only searches for data and functions 2025-10-31 11:36:26 +01:00
skypjack
f3fa359edf natvis: meta 2025-10-30 12:32:22 +01:00
skypjack
cba31e00ea meta: as_is -> as_value / as_auto -> as_is (breaking) 2025-10-29 17:40:18 +01:00
skypjack
d366a5ed92 meta: as_auto policy 2025-10-28 15:34:45 +01:00
skypjack
d92bea4afd test: non-regression tests for meta_type::lookup constness 2025-10-27 00:01:13 +01:00
skypjack
bbfd458d2d test: more about overloaded meta functions 2025-10-26 23:51:18 +01:00
skypjack
87a114ccc6 test: more about meta_func 2025-10-26 23:37:43 +01:00
skypjack
6852415ffc test: minor changes 2025-10-26 15:11:04 +01:00
skypjack
15f65bd58e core/meta: minor changes 2025-10-26 13:59:09 +01:00
skypjack
54c7c47e3d .*: updated TODO 2025-10-26 13:58:58 +01:00
skypjack
5e6ccdedc8 meta: not going to drop policy anymore, we can use it safely 2025-10-24 10:19:42 +02:00
skypjack
dadf493aca meta: cleanup 2025-10-23 18:39:30 +02:00
skypjack
3e5842a467 meta: try to make the linter happy 2025-10-23 18:32:19 +02:00
skypjack
75ae0a10d0 any: cleanup 2025-10-23 18:19:54 +02:00
skypjack
e87a8e4b63 storage: NOLINT odissey :) 2025-10-23 17:58:55 +02:00
skypjack
41282b0ecc entity: reduce number of NOLINT 2025-10-23 17:28:13 +02:00
skypjack
030095f48d .*: updated TODO 2025-10-23 17:27:47 +02:00
skypjack
e18061bf86 any: avoid vtable calls to release trivially destructible objects (perf) 2025-10-23 11:58:29 +02:00
Michele Caini
38bcbf41a3 any: discard pointless NOLINT 2025-10-22 23:21:27 +02:00
Michele Caini
602626c840 .*: cleanup TODO 2025-10-22 23:17:03 +02:00
skypjack
41e359502d any: avoid vtable calls to get data (perf) 2025-10-22 17:09:41 +02:00
skypjack
8e64f8accf any: info request to get rid of descriptor 2025-10-22 15:58:15 +02:00
skypjack
0afab946da meta: reduce uses of basic_any<...>::policy() 2025-10-21 15:10:52 +02:00
skypjack
20d77024f7 *: updated TODO 2025-10-21 15:10:23 +02:00
skypjack
b1bb9b4b5b test: reduce uses of basic_any<...>::policy() 2025-10-21 15:10:17 +02:00
skypjack
978e003dbc meta: internal changes 2025-10-21 15:09:51 +02:00
skypjack
47c65a20a1 any: clang-tidy docet 2025-10-20 17:20:29 +02:00
skypjack
31d845a5f5 meta: clang-tidy docet 2025-10-20 17:18:26 +02:00
skypjack
03c5661df9 meta: rework internal fake vtable 2025-10-20 15:57:32 +02:00
skypjack
48644e568f meta: cleanup 2025-10-20 15:54:35 +02:00
skypjack
19c0e8029d meta: remove the resolve function from meta_any to reduce the footprint 2025-10-20 15:14:08 +02:00
skypjack
3562bec4a7 meta: reduce internal uses of meta_any::resolve 2025-10-18 23:11:35 +02:00
skypjack
e863e4fe54 meta: reduce internal uses of meta_any::resolve 2025-10-18 12:43:39 +02:00
skypjack
c5d89597fc meta: avoid using meta_any::resolve when comparing 2025-10-18 12:40:33 +02:00
skypjack
091eef0e4c any: void fake vtable for the win 2025-10-18 11:48:32 +02:00
skypjack
96b56ca630 any: try to make all compilers happy again 2025-10-17 16:50:19 +02:00
skypjack
56d78c767a any: no delete for const void * 2025-10-17 16:49:22 +02:00
skypjack
1f57b2795f any: cleanup 2025-10-17 16:46:08 +02:00
skypjack
de054655ab any: doc 2025-10-17 16:41:27 +02:00
skypjack
bee74568e5 *; updated TODO 2025-10-17 08:18:14 +02:00
skypjack
ebb2af1225 any: make ::initialize fully support void 2025-10-17 08:13:41 +02:00
skypjack
1487ad292b any: avoid using vtable ptr with has_value 2025-10-16 23:31:55 +02:00
skypjack
d3b8e92294 any: cleanup 2025-10-16 13:59:08 +02:00
skypjack
ab1d7d0126 any: make vtable void-friendly 2025-10-15 17:48:09 +02:00
skypjack
01b21afca0 any: make in_situ void-friendly 2025-10-15 17:46:25 +02:00
skypjack
ef0056908a core: doc 2025-10-15 11:13:27 +02:00
skypjack
b021c78550 natvis: update basic_any view 2025-10-14 15:23:27 +02:00
skypjack
aedaa7f6a4 any: cleanup 2025-10-13 14:56:36 +02:00
skypjack
e15af7b500 doc: minor changes 2025-10-10 15:11:24 +02:00
skypjack
38b34eb986 any: add comments for things to cleanup :) 2025-10-10 12:58:14 +02:00
skypjack
b4fc627c99 any: use vtable instead of descriptor 2025-10-10 12:54:54 +02:00
skypjack
c9364d3af5 *: updated TODO 2025-10-10 12:49:49 +02:00
skypjack
baa0d2c23c any: use vtable instead of descriptor for early exit 2025-10-10 10:53:25 +02:00
skypjack
dc154a4b8a build: also test toolset v143 for backward compatibility 2025-10-10 10:24:22 +02:00
skypjack
ba0e9fab45 any: reduce direct uses of descriptor 2025-10-10 10:08:44 +02:00
skypjack
4ab5a375e8 *: updated TODO 2025-10-10 10:08:18 +02:00
skypjack
f63f36a411 any: basic_any_storage, prepare for vtable-less optimizations 2025-10-09 15:52:43 +02:00
skypjack
f2b8177aaf any: avoid name clashing if possible 2025-10-09 09:24:18 +02:00
skypjack
53fab33423 core: cleanup headers 2025-10-08 15:41:26 +02:00
skypjack
4a252bdb84 any: minor changes 2025-10-08 14:24:33 +02:00
skypjack
cece4be1ec any: prepare for ebco 2025-10-08 14:20:48 +02:00
skypjack
8f23d743ca *: updated TODO 2025-10-08 12:29:30 +02:00
skypjack
0fa1bc5999 test: cleanup 2025-10-08 12:28:22 +02:00
skypjack
281276e84e meta: rollback some stuff to make everything clang-tidy friendly 2025-10-07 18:52:46 +02:00
skypjack
f73409e9ad meta: safe self-assignment for meta_any 2025-10-07 17:33:45 +02:00
skypjack
740cf006d9 any: safe self-assignment 2025-10-07 17:33:32 +02:00
skypjack
828ecb57e6 any: internal changes 2025-10-07 17:24:22 +02:00
skypjack
8794888c98 meta: cleanup 2025-10-07 17:23:29 +02:00
skypjack
dd7b96fc9e test: make them clang-tidy friendly 2025-10-07 15:59:01 +02:00
skypjack
1116c33ada test: minor changes 2025-10-07 15:30:58 +02:00
skypjack
38fe05db85 meta: minor changes 2025-10-07 12:47:35 +02:00
skypjack
b2d4832842 clang-tidy: typo 2025-10-07 12:37:29 +02:00
skypjack
4c19d2844e clang-tidy: refine config to match my tastes too :) 2025-10-07 11:46:27 +02:00
skypjack
6f2e5090e1 any: [[nodiscard]] 2025-10-07 11:45:20 +02:00
skypjack
25e5123128 meta: remove redundant initializer 2025-10-07 11:23:38 +02:00
skypjack
8c3ca20d19 clang-tidy: I do actually prefer else-after-return style 2025-10-07 11:21:45 +02:00
skypjack
ee7f24be63 meta: no else after return 2025-10-07 11:15:17 +02:00
skypjack
a5f58ef49f meta: clang-tidy 2025-10-07 11:11:01 +02:00
skypjack
ba5c4ba121 meta: use any::has_value (perf) 2025-10-07 09:53:52 +02:00
skypjack
fcf455f7ef any: ::has_value overloads 2025-10-07 09:24:21 +02:00
skypjack
6836d5b024 meta: refine meta_any::assign 2025-10-06 17:07:09 +02:00
skypjack
8142136940 meta: internal changes 2025-10-03 17:42:41 +02:00
skypjack
e76bbb2015 test: code coverage 2025-10-03 17:36:50 +02:00
skypjack
6f57622eff *: updated TODO 2025-10-03 17:32:16 +02:00
skypjack
0c5cab164a any: missing template keywords 2025-10-03 14:21:22 +02:00
skypjack
c67a62de81 *: updated TODO 2025-10-03 14:13:59 +02:00
skypjack
80eba8afb4 any: typed ::data with fallback (perf) 2025-10-03 14:12:46 +02:00
skypjack
0d6615a767 meta: cleanup 2025-10-02 17:05:00 +02:00
skypjack
fc4d18ce44 meta: cleanup 2025-10-02 15:42:18 +02:00
skypjack
ddf28480dc *: internal changes 2025-10-01 13:12:11 +02:00
skypjack
f49f2331ba doc: minor changes 2025-10-01 13:12:00 +02:00
skypjack
6526d67d16 any: internal changes 2025-09-30 15:13:15 +02:00
skypjack
4d90521fcf *: updated TODO 2025-09-30 14:58:05 +02:00
skypjack
9165a1df1e meta: minor changes 2025-09-30 14:57:33 +02:00
skypjack
a1406baf61 meta: cleanup 2025-09-29 18:56:41 +02:00
skypjack
645b3563a9 natvis: meta 2025-09-29 08:38:12 +02:00
skypjack
91eecc1efd sparse set: minor changes 2025-09-29 08:38:03 +02:00
skypjack
0782855880 meta: internal changes 2025-09-28 17:43:34 +02:00
skypjack
b9bfeb9b3d meta: bring meta custom back in place 2025-09-26 16:56:03 +02:00
skypjack
0409aab6bb meta: cleanup 2025-09-26 15:09:10 +02:00
skypjack
947d89844b *: no entt:: namespace 2025-09-26 15:05:39 +02:00
Ted de Munnik
ff8544f97d sparse set: simplify try_emplace iterator computation to improve MSVC codegen (#1286) 2025-09-26 11:20:58 +02:00
skypjack
c777cda857 meta: support {} on static calls 2025-09-25 16:37:29 +02:00
skypjack
a90584f1ce meta: update meta_handle context propagation (with non-regression tests) 2025-09-25 15:44:01 +02:00
skypjack
4e47c535c4 meta: meta_any -> meta_handle 2025-09-24 15:52:32 +02:00
skypjack
84fee2848f tools: update davey 2025-09-24 15:42:37 +02:00
skypjack
42259fb1ce meta: reintroduce meta_handle default ctor for utilities 2025-09-24 15:41:40 +02:00
skypjack
e38e88362f meta: cleanup meta_handle further (breaking change) 2025-09-24 15:30:16 +02:00
skypjack
14be4141fa meta: strip deprecated ctor from meta_handle (breaking change) 2025-09-24 15:29:47 +02:00
skypjack
6a4ca4c4ba tools: update davey 2025-09-24 15:19:51 +02:00
skypjack
71e61b5270 meta: make meta_handle support empty construction from nullptr 2025-09-24 15:14:33 +02:00
skypjack
affe6ca7b8 meta: internal changes 2025-09-24 15:13:03 +02:00
skypjack
f0a07c4530 meta: avoid shadow warnings 2025-09-24 15:01:03 +02:00
skypjack
a003575811 meta: avoid useless moves 2025-09-24 14:58:47 +02:00
skypjack
be3f022445 meta: struct -> class 2025-09-24 14:57:04 +02:00
skypjack
4b5b3fec41 meta: deferred meta handle creation (perf) 2025-09-24 14:55:41 +02:00
skypjack
4debb79a60 meta: improved meta_handle support to meta_any 2025-09-24 13:07:24 +02:00
skypjack
f3b763d973 meta: deprecate ctors that should not exist 2025-09-24 13:06:05 +02:00
skypjack
356024ed6e doc: cleanup 2025-09-24 12:38:57 +02:00
skypjack
e8e62638fc meta: deprecate meta_handle ctor 2025-09-24 12:33:54 +02:00
skypjack
6ecf1f35c0 meta: revert automatic policy (too convenient a feature) 2025-09-24 12:30:21 +02:00
skypjack
05863c6a15 meta: automatic policy (perf, breaking change) 2025-09-24 12:02:56 +02:00
skypjack
1a02b6ad57 meta: refine meta_any move ctor 2025-09-23 15:53:50 +02:00
skypjack
5ee1f3fec1 meta: internal changes 2025-09-23 15:53:18 +02:00
skypjack
f44c9a1c05 meta: deprecate a few functions that should not be there 2025-09-23 09:12:34 +02:00
skypjack
715e5318de meta: refine try_convert/allow_cast 2025-09-22 16:35:11 +02:00
Tobias Backer Dirks
211e5fddc1 doc: add Game link to EnTT In Action (#1284) 2025-09-22 09:15:21 +02:00
skypjack
9347e91db6 meta: stable meta types 2025-09-19 11:30:21 +02:00
skypjack
bebfbada68 meta: refine factory init step 2025-09-19 11:28:26 +02:00
skypjack
c239821d12 meta: keep supporting invalid types 2025-09-19 10:46:28 +02:00
skypjack
293cbe2769 meta: avoid copying meta type nodes 2025-09-19 09:49:33 +02:00
skypjack
3587eaed9a meta: deprecate meta_type::operator bool, meta types are never invalid 2025-09-19 08:54:45 +02:00
skypjack
cb9d8ac3f0 meta: meta_type is void (and safe again) when empty 2025-09-18 17:44:32 +02:00
skypjack
e544755bbc meta: refine meta_any::fetch_node function 2025-09-18 17:33:56 +02:00
skypjack
e2003ff17a natvis: meta_any view 2025-09-18 17:33:16 +02:00
skypjack
6232c93907 meta: a bunch of [[nodiscard]] 2025-09-18 17:26:34 +02:00
skypjack
6153cdcbbe meta: lazy_node -> node 2025-09-18 17:25:21 +02:00
skypjack
89af917748 natvis: updated meta_any/meta_type_node views 2025-09-18 14:25:23 +02:00
skypjack
28466a7316 meta: lighter meta type objects (perf) 2025-09-18 14:11:24 +02:00
skypjack
33efea3f03 meta: avoid moving meta handles again and again (perf) 2025-09-17 09:13:42 +02:00
skypjack
55f1a47fcc meta: lazy_node pointer in meta_any (perf) 2025-09-16 10:44:35 +02:00
skypjack
e9b3eee6d3 meta: static local meta type node fallback 2025-09-15 14:48:15 +02:00
skypjack
b400927f02 meta: missing [[nodiscard]] 2025-09-12 18:06:07 +02:00
skypjack
a6d566ae4a natvis: update meta type and meta type descriptor views 2025-09-12 17:44:13 +02:00
skypjack
93979e13ac meta: typo 2025-09-12 17:43:50 +02:00
skypjack
a77e40093d meta: cleanup 2025-09-12 17:00:24 +02:00
skypjack
695ac44673 meta: prepare for more perf improvements 2025-09-12 16:23:37 +02:00
skypjack
1cc078e4fb test: cleanup 2025-09-12 16:11:00 +02:00
skypjack
94cd89a72a doc: typo 2025-09-12 14:38:58 +02:00
skypjack
5497291c29 meta: avoid copying meta_custom_node as not needed (perf) 2025-09-11 17:54:10 +02:00
skypjack
3c75f7e2fd meta: move custom data to meta type node details (perf) 2025-09-11 17:52:26 +02:00
skypjack
03d8cccc86 meta: use unique_ptr internall for meta_func_node 2025-09-11 17:30:31 +02:00
skypjack
47bfffc7cc meta: avoid moving handles if not needed 2025-09-10 19:09:02 +02:00
skypjack
9795f60bc1 meta: use ctx only for return type 2025-09-10 19:08:31 +02:00
skypjack
b3b48db957 meta: cleanup 2025-09-09 18:21:30 +02:00
skypjack
7d2b4fcf57 meta: internal changes 2025-09-09 17:30:09 +02:00
skypjack
3af8525eb8 natvis: refine meta_any view 2025-09-09 08:27:30 +02:00
skypjack
3306b72f87 natvis: refine basic_any view 2025-09-09 08:27:14 +02:00
skypjack
91a239a277 meta: typo 2025-09-09 08:13:22 +02:00
skypjack
fdaaeea52d meta: lazy initialization for nodes in meta_any 2025-09-08 15:43:01 +02:00
skypjack
d81e57daa1 meta: use fwd ctors internally 2025-09-08 08:39:57 +02:00
skypjack
ed84c6e9ff meta: early exit from allow_cast 2025-09-05 15:34:04 +02:00
skypjack
fe872f6a0d test: cleanup 2025-09-05 15:00:42 +02:00
skypjack
d8aa90234a meta: cleanup internal::try_convert 2025-09-05 13:04:20 +02:00
skypjack
28d8041da6 doc: typo 2025-09-05 12:58:35 +02:00
skypjack
1f53d163b7 meta: prepare internal::try_cast for lazy load on nodes 2025-09-05 12:57:30 +02:00
skypjack
7f38b7668e meta: break pointless meta destructors support for perf reasons (breaking change, use type destructors instead) 2025-09-04 15:05:15 +02:00
skypjack
dc70d97760 *: updated TODO 2025-09-04 15:03:59 +02:00
skypjack
4d0d48f7d9 test: delete meta_drop.cpp 2025-09-04 15:03:42 +02:00
skypjack
4ecaf18b20 doc: update meta, drop reference to meta destructors 2025-09-04 14:57:28 +02:00
skypjack
30a66cf513 bazel: cleanup 2025-09-04 14:57:10 +02:00
skypjack
9bbc8ad014 natvis: remove view for meta_dtor_node 2025-09-04 14:55:35 +02:00
skypjack
626b882f26 meta: reduce uses of node in meta_any 2025-09-03 17:00:22 +02:00
skypjack
9b44fa47d0 meta: guard try_cast to reduce uses of the internal node 2025-09-02 17:53:42 +02:00
skypjack
9170fc850b meta: cleanup 2025-09-02 14:29:39 +02:00
skypjack
973f010d85 meta: early exit when assigning 2025-09-02 14:20:18 +02:00
skypjack
6241543fe2 meta: cleanup 2025-09-02 10:33:31 +02:00
skypjack
5e99452cf3 meta: avoid using node in meta_any if not necessary 2025-09-01 18:41:34 +02:00
skypjack
d058ccd084 meta: avoid using node in meta_any if not necessary 2025-09-01 14:23:26 +02:00
skypjack
7e4694ce69 meta: prepare for lazy node initialization 2025-09-01 13:47:15 +02:00
skypjack
b3b7975290 *: updated TODO 2025-08-29 17:33:57 +02:00
skypjack
5ee0a39616 meta: review allow_cast 2025-08-29 15:24:24 +02:00
skypjack
27b9a25d06 meta: cleanup comparison operator 2025-08-29 11:41:54 +02:00
skypjack
9f5a6260ce meta: node_or_assert for data/func 2025-08-29 09:59:28 +02:00
skypjack
b4b85c8d53 meta: remove unnecessary checks 2025-08-29 09:55:27 +02:00
skypjack
4bc11e4297 natvis: meta 2025-08-29 09:34:32 +02:00
skypjack
ad291d4280 natvis: core 2025-08-29 09:34:26 +02:00
skypjack
f61fd4584a meta: skip unnecessary resolve calls (perf) 2025-08-28 16:56:31 +02:00
skypjack
536dba3048 meta: rollback safe mode for meta data and functions (perf, not necessary) 2025-08-28 13:38:41 +02:00
skypjack
4760a09d61 meta: avoid creating a new shared_ptr if not needed (perf) 2025-08-28 12:53:01 +02:00
skypjack
47ff98e25b storage: quit accepting (and silently discarding) arguments for empty types when invoking ::insert 2025-08-27 12:08:25 +02:00
skypjack
a638df6cae storage: quit accepting (and silently discarding) arguments for empty types when invoking ::emplace 2025-08-26 11:05:01 +02:00
skypjack
4f1626fce9 runtime view: rework/get rid of NOLINT 2025-08-26 10:56:02 +02:00
skypjack
9b3965c2c9 family: rework/get rid of NOLINT 2025-08-26 10:51:45 +02:00
skypjack
536a43be90 storage: add proper swap function to entity specialization - close #1273 2025-08-25 15:18:00 +02:00
Christoph
09b0e7f528 meta: add parentheses around max to prevent conflicts with max macro on Windows (#1278) 2025-08-25 08:59:16 +02:00
skypjack
76b6841c51 organizer: make graph() const - close #1274 2025-08-25 08:57:41 +02:00
skypjack
4507c546c1 meta: reduce instantiations on meta containers 2025-08-07 11:04:27 +02:00
skypjack
add999d105 meta: internal changes 2025-08-06 12:20:57 +02:00
skypjack
9cefbc3c79 meta: reduce size of fake vtable/template functions 2025-08-06 12:20:44 +02:00
skypjack
22347cfbd0 meta: remove pointless check 2025-08-05 14:47:39 +02:00
skypjack
6966eabd25 resource: minor changes 2025-08-05 13:59:23 +02:00
skypjack
a32e004c92 test: a few more 2025-08-05 13:59:09 +02:00
skypjack
49d9385aa4 process: handle_type to make the type opaque for future changes 2025-08-05 13:36:13 +02:00
skypjack
9970ba2284 davey: remove useless macro 2025-08-04 16:33:08 +02:00
skypjack
20cc85011e davey: no more entt:: 2025-08-04 16:21:22 +02:00
skypjack
7f5d4fb8d2 davey: minor changes 2025-08-04 16:19:13 +02:00
skypjack
03e046f880 davey: revert recent changes, move towards multiple tools with shared functions instead 2025-08-04 16:13:52 +02:00
skypjack
436f077e0b *: updated TODO 2025-08-04 16:12:14 +02:00
skypjack
d76cfb06f0 davey: prepare to support editor mode 2025-08-04 15:06:11 +02:00
Aaron Heysse
2bb947f6de doc: fixed variadic template args on a resource loader example (#1272) 2025-08-01 11:55:28 +02:00
skypjack
8a8f8dfd1b test: refine tests for dense_map 2025-07-31 14:49:47 +02:00
skypjack
15b8db6f61 doc: dense map 2025-07-31 14:49:38 +02:00
Terens
05e0a22fb3 dense_map: transparent lookup for at (#1270) 2025-07-31 14:29:44 +02:00
skypjack
ada11c4be1 test: headers cleanup 2025-07-30 14:09:56 +02:00
skypjack
7dbb59adc9 test: increase code coverage 2025-07-30 12:04:57 +02:00
skypjack
02440e17ae test: code coverage 2025-07-30 10:35:11 +02:00
skypjack
fa234af36b gh: update iwyu version 2025-07-30 10:24:36 +02:00
skypjack
40fdd26788 test: make clang-tidy happy 2025-07-30 10:21:10 +02:00
skypjack
6fa668900e natvis: sparse_set/storage/handle 2025-07-29 09:27:16 +02:00
skypjack
17e88e506c test: scheduler - close #1257 2025-07-28 15:10:11 +02:00
skypjack
3bcbea785a test: process 2025-07-28 14:32:42 +02:00
skypjack
3bac3d4bbe process: make inheritance from enable_shared_from_this public 2025-07-28 14:28:54 +02:00
skypjack
abdb293106 doc: a note about shared processes 2025-07-28 14:28:29 +02:00
skypjack
f4b960dc93 process: enable_shared_from_this to allow callers to get valid references 2025-07-28 14:20:38 +02:00
skypjack
71c217da28 *: updated TODO 2025-07-25 18:17:40 +02:00
skypjack
4777ca2980 doc: process 2025-07-25 18:17:20 +02:00
skypjack
4aabca2f0b natvis: process 2025-07-25 18:02:42 +02:00
skypjack
5c31fda9bd process/scheduler: refine API (finally), add allocator support - see #1257 2025-07-25 14:59:39 +02:00
skypjack
eecd1dc636 doc: process (draft) 2025-07-24 18:48:16 +02:00
Michele Caini
998f4b45da doc: process (wip - tbd) 2025-07-23 18:11:14 +02:00
Michele Caini
fb0b7e2aab *: updated TODO 2025-07-23 18:01:07 +02:00
Michele Caini
cbb50c1292 process: prepare migration to allocator-aware model 2025-07-23 16:05:13 +02:00
Michele Caini
fcd265a6ed process: shared_from_this is no longer required 2025-07-23 10:55:42 +02:00
Michele Caini
357ee3f994 scheduler: remove unnecessary template parameter 2025-07-22 10:59:40 +02:00
Michele Caini
e35eed905b process: safety net 2025-07-22 10:58:00 +02:00
Michele Caini
7942648035 process/scheduler: refine API - see #1257 2025-07-22 10:51:15 +02:00
Michele Caini
93dca93616 process: prepare for a more user-friendly attach/then API 2025-07-21 12:51:38 +02:00
Michele Caini
9f10d32b73 meta: move meta_dynamic_extent to fwd.hpp 2025-07-21 12:02:35 +02:00
Michele Caini
21e82abfa7 scheduler: cleanup API 2025-07-18 18:59:08 +02:00
Michele Caini
7b48069bd5 process: process_from utility 2025-07-18 18:58:46 +02:00
Michele Caini
5721e62ed0 *: updated TODO 2025-07-18 18:10:55 +02:00
Michele Caini
e140399725 doc: cleanup 2025-07-17 18:23:45 +02:00
Michele Caini
7f1a2b1887 doc: refine doc for process adaptor 2025-07-17 18:22:35 +02:00
Michele Caini
0fd70878b3 doc: process_adaptor 2025-07-17 18:17:14 +02:00
Michele Caini
06e88ce062 test: prepare to refactor scheduler API 2025-07-17 18:06:08 +02:00
Michele Caini
b5201a56df scheduler: add a plain attach for shared processes - see #1257 2025-07-16 15:26:18 +02:00
Michele Caini
9c05f1d8f3 natvis: process 2025-07-16 15:08:58 +02:00
Michele Caini
093718a1c6 process: add base_type alias to process_adaptor 2025-07-16 15:08:47 +02:00
skypjack
c07cb32ef8 *: updated TODO 2025-07-15 18:56:14 +02:00
skypjack
04da178800 process; process as first argument in process_adaptor 2025-07-15 09:47:55 +02:00
skypjack
5ab8da50cb *: updated TODO 2025-07-15 09:47:31 +02:00
skypjack
da684a4159 process: basic_process_adaptor -> process_adaptor 2025-07-15 09:31:44 +02:00
skypjack
ba20bde250 process: refine basic_process_adaptor implementation to use deduction guides 2025-07-15 09:30:04 +02:00
skypjack
cec550ad11 process: pass directly the base process to callbacks 2025-07-14 18:00:05 +02:00
skypjack
62b56e1b56 process: move basic_process_adaptor design from base class functor to member 2025-07-14 17:20:34 +02:00
skypjack
b045846579 test: minor changes 2025-07-14 16:48:03 +02:00
skypjack
665aa3c4cf *: updated TODO 2025-07-02 16:31:53 +02:00
skypjack
0f49a1fe27 davey: initial doc (despite the work-in-progress nature) 2025-07-02 09:41:20 +02:00
skypjack
ad4ab9dc08 davey: fix entry points for storage types 2025-07-02 09:37:15 +02:00
skypjack
1293990605 davey: comment out internals to avoid warnings due to missing doc 2025-07-01 16:43:26 +02:00
skypjack
c71bf6e8d9 process: avoid unqualified lookup 2025-07-01 16:37:42 +02:00
skypjack
716c67d3a5 process: enable_shared_from_this was actually useful here 2025-07-01 15:32:37 +02:00
skypjack
33b1daafa0 process: refine then return type 2025-06-30 17:32:27 +02:00
skypjack
2532158860 scheduler: process type 2025-06-30 17:32:04 +02:00
skypjack
da65e347b3 doc: typo 2025-06-30 08:35:01 +02:00
skypjack
c940bc8015 process: enable_shared_from_this is no longer required 2025-06-30 08:33:25 +02:00
skypjack
9c4676f4c0 process: merge peek/release, no need for two functions 2025-06-30 08:26:24 +02:00
skypjack
3b64dae13e natvis: process 2025-06-30 08:18:09 +02:00
skypjack
3126b3efbf scheduler: review internals to use peek/release/whatever - see #1257 2025-06-27 11:27:12 +02:00
skypjack
01e12fa011 doc: cleanup 2025-06-27 11:26:28 +02:00
skypjack
1d163ee113 process: then/release/peek for child processes 2025-06-27 09:12:02 +02:00
skypjack
f97ecd3ba4 process: rollback token based ctor, prep to move continuation to process 2025-06-25 19:16:29 +02:00
skypjack
3d66676c1b doc: a note about ENTT_INSTALL - close #1265 2025-06-25 18:13:26 +02:00
skypjack
bf28bab7ba natvis: basic_scheduler 2025-06-25 16:43:53 +02:00
skypjack
a6c2dc89ac scheduler/process: abort immediate param does not propagate anymore 2025-06-24 18:12:27 +02:00
skypjack
e36ff3d50e build: move tools to source dir 2025-06-24 17:41:01 +02:00
skypjack
dd4c36333b natvis: basic_process 2025-06-24 17:12:04 +02:00
skypjack
9e328ce232 build: remove extra > from HEADERS_FILES 2025-06-23 13:53:53 +02:00
skypjack
624d0994ff build: BUILD_INTERFACE vs INSTALL_INTERFACE for headers 2025-06-23 10:38:35 +02:00
skypjack
6ec1ee6072 build: refine cmake natvis to reduce boilerplate 2025-06-23 10:37:57 +02:00
skypjack
5642788046 process: struct vs class decl 2025-06-23 08:48:38 +02:00
skypjack
e8e77bf011 build: minor changes 2025-06-23 08:13:07 +02:00
skypjack
964dc6ec87 build: BUILD_INTERFACE vs INSTALL_INTERFACE 2025-06-20 16:24:41 +02:00
skypjack
a0d738215a build: move natvis files to source dir 2025-06-20 15:17:39 +02:00
skypjack
9ae1181ca7 build: also install natvis files 2025-06-20 15:09:39 +02:00
skypjack
3b89567d77 type_info: wrap ENTT_PRETTY_FUNCTION_PREFIX/SUFFIX with an ifdef 2025-06-20 13:26:13 +02:00
skypjack
09bb10377b process: basic_process_adaptor vs process_adaptor 2025-06-19 17:16:56 +02:00
skypjack
ffbc5519c1 process: start refining process_adaptor 2025-06-19 13:02:12 +02:00
skypjack
c80b634497 process: basic_process vs process 2025-06-18 12:22:41 +02:00
skypjack
db6c6573af scheduler: suppress warnings due to shadow variables 2025-06-17 15:27:11 +02:00
skypjack
7257dd50c6 process: single allocate ::function 2025-06-17 15:26:27 +02:00
skypjack
5f0af48b4c *: updated TODO 2025-06-17 08:16:10 +02:00
skypjack
41ebe0e779 scheduler/process: factory-like model, first draft, work in progress - see #1257 2025-06-17 08:16:05 +02:00
skypjack
0cc95c947e scheduler: further cleanup 2025-06-16 14:49:01 +02:00
skypjack
991f66e1a3 scheduler: cleanup to reduce allocations 2025-06-16 14:42:43 +02:00
skypjack
d75efb9f16 *: updated TODO 2025-06-13 18:31:32 +02:00
skypjack
3575f00bb4 doc: minor changes 2025-06-13 18:31:18 +02:00
skypjack
f532221685 doc: You are Circle 2025-06-13 08:02:30 +02:00
skypjack
f502827e88 tests: update to match the last changes to the process and scheduler classes 2025-06-11 17:11:21 +02:00
skypjack
50c842883f scheduler: init calls are no longer required 2025-06-11 17:10:59 +02:00
skypjack
2f5b25b189 process: minor changes 2025-06-11 17:10:37 +02:00
skypjack
fb0fedf8fc doc: process 2025-06-11 14:10:07 +02:00
skypjack
45e9c12b29 process: remove init, we have constructors already 2025-06-11 14:10:01 +02:00
skypjack
e52957fabb scheduler: reduce process_handler to a single class 2025-06-10 08:04:50 +02:00
skypjack
209f333252 scheduler: keep removing logic from process handler 2025-06-09 14:50:47 +02:00
skypjack
aa5c0d14ed scheduler: prepare to get rid of process handler 2025-06-09 14:46:15 +02:00
skypjack
ca40a3e62f process: move away from the crtp idiom - see #1257 2025-06-06 15:15:20 +02:00
skypjack
8574335fe6 process: make succeed and fail functions public 2025-06-05 15:12:21 +02:00
skypjack
9493756895 test: cleanup 2025-06-05 15:10:52 +02:00
skypjack
df5e57f381 process: make pause/unpause public - see #1257 2025-06-04 19:04:56 +02:00
skypjack
62e05abc77 doc: process 2025-06-03 10:50:37 +02:00
skypjack
d7d63b09b7 registry: context .clear() function - close #1262 2025-06-03 10:49:10 +02:00
skypjack
ae7b3d87ee test: avoid shadow variable warning 2025-05-29 17:49:13 +02:00
skypjack
e2ef08f129 clang-tidy: suppress ok-ish warning 2025-05-29 17:45:10 +02:00
skypjack
8dfa4aa677 test: cleanup 2025-05-29 17:39:29 +02:00
skypjack
31522ce703 *: updated TODO 2025-05-29 14:23:17 +02:00
skypjack
25cad2543d test: reduce NOLINT usage 2025-05-29 14:23:12 +02:00
skypjack
34e50d6550 organizer: a const registry is not a sync point - close #1258 2025-05-28 18:28:22 +02:00
skypjack
b7a06c535d hashed string: suppress linter warning 2025-05-27 15:36:49 +02:00
skypjack
bf5b188b13 test: missing include 2025-05-27 09:20:47 +02:00
skypjack
20af487ea9 test: minor changes 2025-05-27 09:10:22 +02:00
skypjack
d037976bcd hashed string: suppress warnings due to pointer arithmetic 2025-05-27 09:07:34 +02:00
skypjack
330bf8e120 sparse set: avoid warning due to missing [[nodiscard]] 2025-05-27 08:58:23 +02:00
skypjack
7064415265 *: updated TODO 2025-05-27 08:58:05 +02:00
skypjack
df07350a46 gh: typo in a workflow 2025-05-27 08:46:44 +02:00
skypjack
66e80820c9 doc: meta built-in names support 2025-05-26 09:20:09 +02:00
skypjack
5d3173f1b4 doc: updated links 2025-05-26 09:19:52 +02:00
skypjack
3b977186ed runtime view: swap-only policy support - close #1252 2025-05-23 15:03:09 +02:00
skypjack
38caafd999 test: runtime view and storage entity with exclude 2025-05-22 15:35:15 +02:00
skypjack
d7232dcf56 test: runtime view and storage entity 2025-05-22 15:32:09 +02:00
skypjack
825f73df6b test: more on the runtime view 2025-05-22 14:48:50 +02:00
skypjack
696e084b02 davey: refine pointer-like path 2025-05-21 17:40:43 +02:00
skypjack
fcc7306407 *: cleanup 2025-05-21 17:40:35 +02:00
skypjack
1f182a25db doc: minor changes 2025-05-21 16:29:02 +02:00
andre-caldas
995018f1a7 doc: improve documentation on for ENTT_USE_ATOMIC (#1251) 2025-05-21 16:23:23 +02:00
skypjack
a6fa6f6122 doc: auto-binding mechanism for signals 2025-05-19 19:26:22 +02:00
skypjack
1b6462a327 davey: better enum support 2025-05-19 13:38:41 +02:00
skypjack
6054e93ecc *: updated TODO 2025-05-19 13:38:39 +02:00
skypjack
3acf03ae1c testbed: more bindings 2025-05-16 11:46:56 +02:00
skypjack
42f4d28715 testbed: cleanup 2025-05-16 09:43:54 +02:00
skypjack
4ee86d8456 meta: forward declare meta_ctx 2025-05-16 09:43:47 +02:00
skypjack
3b2adc999d davey: davey_data is no longer required 2025-05-15 10:52:21 +02:00
skypjack
87ec44d3ee natvis: meta 2025-05-15 09:41:37 +02:00
skypjack
182a6d5fe4 meta: refine label support implementation 2025-05-14 14:53:02 +02:00
skypjack
49b5b5b2b1 hashed_string: make const char * conversion explicit 2025-05-12 16:05:35 +02:00
skypjack
f984ae4930 hashed_string: break the dependency with string_view 2025-05-12 09:09:35 +02:00
skypjack
e6ef506f2f natvis: label on meta elements 2025-05-09 17:33:29 +02:00
skypjack
2d1ae3d5eb *: updated TODO 2025-05-09 17:23:24 +02:00
skypjack
d775841457 meta: built-in labels for meta data 2025-05-09 17:22:57 +02:00
skypjack
2f60398892 *: updated TODO 2025-05-09 17:20:17 +02:00
skypjack
393a347bad meta: prepare factory for labels on data 2025-05-09 14:57:48 +02:00
skypjack
6a4c594792 doc: cleanup 2025-05-09 09:29:56 +02:00
skypjack
154c16a9d3 meta: built-in labels for meta functions 2025-05-08 16:46:54 +02:00
skypjack
9536039932 meta: refine type label impl and tests 2025-05-08 10:17:49 +02:00
skypjack
cad91fd0d1 *: updated TODO 2025-05-05 18:31:30 +02:00
skypjack
7615e55e96 meta: built-in labels for meta types 2025-05-05 18:31:22 +02:00
skypjack
0f54af1a11 doc: typo 2025-05-05 18:30:16 +02:00
skypjack
a439cf476c meta: prepare for built-in name support 2025-05-05 17:50:24 +02:00
skypjack
121e5a0ca8 hashed_string: minor changes 2025-05-05 17:49:59 +02:00
skypjack
328e53bd7b testbed: use meta info 2025-05-02 14:13:03 +02:00
skypjack
4dd03da2e2 testbed: meta setup function proto 2025-05-02 11:36:45 +02:00
skypjack
ebab67c314 davey: davey_data constructor 2025-05-02 11:10:32 +02:00
skypjack
5ddab2b9d9 testbed: refine a couple of components 2025-05-02 09:12:03 +02:00
skypjack
265f00becf testbed: setup some components to start with... 2025-04-30 16:24:16 +02:00
skypjack
340888e4e2 testbed: bare minimum rendering system 2025-04-30 16:24:01 +02:00
skypjack
f5bbc895b5 testbed: updated components 2025-04-30 16:23:47 +02:00
skypjack
57a6c6b238 testbed: bare minimal input system 2025-04-30 16:06:55 +02:00
skypjack
cb10854bc8 testbed: a bunch of components to start with 2025-04-30 16:06:17 +02:00
skypjack
1796b18284 testbed: setup hud system for later use 2025-04-29 14:48:23 +02:00
skypjack
f505050391 testbed: prepare input system 2025-04-28 17:01:53 +02:00
skypjack
b0afed4bd0 davey: present_view function (with quick test example) 2025-04-24 19:15:36 +02:00
skypjack
292cbd3d95 davey: use right entity type 2025-04-24 19:03:47 +02:00
skypjack
756e861346 davey: internal changes 2025-04-24 17:35:41 +02:00
skypjack
6db98b40a1 davey: cleanup 2025-04-24 17:31:41 +02:00
skypjack
a9852a2d2f testbed: quick check for storage window 2025-04-23 17:49:41 +02:00
skypjack
7302b1544d davey: swap entity and storage in registry view 2025-04-23 17:48:06 +02:00
skypjack
491956a69e davey: typo 2025-04-23 17:46:32 +02:00
skypjack
2b94080ee5 davey: missing template keyword 2025-04-23 09:30:58 +02:00
skypjack
4732bd1bf7 davey: entry point for views 2025-04-23 08:59:06 +02:00
skypjack
2bdb44e3ba davey: typo 2025-04-23 08:54:40 +02:00
skypjack
d805c29b73 davey: minor changes 2025-04-22 19:03:43 +02:00
skypjack
a9187c1f45 davey: rework storage path 2025-04-22 18:56:10 +02:00
skypjack
4e76ae5cc6 davey: refine storage tab 2025-04-22 14:07:04 +02:00
skypjack
54b6ee0f27 davey: view entry point (draft) 2025-04-22 13:56:57 +02:00
skypjack
bf8c83e798 davey: make registry tab bar embeddable 2025-04-17 11:51:16 +02:00
skypjack
4aed49b371 davey: context-less single storage overload 2025-04-17 11:50:18 +02:00
skypjack
c1df295845 davey: break entity_tab dependency on registry class 2025-04-17 11:44:58 +02:00
skypjack
265057be83 davey: break storage_tab dependency on registry class 2025-04-17 11:38:03 +02:00
skypjack
021fbb6c69 davey: break present_entity dependency on registry class 2025-04-17 11:33:22 +02:00
skypjack
843480fdf6 davey: missing template keywords 2025-04-17 11:15:09 +02:00
skypjack
3eb78a175e davey: add missing template keyword 2025-04-16 19:40:31 +02:00
skypjack
de1ae66966 davey: refine specifiers 2025-04-16 19:39:43 +02:00
skypjack
ab9a62bcd8 davey: avoid using deprecated members 2025-04-16 19:31:51 +02:00
skypjack
41d9e44a23 davey: present_element (first draft, read-only) 2025-04-15 19:27:18 +02:00
skypjack
693b85fe40 davey: DAVEY_OR macro (for later uses) 2025-04-15 19:15:25 +02:00
skypjack
7a90e2a93b type_info: rename things for the best 2025-04-14 15:35:34 +02:00
skypjack
11c3ea2fdb type_info: full_type_name returns a non-null name in all cases 2025-04-14 15:32:14 +02:00
skypjack
57d604bcb2 type_info: full_type_name vs stripped_type_name 2025-04-14 15:24:00 +02:00
skypjack
63e38e40dd type_info: stripped_type_name returns empty views if name is not available 2025-04-14 15:20:27 +02:00
skypjack
c66f116449 build: refine cmake for testbed 2025-04-11 18:27:25 +02:00
skypjack
55fb754614 davey: work in progress 2025-04-11 18:16:25 +02:00
skypjack
afe687e9d7 testbed: temp init function for test purposes 2025-04-11 18:16:03 +02:00
Michele Caini
6ca9c77b26 davey: missing includes 2025-04-10 16:59:50 +02:00
Michele Caini
990956980b *: updated TODO 2025-04-10 16:59:42 +02:00
Michele Caini
f7f30bc5b6 davey: work in progress... 2025-04-10 13:01:53 +02:00
Michele Caini
81b8d01e96 testbed: try to use gha-setup-ninja to setup ninja on the CI 2025-04-09 10:46:07 +02:00
Michele Caini
7ecfe45923 meta: rollback factory setup support to get wip in e healthy state (damn MSVC that accepts everything) 2025-04-09 10:30:52 +02:00
Michele Caini
5212f0c44a test: scoped functions 2025-04-09 10:23:31 +02:00
Michele Caini
fa58cedd2e meta: avoid shadow-warnings 2025-04-09 10:20:30 +02:00
Michele Caini
8b1b7a0fc9 test: add missing template keywords 2025-04-09 10:11:45 +02:00
Michele Caini
2a37b972d9 meta: meta_factory::setup function 2025-04-09 09:26:00 +02:00
Michele Caini
df7ebd244b doc: minor changes 2025-04-08 17:13:17 +02:00
Michele Caini
b4b1cf38a6 test: cleanup 2025-04-08 17:12:39 +02:00
Michele Caini
292d57a60b meta: add meta_factory to fwd file 2025-04-08 12:08:27 +02:00
Michele Caini
9d6850636a meta_factory: type -> element_type to avoid collisions 2025-04-08 11:41:38 +02:00
Michele Caini
8879a76e88 meta_factory: expose underlying type 2025-04-07 17:50:56 +02:00
Michele Caini
0fa38bf0e0 testbed: work in progress (if only I had more time these days...) 2025-04-07 17:46:21 +02:00
Michele Caini
36eba38f62 *: updated TODO 2025-04-07 17:45:45 +02:00
Michele Caini
fe8d7d78c4 meta: drop unnecessary static keywords 2025-04-04 12:35:12 +02:00
Michele Caini
ff32d77eba meta: deprecate fixed_size in favor of extent/meta_dynamic_extent 2025-04-04 12:00:15 +02:00
Michele Caini
84f60ea2be build: use ninja generator for testbed windows workflow 2025-04-03 16:05:38 +02:00
Michele Caini
cc6fe33709 build: add ubuntu required packages to testbed workflow 2025-04-03 15:58:47 +02:00
Michele Caini
5b3f04c5ff build: testbed GH workflow (first attempt) 2025-04-03 14:35:47 +02:00
Michele Caini
dc0bf7bdd8 build: fix typo in GH workflow 2025-04-03 14:35:27 +02:00
Michele Caini
13f21dd942 build: tools.yml -> analyzer.yml 2025-04-01 14:30:47 +02:00
Michele Caini
fbe69ae90b tools: davey trauma :) 2025-04-01 14:23:15 +02:00
Michele Caini
dedad6a03e testbed: updated SDL3 version 2025-04-01 11:19:16 +02:00
Michele Caini
86d3d2207a testbed: application skel 2025-03-31 14:41:50 +02:00
Michele Caini
9c39f76774 build: update tests to make them work (again) with cr 2025-03-31 14:31:51 +02:00
Michele Caini
8c04222a40 testbed: app -> application 2025-03-28 11:28:32 +01:00
Michele Caini
d84a18e456 testbed: context class 2025-03-28 11:25:29 +01:00
Michele Caini
e42231fab0 sparse_set: deprecate type() in favor of info() for consistency 2025-03-27 10:23:26 +01:00
Michele Caini
d60efe04fd poly: deprecate type() in favor of info() for consistency 2025-03-26 15:19:15 +01:00
Michele Caini
6e7ca646ec any: deprecate type() in favor of info() for consistency 2025-03-26 15:16:19 +01:00
Michele Caini
cac3e4c62b test: minor changes 2025-03-26 15:15:57 +01:00
Michele Caini
0e323954e4 build: testbed initial setup 2025-03-25 15:44:52 +01:00
Michele Caini
385f5a4379 doc: typo 2025-03-25 11:55:36 +01:00
Michele Caini
e80d20a740 build: prepare testbed executable 2025-03-25 11:43:56 +01:00
Justin Braben
9d4db9738a build: fix delegate compare test in Release mode (#1242) 2025-03-25 11:40:32 +01:00
Michele Caini
f54859bedf build: prepare for tools and testbed 2025-03-21 09:39:53 +01:00
Michele Caini
556ff733d5 meta: drop deprecated utilities 2025-03-21 08:08:45 +01:00
Michele Caini
4027bfff69 meta: drop deprecated accessors on meta_any 2025-03-21 07:51:17 +01:00
Michele Caini
dbcc0baa7b meta: drop the meta<Type> function 2025-03-20 19:02:01 +01:00
Michele Caini
d45c2aca8a meta: drop multi-setter support for meta data 2025-03-20 09:26:25 +01:00
Michele Caini
fd015edb98 meta: drop meta_any_policy 2025-03-20 09:21:38 +01:00
Michele Caini
5ccb46383c storage: drop deprecated functions 2025-03-20 09:20:44 +01:00
Michele Caini
fa6fdc4059 now working on version 3.16.0 2025-03-19 17:09:40 +01:00
252 changed files with 15921 additions and 12708 deletions

View File

@@ -5,8 +5,8 @@ build --enable_runfiles
build --incompatible_strict_action_env
# required for googletest
build:linux --cxxopt=-std=c++17
build:macos --cxxopt=-std=c++17
build:linux --cxxopt=-std=c++20
build:macos --cxxopt=-std=c++20
common:ci --announce_rc
common:ci --verbose_failures

View File

@@ -3,6 +3,7 @@ BasedOnStyle: llvm
AccessModifierOffset: -4
AlignEscapedNewlines: DontAlign
AllowShortBlocksOnASingleLine: Always
AllowShortCompoundRequirementOnASingleLine: true
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: WithoutElse
@@ -10,6 +11,8 @@ AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: true
AlwaysBreakTemplateDeclarations: Yes
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Attach
BreakBeforeConceptDeclarations: Always
BreakBeforeTernaryOperators: true
ColumnLimit: 0
DerivePointerAlignment: false
@@ -26,17 +29,33 @@ IncludeCategories:
Priority: 5
IncludeIsMainRegex: "^$"
IndentPPDirectives: AfterHash
IndentRequiresClause: false
IndentWidth: 4
InsertBraces: true
InsertNewlineAtEOF: true
KeepEmptyLinesAtTheStartOfBlocks: false
Language: Cpp
PointerAlignment: Right
RequiresClausePosition: OwnLineWithBrace
RequiresExpressionIndentation: OuterScope
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: false
SpaceAroundPointerQualifiers: After
SpaceBeforeCaseColon: false
SpaceBeforeCtorInitializerColon: false
SpaceBeforeInheritanceColon: false
SpaceBeforeParens: Never
SpaceBeforeParens: Custom
SpaceBeforeParensOptions:
AfterControlStatements: false
AfterForeachMacros: false
AfterFunctionDeclarationName: false
AfterFunctionDefinitionName: false
AfterIfMacros: false
AfterOverloadedOperator: false
AfterPlacementOperator: false
AfterRequiresInClause: true
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceBeforeRangeBasedForLoopColon: false
Standard: Latest
TabWidth: 4

View File

@@ -18,8 +18,10 @@ Checks: >
performance-*,
portability-*,
readability-*,
-readability-else-after-return,
-readability-function-cognitive-complexity,
-readability-named-parameter,
-readability-redundant-member-init,
-readability-uppercase-literal-suffix,
CheckOptions:
- key: cppcoreguidelines-avoid-magic-numbers.IgnoreAllFloatingPointValues

View File

@@ -1,9 +1,9 @@
name: tools
name: analyzer
on:
push:
branches:
- tools
- analyzer
jobs:
@@ -11,8 +11,8 @@ jobs:
timeout-minutes: 60
env:
IWYU: "0.23"
LLVM: "19"
IWYU: "0.24"
LLVM: "20"
runs-on: ubuntu-latest
continue-on-error: true
@@ -55,7 +55,7 @@ jobs:
-DENTT_BUILD_EXAMPLE=ON \
-DENTT_BUILD_LIB=ON \
-DENTT_BUILD_SNAPSHOT=ON \
-DENTT_BUILD_TOOLS=ON \
-DENTT_BUILD_TESTBED=ON \
-DCMAKE_CXX_INCLUDE_WHAT_YOU_USE="include-what-you-use;-Xiwyu;--mapping_file=${GITHUB_WORKSPACE}/entt.imp;-Xiwyu;--no_fwd_decls;-Xiwyu;--verbose=1" \
..
make -j4
@@ -78,7 +78,7 @@ jobs:
-DENTT_BUILD_EXAMPLE=ON \
-DENTT_BUILD_LIB=ON \
-DENTT_BUILD_SNAPSHOT=ON \
-DENTT_BUILD_TOOLS=ON \
-DENTT_BUILD_TESTBED=ON \
-DENTT_USE_CLANG_TIDY=ON \
..
make -j4

View File

@@ -40,10 +40,10 @@ jobs:
windows:
strategy:
matrix:
toolset: [default, v142, clang-cl]
toolset: [default, v143, clang-cl]
include:
- toolset: v142
toolset_option: -T"v142"
- toolset: v143
toolset_option: -T"v143"
- toolset: clang-cl
toolset_option: -T"ClangCl"
@@ -85,7 +85,7 @@ jobs:
matrix:
os: [windows-latest, macOS-latest, ubuntu-latest]
id_type: ["std::uint32_t", "std::uint64_t"]
cxx_std: [cxx_std_17, cxx_std_20]
cxx_std: [cxx_std_20, cxx_std_23]
timeout-minutes: 15
runs-on: ${{ matrix.os }}

View File

@@ -2,7 +2,7 @@ name: deploy
on:
release:
types: published
types: [published]
jobs:

View File

@@ -11,7 +11,7 @@ jobs:
matrix:
compiler: [clang++]
id_type: ["std::uint32_t", "std::uint64_t"]
cxx_std: [cxx_std_17, cxx_std_20]
cxx_std: [cxx_std_20, cxx_std_23]
runs-on: ubuntu-latest

79
.github/workflows/testbed.yml vendored Normal file
View File

@@ -0,0 +1,79 @@
name: testbed
on: [push]
jobs:
linux:
timeout-minutes: 15
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install required packages
run: |
sudo apt update
sudo apt install -y \
build-essential \
git \
make \
pkg-config \
cmake \
ninja-build \
gnome-desktop-testing \
libasound2-dev \
libpulse-dev \
libaudio-dev \
libjack-dev \
libsndio-dev \
libx11-dev \
libxext-dev \
libxrandr-dev \
libxcursor-dev \
libxfixes-dev \
libxi-dev \
libxss-dev \
libxtst-dev \
libxkbcommon-dev \
libdrm-dev \
libgbm-dev \
libgl1-mesa-dev \
libgles2-mesa-dev \
libegl1-mesa-dev \
libdbus-1-dev \
libibus-1.0-dev \
libudev-dev \
libpipewire-0.3-dev \
libwayland-dev \
libdecor-0-dev \
liburing-dev
- name: Compile testbed
working-directory: build
run: |
cmake -DENTT_BUILD_TESTBED=ON ..
make -j4
windows:
timeout-minutes: 15
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: seanmiddleditch/gha-setup-ninja@master
- name: Compile testbed
working-directory: build
run: |
cmake -DENTT_BUILD_TESTBED=ON .. -G Ninja
cmake --build . -j 4
macos:
timeout-minutes: 15
runs-on: macOS-latest
steps:
- uses: actions/checkout@v4
- name: Compile testbed
working-directory: build
run: |
cmake -DENTT_BUILD_TESTBED=ON ..
make -j4

View File

@@ -1,6 +1,6 @@
# EnTT
cmake_minimum_required(VERSION 3.15.7)
cmake_minimum_required(VERSION 3.28)
# Read project version
@@ -22,10 +22,10 @@ project(
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug)
endif()
message(VERBOSE "*")
message(VERBOSE "* ${PROJECT_NAME} v${PROJECT_VERSION} (${CMAKE_BUILD_TYPE})")
message(VERBOSE "* Copyright (c) 2017-2025 Michele Caini <michele.caini@gmail.com>")
message(VERBOSE "* Copyright (c) 2017-2026 Michele Caini <michele.caini@gmail.com>")
message(VERBOSE "*")
# CMake stuff
@@ -93,7 +93,7 @@ target_include_directories(
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
target_compile_features(EnTT INTERFACE cxx_std_17)
target_compile_features(EnTT INTERFACE cxx_std_20)
if(ENTT_HAS_LIBCPP)
target_compile_options(EnTT BEFORE INTERFACE -stdlib=libc++)
@@ -105,14 +105,114 @@ 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/.*")
endif()
set(ENTT_CLANG_TIDY_OPTIONS ";--config-file=${EnTT_SOURCE_DIR}/.clang-tidy;--header-filter=${EnTT_SOURCE_DIR}/src/entt/.*")
if(MSVC AND NOT (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang"))
set(ENTT_CLANG_TIDY_OPTIONS "${ENTT_CLANG_TIDY_OPTIONS};--extra-arg=/EHsc;--extra-arg=/wd4996")
endif()
set(CMAKE_CXX_CLANG_TIDY "${ENTT_CLANG_TIDY_EXECUTABLE}${ENTT_CLANG_TIDY_OPTIONS}")
endif()
# Add EnTT goodies
option(ENTT_INCLUDE_HEADERS "Add all EnTT headers to the EnTT target." OFF)
option(ENTT_INCLUDE_NATVIS "Add EnTT natvis files to the EnTT target." OFF)
if(ENTT_INCLUDE_HEADERS)
set(
HEADERS_FILES
config/config.h
config/macro.h
config/version.h
container/dense_map.hpp
container/dense_set.hpp
container/table.hpp
container/fwd.hpp
core/algorithm.hpp
core/any.hpp
core/bit.hpp
core/compressed_pair.hpp
core/concepts.hpp
core/enum.hpp
core/family.hpp
core/fwd.hpp
core/hashed_string.hpp
core/ident.hpp
core/iterator.hpp
core/memory.hpp
core/monostate.hpp
core/ranges.hpp
core/tuple.hpp
core/type_info.hpp
core/type_traits.hpp
core/utility.hpp
entity/component.hpp
entity/entity.hpp
entity/fwd.hpp
entity/group.hpp
entity/handle.hpp
entity/mixin.hpp
entity/helper.hpp
entity/organizer.hpp
entity/ranges.hpp
entity/registry.hpp
entity/runtime_view.hpp
entity/snapshot.hpp
entity/sparse_set.hpp
entity/storage.hpp
entity/view.hpp
graph/adjacency_matrix.hpp
graph/dot.hpp
graph/flow.hpp
graph/fwd.hpp
locator/locator.hpp
meta/adl_pointer.hpp
meta/container.hpp
meta/context.hpp
meta/factory.hpp
meta/fwd.hpp
meta/meta.hpp
meta/node.hpp
meta/pointer.hpp
meta/policy.hpp
meta/range.hpp
meta/resolve.hpp
meta/template.hpp
meta/type_traits.hpp
meta/utility.hpp
poly/fwd.hpp
poly/poly.hpp
process/fwd.hpp
process/process.hpp
process/scheduler.hpp
resource/cache.hpp
resource/fwd.hpp
resource/loader.hpp
resource/resource.hpp
signal/delegate.hpp
signal/dispatcher.hpp
signal/emitter.hpp
signal/fwd.hpp
signal/sigh.hpp
stl/functional.hpp
stl/iterator.hpp
stl/memory.hpp
tools/davey.hpp
entt.hpp
fwd.hpp
tools.hpp
)
list(TRANSFORM HEADERS_FILES APPEND ">" OUTPUT_VARIABLE HEADERS_BUILD_INTERFACE)
list(TRANSFORM HEADERS_BUILD_INTERFACE PREPEND "$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/")
list(TRANSFORM HEADERS_FILES APPEND ">" OUTPUT_VARIABLE HEADERS_INSTALL_INTERFACE)
list(TRANSFORM HEADERS_INSTALL_INTERFACE PREPEND "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/entt/")
target_sources(EnTT INTERFACE ${HEADERS_BUILD_INTERFACE} ${HEADERS_INSTALL_INTERFACE})
endif()
if(ENTT_INCLUDE_NATVIS)
if(MSVC)
set(ENTT_HAS_NATVIS TRUE CACHE BOOL "" FORCE)
@@ -124,104 +224,29 @@ if(ENTT_INCLUDE_NATVIS)
endif()
endif()
if(ENTT_INCLUDE_HEADERS)
target_sources(
EnTT
INTERFACE
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/config/config.h>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/config/macro.h>
$<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>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/fwd.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/hashed_string.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/ident.hpp>
$<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>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/utility.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/component.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/entity.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/fwd.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/group.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/handle.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/mixin.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/helper.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/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>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/sparse_set.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/storage.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/view.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/graph/adjacency_matrix.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/graph/dot.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/graph/flow.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/graph/fwd.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/locator/locator.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/adl_pointer.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/container.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/context.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/factory.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/fwd.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/meta.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/node.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/pointer.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/policy.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/range.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/resolve.hpp>
$<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/poly/fwd.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/poly/poly.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/process/fwd.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/process/process.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/process/scheduler.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/resource/cache.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/resource/fwd.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/resource/loader.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/resource/resource.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/signal/delegate.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/signal/dispatcher.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/signal/emitter.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/signal/fwd.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/signal/sigh.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entt.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/fwd.hpp>
)
endif()
if(ENTT_HAS_NATVIS)
target_sources(
EnTT
INTERFACE
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/config.natvis>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/container.natvis>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/core.natvis>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/entity.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/poly.natvis>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/process.natvis>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/resource.natvis>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/signal.natvis>
set(
NATVIS_FILES
config.natvis
container.natvis
core.natvis
entity.natvis
graph.natvis
locator.natvis
meta.natvis
poly.natvis
process.natvis
resource.natvis
signal.natvis
)
list(TRANSFORM NATVIS_FILES APPEND ">" OUTPUT_VARIABLE NATVIS_BUILD_INTERFACE)
list(TRANSFORM NATVIS_BUILD_INTERFACE PREPEND "$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/natvis/")
list(TRANSFORM NATVIS_FILES APPEND ">" OUTPUT_VARIABLE NATVIS_INSTALL_INTERFACE)
list(TRANSFORM NATVIS_INSTALL_INTERFACE PREPEND "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/entt/natvis/")
target_sources(EnTT INTERFACE ${NATVIS_BUILD_INTERFACE} ${NATVIS_INSTALL_INTERFACE})
endif()
# Install EnTT and all related files
@@ -296,29 +321,36 @@ if(ENTT_INSTALL)
FILES_MATCHING
PATTERN "*.h"
PATTERN "*.hpp"
PATTERN "*.natvis"
)
export(PACKAGE EnTT)
endif()
# Tests
# Tests and testbed
option(ENTT_BUILD_TESTING "Enable building tests." OFF)
option(ENTT_BUILD_TESTBED "Enable building testbed." OFF)
if(ENTT_BUILD_TESTING)
option(ENTT_FIND_GTEST_PACKAGE "Enable finding gtest package." OFF)
if(ENTT_BUILD_TESTING OR ENTT_BUILD_TESTBED)
set(ENTT_ID_TYPE std::uint32_t CACHE STRING "Type of identifiers to use for tests and testbed")
set(ENTT_CXX_STD cxx_std_20 CACHE STRING "C++ standard revision to use for tests and testbed")
option(ENTT_BUILD_BENCHMARK "Build benchmark." OFF)
option(ENTT_BUILD_EXAMPLE "Build examples." OFF)
option(ENTT_BUILD_LIB "Build lib tests." OFF)
option(ENTT_BUILD_SNAPSHOT "Build snapshot test with Cereal." OFF)
set(ENTT_ID_TYPE std::uint32_t CACHE STRING "Type of identifiers to use for the tests")
set(ENTT_CXX_STD cxx_std_17 CACHE STRING "C++ standard revision to use for the tests")
include(CTest)
enable_testing()
add_subdirectory(test)
# Tests and tesetbed do not work together because SDL gets confused with EnTT tests
if(ENTT_BUILD_TESTING)
option(ENTT_FIND_GTEST_PACKAGE "Enable finding gtest package." OFF)
option(ENTT_BUILD_BENCHMARK "Build benchmark." OFF)
option(ENTT_BUILD_EXAMPLE "Build examples." OFF)
option(ENTT_BUILD_LIB "Build lib tests." OFF)
option(ENTT_BUILD_SNAPSHOT "Build snapshot test with Cereal." OFF)
include(CTest)
enable_testing()
add_subdirectory(test)
elseif(ENTT_BUILD_TESTBED)
add_subdirectory(testbed)
endif()
endif()
# Documentation

View File

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

@@ -217,12 +217,12 @@ the include paths.
## Requirements
To be able to use `EnTT`, users must provide a full-featured compiler that
supports at least C++17.<br/>
supports at least C++20.<br/>
The requirements below are mandatory to compile the tests and to extract the
documentation:
* `CMake` version 3.7 or later.
* `Doxygen` version 1.8 or later.
* `CMake` version 3.28 or later.
* `Doxygen` version 1.14 or later.
Alternatively, [Bazel](https://bazel.build) is also supported as a build system
(credits to [zaucy](https://github.com/zaucy) who offered to maintain it).<br/>
@@ -232,7 +232,8 @@ build system of the library.
## CMake
To use `EnTT` from a `CMake` project, just link an existing target to the
`EnTT::EnTT` alias.<br/>
`EnTT::EnTT` alias.
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/>
@@ -240,12 +241,17 @@ 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.
Note that all `install` calls are guarded by the `ENTT_INSTALL` option to allow
using `EnTT` as a submodule without conflicting with user logic.<br/>
It is therefore necessary to set the option to true to take advantage of the
installation logic provided by this library.
## Natvis support
When using `CMake`, just enable the option `ENTT_INCLUDE_NATVIS` and enjoy
it.<br/>
Otherwise, most of the tools are covered via Natvis and all files can be found
in the `natvis` directory, divided by module.<br/>
in the `natvis` subdirectory, divided by module.<br/>
If you spot errors or have suggestions, any contribution is welcome!
## Packaging Tools
@@ -319,7 +325,7 @@ If you spot errors or have suggestions, any contribution is welcome!
`bazel` project, add the following to your `MODULE.bazel` file:
```starlark
bazel_dep(name = "entt", version = "3.12.2")
bazel_dep(name = "entt", version = "3.16.0")
```
EnTT will now be available as `@entt` (short for `@entt//:entt`) to be used
@@ -403,7 +409,7 @@ know who has participated so far.
# License
Code and documentation Copyright (c) 2017-2025 Michele Caini.<br/>
Code and documentation Copyright (c) 2017-2026 Michele Caini.<br/>
Colorful logo Copyright (c) 2018-2021 Richard Caseres.
Code released under

23
TODO
View File

@@ -13,25 +13,26 @@ TODO:
* review all NOLINT
* bring nested groups back in place (see bd34e7f)
* work stealing job system (see #100) + mt scheduler based on const awareness for types
* view: reduce inst due to/improve perf with index-based approach in dispatch_get/pick_and_each/each (single type too, define storage ::at and ::at_as_tuple)
* view: update natvis as needed after the last rework, merge pools/filter in the same array, drop check (?) and turn view into a position
* view: type-only view_iterator (dyn get/excl sizes), type-only basic_common_view (dyn get/excl sizes with pointer to array from derived)
* combine version-mask-vs-version-bits tricks with reserved bits to allow things like enabling/disabling
* self contained entity traits to avoid explicit specializations (ie enum constants)
* auto type info data from types if present
* test: push sharing types further
* storage entity: fast range-push from above
* table: pop back to support swap and pop, single column access, empty type optimization
* review cmake warning about FetchContent_Populate (need .28 and EXCLUDE_FROM_ALL for FetchContent)
* suppress -Wself-move on CI with g++13
* view specializations for multi, single and filtered elements
* don't pass reactive storage by default to callback
* runtime types support for meta for types that aren't backed by C++ types
* built-in no-pagination storage - no_pagination page size as limits::max
* any cdynamic to support const ownership construction
* allow passing arguments to meta setter/getter (we can fallback on meta invoke probably)
* FetchContent_Populate -> FetchContent_MakeAvailable warnings
* doc: IMPLICIT_DIR_DOCS for dir docs or \dir
* meta non-const allow_cast overloads: (const int &) to (int &) is not allowed, but (const int &) to (double &) is allowed (support only for convertibles)
* improve non-const allow cast with in-place switch
* meta fixed_size could return the size directly if present
* review build process for testbed (i.e. tests first due to SDL)
* use unique_ptr or any for meta_custom_node
* paged vector as a standalone class
* resource: shared_from_this?
* finish the imgui viewer/editor!
* archetype-like a-là EnTT support (see my own notes)
* organizer: view/storage only based model, no registry
* introduce a way to inject stl from outside too
* redesign snapshot as a whole
* explore "runtime" mode for hashed string where the source is copied internally
* storage: shrink_to_fit does not work with reentrant destructor?
* test trivially_destructible optimization

View File

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

View File

@@ -1,9 +1,9 @@
cmake_minimum_required(VERSION 3.7.2)
cmake_minimum_required(VERSION 3.28)
project(test_package)
set(CMAKE_VERBOSE_MAKEFILE TRUE)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)

View File

@@ -1,6 +1,6 @@
# Doxygen configuration (documentation)
find_package(Doxygen 1.13)
find_package(Doxygen 1.14)
if(DOXYGEN_FOUND)
include(FetchContent)
@@ -12,15 +12,10 @@ if(DOXYGEN_FOUND)
GIT_SHALLOW 1
)
FetchContent_GetProperties(doxygen-awesome-css)
if(NOT doxygen-awesome-css_POPULATED)
FetchContent_Populate(doxygen-awesome-css)
set(doxygen-awesome-css_INCLUDE_DIR ${doxygen-awesome-css_SOURCE_DIR})
endif()
FetchContent_MakeAvailable(doxygen-awesome-css)
set(DOXY_SOURCE_DIRECTORY ${EnTT_SOURCE_DIR}/src)
set(DOXY_CSS_DIRECTORY ${doxygen-awesome-css_INCLUDE_DIR})
set(DOXY_CSS_DIRECTORY ${doxygen-awesome-css_SOURCE_DIR})
set(DOXY_DOCS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
set(DOXY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
@@ -37,6 +32,7 @@ if(DOXYGEN_FOUND)
md/core.md
md/entity.md
md/faq.md
md/graph.md
md/lib.md
md/links.md
md/locator.md
@@ -46,7 +42,6 @@ if(DOXYGEN_FOUND)
md/reference.md
md/resource.md
md/signal.md
md/unreal.md
doxy.in
)

View File

@@ -4,7 +4,8 @@
* [Introduction](#introduction)
* [Definitions](#definitions)
* [ENTT_NOEXCEPTION](#entt_noexception)
* [ENTT_USE_STL](#entt_use_stl)
* [ENTT_NO_EXCEPTION](#entt_no_exception)
* [ENTT_USE_ATOMIC](#entt_use_atomic)
* [ENTT_ID_TYPE](#entt_id_type)
* [ENTT_SPARSE_PAGE](#entt_sparse_page)
@@ -13,7 +14,9 @@
* [ENTT_ASSERT_CONSTEXPR](#entt_assert_constexpr)
* [ENTT_DISABLE_ASSERT](#entt_disable_assert)
* [ENTT_NO_ETO](#entt_no_eto)
* [ENTT_NO_MIXIN](#entt_no_mixin)
* [ENTT_STANDARD_CPP](#entt_standard_cpp)
* [Configuration injection](#configuration-injection)
# Introduction
@@ -33,7 +36,14 @@ Each parameter can result in internal library definitions. It is not recommended
to try to also modify these definitions, since there is no guarantee that they
will remain stable over time unlike the options below.
## ENTT_NOEXCEPTION
## ENTT_USE_STL
Intended for testing purposes, it forces the use of built-in replacements of
some parts of the standard library that aren't always available otherwise.<br/>
`EnTT` _detects_ these cases on its own, and users should never define this
variable explicitly. However, it's still possible if desired.
## ENTT_NO_EXCEPTION
Define this variable without assigning any value to it to turn off exception
handling in `EnTT`.<br/>
@@ -45,8 +55,9 @@ also limited to this library only.
In general, `EnTT` does not offer primitives to support multi-threading. Many of
the features can be split over multiple threads without any explicit control and
the user is the one who knows if a synchronization point is required.<br/>
However, some features are not easily accessible to users and are made
thread-safe by means of this definition.
However, some internal static data shared between threads should be atomic when
using `EnTT` from multiple threads, even when dealing with local storage. Define
this macro without assigning any value to it to get the job done.
## ENTT_ID_TYPE
@@ -105,6 +116,13 @@ never instantiated nor stored by the ECS module of `EnTT`.<br/>
Use this variable to treat these types like all others and therefore to create a
dedicated storage for them.
## ENTT_NO_MIXIN
`EnTT` automatically assigns mixins to all storage types to support signaling
when creating, destroying, and modifying elements.<br/>
Mixins can have a (most likely negligible) cost in terms of performance and
compilation time. If unwanted, this macro suppresses automatic generation.
## ENTT_STANDARD_CPP
`EnTT` mixes non-standard language features with others that are perfectly
@@ -113,3 +131,14 @@ This definition prevents the library from using non-standard techniques, that
is, functionalities that are not fully compliant with the standard C++.<br/>
While there are no known portability issues at the time of this writing, this
should make the library fully portable anyway if needed.
# Configuration injection
Configuration variables are provided via code or injected directly from the
outside via a dedicated file.<br/>
`EnTT` uses `__has_include` internally and looks for a specific path, namely
`<entt/ext/config.h>`. This can be provided by the user by setting the include
paths appropriately.<br/>
For example, `CMake` allows users to _bind_ additional include directories to a
target with `target_include_directories`. See the test suite, and in particular
the `config_ext` test for a practical example.

View File

@@ -201,19 +201,15 @@ 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:
Some general purpose utilities, such as the fast module function:
```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.
Where `modulus` is necessarily a power of two. 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
@@ -449,9 +445,9 @@ Some are geared towards simplifying the implementation of (internal or external)
allocator aware containers. Others are designed to help the developer with
everyday problems.
The former are very specific and for niche problems. These are tools designed to
unwrap fancy or plain pointers (`to_address`) or to help forget the meaning of
acronyms like _POCCA_, _POCMA_ or _POCS_.<br/>
The former are very specific and for niche problems. For example, there are
tools designed to help forget the meaning of acronyms like _POCCA_, _POCMA_ or
_POCS_.<br/>
I will not describe them here in detail. Instead, I recommend reading the inline
documentation to those interested in the subject.
@@ -466,7 +462,7 @@ The `allocate_unique` function follows this proposal, making a virtue out of
necessity:
```cpp
std::unique_ptr<my_type, entt::allocation_deleter<my_type>> ptr = entt::allocate_unique<my_type>(allocator, arguments);
std::unique_ptr<my_type, entt::allocation_deleter<allocator_type>> ptr = entt::allocate_unique<my_type>(allocator, arguments);
```
Although the internal implementation is slightly different from what is proposed
@@ -528,12 +524,13 @@ Basically, the whole system relies on a handful of classes. In particular:
associative containers or for positional accesses in a vector or an array.
An external generator can also be used if needed. In fact, `type_index` can be
specialized by type and is also _sfinae-friendly_ in order to allow more
specialized by type or constrained with a concept in order to allow more
refined specializations such as:
```cpp
template<typename Type>
struct entt::type_index<Type, std::void_d<decltype(Type::index())>> {
requires requires { { Type::index() } -> std::same_as<entt::id_type>; }
struct entt::type_index<Type> {
static entt::id_type value() noexcept {
return Type::index();
}
@@ -562,8 +559,8 @@ Basically, the whole system relies on a handful of classes. In particular:
identifiers remain stable across executions. Moreover, they are generated
at runtime and are no longer a compile-time thing.
As it happens with `type_index`, also `type_hash` is a _sfinae-friendly_ class
that can be specialized in order to customize its behavior globally or on a
As it happens with `type_index`, also `type_hash` can be specialized or
constrained with a concept in order to customize its behavior globally or on a
per-type or per-traits basis.
* The name associated with a given type:
@@ -593,8 +590,8 @@ Basically, the whole system relies on a handful of classes. In particular:
purposes. Users can prevent the library from using these features by means of
the `ENTT_STANDARD_CPP` definition. In this case, the name is just empty.
As it happens with `type_index`, also `type_name` is a _sfinae-friendly_ class
that can be specialized in order to customize its behavior globally or on a
As it happens with `type_index`, also `type_name` can be specialized or
constrained with a concept in order to customize its behavior globally or on a
per-type or per-traits basis.
These are then combined into utilities that aim to offer an API that is somewhat
@@ -639,7 +636,7 @@ These are the information made available by `type_info`:
This is also an alias for the following:
```cpp
auto idx = entt::type_index<std::remove_cv_t<std::remove_reference_t<a_type>>>::value();
auto idx = entt::type_index<std::remove_cvref_t<a_type>>::value();
```
* The hash value associated with a given type:
@@ -651,7 +648,7 @@ These are the information made available by `type_info`:
This is also an alias for the following:
```cpp
auto hash = entt::type_hash<std::remove_cv_t<std::remove_reference_t<a_type>>>::value();
auto hash = entt::type_hash<std::remove_cvref_t<a_type>>::value();
```
* The name associated with a given type:
@@ -663,7 +660,7 @@ These are the information made available by `type_info`:
This is also an alias for the following:
```cpp
auto name = entt::type_name<std::remove_cv_t<std::remove_reference_t<a_type>>>::value();
auto name = entt::type_name<std::remove_cvref_t<a_type>>::value();
```
Where all accessed features are available at compile-time, the `type_info` class
@@ -919,10 +916,6 @@ It is not possible to escape the temptation to add utilities of some kind to a
library. In fact, `EnTT` also provides a handful of tools to simplify the
life of developers:
* `entt::identity`: the identity function object that will be available with
C++20. It returns its argument unchanged and nothing more. It is useful as a
sort of _do nothing_ function in template programming.
* `entt::overload`: a tool to disambiguate different overloads from their
function type. It works with both free and member functions.<br/>
Consider the following definition:

View File

@@ -11,6 +11,7 @@
* [Vademecum](#vademecum)
* [The Registry, the Entity and the Component](#the-registry-the-entity-and-the-component)
* [Observe changes](#observe-changes)
* [Auto-binding](#auto-binding)
* [Entity lifecycle](#entity-lifecycle)
* [Listeners disconnection](#listeners-disconnection)
* [They call me reactive storage](#they-call-me-reactive-storage)
@@ -409,6 +410,27 @@ There are many useful but less known functionalities that are not described
here, such as the connection objects or the possibility to attach listeners with
a list of parameters that is shorter than that of the signal itself.
### Auto-binding
Users don't need to create bindings manually each and every time. For managed
types, they can have `EnTT` setup listeners automatically.<br/>
The library searches the types for functions with specific names and signatures,
as in the following example:
```cpp
struct my_type {
static void on_construct(entt::registry &registry, const entt::entity entt);
static void on_update(entt::registry &registry, const entt::entity entt);
static void on_destroy(entt::registry &registry, const entt::entity entt);
// ...
};
```
As soon as a storage is created for such a defined type, these functions are
associated with the respective signals. The function name is self-explanatory of
the target signal.
### Entity lifecycle
Observing entities is also possible. In this case, the user must use the entity
@@ -1013,9 +1035,10 @@ registry.ctx().erase<my_type>();
registry.ctx().erase<my_type>("my_variable"_hs);
```
A context variable must be both default constructible and movable. If the
supplied type does not match that of the variable when using a _name_, the
operation fails.<br/>
There are no strict requirements on the type of a context variable, such as that
it must be constructible or movable by default. However, if the supplied type
does not match that of the variable when using a _name_, the operation
fails.<br/>
For all users who want to use the context but do not want to create elements,
the `contains` and `find` functions are also available:
@@ -1328,7 +1351,8 @@ struct transform {
The `component_traits` class template takes care of _extracting_ the properties
from the supplied type.<br/>
Plus, it is _sfinae-friendly_ and also supports feature-based specializations.
Plus, it can be specialized and constrained with a concept to further customize
it on a per type or per feature basis.
## Empty type optimization
@@ -1356,7 +1380,7 @@ optimization selectively rather than globally.
## Void storage
A void storage (or `entt::storage<void>` or `entt::basic_storage<Type, void>`),
A void storage (`entt::storage<void>` or `entt::basic_storage<void, Entity>`),
is a fully functional storage type used to create pools not associated with a
particular component type.<br/>
From a technical point of view, it is in all respects similar to a storage for
@@ -1548,7 +1572,7 @@ is not known), there is always the possibility of receiving a `type_info` object
for the type of elements associated with the entities (if any):
```cpp
if(entt::type_id<velocity>() == base.type()) {
if(entt::type_id<velocity>() == base.info()) {
// ...
}
```
@@ -1855,7 +1879,7 @@ specific _queries_.<br/>
The type returned when combining multiple elements together is itself a view,
more in general a multi component one.
Combining different elements tries to mimic C++20 ranges:
Combining different elements tries to mimic ranges:
```cpp
auto view = registry.view<position>();
@@ -2134,13 +2158,13 @@ It means that views and groups generated by a const registry also propagate the
constness to the types involved. As an example:
```cpp
entt::view<const position, const velocity> view = std::as_const(registry).view<const position, const velocity>();
entt::view<entt::get_t<const position, const velocity>> view = std::as_const(registry).view<const position, const velocity>();
```
Consider the following definition for a non-const view instead:
```cpp
entt::view<position, const velocity> view = registry.view<position, const velocity>();
entt::view<entt::get_t<position, const velocity>> view = registry.view<position, const velocity>();
```
In the example above, `view` is used to access either read-only or writable
@@ -2345,10 +2369,10 @@ expedients.
Finally, `EnTT` is configured via a few compile-time definitions to make some of
its parts implicitly thread-safe, roughly speaking only the ones that really
make sense and cannot be turned around.<br/>
In particular, when multiple instances of objects referencing the type index
generator (such as the `registry` class) are used in different threads, then it
might be useful to define `ENTT_USE_ATOMIC`.<br/>
See the relevant documentation for more information.
When using multiple threads with `EnTT`, you should define `ENTT_USE_ATOMIC`
unless you know exactly what you are doing. This is true even if each thread
only uses thread local data. For more information, see
[this section](config.md#entt_use_atomic).
## Iterators

View File

@@ -194,11 +194,11 @@ a solution in this case:
```cpp
template<>
struct entt::type_hash<Type> final {
[[nodiscard]] static constexpr id_type value() noexcept {
[[nodiscard]] static consteval id_type value() noexcept {
return hashed_string::value("Type");
}
[[nodiscard]] constexpr operator id_type() const noexcept {
[[nodiscard]] consteval operator id_type() const noexcept {
return value();
}
};

View File

@@ -28,8 +28,8 @@ The section dedicated to `type_info` contains all the details to get around the
issue in a concise and elegant way. Please refer to the specific documentation.
When working with linked libraries, compile definitions `ENTT_API_EXPORT` and
`ENTT_API_IMPORT` are to import or export symbols, so as to make everything work
nicely across boundaries.<br/>
`ENTT_API_IMPORT` are there to import or export symbols, so as to make
everything work nicely across boundaries.<br/>
On the other hand, everything should run smoothly when working with plugins or
shared libraries that do not export any symbols.

View File

@@ -141,6 +141,14 @@ I hope the following lists can grow much more in the future.
small demo game with ships and bullets flying everywhere on the screen.
* [Lichgate](https://buas.itch.io/lichgate): step into the robes of a powerful
mage determined to halt the relentless hordes of undead.
* [You Are Circle](https://store.steampowered.com/app/3578190/You_Are_Circle/):
a roguelite top-down shooter with a high-contrast vector line aesthetic.
* [EnTT Dino](https://github.com/omgitsaheadcrab/entt_dino): a Dinosaur Game
clone in C++ using only `SDL2` and `EnTT`.
* [Bim!](https://github.com/j-jorge/bim): a last-man-standing arcade online
game for Android.
* [MonsterWar](https://github.com/WispSnow/MonsterWar): a tower defense game
developed in C++ with `SDL3`, `EnTT`, and a few other libraries.
## Engines and the like:
@@ -241,6 +249,11 @@ I hope the following lists can grow much more in the future.
* [Darmok](https://github.com/miguelibero/darmok): another C++ game engine.
* [Magique](https://github.com/gk646/magique): 2D game engine for programmers
(or those yet to be).
* [Physecs](https://github.com/thfProjects/Physecs): real-time 3D rigid body
physics simulation built on `EnTT`.
* [KODZA](https://gitlab.com/arqitek/kodza/): A work in progress game engine.
* [Omnix](https://github.com/Ace-codes-swift/Omnix): An under-development,
multi-purpose 3D engine for `macOS`, using `EnTT` for the ECS.
## Articles, videos and blog posts:
@@ -321,6 +334,9 @@ I hope the following lists can grow much more in the future.
[Collector](https://play.google.com/store/apps/details?id=com.esri.arcgis.collector)
and
[Navigator](https://play.google.com/store/apps/details?id=com.esri.navigator).
* [OneArc](https://onearc.com/): [licenses](https://onearc.com/third-party-licensors/)
do not lie. Their products use EnTT in some way, but it is not known _what_
way.
* [FASTSUITE Edition 2](https://www.fastsuite.com/en_EN/fastsuite/fastsuite-edition-2.html)
by [Cenit](http://www.cenit.com/en_EN/about-us/overview.html): they use
`EnTT` to drive their simulation, that is, the communication between robot

View File

@@ -3,10 +3,11 @@
# Table of Contents
* [Introduction](#introduction)
* [Names and identifiers](#names-and-identifiers)
* [Identifiers](#identifiers)
* [Reflection in a nutshell](#reflection-in-a-nutshell)
* [Any to the rescue](#any-to-the-rescue)
* [Enjoy the runtime](#enjoy-the-runtime)
* [Tell me your name](#tell-me-your-name)
* [Container support](#container-support)
* [Pointer-like types](#pointer-like-types)
* [Template information](#template-information)
@@ -33,19 +34,19 @@ reflection system for `EnTT`. Maybe I did not do better than others or maybe
yes, time will tell me, but at least I can model this tool around the library to
which it belongs and not the opposite.
# Names and identifiers
# Identifiers
The meta system does not force users to rely on the tools provided by the
library when it comes to working with names and identifiers. It does this by
offering an API that works with opaque identifiers that may or may not be
generated by means of a hashed string.<br/>
library when it comes to working with identifiers. It does this by offering an
API that works with integral values that may or may not be generated by means of
a hashed string.<br/>
This means that users can assign any type of identifier to the meta objects, as
long as they are numeric. It does not matter if they are generated at runtime,
at compile-time or with custom functions.
That being said, the examples in the following sections are all based on the
`hashed_string` class as provided by this library. Therefore, where an
identifier is required, it is likely that a user defined literal is used as
That being said, some of the examples in the following sections are based on the
`hashed_string` class as provided by this library. Therefore, where an integral
identifier is provided, it is likely that a user defined literal is used as
follows:
```cpp
@@ -82,8 +83,8 @@ However, it is also possible to assign custom identifiers to meta types:
entt::meta_factory<my_type>{}.type("reflected_type"_hs);
```
Identifiers are used to _retrieve_ meta types at runtime by _name_ other than by
type.<br/>
Identifiers are used instead of the type to _retrieve_ meta types at runtime, if
necessary.<br/>
However, users can be interested in adding features to a reflected type so that
the reflection system can use it correctly under the hood, while they do not
want to also make the type _searchable_. In this case, it is sufficient not to
@@ -103,17 +104,6 @@ generally used to create the following:
Meta default constructors are implicitly generated, if possible.
* _Destructors_. Both free functions and member functions are valid destructors:
```cpp
entt::meta_factory<my_type>{}.dtor<&destroy>();
```
The purpose is to offer the possibility to free up resources that require
_special treatment_ before an object is actually destroyed.<br/>
A function should neither delete nor explicitly invoke the destructor of a
given instance.
* _Data members_. Meta data members are actual data members of the underlying
type but also static and global variables or constants of any kind. From the
point of view of the client, all the variables associated with the reflected
@@ -127,7 +117,7 @@ generally used to create the following:
```
The `data` function requires the identifier to use for the meta data member.
Users can then access it by _name_ at runtime.<br/>
This is then used for runtime access.<br/>
Data members are also defined by means of a setter and getter pair. These are
either free functions, class members or a mix of them. This approach is also
convenient to create read-only properties from a non-const data member:
@@ -149,7 +139,7 @@ generally used to create the following:
```
The `func` function requires the identifier to use for the meta data function.
Users can then access it by _name_ at runtime.<br/>
This is then used for runtime access.<br/>
Overloading of meta functions is supported. Overloaded functions are resolved
at runtime by the reflection system according to the types of the arguments.
@@ -240,7 +230,7 @@ In all cases, the returned value is an instance of `meta_type` (possibly with
its id). These objects offer an API to know their _runtime identifiers_, to
iterate all the meta objects associated with them and even to build instances of
the underlying type.<br/>
Meta data members and functions are accessed by name:
Meta data members and functions are accessed by means of their identifiers:
* Meta data members:
@@ -268,6 +258,9 @@ Meta data members and functions are accessed by name:
addition, a meta function object is used to invoke the underlying function and
then get the return value in the form of a `meta_any` object.
Both functions search for the elements throughout the meta type hierarchy.
However, they offer the option of passing a second boolean argument to stop the
search at the top-level meta type.<br/>
All the meta objects thus obtained as well as the meta types explicitly convert
to a boolean value to check for validity:
@@ -292,19 +285,49 @@ type.<br/>
In particular, the `construct` member function accepts a variable number of
arguments and searches for a match. It then returns a `meta_any` object that may
or may not be initialized, depending on whether a suitable constructor was found
or not.
There is no object that wraps the destructor of a meta type nor a `destroy`
member function in its API. Destructors are invoked implicitly by `meta_any`
behind the scenes and users have not to deal with them explicitly. Furthermore,
they have no name, cannot be searched and would not have member functions to
expose anyway.<br/>
Similarly, conversion functions are not directly accessible. They are used
internally by `meta_any` and the meta objects when needed.
or not.<br/>
Conversion functions are not accessible instead. They are used internally by
`meta_any` and the meta objects when needed.
Meta types and meta objects in general contain much more than what was said.
Refer to the inline documentation for further details.
### Tell me your name
For meta types, data and functions, users can also provide custom _names_:
```cpp
entt::meta_factory<my_type>{}
.type("type"_hs, "my_type")
.data<&variable>("data"_hs, "variable")
.func<&function>("func"_hs, "function");
```
The _label_ provided **should** be a string literal. The library does not make
copies. It is up to the user to guarantee the lifetime of the name itself.<br/>
String identifiers are returned from the meta objects via the `name` function:
```cpp
const char *name = entt::resolve<my_type>().name();
```
Since most of the time there is no need to differentiate the _name_ from the
numeric identifier associated with a meta object, `EnTT` also offers a more
compact version of these functions:
```cpp
entt::meta_factory<my_type>{}
.type("my_type")
.data<&variable>("variable")
.func<&function>("function");
```
Again, the name provided **should** be a string literal. The string is then used
to generate a numeric identifier with the `hashed_string` class.<br/>
Despite support for names, there are no string-based lookup functions available.
That is, types (`resolve`) as well as data members (`data`) and function members
(`func`) are only _searchable_ by numeric identifiers.
## Container support
The runtime reflection system also supports containers of all types.<br/>
@@ -312,31 +335,27 @@ Moreover, _containers_ does not necessarily mean those offered by the C++
standard library. In fact, user defined data structures can also work with the
meta system in many cases.
To make a container be recognized as such by the meta system, users are required
to provide specializations for either the `meta_sequence_container_traits` class
or the `meta_associative_container_traits` class, according to the actual _type_
of the container.<br/>
`EnTT` already exports the specializations for some common classes. In
particular:
Containers are automatically _detected_ based on some common _traits_.<br/>
For example, and not limited to, a sequence container must have a `begin`/`end`
pair that returns a forward iterator, while an associative container must also
provide a `key_type` member and a `find` function.<br/>
If a container is not recognized as such, it is still possible to provide an
_adapter_ by specializing the template classes `meta_sequence_container_traits`
and `meta_associative_container_traits`. Similarly, users can _inhibit_ the
detection of a type as a meta container by specializing the right traits class
but without providing any definition.
* `std::vector`, `std::array`, `std::deque` and `std::list` (but not
`std::forward_list`) are supported as _sequence containers_.
* `std::map`, `std::set` and their unordered counterparts are supported as
_associative containers_.
It is important to include the header file `container.hpp` to make these
specializations available to the compiler when needed.<br/>
The same file also contains many examples for the users that are interested in
Standard library containers are generally exported as meta containers out of the
box (with the exception of `std::string`, which is not considered a sequence
container on purpose).<br/>
However, it is important to include the header file `container.hpp` to make
the right specializations available to the compiler when needed.<br/>
The same file also contains some examples for the users that are interested in
making their own containers available to the meta system.
When a specialization of the `meta_sequence_container_traits` class exists, the
meta system treats the wrapped type as a sequence container. In a similar way,
a type is treated as an associative container if a specialization of the
`meta_associative_container_traits` class is found for it.<br/>
Proxy objects are returned by dedicated members of the `meta_any` class. The
following is a deliberately verbose example of how users can access a proxy
object for a sequence container:
For meta containers, the `meta_any` class returns properly initialized proxy
objects for ease of use. The following is a deliberately verbose example of how
users can access a proxy object for a sequence container:
```cpp
std::vector<int> vec{1, 2, 3};
@@ -349,7 +368,7 @@ if(any.type().is_sequence_container()) {
}
```
The method to use to get a proxy object for associative containers is
Proxy object for associative containers are accessed in the same way by calling
`as_associative_container` instead.<br/>
It is not necessary to perform a double check actually. Instead, it is enough to
query the meta type or verify that the proxy object is valid. In fact, proxies
@@ -767,7 +786,7 @@ other problems.
There are a few alternatives available at the moment:
* The _as-is_ policy, associated with the type `entt::as_is_t`.<br/>
* The _as-value_ policy, associated with the type `entt::as_value_t`.<br/>
This is the default policy. In general, it should never be used explicitly,
since it is implicitly selected if no other policy is specified.<br/>
In this case, the return values of the functions as well as the properties
@@ -805,6 +824,18 @@ There are a few alternatives available at the moment:
`as_ref_t` _adapts_ to the constness of the passed object and to that of the
return type if any.
* The _as-is_ policy, associated with the type `entt::as_is_t`.<br/>
Useful for decoupling meta type creation code from calling code while still
preserving the behavior of data members and member functions as defined:
```cpp
entt::meta_factory<my_type>{}.func<&my_type::any_member, entt::as_is_t>("member"_hs);
```
For data members or member functions that return a reference type, the value
is returned by reference with the same constness. In all other cases, the
value is returned by copy.
Some uses are rather trivial, but it is useful to note that there are some less
obvious corner cases that can in turn be solved with the use of policies.
@@ -867,12 +898,9 @@ entt::meta_factory<my_type>{}.traits(my_traits::required | my_traits::hidden);
In the example above, `EnTT` bitmask enum support is used, but any integral
value is fine, as long as it does not exceed 16 bits.<br/>
It is 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:
Traits can be assigned at different times. Subsequent calls to the `traits`
function do not reset previously set values. However, users must reset the
factory to the meta object of interest:
```cpp
entt::meta_factory<my_type>{}

View File

@@ -230,14 +230,14 @@ For a deduced concept, inheritance is achieved in a few steps:
```cpp
struct DrawableAndErasable: entt::type_list<> {
template<typename Base>
struct type: typename Drawable::type<Base> {
struct type: Drawable::type<Base> {
static constexpr auto base = Drawable::impl<Drawable::type<entt::poly_inspector>>::size;
void erase() { entt::poly_call<base + 0>(*this); }
};
template<typename Type>
using impl = entt::value_list_cat_t<
typename Drawable::impl<Type>,
Drawable::impl<Type>,
entt::value_list<&Type::erase>
>;
};

View File

@@ -4,7 +4,8 @@
* [Introduction](#introduction)
* [The process](#the-process)
* [Adaptor](#adaptor)
* [Continuation](#continuation)
* [Shared process](#shared-process)
* [The scheduler](#the-scheduler)
# Introduction
@@ -17,28 +18,20 @@ to define and execute cooperative processes.
# The process
A typical task inherits from the `process` class template that stays true to the
CRTP idiom. Moreover, derived classes specify what the intended type for elapsed
times is.
A typical task inherits from the `process` class template. Derived classes also
specify what the intended type for elapsed times is.
A process should expose publicly the following member functions whether needed
(note that it is not required to define a function unless the derived class
wants to _override_ the default behavior):
A process should implement the following member functions whether needed (note
that it is not required to define a function unless the derived class wants to
_override_ the default behavior):
* `void update(Delta, void *);`
This is invoked once per tick until a process is explicitly aborted or ends
either with or without errors. Technically speaking, this member function is
not strictly required. However, each process should at least define it to work
either with or without errors. Each process should at least define it to work
_properly_. The `void *` parameter is an opaque pointer to user data (if any)
forwarded directly to the process during an update.
* `void init();`
This is invoked when the process joins the running queue of a scheduler. It
happens usually when the process is attached to the scheduler if it is a top
level one, or when it replaces its parent if it is a _continuation_.
* `void succeeded();`
This is invoked in case of success, immediately after an update and during the
@@ -52,22 +45,24 @@ wants to _override_ the default behavior):
* `void aborted();`
This is invoked only if a process is explicitly aborted. There is no guarantee
that it executes in the same tick, it depends solely on whether the process is
that it executes in the same tick. It depends solely on whether the process is
aborted immediately or not.
Derived classes can also change the internal state of a process by invoking
`succeed` and `fail`, as well as `pause` and `unpause` the process itself.<br/>
All these are protected member functions made available to manage the life cycle
of a process from a derived class.
A class can also change the state of a process by invoking `succeed` and `fail`,
as well as `pause` and `unpause` the process itself.<br/>
All these are public member functions made available to manage the life cycle of
a process easily.
Here is a minimal example for the sake of curiosity:
```cpp
struct my_process: entt::process<my_process, std::uint32_t> {
using delta_type = std::uint32_t;
struct my_process: entt::process {
using allocator_type = entt::process::allocator_type;
using delta_type = entt::process::delta_type;
my_process(delta_type delay)
: remaining{delay}
my_process(const allocator_type &allocator, delta_type delay)
: entt::process{allocator},
remaining{delay}
{}
void update(delta_type delta, void *) {
@@ -85,33 +80,49 @@ private:
};
```
## Adaptor
## Continuation
Lambdas and functors cannot be used directly with a scheduler because they are
not properly defined processes with managed life cycles.<br/>
This class helps in filling the gap and turning lambdas and functors into
full-featured processes usable by a scheduler.
The function call operator has a signature similar to the one of the `update`
function of a process but for the fact that it receives two extra callbacks to
invoke whenever a process terminates with success or with an error:
A process may be followed by other processes upon successful termination.<br/>
This pairing can be set up at creation time, keeping the processes conceptually
separate from each other while still combining them at runtime:
```cpp
void(Delta delta, void *data, auto succeed, auto fail);
my_process process{};
process.then<my_other_process>();
```
Parameters have the following meaning:
This approach allows processes to be developed in isolation and combined to
define complex actions.<br/>
For example, a delayed operation where a parent process (such as a timer)
_schedules_ a child process (the deferred task) once the time is over.
* `delta` is the elapsed time.
* `data` is an opaque pointer to user data if any, `nullptr` otherwise.
* `succeed` is a function to call when a process terminates with success.
* `fail` is a function to call when a process terminates with errors.
The `then` function also accepts lambdas, which are associated with a dedicated
process internally:
Both `succeed` and `fail` accept no parameters at all.
```cpp
process.then([](entt::process &proc, std::uint32_t delta, void *data) {
// ...
})
```
Note that usually users should not worry about creating adaptors at all. A
scheduler creates them internally, each and every time a lambda or a functor is
used as a process.
The lambda function is such that it accepts a reference to the process that
manages it (to be able to terminate it, pause it and so on), plus the usual
values also passed to the `update` function.
## Shared process
All processes inherit from `std::enable_shared_from_this` to allow sharing with
the caller.<br/>
The returned smart pointer was created using the allocator associated with the
scheduler and therefore all its processes. This same allocator is available by
invoking `get_allocator` on the process itself.
As far as possible, sharing a process is not intended to allow the caller to
manage it. This could actually compromise the proper functioning of the
scheduler and the process itself.<br/>
Rather, the purpose is to allow the callers to save a valid reference to the
process, allowing them to intervene in its lifecycle through calls like `pause`
and the like.
# The scheduler
@@ -124,7 +135,7 @@ it is a good candidate to run one more time the next tick.<br/>
A process can also have a _child_. In this case, the parent process is replaced
with its child when it terminates and only if it returns with success. In case
of errors, both the parent process and its child are discarded. This way, it is
easy to create a chain of processes to run sequentially.
easy to create a _chain of processes_ to run sequentially.
Using a scheduler is straightforward. To create it, users must provide only the
type for the elapsed times and no arguments at all:
@@ -137,7 +148,7 @@ Otherwise, the `scheduler` alias is also available for the most common cases. It
uses `std::uint32_t` as a default type:
```cpp
entt::scheduler scheduler;
entt::scheduler scheduler{};
```
The class has member functions to query its internal data structures, like
@@ -154,34 +165,33 @@ entt::scheduler::size_type size = scheduler.size();
scheduler.clear();
```
To attach a process to a scheduler, there are mainly two ways:
To attach a process to a scheduler, invoke the `attach` function with a process
type and the arguments to use to construct it:
* If the process inherits from the `process` class template, it is enough to
indicate its type and submit all the parameters required to construct it to
the `attach` member function:
```cpp
scheduler.attach<my_process>(_1000u);
```
```cpp
scheduler.attach<my_process>(1000u);
```
The scheduler will also provide the process with its allocator as the first
argument.<br>
In case of lambdas or functors, the required signature is the one already seen
for the `then` function of a process:
* Otherwise, in case of a lambda or a functor, it is enough to provide an
instance of the class to the `attach` member function:
```cpp
scheduler.attach([](entt::process &, std::uint32_t, void *){ /* ... */ });
```
```cpp
scheduler.attach([](auto...){ /* ... */ });
```
In both cases, the scheduler is returned and its `then` member function can be
used to create chains of processes to run sequentially.<br/>
In both cases, the newly created process is returned by reference and its `then`
member function is used to create chains of processes to run sequentially.<br/>
As a minimal example of use:
```cpp
// schedules a task in the form of a lambda function
scheduler.attach([](auto delta, void *, auto succeed, auto fail) {
scheduler.attach([](entt::process &, std::uint32_t, void *) {
// ...
})
// appends a child in the form of another lambda function
.then([](auto delta, void *, auto succeed, auto fail) {
.then([](entt::process &, std::uint32_t, void *) {
// ...
})
// appends a child in the form of a process class
@@ -209,3 +219,6 @@ scheduler.abort(true);
// ... or gracefully during the next tick
scheduler.abort();
```
The argument passed to the `abort` function indicates whether execution should
be stopped immediately or processes should be notified on the next tick.

View File

@@ -112,13 +112,13 @@ struct my_loader {
struct from_disk_tag{};
struct from_network_tag{};
template<typename Args>
template<typename... Args>
result_type operator()(from_disk_tag, Args&&... args) {
// ...
return std::make_shared<my_resource>(std::forward<Args>(args)...);
}
template<typename Args>
template<typename... Args>
result_type operator()(from_network_tag, Args&&... args) {
// ...
return std::make_shared<my_resource>(std::forward<Args>(args)...);

View File

@@ -9,6 +9,7 @@
* [Raw access](#raw-access)
* [Signals](#signals)
* [Event dispatcher](#event-dispatcher)
* [Connect, disconnect, publish](#connect-disconnect-publish)]
* [Named queues](#named-queues)
* [Event emitter](#event-emitter)
@@ -382,34 +383,45 @@ signal.collect(std::ref(collector));
# Event dispatcher
The event dispatcher class allows users to trigger immediate events or to queue
and publish them all together later.<br/>
This class lazily instantiates its queues. Therefore, it is not necessary to
_announce_ the event types in advance:
and publish them all together later:
```cpp
// define a general purpose dispatcher
entt::dispatcher dispatcher{};
```
A listener registered with a dispatcher is such that its type offers one or more
member functions that take arguments of type `Event &` for any type of event,
regardless of the return value.<br/>
These functions are linked directly via `connect` to a _sink_:
This class lazily instantiates its queues. Therefore, it is not necessary to
_announce_ the event types in advance.
## Connect, disconnect, publish
Listeners registered with a dispatcher are mainly of two types: free and member
functions. Lambdas as template functions are also accepted and belong to the
first group.<br/>
In all cases, a listener accepts an argument of type `Event &` for any type
of event, regardless of the return value.
Listeners are linked directly via `connect` to a _sink_ object:
```cpp
struct an_event { int value; };
struct another_event {};
void on_event(const an_event &event) { /* ... */ }
struct listener {
void receive(const an_event &) { /* ... */ }
void method(const another_event &) { /* ... */ }
// Member function listener
void on_event(const another_event &) { /* ... */ }
};
// ...
// free function listener
dispatcher.sink<an_event>().connect<&on_event>();
listener listener;
dispatcher.sink<an_event>().connect<&listener::receive>(listener);
dispatcher.sink<another_event>().connect<&listener::method>(listener);
// member function listener
dispatcher.sink<another_event>().connect<&listener::on_event>(listener);
```
Note that connecting listeners within event handlers can result in undefined
@@ -418,7 +430,13 @@ The `disconnect` member function is used to remove one listener at a time or all
of them at once:
```cpp
dispatcher.sink<an_event>().disconnect<&listener::receive>(listener);
// disconnects a free function
dispatcher.sink<an_event>().disconnect<&on_event>();
// disconnect a member function of an instance
dispatcher.sink<another_event>().disconnect<&listener::on_event>(listener);
// disconnect all member functions of an instance, if any
dispatcher.sink<another_event>().disconnect(&listener);
```
@@ -427,7 +445,7 @@ to all the listeners registered so far:
```cpp
dispatcher.trigger(an_event{42});
dispatcher.trigger<another_event>();
dispatcher.trigger(another_event{});
```
Listeners are invoked immediately, order of execution is not guaranteed. This

View File

@@ -1,103 +0,0 @@
# EnTT and Unreal Engine
# Table of Contents
* [Enable Cpp17](#enable-cpp17)
* [EnTT as a third party module](#entt-as-a-third-party-module)
* [Include EnTT](#include-entt)
## Enable Cpp17
> Skip this part if you are working with UE5, Since UE5 uses cpp17 by default.
As of writing (Unreal Engine v4.25), the default C++ standard of Unreal Engine
is C++14.<br/>
On the other hand, note that `EnTT` requires C++17 to compile. To enable it, in
the main module of the project there should be a `<Game Name>.Build.cs` file,
the constructor of which must contain the following lines:
```cs
PCHUsage = PCHUsageMode.NoSharedPCHs;
PrivatePCHHeaderFile = "<PCH filename>.h";
CppStandard = CppStandardVersion.Cpp17;
```
Replace `<PCH filename>.h` with the name of the already existing PCH header
file, if any.<br/>
In case the project does not already contain a file of this type, it is possible
to create one with the following content:
```cpp
#pragma once
#include "CoreMinimal.h"
```
Remember to remove any old `PCHUsage = <...>` line that was previously there. At
this point, C++17 support should be in place.<br/>
Try to compile the project to ensure it works as expected before following
further steps.
Note that updating a *project* to C++17 does not necessarily mean that the IDE
in use will also start to recognize its syntax.<br/>
If the plan is to use C++17 in the project too, check the specific instructions
for the IDE in use.
## EnTT as a third party module
Once this point is reached, the `Source` directory should look like this:
```
Source
| MyGame.Target.cs
| MyGameEditor.Target.cs
|
+---MyGame
| | MyGame.Build.cs
| | MyGame.h (PCH Header file)
|
\---ThirdParty
\---EnTT
| EnTT.Build.cs
|
\---entt (GitHub repository content inside)
```
To make this happen, create the folder `ThirdParty` under `Source` if it does
not exist already. Then, add an `EnTT` folder under `ThirdParty`.<br/>
Within the latter, create a new file `EnTT.Build.cs` with the following content:
```cs
using System.IO;
using UnrealBuildTool;
public class EnTT: ModuleRules {
public EnTT(ReadOnlyTargetRules Target) : base(Target) {
Type = ModuleType.External;
PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "entt", "src", "entt"));
}
}
```
The last line indicates that the actual files will be found in the directory
`EnTT/entt/src/entt`.<br/>
Download the repository for `EnTT` and place it next to `EnTT.Build.cs` or
update the path above accordingly.
Finally, open the `<Game Name>.Build.cs` file and add `EnTT` as a dependency at
the end of the list:
```cs
PublicDependencyModuleNames.AddRange(new[] {
"Core", "CoreUObject", "Engine", "InputCore", [...], "EnTT"
});
```
Note that some IDEs might require a restart to start recognizing the new module
for code-highlighting features and such.
## Include EnTT
In any source file of the project, add `#include "entt.hpp"` or any other path
to the file from `EnTT` to use it.<br/>
Try to create a registry as `entt::registry registry;` to make sure everything
compiles fine.

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,17 +1,23 @@
#ifndef ENTT_CONFIG_CONFIG_H
#define ENTT_CONFIG_CONFIG_H
#if __has_include(<entt/ext/config.h>)
# include <entt/ext/config.h>
#endif
#include "version.h"
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
# define ENTT_CONSTEXPR
#ifdef ENTT_USE_STL
# define ENTT_FORCE_STL
#endif
#if defined(__cpp_exceptions) && !defined(ENTT_NO_EXCEPTION)
# define ENTT_THROW throw
# define ENTT_TRY try
# define ENTT_CATCH catch(...)
#else
# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20)
# define ENTT_THROW
# define ENTT_TRY if(true)
# define ENTT_CATCH if(false)
@@ -95,6 +101,32 @@
# endif
#endif
#ifndef ENTT_EXPORT
# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER
# define ENTT_EXPORT __declspec(dllexport)
# define ENTT_IMPORT __declspec(dllimport)
# define ENTT_HIDDEN
# elif defined __GNUC__ && __GNUC__ >= 4
# define ENTT_EXPORT __attribute__((visibility("default")))
# define ENTT_IMPORT __attribute__((visibility("default")))
# define ENTT_HIDDEN __attribute__((visibility("hidden")))
# else /* Unsupported compiler */
# define ENTT_EXPORT
# define ENTT_IMPORT
# define ENTT_HIDDEN
# endif
#endif
#ifndef ENTT_API
# if defined ENTT_API_EXPORT
# define ENTT_API ENTT_EXPORT
# elif defined ENTT_API_IMPORT
# define ENTT_API ENTT_IMPORT
# else /* No API */
# define ENTT_API
# endif
#endif
#if defined _MSC_VER
# pragma detect_mismatch("entt.version", ENTT_VERSION)
# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))

View File

@@ -5,8 +5,8 @@
// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*)
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 15
#define ENTT_VERSION_MAJOR 4
#define ENTT_VERSION_MINOR 0
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION \

View File

@@ -1,7 +1,10 @@
#ifndef ENTT_CONTAINER_DENSE_MAP_HPP
#define ENTT_CONTAINER_DENSE_MAP_HPP
#include <bit>
#include <cmath>
#include <compare>
#include <concepts>
#include <cstddef>
#include <functional>
#include <iterator>
@@ -17,13 +20,16 @@
#include "../core/iterator.hpp"
#include "../core/memory.hpp"
#include "../core/type_traits.hpp"
#include "../stl/iterator.hpp"
#include "fwd.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
static constexpr std::size_t dense_map_placeholder_position = (std::numeric_limits<std::size_t>::max)();
template<typename Key, typename Type>
struct dense_map_node final {
using value_type = std::pair<Key, Type>;
@@ -33,18 +39,16 @@ struct dense_map_node final {
: next{pos},
element{std::forward<Args>(args)...} {}
template<typename Allocator, typename... Args>
dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args)
template<typename... Args>
dense_map_node(std::allocator_arg_t, const auto &allocator, const std::size_t pos, Args &&...args)
: next{pos},
element{entt::make_obj_using_allocator<value_type>(allocator, std::forward<Args>(args)...)} {}
template<typename Allocator>
dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other)
dense_map_node(std::allocator_arg_t, const auto &allocator, const dense_map_node &other)
: next{other.next},
element{entt::make_obj_using_allocator<value_type>(allocator, other.element)} {}
template<typename Allocator>
dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other)
dense_map_node(std::allocator_arg_t, const auto &allocator, dense_map_node &&other)
: next{other.next},
element{entt::make_obj_using_allocator<value_type>(allocator, std::move(other.element))} {}
@@ -57,6 +61,7 @@ class dense_map_iterator final {
template<typename>
friend class dense_map_iterator;
static_assert(std::is_pointer_v<It>, "Not a pointer type");
using first_type = decltype(std::as_const(std::declval<It>()->element.first));
using second_type = decltype((std::declval<It>()->element.second));
@@ -74,7 +79,8 @@ public:
constexpr dense_map_iterator(const It iter) noexcept
: it{iter} {}
template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
template<typename Other>
requires (!std::same_as<It, Other> && std::constructible_from<It, Other>)
constexpr dense_map_iterator(const dense_map_iterator<Other> &other) noexcept
: it{other.it} {}
@@ -126,59 +132,31 @@ public:
return operator[](0);
}
template<typename Lhs, typename Rhs>
friend constexpr std::ptrdiff_t operator-(const dense_map_iterator<Lhs> &, const dense_map_iterator<Rhs> &) noexcept;
template<typename Other>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator<Other> &other) const noexcept {
return it - other.it;
}
template<typename Lhs, typename Rhs>
friend constexpr bool operator==(const dense_map_iterator<Lhs> &, const dense_map_iterator<Rhs> &) noexcept;
template<typename Other>
[[nodiscard]] constexpr bool operator==(const dense_map_iterator<Other> &other) const noexcept {
return it == other.it;
}
template<typename Lhs, typename Rhs>
friend constexpr bool operator<(const dense_map_iterator<Lhs> &, const dense_map_iterator<Rhs> &) noexcept;
template<typename Other>
[[nodiscard]] constexpr auto operator<=>(const dense_map_iterator<Other> &other) const noexcept {
return it <=> other.it;
}
private:
It it;
};
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
return lhs.it - rhs.it;
}
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator==(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
return lhs.it == rhs.it;
}
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator!=(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
return !(lhs == rhs);
}
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator<(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
return lhs.it < rhs.it;
}
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator>(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
return rhs < lhs;
}
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator<=(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
return !(lhs > rhs);
}
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator>=(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
return !(lhs < rhs);
}
template<typename It>
class dense_map_local_iterator final {
template<typename>
friend class dense_map_local_iterator;
static_assert(std::is_pointer_v<It>, "Not a pointer type");
using first_type = decltype(std::as_const(std::declval<It>()->element.first));
using second_type = decltype((std::declval<It>()->element.second));
@@ -190,21 +168,20 @@ public:
using iterator_category = std::input_iterator_tag;
using iterator_concept = std::forward_iterator_tag;
constexpr dense_map_local_iterator() noexcept
: it{},
offset{} {}
constexpr dense_map_local_iterator() noexcept = default;
constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept
: it{iter},
offset{pos} {}
template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
template<typename Other>
requires (!std::same_as<It, Other> && std::constructible_from<It, Other>)
constexpr dense_map_local_iterator(const dense_map_local_iterator<Other> &other) noexcept
: it{other.it},
offset{other.offset} {}
constexpr dense_map_local_iterator &operator++() noexcept {
return (offset = it[static_cast<typename It::difference_type>(offset)].next), *this;
return (offset = it[static_cast<difference_type>(offset)].next), *this;
}
constexpr dense_map_local_iterator operator++(int) noexcept {
@@ -217,29 +194,24 @@ public:
}
[[nodiscard]] constexpr reference operator*() const noexcept {
const auto idx = static_cast<typename It::difference_type>(offset);
const auto idx = static_cast<difference_type>(offset);
return {it[idx].element.first, it[idx].element.second};
}
template<typename Other>
[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator<Other> &other) const noexcept {
return offset == other.offset;
}
[[nodiscard]] constexpr std::size_t index() const noexcept {
return offset;
}
private:
It it;
std::size_t offset;
It it{};
std::size_t offset{dense_map_placeholder_position};
};
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator<Lhs> &lhs, const dense_map_local_iterator<Rhs> &rhs) noexcept {
return lhs.index() == rhs.index();
}
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator<Lhs> &lhs, const dense_map_local_iterator<Rhs> &rhs) noexcept {
return !(lhs == rhs);
}
} // namespace internal
/*! @endcond */
@@ -260,6 +232,7 @@ template<typename Key, typename Type, typename Hash, typename KeyEqual, typename
class dense_map {
static constexpr float default_threshold = 0.875f;
static constexpr std::size_t minimum_capacity = 8u;
static constexpr std::size_t placeholder_position = internal::dense_map_placeholder_position;
using node_type = internal::dense_map_node<Key, Type>;
using alloc_traits = std::allocator_traits<Allocator>;
@@ -267,28 +240,25 @@ class dense_map {
using sparse_container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
using packed_container_type = std::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
template<typename Other>
[[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept {
[[nodiscard]] std::size_t key_to_bucket(const auto &key) const noexcept {
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
return fast_mod(static_cast<size_type>(sparse.second()(key)), bucket_count());
}
template<typename Other>
[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) {
for(auto it = begin(bucket), last = end(bucket); it != last; ++it) {
if(packed.second()(it->first, key)) {
return begin() + static_cast<difference_type>(it.index());
[[nodiscard]] auto constrained_find(const auto &key, const std::size_t bucket) {
for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].next) {
if(packed.second()(packed.first()[offset].element.first, key)) {
return begin() + static_cast<iterator::difference_type>(offset);
}
}
return end();
}
template<typename Other>
[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const {
for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) {
if(packed.second()(it->first, key)) {
return cbegin() + static_cast<difference_type>(it.index());
[[nodiscard]] auto constrained_find(const auto &key, const std::size_t bucket) const {
for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].next) {
if(packed.second()(packed.first()[offset].element.first, key)) {
return cbegin() + static_cast<const_iterator::difference_type>(offset);
}
}
@@ -361,13 +331,13 @@ public:
/*! @brief Type of function to use to compare the keys for equality. */
using key_equal = KeyEqual;
/*! @brief Input iterator type. */
using iterator = internal::dense_map_iterator<typename packed_container_type::iterator>;
using iterator = internal::dense_map_iterator<typename packed_container_type::pointer>;
/*! @brief Constant input iterator type. */
using const_iterator = internal::dense_map_iterator<typename packed_container_type::const_iterator>;
using const_iterator = internal::dense_map_iterator<typename packed_container_type::const_pointer>;
/*! @brief Input iterator type. */
using local_iterator = internal::dense_map_local_iterator<typename packed_container_type::iterator>;
using local_iterator = internal::dense_map_local_iterator<typename packed_container_type::pointer>;
/*! @brief Constant input iterator type. */
using const_local_iterator = internal::dense_map_local_iterator<typename packed_container_type::const_iterator>;
using const_local_iterator = internal::dense_map_local_iterator<typename packed_container_type::const_pointer>;
/*! @brief Default constructor. */
dense_map()
@@ -481,7 +451,7 @@ public:
* @return An iterator to the first instance of the internal array.
*/
[[nodiscard]] const_iterator cbegin() const noexcept {
return packed.first().begin();
return packed.first().data();
}
/*! @copydoc cbegin */
@@ -491,7 +461,7 @@ public:
/*! @copydoc begin */
[[nodiscard]] iterator begin() noexcept {
return packed.first().begin();
return packed.first().data();
}
/**
@@ -500,7 +470,7 @@ public:
* internal array.
*/
[[nodiscard]] const_iterator cend() const noexcept {
return packed.first().end();
return packed.first().data() + packed.first().size();
}
/*! @copydoc cend */
@@ -510,7 +480,7 @@ public:
/*! @copydoc end */
[[nodiscard]] iterator end() noexcept {
return packed.first().end();
return packed.first().data() + packed.first().size();
}
/**
@@ -565,19 +535,17 @@ public:
* @tparam Arg Type of the key-value pair to insert into the container.
*/
template<typename Arg>
std::enable_if_t<std::is_constructible_v<value_type, Arg &&>, std::pair<iterator, bool>>
insert(Arg &&value) {
requires std::constructible_from<value_type, Arg &&>
std::pair<iterator, bool> insert(Arg &&value) {
return insert_or_do_nothing(std::forward<Arg>(value).first, std::forward<Arg>(value).second);
}
/**
* @brief Inserts elements into the container, if their keys do not exist.
* @tparam It Type of input iterator.
* @param first An iterator to the first element of the range of elements.
* @param last An iterator past the last element of the range of elements.
*/
template<typename It>
void insert(It first, It last) {
void insert(stl::input_iterator auto first, stl::input_iterator auto last) {
for(; first != last; ++first) {
insert(*first);
}
@@ -695,7 +663,7 @@ public:
* @return Number of elements removed (either 0 or 1).
*/
size_type erase(const key_type &key) {
for(size_type *curr = &sparse.first()[key_to_bucket(key)]; *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].next) {
for(size_type *curr = &sparse.first()[key_to_bucket(key)]; *curr != placeholder_position; curr = &packed.first()[*curr].next) {
if(packed.second()(packed.first()[*curr].element.first, key)) {
const auto index = *curr;
*curr = packed.first()[*curr].next;
@@ -725,6 +693,26 @@ public:
return it->second;
}
/**
* @brief Accesses a given element with bounds checking.
* @param key A key of an element to find.
* @return A reference to the mapped value of the requested element.
*/
[[nodiscard]] mapped_type const &at(const auto &key) const
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
auto it = find(key);
ENTT_ASSERT(it != cend(), "Invalid key");
return it->second;
}
/*! @copydoc at */
[[nodiscard]] mapped_type &at(const auto &key)
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
auto it = find(key);
ENTT_ASSERT(it != end(), "Invalid key");
return it->second;
}
/**
* @brief Accesses or inserts a given element.
* @param key A key of an element to find or insert.
@@ -754,13 +742,11 @@ public:
/**
* @brief Returns the number of elements matching a key (either 1 or 0).
* @tparam Other Type of the key value of an element to search for.
* @param key Key value of an element to search for.
* @return Number of elements matching the key (either 1 or 0).
*/
template<typename Other>
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, size_type>>
count(const Other &key) const {
[[nodiscard]] size_type count(const auto &key) const
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
return find(key) != end();
}
@@ -782,21 +768,18 @@ public:
/**
* @brief Finds an element with a key that compares _equivalent_ to a given
* key.
* @tparam Other Type of the key value of an element to search for.
* @param key Key value of an element to search for.
* @return An iterator to an element with the given key. If no such element
* is found, a past-the-end iterator is returned.
*/
template<typename Other>
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, iterator>>
find(const Other &key) {
[[nodiscard]] iterator find(const auto &key)
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
return constrained_find(key, key_to_bucket(key));
}
/*! @copydoc find */
template<typename Other>
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, const_iterator>>
find(const Other &key) const {
[[nodiscard]] const_iterator find(const auto &key) const
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
return constrained_find(key, key_to_bucket(key));
}
@@ -820,22 +803,19 @@ public:
/**
* @brief Returns a range containing all elements that compare _equivalent_
* to a given key.
* @tparam Other Type of an element to search for.
* @param key Key value of an element to search for.
* @return A pair of iterators pointing to the first element and past the
* last element of the range.
*/
template<typename Other>
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, std::pair<iterator, iterator>>>
equal_range(const Other &key) {
[[nodiscard]] std::pair<iterator, iterator> equal_range(const auto &key)
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
const auto it = find(key);
return {it, it + !(it == end())};
}
/*! @copydoc equal_range */
template<typename Other>
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, std::pair<const_iterator, const_iterator>>>
equal_range(const Other &key) const {
[[nodiscard]] std::pair<const_iterator, const_iterator> equal_range(const auto &key) const
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
const auto it = find(key);
return {it, it + !(it == cend())};
}
@@ -852,13 +832,11 @@ public:
/**
* @brief Checks if the container contains an element with a key that
* compares _equivalent_ to a given value.
* @tparam Other Type of the key value of an element to search for.
* @param key Key value of an element to search for.
* @return True if there is such an element, false otherwise.
*/
template<typename Other>
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, bool>>
contains(const Other &key) const {
[[nodiscard]] bool contains(const auto &key) const
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
return (find(key) != cend());
}
@@ -868,7 +846,7 @@ public:
* @return An iterator to the beginning of the given bucket.
*/
[[nodiscard]] const_local_iterator cbegin(const size_type index) const {
return {packed.first().begin(), sparse.first()[index]};
return {packed.first().data(), sparse.first()[index]};
}
/**
@@ -886,7 +864,7 @@ public:
* @return An iterator to the beginning of the given bucket.
*/
[[nodiscard]] local_iterator begin(const size_type index) {
return {packed.first().begin(), sparse.first()[index]};
return {packed.first().data(), sparse.first()[index]};
}
/**
@@ -895,7 +873,7 @@ public:
* @return An iterator to the end of the given bucket.
*/
[[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const {
return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
return {};
}
/**
@@ -913,7 +891,7 @@ public:
* @return An iterator to the end of the given bucket.
*/
[[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) {
return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
return {};
}
/**
@@ -986,11 +964,11 @@ public:
const auto cap = static_cast<size_type>(static_cast<float>(size()) / max_load_factor());
value = value > cap ? value : cap;
if(const auto sz = next_power_of_two(value); sz != bucket_count()) {
if(const auto sz = std::bit_ceil(value); sz != bucket_count()) {
sparse.first().resize(sz);
for(auto &&elem: sparse.first()) {
elem = (std::numeric_limits<size_type>::max)();
elem = placeholder_position;
}
for(size_type pos{}, last = size(); pos < last; ++pos) {
@@ -1034,7 +1012,7 @@ private:
} // namespace entt
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace std {
template<typename Key, typename Value, typename Allocator>

View File

@@ -1,7 +1,10 @@
#ifndef ENTT_CONTAINER_DENSE_SET_HPP
#define ENTT_CONTAINER_DENSE_SET_HPP
#include <bit>
#include <cmath>
#include <compare>
#include <concepts>
#include <cstddef>
#include <functional>
#include <iterator>
@@ -15,20 +18,25 @@
#include "../core/bit.hpp"
#include "../core/compressed_pair.hpp"
#include "../core/type_traits.hpp"
#include "../stl/iterator.hpp"
#include "fwd.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
static constexpr std::size_t dense_set_placeholder_position = (std::numeric_limits<std::size_t>::max)();
template<typename It>
class dense_set_iterator final {
template<typename>
friend class dense_set_iterator;
static_assert(std::is_pointer_v<It>, "Not a pointer type");
public:
using value_type = typename It::value_type::second_type;
using value_type = std::remove_const_t<std::remove_pointer_t<It>>::second_type;
using pointer = const value_type *;
using reference = const value_type &;
using difference_type = std::ptrdiff_t;
@@ -40,7 +48,8 @@ public:
constexpr dense_set_iterator(const It iter) noexcept
: it{iter} {}
template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
template<typename Other>
requires (!std::same_as<It, Other> && std::constructible_from<It, Other>)
constexpr dense_set_iterator(const dense_set_iterator<Other> &other) noexcept
: it{other.it} {}
@@ -92,81 +101,53 @@ public:
return operator[](0);
}
template<typename Lhs, typename Rhs>
friend constexpr std::ptrdiff_t operator-(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept;
template<typename Other>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator<Other> &other) const noexcept {
return it - other.it;
}
template<typename Lhs, typename Rhs>
friend constexpr bool operator==(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept;
template<typename Other>
[[nodiscard]] constexpr bool operator==(const dense_set_iterator<Other> &other) const noexcept {
return it == other.it;
}
template<typename Lhs, typename Rhs>
friend constexpr bool operator<(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept;
template<typename Other>
[[nodiscard]] constexpr auto operator<=>(const dense_set_iterator<Other> &other) const noexcept {
return it <=> other.it;
}
private:
It it;
};
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
return lhs.it - rhs.it;
}
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator==(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
return lhs.it == rhs.it;
}
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator!=(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
return !(lhs == rhs);
}
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator<(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
return lhs.it < rhs.it;
}
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator>(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
return rhs < lhs;
}
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator<=(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
return !(lhs > rhs);
}
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator>=(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
return !(lhs < rhs);
}
template<typename It>
class dense_set_local_iterator final {
template<typename>
friend class dense_set_local_iterator;
static_assert(std::is_pointer_v<It>, "Not a pointer type");
public:
using value_type = typename It::value_type::second_type;
using value_type = std::remove_const_t<std::remove_pointer_t<It>>::second_type;
using pointer = const value_type *;
using reference = const value_type &;
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
constexpr dense_set_local_iterator() noexcept
: it{},
offset{} {}
constexpr dense_set_local_iterator() noexcept = default;
constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept
: it{iter},
offset{pos} {}
template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
template<typename Other>
requires (!std::same_as<It, Other> && std::constructible_from<It, Other>)
constexpr dense_set_local_iterator(const dense_set_local_iterator<Other> &other) noexcept
: it{other.it},
offset{other.offset} {}
constexpr dense_set_local_iterator &operator++() noexcept {
return offset = it[static_cast<typename It::difference_type>(offset)].first, *this;
return offset = it[static_cast<difference_type>(offset)].first, *this;
}
constexpr dense_set_local_iterator operator++(int) noexcept {
@@ -175,32 +156,27 @@ public:
}
[[nodiscard]] constexpr pointer operator->() const noexcept {
return std::addressof(it[static_cast<typename It::difference_type>(offset)].second);
return std::addressof(it[static_cast<difference_type>(offset)].second);
}
[[nodiscard]] constexpr reference operator*() const noexcept {
return *operator->();
}
template<typename Other>
[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator<Other> &other) const noexcept {
return offset == other.offset;
}
[[nodiscard]] constexpr std::size_t index() const noexcept {
return offset;
}
private:
It it;
std::size_t offset;
It it{};
std::size_t offset{dense_set_placeholder_position};
};
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator<Lhs> &lhs, const dense_set_local_iterator<Rhs> &rhs) noexcept {
return lhs.index() == rhs.index();
}
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator<Lhs> &lhs, const dense_set_local_iterator<Rhs> &rhs) noexcept {
return !(lhs == rhs);
}
} // namespace internal
/*! @endcond */
@@ -220,6 +196,7 @@ template<typename Type, typename Hash, typename KeyEqual, typename Allocator>
class dense_set {
static constexpr float default_threshold = 0.875f;
static constexpr std::size_t minimum_capacity = 8u;
static constexpr std::size_t placeholder_position = internal::dense_set_placeholder_position;
using node_type = std::pair<std::size_t, Type>;
using alloc_traits = std::allocator_traits<Allocator>;
@@ -227,27 +204,24 @@ class dense_set {
using sparse_container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
using packed_container_type = std::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
template<typename Other>
[[nodiscard]] std::size_t value_to_bucket(const Other &value) const noexcept {
[[nodiscard]] std::size_t value_to_bucket(const auto &value) const noexcept {
return fast_mod(static_cast<size_type>(sparse.second()(value)), bucket_count());
}
template<typename Other>
[[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) {
for(auto it = begin(bucket), last = end(bucket); it != last; ++it) {
if(packed.second()(*it, value)) {
return begin() + static_cast<difference_type>(it.index());
[[nodiscard]] auto constrained_find(const auto &value, const std::size_t bucket) {
for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].first) {
if(packed.second()(packed.first()[offset].second, value)) {
return begin() + static_cast<iterator::difference_type>(offset);
}
}
return end();
}
template<typename Other>
[[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) const {
for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) {
if(packed.second()(*it, value)) {
return cbegin() + static_cast<difference_type>(it.index());
[[nodiscard]] auto constrained_find(const auto &value, const std::size_t bucket) const {
for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].first) {
if(packed.second()(packed.first()[offset].second, value)) {
return cbegin() + static_cast<const_iterator::difference_type>(offset);
}
}
@@ -302,17 +276,17 @@ public:
/*! @brief Type of function to use to compare the elements for equality. */
using key_equal = KeyEqual;
/*! @brief Random access iterator type. */
using iterator = internal::dense_set_iterator<typename packed_container_type::iterator>;
using iterator = internal::dense_set_iterator<typename packed_container_type::pointer>;
/*! @brief Constant random access iterator type. */
using const_iterator = internal::dense_set_iterator<typename packed_container_type::const_iterator>;
using const_iterator = internal::dense_set_iterator<typename packed_container_type::const_pointer>;
/*! @brief Reverse iterator type. */
using reverse_iterator = std::reverse_iterator<iterator>;
/*! @brief Constant reverse iterator type. */
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
/*! @brief Forward iterator type. */
using local_iterator = internal::dense_set_local_iterator<typename packed_container_type::iterator>;
using local_iterator = internal::dense_set_local_iterator<typename packed_container_type::pointer>;
/*! @brief Constant forward iterator type. */
using const_local_iterator = internal::dense_set_local_iterator<typename packed_container_type::const_iterator>;
using const_local_iterator = internal::dense_set_local_iterator<typename packed_container_type::const_pointer>;
/*! @brief Default constructor. */
dense_set()
@@ -426,7 +400,7 @@ public:
* @return An iterator to the first instance of the internal array.
*/
[[nodiscard]] const_iterator cbegin() const noexcept {
return packed.first().begin();
return packed.first().data();
}
/*! @copydoc cbegin */
@@ -436,7 +410,7 @@ public:
/*! @copydoc begin */
[[nodiscard]] iterator begin() noexcept {
return packed.first().begin();
return packed.first().data();
}
/**
@@ -445,7 +419,7 @@ public:
* internal array.
*/
[[nodiscard]] const_iterator cend() const noexcept {
return packed.first().end();
return packed.first().data() + packed.first().size();
}
/*! @copydoc cend */
@@ -455,7 +429,7 @@ public:
/*! @copydoc end */
[[nodiscard]] iterator end() noexcept {
return packed.first().end();
return packed.first().data() + packed.first().size();
}
/**
@@ -547,12 +521,10 @@ public:
/**
* @brief Inserts elements into the container, if they do not exist.
* @tparam It Type of input iterator.
* @param first An iterator to the first element of the range of elements.
* @param last An iterator past the last element of the range of elements.
*/
template<typename It>
void insert(It first, It last) {
void insert(stl::input_iterator auto first, stl::input_iterator auto last) {
for(; first != last; ++first) {
insert(*first);
}
@@ -624,7 +596,7 @@ public:
* @return Number of elements removed (either 0 or 1).
*/
size_type erase(const value_type &value) {
for(size_type *curr = &sparse.first()[value_to_bucket(value)]; *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].first) {
for(size_type *curr = &sparse.first()[value_to_bucket(value)]; *curr != placeholder_position; curr = &packed.first()[*curr].first) {
if(packed.second()(packed.first()[*curr].second, value)) {
const auto index = *curr;
*curr = packed.first()[*curr].first;
@@ -647,13 +619,11 @@ public:
/**
* @brief Returns the number of elements matching a key (either 1 or 0).
* @tparam Other Type of the key value of an element to search for.
* @param key Key value of an element to search for.
* @return Number of elements matching the key (either 1 or 0).
*/
template<typename Other>
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, size_type>>
count(const Other &key) const {
[[nodiscard]] size_type count(const auto &key) const
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
return find(key) != end();
}
@@ -674,21 +644,18 @@ public:
/**
* @brief Finds an element that compares _equivalent_ to a given value.
* @tparam Other Type of an element to search for.
* @param value Value of an element to search for.
* @return An iterator to an element with the given value. If no such
* element is found, a past-the-end iterator is returned.
*/
template<typename Other>
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, iterator>>
find(const Other &value) {
[[nodiscard]] iterator find(const auto &value)
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
return constrained_find(value, value_to_bucket(value));
}
/*! @copydoc find */
template<typename Other>
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, const_iterator>>
find(const Other &value) const {
[[nodiscard]] const_iterator find(const auto &value) const
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
return constrained_find(value, value_to_bucket(value));
}
@@ -712,22 +679,19 @@ public:
/**
* @brief Returns a range containing all elements that compare _equivalent_
* to a given value.
* @tparam Other Type of an element to search for.
* @param value Value of an element to search for.
* @return A pair of iterators pointing to the first element and past the
* last element of the range.
*/
template<typename Other>
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, std::pair<iterator, iterator>>>
equal_range(const Other &value) {
[[nodiscard]] std::pair<iterator, iterator> equal_range(const auto &value)
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
const auto it = find(value);
return {it, it + !(it == end())};
}
/*! @copydoc equal_range */
template<typename Other>
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, std::pair<const_iterator, const_iterator>>>
equal_range(const Other &value) const {
[[nodiscard]] std::pair<const_iterator, const_iterator> equal_range(const auto &value) const
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
const auto it = find(value);
return {it, it + !(it == cend())};
}
@@ -744,13 +708,11 @@ public:
/**
* @brief Checks if the container contains an element that compares
* _equivalent_ to a given value.
* @tparam Other Type of an element to search for.
* @param value Value of an element to search for.
* @return True if there is such an element, false otherwise.
*/
template<typename Other>
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, bool>>
contains(const Other &value) const {
[[nodiscard]] bool contains(const auto &value) const
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
return (find(value) != cend());
}
@@ -760,7 +722,7 @@ public:
* @return An iterator to the beginning of the given bucket.
*/
[[nodiscard]] const_local_iterator cbegin(const size_type index) const {
return {packed.first().begin(), sparse.first()[index]};
return {packed.first().data(), sparse.first()[index]};
}
/**
@@ -778,7 +740,7 @@ public:
* @return An iterator to the beginning of the given bucket.
*/
[[nodiscard]] local_iterator begin(const size_type index) {
return {packed.first().begin(), sparse.first()[index]};
return {packed.first().data(), sparse.first()[index]};
}
/**
@@ -787,7 +749,7 @@ public:
* @return An iterator to the end of the given bucket.
*/
[[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const {
return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
return {};
}
/**
@@ -805,7 +767,7 @@ public:
* @return An iterator to the end of the given bucket.
*/
[[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) {
return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
return {};
}
/**
@@ -878,11 +840,11 @@ public:
const auto cap = static_cast<size_type>(static_cast<float>(size()) / max_load_factor());
value = value > cap ? value : cap;
if(const auto sz = next_power_of_two(value); sz != bucket_count()) {
if(const auto sz = std::bit_ceil(value); sz != bucket_count()) {
sparse.first().resize(sz);
for(auto &&elem: sparse.first()) {
elem = (std::numeric_limits<size_type>::max)();
elem = placeholder_position;
}
for(size_type pos{}, last = size(); pos < last; ++pos) {

View File

@@ -1,10 +1,10 @@
#ifndef ENTT_CONTAINER_TABLE_HPP
#define ENTT_CONTAINER_TABLE_HPP
#include <concepts>
#include <cstddef>
#include <iterator>
#include <tuple>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/iterator.hpp"
@@ -12,7 +12,7 @@
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename... It>
@@ -34,7 +34,8 @@ public:
constexpr table_iterator(It... from) noexcept
: it{from...} {}
template<typename... Other, typename = std::enable_if_t<(std::is_constructible_v<It, Other> && ...)>>
template<typename... Other>
requires (std::constructible_from<It, Other> && ...)
constexpr table_iterator(const table_iterator<Other...> &other) noexcept
: table_iterator{std::get<Other>(other.it)...} {}
@@ -85,54 +86,25 @@ public:
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... Other>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const table_iterator<Other...> &other) const noexcept {
return std::get<0>(it) - std::get<0>(other.it);
}
template<typename... Lhs, typename... Rhs>
friend constexpr bool operator==(const table_iterator<Lhs...> &, const table_iterator<Rhs...> &) noexcept;
template<typename... Other>
[[nodiscard]] constexpr bool operator==(const table_iterator<Other...> &other) const noexcept {
return std::get<0>(it) == std::get<0>(other.it);
}
template<typename... Lhs, typename... Rhs>
friend constexpr bool operator<(const table_iterator<Lhs...> &, const table_iterator<Rhs...> &) noexcept;
template<typename... Other>
[[nodiscard]] constexpr auto operator<=>(const table_iterator<Other...> &other) const noexcept {
return std::get<0>(it) <=> std::get<0>(other.it);
}
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 */
@@ -198,11 +170,9 @@ public:
/**
* @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)
explicit basic_table(const auto &allocator)
: payload{Container{allocator}...} {}
/**
@@ -449,7 +419,7 @@ private:
} // namespace entt
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace std {
template<typename... Container, typename Allocator>

View File

@@ -2,11 +2,13 @@
#define ENTT_CORE_ALGORITHM_HPP
#include <algorithm>
#include <concepts>
#include <functional>
#include <iterator>
#include <utility>
#include <vector>
#include "utility.hpp"
#include "../stl/functional.hpp"
#include "../stl/iterator.hpp"
namespace entt {
@@ -24,7 +26,6 @@ struct std_sort {
*
* Sorts the elements in a range using the given binary comparison function.
*
* @tparam It Type of random access iterator.
* @tparam Compare Type of comparison function object.
* @tparam Args Types of arguments to forward to the sort function.
* @param first An iterator to the first element of the range to sort.
@@ -32,8 +33,8 @@ struct std_sort {
* @param compare A valid comparison function object.
* @param args Arguments to forward to the sort function, if any.
*/
template<typename It, typename Compare = std::less<>, typename... Args>
void operator()(It first, It last, Compare compare = Compare{}, Args &&...args) const {
template<typename Compare = std::less<>, typename... Args>
void operator()(stl::random_access_iterator auto first, stl::random_access_iterator auto last, Compare compare = Compare{}, Args &&...args) const {
std::sort(std::forward<Args>(args)..., std::move(first), std::move(last), std::move(compare));
}
};
@@ -45,14 +46,13 @@ struct insertion_sort {
*
* Sorts the elements in a range using the given binary comparison function.
*
* @tparam It Type of random access iterator.
* @tparam Compare Type of comparison function object.
* @param first An iterator to the first element of the range to sort.
* @param last An iterator past the last element of the range to sort.
* @param compare A valid comparison function object.
*/
template<typename It, typename Compare = std::less<>>
void operator()(It first, It last, Compare compare = Compare{}) const {
template<typename Compare = std::less<>>
void operator()(stl::random_access_iterator auto first, stl::random_access_iterator auto last, Compare compare = Compare{}) const {
if(first < last) {
for(auto it = first + 1; it < last; ++it) {
auto value = std::move(*it);
@@ -76,9 +76,8 @@ struct insertion_sort {
* @tparam N Maximum number of bits to sort.
*/
template<std::size_t Bit, std::size_t N>
requires ((N % Bit) == 0) // The maximum number of bits to sort must be a multiple of the number of bits processed per pass
struct radix_sort {
static_assert((N % Bit) == 0, "The maximum number of bits to sort must be a multiple of the number of bits processed per pass");
/**
* @brief Sorts the elements in a range.
*
@@ -94,13 +93,13 @@ struct radix_sort {
* @param last An iterator past the last element of the range to sort.
* @param getter A valid _getter_ function object.
*/
template<typename It, typename Getter = identity>
template<stl::random_access_iterator It, typename Getter = stl::identity>
void operator()(It first, It last, Getter getter = Getter{}) const {
if(first < last) {
constexpr auto passes = N / Bit;
using value_type = typename std::iterator_traits<It>::value_type;
using difference_type = typename std::iterator_traits<It>::difference_type;
using value_type = std::iterator_traits<It>::value_type;
using difference_type = std::iterator_traits<It>::difference_type;
std::vector<value_type> aux(static_cast<std::size_t>(std::distance(first, last)));
auto part = [getter = std::move(getter)](auto from, auto to, auto out, auto start) {

View File

@@ -1,162 +1,185 @@
#ifndef ENTT_CORE_ANY_HPP
#define ENTT_CORE_ANY_HPP
#include <concepts>
#include <cstddef>
#include <memory>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/utility.hpp"
#include "../core/concepts.hpp"
#include "fwd.hpp"
#include "type_info.hpp"
#include "type_traits.hpp"
#include "utility.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
enum class any_request : std::uint8_t {
info,
transfer,
assign,
destroy,
compare,
copy,
move,
get
move
};
template<std::size_t Len, std::size_t Align>
struct basic_any_storage {
static constexpr bool has_buffer = true;
union {
const void *instance{};
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
alignas(Align) std::byte buffer[Len];
};
};
template<std::size_t Align>
struct basic_any_storage<0u, Align> {
static constexpr bool has_buffer = false;
const void *instance{};
};
template<typename Type, std::size_t Len, std::size_t Align>
// NOLINTNEXTLINE(bugprone-sizeof-expression)
struct in_situ: std::bool_constant<(Len != 0u) && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v<Type>> {};
template<std::size_t Len, std::size_t Align>
struct in_situ<void, Len, Align>: std::false_type {};
} // namespace internal
/*! @endcond */
/**
* @brief A SBO friendly, type-safe container for single values of any type.
* @tparam Len Size of the storage reserved for the small buffer optimization.
* @tparam Len Size of the buffer reserved for the small buffer optimization.
* @tparam Align Optional alignment requirement.
*/
template<std::size_t Len, std::size_t Align>
class basic_any {
class basic_any: private internal::basic_any_storage<Len, Align> {
using request = internal::any_request;
using base_type = internal::basic_any_storage<Len, Align>;
using vtable_type = const void *(const request, const basic_any &, const void *);
struct storage_type {
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
alignas(Align) std::byte data[Len + static_cast<std::size_t>(Len == 0u)];
};
using deleter_type = void(const basic_any &);
template<typename Type>
// NOLINTNEXTLINE(bugprone-sizeof-expression)
static constexpr bool in_situ = (Len != 0u) && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v<Type>;
static constexpr bool in_situ_v = internal::in_situ<Type, Len, Align>::value;
template<typename Type>
template<cvref_unqualified Type>
static const void *basic_vtable(const request req, const basic_any &value, const void *other) {
static_assert(!std::is_void_v<Type> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
const Type *elem = nullptr;
if constexpr(in_situ<Type>) {
elem = (value.mode == any_policy::embedded) ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance);
} else {
elem = static_cast<const Type *>(value.instance);
}
switch(req) {
case request::transfer:
switch(const auto *elem = static_cast<const Type *>(value.data()); req) {
using enum internal::any_request;
case info:
return &type_id<Type>();
case transfer:
if constexpr(std::is_move_assignable_v<Type>) {
// NOLINTNEXTLINE(bugprone-casting-through-void)
*const_cast<Type *>(elem) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
return other;
}
[[fallthrough]];
case request::assign:
case assign:
if constexpr(std::is_copy_assignable_v<Type>) {
*const_cast<Type *>(elem) = *static_cast<const Type *>(other);
return other;
}
break;
case request::destroy:
if constexpr(in_situ<Type>) {
(value.mode == any_policy::embedded) ? elem->~Type() : (delete elem);
} else if constexpr(std::is_array_v<Type>) {
delete[] elem;
} else {
delete elem;
}
break;
case request::compare:
case compare:
if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) {
return (*elem == *static_cast<const Type *>(other)) ? other : nullptr;
} else {
return (elem == other) ? other : nullptr;
}
case request::copy:
case copy:
if constexpr(std::is_copy_constructible_v<Type>) {
// NOLINTNEXTLINE(bugprone-casting-through-void)
static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*elem);
}
break;
case request::move:
case move:
ENTT_ASSERT(value.mode == any_policy::embedded, "Unexpected policy");
if constexpr(in_situ<Type>) {
if constexpr(in_situ_v<Type>) {
// NOLINTNEXTLINE(bugprone-casting-through-void, bugprone-multi-level-implicit-pointer-conversion)
return ::new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(elem))};
}
[[fallthrough]];
case request::get:
ENTT_ASSERT(value.mode == any_policy::embedded, "Unexpected policy");
if constexpr(in_situ<Type>) {
// NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion)
return elem;
return ::new(&static_cast<basic_any *>(const_cast<void *>(other))->buffer) Type{std::move(*const_cast<Type *>(elem))};
}
}
return nullptr;
}
template<cvref_unqualified Type>
static void basic_deleter(const basic_any &value) {
ENTT_ASSERT((value.mode == any_policy::dynamic) || ((value.mode == any_policy::embedded) && !std::is_trivially_destructible_v<Type>), "Unexpected policy");
const auto *elem = static_cast<const Type *>(value.data());
if constexpr(in_situ_v<Type>) {
(value.mode == any_policy::embedded) ? elem->~Type() : (delete elem);
} else if constexpr(std::is_array_v<Type>) {
delete[] elem;
} else {
delete elem;
}
}
template<typename Type, typename... Args>
void initialize([[maybe_unused]] Args &&...args) {
if constexpr(!std::is_void_v<Type>) {
using plain_type = std::remove_cv_t<std::remove_reference_t<Type>>;
using plain_type = std::remove_cvref_t<Type>;
info = &type_id<plain_type>();
vtable = basic_vtable<plain_type>;
vtable = basic_vtable<plain_type>;
underlying_type = type_hash<plain_type>::value();
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;
// NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion)
instance = (std::addressof(args), ...);
} else if constexpr(in_situ<plain_type>) {
mode = any_policy::embedded;
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 {
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
::new(&storage) plain_type(std::forward<Args>(args)...);
}
if constexpr(std::is_void_v<Type>) {
deleter = nullptr;
mode = any_policy::empty;
this->instance = nullptr;
} else if constexpr(std::is_lvalue_reference_v<Type>) {
deleter = nullptr;
mode = std::is_const_v<std::remove_reference_t<Type>> ? any_policy::cref : any_policy::ref;
static_assert((std::is_lvalue_reference_v<Args> && ...) && (sizeof...(Args) == 1u), "Invalid arguments");
// NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion)
this->instance = (std::addressof(args), ...);
} else if constexpr(in_situ_v<plain_type>) {
if constexpr(std::is_trivially_destructible_v<plain_type>) {
deleter = nullptr;
} else {
mode = any_policy::dynamic;
deleter = &basic_deleter<plain_type>;
}
if constexpr(std::is_aggregate_v<plain_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<plain_type>)) {
instance = new plain_type{std::forward<Args>(args)...};
} else if constexpr(std::is_array_v<plain_type>) {
static_assert(sizeof...(Args) == 0u, "Invalid arguments");
instance = new plain_type[std::extent_v<plain_type>]();
} else {
instance = new plain_type(std::forward<Args>(args)...);
}
mode = any_policy::embedded;
if constexpr(std::is_aggregate_v<plain_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<plain_type>)) {
::new(&this->buffer) plain_type{std::forward<Args>(args)...};
} else {
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
::new(&this->buffer) plain_type(std::forward<Args>(args)...);
}
} else {
deleter = &basic_deleter<plain_type>;
mode = any_policy::dynamic;
if constexpr(std::is_aggregate_v<plain_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<plain_type>)) {
this->instance = new plain_type{std::forward<Args>(args)...};
} else if constexpr(std::is_array_v<plain_type>) {
static_assert(sizeof...(Args) == 0u, "Invalid arguments");
this->instance = new plain_type[std::extent_v<plain_type>]();
} else {
this->instance = new plain_type(std::forward<Args>(args)...);
}
}
}
basic_any(const basic_any &other, const any_policy pol) noexcept
: instance{other.data()},
info{other.info},
vtable{other.vtable},
mode{pol} {}
void invoke_deleter_if_exists() {
if(deleter != nullptr) {
deleter(*this);
}
}
public:
/*! @brief Size of the internal storage. */
/*! @brief Size of the internal buffer. */
static constexpr auto length = Len;
/*! @brief Alignment requirement. */
static constexpr auto alignment = Align;
@@ -173,7 +196,7 @@ public:
*/
template<typename Type, typename... Args>
explicit basic_any(std::in_place_type_t<Type>, Args &&...args)
: instance{} {
: base_type{} {
initialize<Type>(std::forward<Args>(args)...);
}
@@ -183,12 +206,14 @@ public:
* @param value A pointer to an object to take ownership of.
*/
template<typename Type>
requires (!std::is_const_v<Type> && !std::is_void_v<Type>)
explicit basic_any(std::in_place_t, Type *value)
: instance{} {
static_assert(!std::is_const_v<Type> && !std::is_void_v<Type>, "Non-const non-void pointer required");
if(value != nullptr) {
: base_type{} {
if(value == nullptr) {
initialize<void>();
} else {
initialize<Type &>(*value);
deleter = &basic_deleter<Type>;
mode = any_policy::dynamic;
}
}
@@ -198,7 +223,8 @@ public:
* @tparam Type Type of object to use to initialize the wrapper.
* @param value An instance of an object to use to initialize the wrapper.
*/
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
template<typename Type>
requires (!std::same_as<std::remove_cvref_t<Type>, basic_any>)
basic_any(Type &&value)
: basic_any{std::in_place_type<std::decay_t<Type>>, std::forward<Type>(value)} {}
@@ -208,9 +234,7 @@ public:
*/
basic_any(const basic_any &other)
: basic_any{} {
if(other.vtable) {
other.vtable(request::copy, other, this);
}
other.vtable(request::copy, other, this);
}
/**
@@ -218,22 +242,21 @@ public:
* @param other The instance to move from.
*/
basic_any(basic_any &&other) noexcept
: instance{},
info{other.info},
: base_type{},
vtable{other.vtable},
deleter{other.deleter},
underlying_type{other.underlying_type},
mode{other.mode} {
if(other.mode == any_policy::embedded) {
other.vtable(request::move, other, this);
} else if(other.mode != any_policy::empty) {
instance = std::exchange(other.instance, nullptr);
this->instance = std::exchange(other.instance, nullptr);
}
}
/*! @brief Frees the internal storage, whatever it means. */
/*! @brief Frees the internal buffer, whatever it means. */
~basic_any() {
if(owner()) {
vtable(request::destroy, *this, nullptr);
}
invoke_deleter_if_exists();
}
/**
@@ -243,10 +266,12 @@ public:
*/
basic_any &operator=(const basic_any &other) {
if(this != &other) {
reset();
invoke_deleter_if_exists();
if(other.vtable) {
if(other) {
other.vtable(request::copy, other, this);
} else {
initialize<void>();
}
}
@@ -255,26 +280,25 @@ public:
/**
* @brief Move assignment operator.
*
* @warning
* Self-moving puts objects in a safe but unspecified state.
*
* @param other The instance to move from.
* @return This any object.
*/
basic_any &operator=(basic_any &&other) noexcept {
reset();
if(this != &other) {
invoke_deleter_if_exists();
if(other.mode == any_policy::embedded) {
other.vtable(request::move, other, this);
} else if(other.mode != any_policy::empty) {
instance = std::exchange(other.instance, nullptr);
if(other.mode == any_policy::embedded) {
other.vtable(request::move, other, this);
} else if(other.mode != any_policy::empty) {
this->instance = std::exchange(other.instance, nullptr);
}
vtable = other.vtable;
deleter = other.deleter;
underlying_type = other.underlying_type;
mode = other.mode;
}
info = other.info;
vtable = other.vtable;
mode = other.mode;
return *this;
}
@@ -284,18 +308,50 @@ public:
* @param value An instance of an object to use to initialize the wrapper.
* @return This any object.
*/
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
template<typename Type>
requires (!std::same_as<std::remove_cvref_t<Type>, basic_any>)
basic_any &operator=(Type &&value) {
emplace<std::decay_t<Type>>(std::forward<Type>(value));
return *this;
}
/**
* @brief Returns the object type if any, `type_id<void>()` otherwise.
* @return The object type if any, `type_id<void>()` otherwise.
* @brief Returns false if a wrapper is empty, true otherwise.
* @return False if the wrapper is empty, true otherwise.
*/
[[nodiscard]] const type_info &type() const noexcept {
return (info == nullptr) ? type_id<void>() : *info;
[[nodiscard]] bool has_value() const noexcept {
return (mode != any_policy::empty);
}
/**
* @brief Returns false if the wrapper does not contain the expected type,
* true otherwise.
* @param req Expected type.
* @return False if the wrapper does not contain the expected type, true
* otherwise.
*/
[[nodiscard]] bool has_value(const type_info &req) const noexcept {
return (underlying_type == req.hash());
}
/**
* @brief Returns false if the wrapper does not contain the expected type,
* true otherwise.
* @tparam Type Expected type.
* @return False if the wrapper does not contain the expected type, true
* otherwise.
*/
template<cvref_unqualified Type>
[[nodiscard]] bool has_value() const noexcept {
return (underlying_type == type_hash<Type>::value());
}
/**
* @brief Returns the object type info if any, `type_id<void>()` otherwise.
* @return The object type info if any, `type_id<void>()` otherwise.
*/
[[nodiscard]] const type_info &info() const noexcept {
return *static_cast<const type_info *>(vtable(request::info, *this, nullptr));
}
/**
@@ -303,7 +359,11 @@ public:
* @return An opaque pointer the contained instance, if any.
*/
[[nodiscard]] const void *data() const noexcept {
return (mode == any_policy::embedded) ? vtable(request::get, *this, nullptr) : instance;
if constexpr(base_type::has_buffer) {
return (mode == any_policy::embedded) ? &this->buffer : this->instance;
} else {
return this->instance;
}
}
/**
@@ -312,7 +372,17 @@ public:
* @return An opaque pointer the contained instance, if any.
*/
[[nodiscard]] const void *data(const type_info &req) const noexcept {
return (type() == req) ? data() : nullptr;
return has_value(req) ? data() : nullptr;
}
/**
* @brief Returns an opaque pointer to the contained instance.
* @tparam Type Expected type.
* @return An opaque pointer the contained instance, if any.
*/
template<typename Type>
[[nodiscard]] const Type *data() const noexcept {
return has_value<std::remove_const_t<Type>>() ? static_cast<const Type *>(data()) : nullptr;
}
/**
@@ -320,7 +390,7 @@ public:
* @return An opaque pointer the contained instance, if any.
*/
[[nodiscard]] void *data() noexcept {
return mode == any_policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data());
return (mode == any_policy::cref) ? nullptr : const_cast<void *>(std::as_const(*this).data());
}
/**
@@ -329,7 +399,21 @@ public:
* @return An opaque pointer the contained instance, if any.
*/
[[nodiscard]] void *data(const type_info &req) noexcept {
return mode == any_policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data(req));
return (mode == any_policy::cref) ? nullptr : const_cast<void *>(std::as_const(*this).data(req));
}
/**
* @brief Returns an opaque pointer to the contained instance.
* @tparam Type Expected type.
* @return An opaque pointer the contained instance, if any.
*/
template<typename Type>
[[nodiscard]] Type *data() noexcept {
if constexpr(std::is_const_v<Type>) {
return std::as_const(*this).template data<std::remove_const_t<Type>>();
} else {
return (mode == any_policy::cref) ? nullptr : const_cast<Type *>(std::as_const(*this).template data<std::remove_const_t<Type>>());
}
}
/**
@@ -340,7 +424,7 @@ public:
*/
template<typename Type, typename... Args>
void emplace(Args &&...args) {
reset();
invoke_deleter_if_exists();
initialize<Type>(std::forward<Args>(args)...);
}
@@ -350,7 +434,7 @@ public:
* @return True in case of success, false otherwise.
*/
bool assign(const basic_any &other) {
if(vtable && mode != any_policy::cref && *info == other.type()) {
if(other && (mode != any_policy::cref) && (underlying_type == other.underlying_type)) {
return (vtable(request::assign, *this, other.data()) != nullptr);
}
@@ -360,12 +444,8 @@ public:
/*! @copydoc assign */
// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
bool assign(basic_any &&other) {
if(vtable && mode != any_policy::cref && *info == other.type()) {
if(auto *val = other.data(); val) {
return (vtable(request::transfer, *this, val) != nullptr);
}
return (vtable(request::assign, *this, std::as_const(other).data()) != nullptr);
if(other && (mode != any_policy::cref) && (underlying_type == other.underlying_type)) {
return (other.mode == any_policy::cref) ? (vtable(request::assign, *this, std::as_const(other).data()) != nullptr) : (vtable(request::transfer, *this, other.data()) != nullptr);
}
return false;
@@ -373,14 +453,8 @@ public:
/*! @brief Destroys contained object */
void reset() {
if(owner()) {
vtable(request::destroy, *this, nullptr);
}
instance = nullptr;
info = nullptr;
vtable = nullptr;
mode = any_policy::empty;
invoke_deleter_if_exists();
initialize<void>();
}
/**
@@ -388,7 +462,7 @@ public:
* @return False if the wrapper is empty, true otherwise.
*/
[[nodiscard]] explicit operator bool() const noexcept {
return vtable != nullptr;
return has_value();
}
/**
@@ -397,20 +471,11 @@ public:
* @return False if the two objects differ in their content, true otherwise.
*/
[[nodiscard]] bool operator==(const basic_any &other) const noexcept {
if(vtable && *info == other.type()) {
if(other && (underlying_type == other.underlying_type)) {
return (vtable(request::compare, *this, other.data()) != nullptr);
}
return (!vtable && !other.vtable);
}
/**
* @brief Checks if two wrappers differ in their content.
* @param other Wrapper with which to compare.
* @return True if the two objects differ in their content, false otherwise.
*/
[[nodiscard]] bool operator!=(const basic_any &other) const noexcept {
return !(*this == other);
return (!*this && !other);
}
/**
@@ -418,12 +483,30 @@ public:
* @return A wrapper that shares a reference to an unmanaged object.
*/
[[nodiscard]] basic_any as_ref() noexcept {
return basic_any{*this, (mode == any_policy::cref ? any_policy::cref : any_policy::ref)};
basic_any other = std::as_const(*this).as_ref();
switch(mode) {
using enum any_policy;
case cref:
case empty:
other.mode = mode;
break;
default:
other.mode = any_policy::ref;
break;
}
return other;
}
/*! @copydoc as_ref */
[[nodiscard]] basic_any as_ref() const noexcept {
return basic_any{*this, any_policy::cref};
basic_any other{};
other.instance = data();
other.vtable = vtable;
other.underlying_type = underlying_type;
other.mode = any_policy::cref;
return other;
}
/**
@@ -443,19 +526,16 @@ public:
}
private:
union {
const void *instance;
storage_type storage;
};
const type_info *info{};
vtable_type *vtable{};
any_policy mode{any_policy::empty};
deleter_type *deleter{};
id_type underlying_type{};
any_policy mode{};
};
/**
* @brief Performs type-safe access to the contained object.
* @tparam Type Type to which conversion is required.
* @tparam Len Size of the storage reserved for the small buffer optimization.
* @tparam Len Size of the buffer reserved for the small buffer optimization.
* @tparam Align Alignment requirement.
* @param data Target any object.
* @return The element converted to the requested type.
@@ -480,7 +560,7 @@ template<typename Type, std::size_t Len, std::size_t Align>
template<typename Type, std::size_t Len, std::size_t Align>
// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
[[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 constexpr(std::is_copy_constructible_v<std::remove_cvref_t<Type>>) {
if(auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); instance) {
return static_cast<Type>(std::move(*instance));
}
@@ -496,8 +576,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]] const Type *any_cast(const basic_any<Len, Align> *data) noexcept {
const auto &info = type_id<std::remove_cv_t<Type>>();
return static_cast<const Type *>(data->data(info));
return data->template data<std::remove_const_t<Type>>();
}
/*! @copydoc any_cast */
@@ -507,15 +586,14 @@ template<typename Type, std::size_t Len, std::size_t Align>
// last attempt to make wrappers for const references return their values
return any_cast<Type>(&std::as_const(*data));
} else {
const auto &info = type_id<std::remove_cv_t<Type>>();
return static_cast<Type *>(data->data(info));
return data->template data<Type>();
}
}
/**
* @brief Constructs a wrapper from a given type, passing it all arguments.
* @tparam Type Type of object to use to initialize the wrapper.
* @tparam Len Size of the storage reserved for the small buffer optimization.
* @tparam Len Size of the buffer reserved for the small buffer optimization.
* @tparam Align Optional alignment requirement.
* @tparam Args Types of arguments to use to construct the new instance.
* @param args Parameters to use to construct the instance.
@@ -528,7 +606,7 @@ template<typename Type, std::size_t Len = basic_any<>::length, std::size_t Align
/**
* @brief Forwards its argument and avoids copies for lvalue references.
* @tparam Len Size of the storage reserved for the small buffer optimization.
* @tparam Len Size of the buffer reserved for the small buffer optimization.
* @tparam Align Optional alignment requirement.
* @tparam Type Type of argument to use to construct the new instance.
* @param value Parameter to use to construct the instance.

View File

@@ -1,30 +0,0 @@
#ifndef ENTT_CORE_ATTRIBUTE_H
#define ENTT_CORE_ATTRIBUTE_H
#ifndef ENTT_EXPORT
# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER
# define ENTT_EXPORT __declspec(dllexport)
# define ENTT_IMPORT __declspec(dllimport)
# define ENTT_HIDDEN
# elif defined __GNUC__ && __GNUC__ >= 4
# define ENTT_EXPORT __attribute__((visibility("default")))
# define ENTT_IMPORT __attribute__((visibility("default")))
# define ENTT_HIDDEN __attribute__((visibility("hidden")))
# else /* Unsupported compiler */
# define ENTT_EXPORT
# define ENTT_IMPORT
# define ENTT_HIDDEN
# endif
#endif
#ifndef ENTT_API
# if defined ENTT_API_EXPORT
# define ENTT_API ENTT_EXPORT
# elif defined ENTT_API_IMPORT
# define ENTT_API ENTT_IMPORT
# else /* No API */
# define ENTT_API
# endif
#endif
#endif

View File

@@ -1,57 +1,13 @@
#ifndef ENTT_CORE_BIT_HPP
#define ENTT_CORE_BIT_HPP
#include <bit>
#include <concepts>
#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 {
// NOLINTNEXTLINE(bugprone-assert-side-effect)
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.
@@ -59,9 +15,9 @@ template<typename 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");
template<std::unsigned_integral Type>
[[nodiscard]] constexpr Type fast_mod(const Type value, const std::size_t mod) noexcept {
ENTT_ASSERT_CONSTEXPR(std::has_single_bit(mod), "Value must be a power of two");
return static_cast<Type>(value & (mod - 1u));
}

View File

@@ -1,6 +1,7 @@
#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP
#define ENTT_CORE_COMPRESSED_PAIR_HPP
#include <concepts>
#include <cstddef>
#include <tuple>
#include <type_traits>
@@ -10,20 +11,21 @@
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename Type, std::size_t, typename = void>
template<typename Type, std::size_t>
struct compressed_pair_element {
using reference = Type &;
using const_reference = const Type &;
template<typename Dummy = Type, typename = std::enable_if_t<std::is_default_constructible_v<Dummy>>>
// NOLINTNEXTLINE(modernize-use-equals-default)
constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<Type>) {}
constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<Type>)
requires std::default_initializable<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>>>
template<typename Arg>
constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v<Type, Arg>)
requires (!std::same_as<std::remove_cvref_t<Arg>, compressed_pair_element>)
: value{std::forward<Arg>(arg)} {}
template<typename... Args, std::size_t... Index>
@@ -43,17 +45,19 @@ private:
};
template<typename Type, std::size_t Tag>
struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Type>>>: Type {
requires is_ebco_eligible_v<Type>
struct compressed_pair_element<Type, Tag>: Type {
using reference = Type &;
using const_reference = const Type &;
using base_type = Type;
template<typename Dummy = Type, typename = std::enable_if_t<std::is_default_constructible_v<Dummy>>>
constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<base_type>)
requires std::default_initializable<Type>
: base_type{} {}
template<typename Arg, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Arg>>, compressed_pair_element>>>
template<typename Arg>
constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v<base_type, Arg>)
requires (!std::same_as<std::remove_cvref_t<Arg>, compressed_pair_element>)
: base_type{std::forward<Arg>(arg)} {}
template<typename... Args, std::size_t... Index>
@@ -99,11 +103,9 @@ public:
*
* This constructor is only available when the types that the pair stores
* are both at least default constructible.
*
* @tparam Dummy Dummy template parameter used for internal purposes.
*/
template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>>
constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v<first_base> && std::is_nothrow_default_constructible_v<second_base>)
requires std::default_initializable<first_type> && std::default_initializable<second_type>
: first_base{},
second_base{} {}
@@ -203,22 +205,22 @@ public:
* reference to the second element if `Index` is 1.
*/
template<std::size_t Index>
requires (Index <= 1u)
[[nodiscard]] constexpr decltype(auto) get() noexcept {
if constexpr(Index == 0u) {
return first();
} else {
static_assert(Index == 1u, "Index out of bounds");
return second();
}
}
/*! @copydoc get */
template<std::size_t Index>
requires (Index <= 1u)
[[nodiscard]] constexpr decltype(auto) get() const noexcept {
if constexpr(Index == 0u) {
return first();
} else {
static_assert(Index == 1u, "Index out of bounds");
return second();
}
}
@@ -263,9 +265,8 @@ struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_
* @tparam Second The type of the second element that the pair stores.
*/
template<size_t Index, typename First, typename Second>
struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<Index == 0u, First, Second> {
static_assert(Index < 2u, "Index out of bounds");
};
requires (Index <= 1u)
struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<Index == 0u, First, Second> {};
} // namespace std

View File

@@ -0,0 +1,17 @@
#ifndef ENTT_CORE_CONCEPTS_HPP
#define ENTT_CORE_CONCEPTS_HPP
#include <type_traits>
namespace entt {
/**
* @brief Specifies that a type is not a cv-qualified reference.
* @tparam Type Type to check.
*/
template<typename Type>
concept cvref_unqualified = std::is_same_v<std::remove_cvref_t<Type>, Type>;
} // namespace entt
#endif

View File

@@ -1,6 +1,7 @@
#ifndef ENTT_CORE_ENUM_HPP
#define ENTT_CORE_ENUM_HPP
#include <concepts>
#include <type_traits>
namespace entt {
@@ -9,12 +10,16 @@ namespace entt {
* @brief Enable bitmask support for enum classes.
* @tparam Type The enum type for which to enable bitmask support.
*/
template<typename Type, typename = void>
template<typename Type>
struct enum_as_bitmask: std::false_type {};
/*! @copydoc enum_as_bitmask */
template<typename Type>
struct enum_as_bitmask<Type, std::void_t<decltype(Type::_entt_enum_as_bitmask)>>: std::is_enum<Type> {};
requires requires {
requires std::is_enum_v<Type>;
{ Type::_entt_enum_as_bitmask } -> std::same_as<Type>;
}
struct enum_as_bitmask<Type>: std::true_type {};
/**
* @brief Helper variable template.
@@ -23,6 +28,14 @@ struct enum_as_bitmask<Type, std::void_t<decltype(Type::_entt_enum_as_bitmask)>>
template<typename Type>
inline constexpr bool enum_as_bitmask_v = enum_as_bitmask<Type>::value;
/**
* @brief Specifies that an enum class supports bitmask operations.
* @tparam Type Enum class type.
*/
template<typename Type>
// check again that it is an enum to deal with incorrect specializations
concept enum_bitmask = std::is_enum_v<Type> && enum_as_bitmask_v<Type>;
} // namespace entt
/**
@@ -33,23 +46,20 @@ inline constexpr bool enum_as_bitmask_v = enum_as_bitmask<Type>::value;
* @return The result of invoking the operator on the underlying types of the
* two values provided.
*/
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
operator|(const Type lhs, const Type rhs) noexcept {
template<entt::enum_bitmask Type>
[[nodiscard]] constexpr Type operator|(const Type lhs, const Type rhs) noexcept {
return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) | static_cast<std::underlying_type_t<Type>>(rhs));
}
/*! @copydoc operator| */
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
operator&(const Type lhs, const Type rhs) noexcept {
template<entt::enum_bitmask Type>
[[nodiscard]] constexpr Type operator&(const Type lhs, const Type rhs) noexcept {
return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) & static_cast<std::underlying_type_t<Type>>(rhs));
}
/*! @copydoc operator| */
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
operator^(const Type lhs, const Type rhs) noexcept {
template<entt::enum_bitmask Type>
[[nodiscard]] constexpr Type operator^(const Type lhs, const Type rhs) noexcept {
return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) ^ static_cast<std::underlying_type_t<Type>>(rhs));
}
@@ -60,37 +70,32 @@ operator^(const Type lhs, const Type rhs) noexcept {
* @return The result of invoking the operator on the underlying types of the
* value provided.
*/
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
operator~(const Type value) noexcept {
template<entt::enum_bitmask Type>
[[nodiscard]] constexpr Type operator~(const Type value) noexcept {
return static_cast<Type>(~static_cast<std::underlying_type_t<Type>>(value));
}
/*! @copydoc operator~ */
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, bool>
operator!(const Type value) noexcept {
template<entt::enum_bitmask Type>
[[nodiscard]] constexpr bool operator!(const Type value) noexcept {
return !static_cast<std::underlying_type_t<Type>>(value);
}
/*! @copydoc operator| */
template<typename Type>
constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &>
operator|=(Type &lhs, const Type rhs) noexcept {
template<entt::enum_bitmask Type>
constexpr Type &operator|=(Type &lhs, const Type rhs) noexcept {
return (lhs = (lhs | rhs));
}
/*! @copydoc operator| */
template<typename Type>
constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &>
operator&=(Type &lhs, const Type rhs) noexcept {
template<entt::enum_bitmask Type>
constexpr Type &operator&=(Type &lhs, const Type rhs) noexcept {
return (lhs = (lhs & rhs));
}
/*! @copydoc operator| */
template<typename Type>
constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &>
operator^=(Type &lhs, const Type rhs) noexcept {
template<entt::enum_bitmask Type>
constexpr Type &operator^=(Type &lhs, const Type rhs) noexcept {
return (lhs = (lhs ^ rhs));
}

View File

@@ -15,8 +15,10 @@ namespace entt {
*/
template<typename...>
class family {
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
inline static ENTT_MAYBE_ATOMIC(id_type) identifier{};
static auto identifier() noexcept {
static ENTT_MAYBE_ATOMIC(id_type) value{};
return value++;
}
public:
/*! @brief Unsigned integer type. */
@@ -24,8 +26,8 @@ public:
/*! @brief Statically generated unique identifier for the given type. */
template<typename... Type>
// at the time I'm writing, clang crashes during compilation if auto is used instead of family_type
inline static const value_type value = identifier++;
// at the time I'm writing, clang crashes during compilation if auto is used instead of value_type
inline static const value_type value = identifier();
};
} // namespace entt

View File

@@ -9,15 +9,15 @@ namespace entt {
/*! @brief Possible modes of an any object. */
enum class any_policy : std::uint8_t {
/*! @brief Default mode, the object does not own any elements. */
/*! @brief Default mode, no element available. */
empty,
/*! @brief Owning mode, the object owns a dynamically allocated element. */
/*! @brief Owning mode, dynamically allocated element. */
dynamic,
/*! @brief Owning mode, the object owns an embedded element. */
/*! @brief Owning mode, embedded element. */
embedded,
/*! @brief Aliasing mode, the object _points_ to a non-const element. */
/*! @brief Aliasing mode, non-const reference. */
ref,
/*! @brief Const aliasing mode, the object _points_ to a const element. */
/*! @brief Const aliasing mode, const reference. */
cref
};

View File

@@ -3,12 +3,11 @@
#include <cstddef>
#include <cstdint>
#include <string_view>
#include "fwd.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename = id_type>
@@ -32,9 +31,9 @@ struct basic_hashed_string {
using size_type = std::size_t;
using hash_type = id_type;
const value_type *repr;
size_type length;
hash_type hash;
const value_type *repr{};
hash_type hash{fnv_1a_params<>::offset};
size_type length{};
};
} // namespace internal
@@ -62,30 +61,19 @@ class basic_hashed_string: internal::basic_hashed_string<Char> {
struct const_wrapper {
// non-explicit constructor on purpose
constexpr const_wrapper(const Char *str) noexcept
constexpr const_wrapper(const base_type::value_type *str) noexcept
: repr{str} {}
const Char *repr;
const base_type::value_type *repr;
};
// FowlerNollVo hash function v. 1a - the good
[[nodiscard]] static constexpr auto helper(const std::basic_string_view<Char> view) noexcept {
base_type base{view.data(), view.size(), params::offset};
for(auto &&curr: view) {
base.hash = (base.hash ^ static_cast<id_type>(curr)) * params::prime;
}
return base;
}
public:
/*! @brief Character type. */
using value_type = typename base_type::value_type;
using value_type = base_type::value_type;
/*! @brief Unsigned integer type. */
using size_type = typename base_type::size_type;
using size_type = base_type::size_type;
/*! @brief Unsigned integer type. */
using hash_type = typename base_type::hash_type;
using hash_type = base_type::hash_type;
/**
* @brief Returns directly the numeric representation of a string view.
@@ -128,7 +116,14 @@ 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})} {}
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
: base_type{str} {
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic)
for(; base_type::length < len; ++base_type::length) {
base_type::hash = (base_type::hash ^ static_cast<id_type>(str[base_type::length])) * params::prime;
}
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
}
/**
* @brief Constructs a hashed string from an array of const characters.
@@ -138,7 +133,12 @@ public:
template<std::size_t N>
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
ENTT_CONSTEVAL basic_hashed_string(const value_type (&str)[N]) noexcept
: base_type{helper({static_cast<const value_type *>(str)})} {}
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
: base_type{str} {
for(; str[base_type::length]; ++base_type::length) {
base_type::hash = (base_type::hash ^ static_cast<id_type>(str[base_type::length])) * params::prime;
}
}
/**
* @brief Explicit constructor on purpose to avoid constructing a hashed
@@ -150,10 +150,16 @@ 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{wrapper.repr} {
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic)
for(; wrapper.repr[base_type::length]; ++base_type::length) {
base_type::hash = (base_type::hash ^ static_cast<id_type>(wrapper.repr[base_type::length])) * params::prime;
}
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
}
/**
* @brief Returns the size a hashed string.
* @brief Returns the size of a hashed string.
* @return The size of the hashed string.
*/
[[nodiscard]] constexpr size_type size() const noexcept {
@@ -177,7 +183,7 @@ public:
}
/*! @copydoc data */
[[nodiscard]] constexpr operator const value_type *() const noexcept {
[[nodiscard]] explicit constexpr operator const value_type *() const noexcept {
return data();
}
@@ -188,6 +194,24 @@ public:
[[nodiscard]] constexpr operator hash_type() const noexcept {
return value();
}
/**
* @brief Compares two hashed strings.
* @param other A valid hashed string.
* @return True if the two hashed strings are identical, false otherwise.
*/
[[nodiscard]] constexpr bool operator==(const basic_hashed_string &other) const noexcept {
return value() == other.value();
}
/**
* @brief Lexicographically compares two hashed strings.
* @param other A valid hashed string.
* @return The relative order between the two hashed strings.
*/
[[nodiscard]] constexpr auto operator<=>(const basic_hashed_string &other) const noexcept {
return value() <=> other.value();
}
};
/**
@@ -209,81 +233,6 @@ template<typename Char, std::size_t N>
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string<Char>;
/**
* @brief Compares two hashed strings.
* @tparam Char Character type.
* @param lhs A valid hashed string.
* @param rhs A valid hashed string.
* @return True if the two hashed strings are identical, false otherwise.
*/
template<typename Char>
[[nodiscard]] constexpr bool operator==(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept {
return lhs.value() == rhs.value();
}
/**
* @brief Compares two hashed strings.
* @tparam Char Character type.
* @param lhs A valid hashed string.
* @param rhs A valid hashed string.
* @return True if the two hashed strings differ, false otherwise.
*/
template<typename Char>
[[nodiscard]] constexpr bool operator!=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept {
return !(lhs == rhs);
}
/**
* @brief Compares two hashed strings.
* @tparam Char Character type.
* @param lhs A valid hashed string.
* @param rhs A valid hashed string.
* @return True if the first element is less than the second, false otherwise.
*/
template<typename Char>
[[nodiscard]] constexpr bool operator<(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept {
return lhs.value() < rhs.value();
}
/**
* @brief Compares two hashed strings.
* @tparam Char Character type.
* @param lhs A valid hashed string.
* @param rhs A valid hashed string.
* @return True if the first element is less than or equal to the second, false
* otherwise.
*/
template<typename Char>
[[nodiscard]] constexpr bool operator<=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept {
return !(rhs < lhs);
}
/**
* @brief Compares two hashed strings.
* @tparam Char Character type.
* @param lhs A valid hashed string.
* @param rhs A valid hashed string.
* @return True if the first element is greater than the second, false
* otherwise.
*/
template<typename Char>
[[nodiscard]] constexpr bool operator>(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept {
return rhs < lhs;
}
/**
* @brief Compares two hashed strings.
* @tparam Char Character type.
* @param lhs A valid hashed string.
* @param rhs A valid hashed string.
* @return True if the first element is greater than or equal to the second,
* false otherwise.
*/
template<typename Char>
[[nodiscard]] constexpr bool operator>=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept {
return !(lhs < rhs);
}
inline namespace literals {
/**

View File

@@ -16,8 +16,7 @@ namespace entt {
template<typename... Type>
class ident {
template<typename Curr, std::size_t... Index>
[[nodiscard]] static constexpr id_type get(std::index_sequence<Index...>) noexcept {
static_assert((std::is_same_v<Curr, Type> || ...), "Invalid type");
[[nodiscard]] static ENTT_CONSTEVAL id_type get(std::index_sequence<Index...>) noexcept {
return (0 + ... + (std::is_same_v<Curr, type_list_element_t<Index, type_list<std::decay_t<Type>...>>> ? id_type{Index} : id_type{}));
}
@@ -27,7 +26,8 @@ public:
/*! @brief Statically generated unique identifier for the given type. */
template<typename Curr>
static constexpr value_type value = get<std::decay_t<Curr>>(std::index_sequence_for<Type...>{});
requires (std::is_same_v<std::remove_cvref_t<Curr>, Type> || ...)
static constexpr value_type value = get<std::remove_cvref_t<Curr>>(std::index_sequence_for<Type...>{});
};
} // namespace entt

View File

@@ -1,10 +1,12 @@
#ifndef ENTT_CORE_ITERATOR_HPP
#define ENTT_CORE_ITERATOR_HPP
#include <concepts>
#include <iterator>
#include <memory>
#include <type_traits>
#include <utility>
#include "../stl/iterator.hpp"
namespace entt {
@@ -52,11 +54,8 @@ private:
* @brief Plain iota iterator (waiting for C++20).
* @tparam Type Value type.
*/
template<typename Type>
class iota_iterator final {
static_assert(std::is_integral_v<Type>, "Not an integral type");
public:
template<std::integral Type>
struct iota_iterator final {
/*! @brief Value type, likely an integral one. */
using value_type = Type;
/*! @brief Invalid pointer type. */
@@ -104,43 +103,28 @@ public:
return current;
}
/**
* @brief Comparison operator.
* @param other A properly initialized iota iterator.
* @return True if the two iterators are identical, false otherwise.
*/
[[nodiscard]] constexpr bool operator==(const iota_iterator &other) const noexcept {
return current == other.current;
}
private:
value_type current;
};
/**
* @brief Comparison operator.
* @tparam Type Value type of the iota iterator.
* @param lhs A properly initialized iota iterator.
* @param rhs A properly initialized iota iterator.
* @return True if the two iterators are identical, false otherwise.
*/
template<typename Type>
[[nodiscard]] constexpr bool operator==(const iota_iterator<Type> &lhs, const iota_iterator<Type> &rhs) noexcept {
return *lhs == *rhs;
}
/**
* @brief Comparison operator.
* @tparam Type Value type of the iota iterator.
* @param lhs A properly initialized iota iterator.
* @param rhs A properly initialized iota iterator.
* @return True if the two iterators differ, false otherwise.
*/
template<typename Type>
[[nodiscard]] constexpr bool operator!=(const iota_iterator<Type> &lhs, const iota_iterator<Type> &rhs) noexcept {
return !(lhs == rhs);
}
/**
* @brief Utility class to create an iterable object from a pair of iterators.
* @tparam It Type of iterator.
* @tparam Sentinel Type of sentinel.
*/
template<typename It, typename Sentinel = It>
template<stl::input_or_output_iterator It, stl::sentinel_for<It> Sentinel = It>
struct iterable_adaptor final {
/*! @brief Value type. */
using value_type = typename std::iterator_traits<It>::value_type;
using value_type = std::iterator_traits<It>::value_type;
/*! @brief Iterator type. */
using iterator = It;
/*! @brief Sentinel type. */

View File

@@ -7,24 +7,10 @@
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../stl/memory.hpp"
namespace entt {
/**
* @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20).
* @tparam Type Pointer type.
* @param ptr Fancy or raw pointer.
* @return A raw pointer that represents the address of the original pointer.
*/
template<typename Type>
[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept {
if constexpr(std::is_pointer_v<std::decay_t<Type>>) {
return ptr;
} else {
return to_address(std::forward<Type>(ptr).operator->());
}
}
/**
* @brief Utility function to design allocation-aware containers.
* @tparam Allocator Type of allocator.
@@ -76,7 +62,7 @@ struct allocation_deleter: private Allocator {
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Pointer type. */
using pointer = typename std::allocator_traits<Allocator>::pointer;
using pointer = std::allocator_traits<Allocator>::pointer;
/**
* @brief Inherited constructors.
@@ -91,7 +77,7 @@ struct allocation_deleter: private Allocator {
*/
constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v<typename allocator_type::value_type>) {
using alloc_traits = std::allocator_traits<Allocator>;
alloc_traits::destroy(*this, to_address(ptr));
alloc_traits::destroy(*this, stl::to_address(ptr));
alloc_traits::deallocate(*this, ptr, 1u);
}
};
@@ -106,17 +92,17 @@ struct allocation_deleter: private Allocator {
* @return A properly initialized unique pointer with a custom deleter.
*/
template<typename Type, typename Allocator, typename... Args>
ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) {
constexpr auto allocate_unique(Allocator &allocator, Args &&...args) {
static_assert(!std::is_array_v<Type>, "Array types are not supported");
using alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<Type>;
using allocator_type = typename alloc_traits::allocator_type;
using alloc_traits = std::allocator_traits<Allocator>::template rebind_traits<Type>;
using allocator_type = alloc_traits::allocator_type;
allocator_type alloc{allocator};
auto ptr = alloc_traits::allocate(alloc, 1u);
ENTT_TRY {
alloc_traits::construct(alloc, to_address(ptr), std::forward<Args>(args)...);
alloc_traits::construct(alloc, stl::to_address(ptr), std::forward<Args>(args)...);
}
ENTT_CATCH {
alloc_traits::deallocate(alloc, ptr, 1u);
@@ -126,7 +112,7 @@ ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) {
return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc};
}
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename Type>
@@ -152,31 +138,30 @@ template<typename Type, typename Other>
struct uses_allocator_construction<std::pair<Type, Other>> {
using type = std::pair<Type, Other>;
template<typename Allocator, typename First, typename Second>
static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept {
template<typename First, typename Second>
static constexpr auto args(const auto &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept {
return std::make_tuple(
std::piecewise_construct,
std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Type>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<First>(first)),
std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Other>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<Second>(second)));
}
template<typename Allocator>
static constexpr auto args(const Allocator &allocator) noexcept {
static constexpr auto args(const auto &allocator) noexcept {
return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{});
}
template<typename Allocator, typename First, typename Second>
static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept {
template<typename First, typename Second>
static constexpr auto args(const auto &allocator, First &&first, Second &&second) noexcept {
return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward<First>(first)), std::forward_as_tuple(std::forward<Second>(second)));
}
template<typename Allocator, typename First, typename Second>
static constexpr auto args(const Allocator &allocator, const std::pair<First, Second> &value) noexcept {
template<typename First, typename Second>
static constexpr auto args(const auto &allocator, const std::pair<First, Second> &value) noexcept {
return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second));
}
template<typename Allocator, typename First, typename Second>
static constexpr auto args(const Allocator &allocator, std::pair<First, Second> &&value) noexcept {
template<typename First, typename Second>
static constexpr auto args(const auto &allocator, std::pair<First, Second> &&value) noexcept {
return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second)));
}
};
@@ -191,14 +176,13 @@ struct uses_allocator_construction<std::pair<Type, Other>> {
* create an object of a given type by means of uses-allocator construction.
*
* @tparam Type Type to return arguments for.
* @tparam Allocator Type of allocator used to manage memory and elements.
* @tparam Args Types of arguments to use to construct the object.
* @param allocator The allocator to use.
* @param args Parameters to use to construct the object.
* @return The arguments needed to create an object of the given type.
*/
template<typename Type, typename Allocator, typename... Args>
constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept {
template<typename Type, typename... Args>
constexpr auto uses_allocator_construction_args(const auto &allocator, Args &&...args) noexcept {
return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...);
}
@@ -209,14 +193,13 @@ constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args
* means of uses-allocator construction.
*
* @tparam Type Type of object to create.
* @tparam Allocator Type of allocator used to manage memory and elements.
* @tparam Args Types of arguments to use to construct the object.
* @param allocator The allocator to use.
* @param args Parameters to use to construct the object.
* @return A newly created object of the given type.
*/
template<typename Type, typename Allocator, typename... Args>
constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) {
template<typename Type, typename... Args>
constexpr Type make_obj_using_allocator(const auto &allocator, Args &&...args) {
return std::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
}
@@ -227,15 +210,14 @@ constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...ar
* means of uses-allocator construction at an uninitialized memory location.
*
* @tparam Type Type of object to create.
* @tparam Allocator Type of allocator used to manage memory and elements.
* @tparam Args Types of arguments to use to construct the object.
* @param value Memory location in which to place the object.
* @param allocator The allocator to use.
* @param args Parameters to use to construct the object.
* @return A pointer to the newly created object of the given type.
*/
template<typename Type, typename Allocator, typename... Args>
constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) {
template<typename Type, typename... Args>
constexpr Type *uninitialized_construct_using_allocator(Type *value, const auto &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)...));
}

View File

@@ -83,7 +83,7 @@ struct forward_apply: private Func {
* @tparam Func Type of underlying invocable object.
*/
template<typename Func>
forward_apply(Func) -> forward_apply<std::remove_reference_t<std::remove_cv_t<Func>>>;
forward_apply(Func) -> forward_apply<std::remove_cvref_t<Func>>;
} // namespace entt

View File

@@ -1,17 +1,17 @@
#ifndef ENTT_CORE_TYPE_INFO_HPP
#define ENTT_CORE_TYPE_INFO_HPP
#include <compare>
#include <string_view>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/attribute.h"
#include "fwd.hpp"
#include "hashed_string.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
struct ENTT_API type_index final {
@@ -21,20 +21,29 @@ struct ENTT_API type_index final {
}
};
template<typename Type>
[[nodiscard]] constexpr const char *pretty_function() noexcept {
#if defined ENTT_PRETTY_FUNCTION
return static_cast<const char *>(ENTT_PRETTY_FUNCTION);
#else
return "";
#endif
}
template<typename Type>
[[nodiscard]] constexpr auto stripped_type_name() noexcept {
#if defined ENTT_PRETTY_FUNCTION
const 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);
const std::string_view full_name{pretty_function<Type>()};
auto first = full_name.find_first_not_of(' ', full_name.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1);
auto value = full_name.substr(first, full_name.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first);
return value;
#else
return std::string_view{""};
return std::string_view{};
#endif
}
template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
[[nodiscard]] constexpr std::string_view type_name(int) noexcept {
[[nodiscard]] ENTT_CONSTEVAL std::string_view type_name(int) noexcept {
constexpr auto value = stripped_type_name<Type>();
return value;
}
@@ -46,7 +55,7 @@ template<typename Type>
}
template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
[[nodiscard]] constexpr id_type type_hash(int) noexcept {
[[nodiscard]] ENTT_CONSTEVAL id_type type_hash(int) noexcept {
constexpr auto stripped = stripped_type_name<Type>();
constexpr auto value = hashed_string::value(stripped.data(), stripped.size());
return value;
@@ -67,7 +76,7 @@ template<typename Type>
* @brief Type sequential identifier.
* @tparam Type Type for which to generate a sequential identifier.
*/
template<typename Type, typename = void>
template<typename Type>
struct ENTT_API type_index final {
/**
* @brief Returns the sequential identifier of a given type.
@@ -88,7 +97,7 @@ struct ENTT_API type_index final {
* @brief Type hash.
* @tparam Type Type for which to generate a hash value.
*/
template<typename Type, typename = void>
template<typename Type>
struct type_hash final {
/**
* @brief Returns the numeric representation of a given type.
@@ -113,7 +122,7 @@ struct type_hash final {
* @brief Type name.
* @tparam Type Type for which to generate a name.
*/
template<typename Type, typename = void>
template<typename Type>
struct type_name final {
/**
* @brief Returns the name of a given type.
@@ -138,9 +147,9 @@ struct type_info final {
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()} {}
: seq{type_index<std::remove_cvref_t<Type>>::value()},
identifier{type_hash<std::remove_cvref_t<Type>>::value()},
alias{type_name<std::remove_cvref_t<Type>>::value()} {}
// NOLINTEND(modernize-use-transparent-functors)
/**
@@ -167,75 +176,30 @@ struct type_info final {
return alias;
}
/**
* @brief Compares two type info objects.
* @param other A type info object.
* @return True if the two type info objects are identical, false otherwise.
*/
[[nodiscard]] constexpr bool operator==(const type_info &other) const noexcept {
return identifier == other.identifier;
}
/**
* @brief Lexicographically compares two type info objects.
* @param other A type info object.
* @return The relative order between the two type info objects.
*/
[[nodiscard]] constexpr auto operator<=>(const type_info &other) const noexcept {
return seq <=> other.seq;
}
private:
id_type seq;
id_type identifier;
std::string_view alias;
};
/**
* @brief Compares the contents of two type info objects.
* @param lhs A type info object.
* @param rhs A type info object.
* @return True if the two type info objects are identical, false otherwise.
*/
[[nodiscard]] constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept {
return lhs.hash() == rhs.hash();
}
/**
* @brief Compares the contents of two type info objects.
* @param lhs A type info object.
* @param rhs A type info object.
* @return True if the two type info objects differ, false otherwise.
*/
[[nodiscard]] constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept {
return !(lhs == rhs);
}
/**
* @brief Compares two type info objects.
* @param lhs A valid type info object.
* @param rhs A valid type info object.
* @return True if the first element is less than the second, false otherwise.
*/
[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept {
return lhs.index() < rhs.index();
}
/**
* @brief Compares two type info objects.
* @param lhs A valid type info object.
* @param rhs A valid type info object.
* @return True if the first element is less than or equal to the second, false
* otherwise.
*/
[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept {
return !(rhs < lhs);
}
/**
* @brief Compares two type info objects.
* @param lhs A valid type info object.
* @param rhs A valid type info object.
* @return True if the first element is greater than the second, false
* otherwise.
*/
[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept {
return rhs < lhs;
}
/**
* @brief Compares two type info objects.
* @param lhs A valid type info object.
* @param rhs A valid type info object.
* @return True if the first element is greater than or equal to the second,
* false otherwise.
*/
[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept {
return !(lhs < rhs);
}
/**
* @brief Returns the type info object associated to a given type.
*
@@ -249,19 +213,18 @@ private:
*/
template<typename Type>
[[nodiscard]] const type_info &type_id() noexcept {
if constexpr(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>) {
if constexpr(std::is_same_v<Type, std::remove_cvref_t<Type>>) {
static const type_info instance{std::in_place_type<Type>};
return instance;
} else {
return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
return type_id<std::remove_cvref_t<Type>>();
}
}
/*! @copydoc type_id */
template<typename Type>
// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
[[nodiscard]] const type_info &type_id(Type &&) noexcept {
return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
[[nodiscard]] const type_info &type_id(const Type &) noexcept {
return type_id<std::remove_cvref_t<Type>>();
}
} // namespace entt

View File

@@ -1,6 +1,7 @@
#ifndef ENTT_CORE_TYPE_TRAITS_HPP
#define ENTT_CORE_TYPE_TRAITS_HPP
#include <concepts>
#include <cstddef>
#include <iterator>
#include <tuple>
@@ -18,7 +19,7 @@ namespace entt {
template<std::size_t N>
struct choice_t
// unfortunately, doxygen cannot parse such a construct
: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
: /*! @cond ENTT_INTERNAL */ choice_t<N - 1> /*! @endcond */
{};
/*! @copybrief choice_t */
@@ -32,37 +33,17 @@ struct choice_t<0> {};
template<std::size_t N>
inline constexpr choice_t<N> choice{};
/**
* @brief Identity type trait.
*
* Useful to establish non-deduced contexts in template argument deduction
* (waiting for C++20) or to provide types through function arguments.
*
* @tparam Type A type.
*/
template<typename Type>
struct type_identity {
/*! @brief Identity type. */
using type = Type;
};
/**
* @brief Helper type.
* @tparam Type A type.
*/
template<typename Type>
using type_identity_t = typename type_identity<Type>::type;
/**
* @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
* @tparam Type The type of which to return the size.
*/
template<typename Type, typename = void>
template<typename Type>
struct size_of: std::integral_constant<std::size_t, 0u> {};
/*! @copydoc size_of */
template<typename Type>
struct size_of<Type, std::void_t<decltype(sizeof(Type))>>
requires requires { sizeof(Type); }
struct size_of<Type>
// NOLINTNEXTLINE(bugprone-sizeof-expression)
: std::integral_constant<std::size_t, sizeof(Type)> {};
@@ -146,7 +127,7 @@ struct type_list_element<0u, type_list<First, Other...>> {
* @tparam List Type list to search into.
*/
template<std::size_t Index, typename List>
using type_list_element_t = typename type_list_element<Index, List>::type;
using type_list_element_t = type_list_element<Index, List>::type;
/*! @brief Primary template isn't defined on purpose. */
template<typename, typename>
@@ -207,7 +188,7 @@ inline constexpr std::size_t type_list_index_v = type_list_index<Type, List>::va
* @return A type list composed by the types of both the type lists.
*/
template<typename... Type, typename... Other>
constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) {
ENTT_CONSTEVAL type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) {
return {};
}
@@ -231,7 +212,7 @@ struct type_list_cat<> {
template<typename... Type, typename... Other, typename... List>
struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> {
/*! @brief A type list composed by the types of all the type lists. */
using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type;
using type = type_list_cat<type_list<Type..., Other...>, List...>::type;
};
/**
@@ -249,9 +230,9 @@ struct type_list_cat<type_list<Type...>> {
* @tparam List Type lists to concatenate.
*/
template<typename... List>
using type_list_cat_t = typename type_list_cat<List...>::type;
using type_list_cat_t = type_list_cat<List...>::type;
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename...>
@@ -276,7 +257,7 @@ struct type_list_unique<type_list<>, Type...> {
template<typename List>
struct type_list_unique {
/*! @brief A type list without duplicate types. */
using type = typename internal::type_list_unique<List>::type;
using type = internal::type_list_unique<List>::type;
};
/**
@@ -284,7 +265,7 @@ struct type_list_unique {
* @tparam List Type list.
*/
template<typename List>
using type_list_unique_t = typename type_list_unique<List>::type;
using type_list_unique_t = type_list_unique<List>::type;
/**
* @brief Provides the member constant `value` to true if a type list contains a
@@ -332,7 +313,7 @@ struct type_list_diff<type_list<Type...>, type_list<Other...>> {
* @tparam List Type lists between which to compute the difference.
*/
template<typename... List>
using type_list_diff_t = typename type_list_diff<List...>::type;
using type_list_diff_t = type_list_diff<List...>::type;
/*! @brief Primary template isn't defined on purpose. */
template<typename, template<typename...> class>
@@ -356,7 +337,7 @@ struct type_list_transform<type_list<Type...>, Op> {
* @tparam Op Unary operation as template class with a type member named `type`.
*/
template<typename List, template<typename...> class Op>
using type_list_transform_t = typename type_list_transform<List, Op>::type;
using type_list_transform_t = type_list_transform<List, Op>::type;
/**
* @brief A class to use to push around lists of constant values, nothing more.
@@ -403,7 +384,7 @@ struct value_list_element<0u, value_list<Value, Other...>> {
* @tparam List Value list to search into.
*/
template<std::size_t Index, typename List>
using value_list_element_t = typename value_list_element<Index, List>::type;
using value_list_element_t = value_list_element<Index, List>::type;
/**
* @brief Helper type.
@@ -472,7 +453,7 @@ inline constexpr std::size_t value_list_index_v = value_list_index<Value, List>:
* @return A value list composed by the values of both the value lists.
*/
template<auto... Value, auto... Other>
constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) {
ENTT_CONSTEVAL value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) {
return {};
}
@@ -496,7 +477,7 @@ struct value_list_cat<> {
template<auto... Value, auto... Other, typename... List>
struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> {
/*! @brief A value list composed by the values of all the value lists. */
using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type;
using type = value_list_cat<value_list<Value..., Other...>, List...>::type;
};
/**
@@ -514,7 +495,7 @@ struct value_list_cat<value_list<Value...>> {
* @tparam List Value lists to concatenate.
*/
template<typename... List>
using value_list_cat_t = typename value_list_cat<List...>::type;
using value_list_cat_t = value_list_cat<List...>::type;
/*! @brief Primary template isn't defined on purpose. */
template<typename>
@@ -546,7 +527,7 @@ struct value_list_unique<value_list<>> {
* @tparam Type A value list.
*/
template<typename Type>
using value_list_unique_t = typename value_list_unique<Type>::type;
using value_list_unique_t = value_list_unique<Type>::type;
/**
* @brief Provides the member constant `value` to true if a value list contains
@@ -594,7 +575,7 @@ struct value_list_diff<value_list<Value...>, value_list<Other...>> {
* @tparam List Value lists between which to compute the difference.
*/
template<typename... List>
using value_list_diff_t = typename value_list_diff<List...>::type;
using value_list_diff_t = value_list_diff<List...>::type;
/*! @brief Same as std::is_invocable, but with tuples. */
template<typename, typename>
@@ -655,12 +636,13 @@ inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::valu
* complete, false otherwise.
* @tparam Type The type to test.
*/
template<typename Type, typename = void>
template<typename Type>
struct is_complete: std::false_type {};
/*! @copydoc is_complete */
template<typename Type>
struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {};
requires requires { sizeof(Type); }
struct is_complete<Type>: std::true_type {};
/**
* @brief Helper variable template.
@@ -674,25 +656,26 @@ inline constexpr bool is_complete_v = is_complete<Type>::value;
* iterator, false otherwise.
* @tparam Type The type to test.
*/
template<typename Type, typename = void>
template<typename Type>
struct is_iterator: std::false_type {};
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename, typename = void>
template<typename>
struct has_iterator_category: std::false_type {};
template<typename Type>
struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
requires requires { typename std::iterator_traits<Type>::iterator_category; }
struct has_iterator_category<Type>: std::true_type {};
} // namespace internal
/*! @endcond */
/*! @copydoc is_iterator */
template<typename Type>
struct is_iterator<Type, std::enable_if_t<!std::is_void_v<std::remove_cv_t<std::remove_pointer_t<Type>>>>>
: internal::has_iterator_category<Type> {};
requires (!std::is_void_v<std::remove_const_t<std::remove_pointer_t<Type>>>)
struct is_iterator<Type>: internal::has_iterator_category<Type> {};
/**
* @brief Helper variable template.
@@ -707,8 +690,7 @@ inline constexpr bool is_iterator_v = is_iterator<Type>::value;
* @tparam Type The type to test
*/
template<typename Type>
struct is_ebco_eligible
: std::bool_constant<std::is_empty_v<Type> && !std::is_final_v<Type>> {};
struct is_ebco_eligible: std::bool_constant<std::is_empty_v<Type> && !std::is_final_v<Type>> {};
/**
* @brief Helper variable template.
@@ -722,12 +704,13 @@ inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value;
* is valid and denotes a type, false otherwise.
* @tparam Type The type to test.
*/
template<typename Type, typename = void>
template<typename Type>
struct is_transparent: std::false_type {};
/*! @copydoc is_transparent */
template<typename Type>
struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {};
requires requires { typename Type::is_transparent; }
struct is_transparent<Type>: std::true_type {};
/**
* @brief Helper variable template.
@@ -736,45 +719,47 @@ struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::tr
template<typename Type>
inline constexpr bool is_transparent_v = is_transparent<Type>::value;
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename, typename = void>
template<typename>
struct has_tuple_size_value: std::false_type {};
template<typename Type>
struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
requires is_complete_v<std::tuple_size<const Type>>
struct has_tuple_size_value<Type>: std::true_type {};
template<typename, typename = void>
template<typename>
struct has_value_type: std::false_type {};
template<typename Type>
struct has_value_type<Type, std::void_t<typename Type::value_type>>: std::true_type {};
requires requires { typename Type::value_type; }
struct has_value_type<Type>: std::true_type {};
template<typename>
[[nodiscard]] constexpr bool dispatch_is_equality_comparable();
[[nodiscard]] ENTT_CONSTEVAL bool dispatch_is_equality_comparable();
template<typename Type, std::size_t... Index>
[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
[[nodiscard]] ENTT_CONSTEVAL bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
return (dispatch_is_equality_comparable<std::tuple_element_t<Index, Type>>() && ...);
}
template<typename>
[[nodiscard]] constexpr bool maybe_equality_comparable(char) {
[[nodiscard]] ENTT_CONSTEVAL bool maybe_equality_comparable(char) {
return false;
}
template<typename Type>
[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval<Type>() == std::declval<Type>()) {
[[nodiscard]] ENTT_CONSTEVAL auto maybe_equality_comparable(int) -> decltype(std::declval<Type>() == std::declval<Type>()) {
return true;
}
template<typename Type>
[[nodiscard]] constexpr bool dispatch_is_equality_comparable() {
[[nodiscard]] ENTT_CONSTEVAL bool dispatch_is_equality_comparable() {
// NOLINTBEGIN(modernize-use-transparent-functors)
if constexpr(std::is_array_v<Type>) {
return false;
} else if constexpr(is_complete_v<std::tuple_size<std::remove_cv_t<Type>>>) {
} else if constexpr(is_complete_v<std::tuple_size<std::remove_const_t<Type>>>) {
if constexpr(has_tuple_size_value<Type>::value) {
return maybe_equality_comparable<Type>(0) && unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
} else {
@@ -838,7 +823,7 @@ struct constness_as<To, const From> {
* @tparam From The type from which to transcribe the constness.
*/
template<typename To, typename From>
using constness_as_t = typename constness_as<To, From>::type;
using constness_as_t = constness_as<To, From>::type;
/**
* @brief Extracts the class of a non-static member object or function.
@@ -867,7 +852,7 @@ public:
* @tparam Member A pointer to a non-static member object or function.
*/
template<typename Member>
using member_class_t = typename member_class<Member>::type;
using member_class_t = member_class<Member>::type;
/**
* @brief Extracts the n-th argument of a _callable_ type.
@@ -877,19 +862,19 @@ using member_class_t = typename member_class<Member>::type;
template<std::size_t Index, typename Candidate>
class nth_argument {
template<typename Ret, typename... Args>
static constexpr type_list<Args...> pick_up(Ret (*)(Args...));
static ENTT_CONSTEVAL type_list<Args...> pick_up(Ret (*)(Args...));
template<typename Ret, typename Class, typename... Args>
static constexpr type_list<Args...> pick_up(Ret (Class ::*)(Args...));
static ENTT_CONSTEVAL type_list<Args...> pick_up(Ret (Class ::*)(Args...));
template<typename Ret, typename Class, typename... Args>
static constexpr type_list<Args...> pick_up(Ret (Class ::*)(Args...) const);
static ENTT_CONSTEVAL type_list<Args...> pick_up(Ret (Class ::*)(Args...) const);
template<typename Type, typename Class>
static constexpr type_list<Type> pick_up(Type Class ::*);
static ENTT_CONSTEVAL type_list<Type> pick_up(Type Class ::*);
template<typename Type>
static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&);
static ENTT_CONSTEVAL decltype(pick_up(&Type::operator())) pick_up(Type &&);
public:
/*! @brief N-th argument of the _callable_ type. */
@@ -902,7 +887,7 @@ public:
* @tparam Candidate A valid function, member function or data member type.
*/
template<std::size_t Index, typename Candidate>
using nth_argument_t = typename nth_argument<Index, Candidate>::type;
using nth_argument_t = nth_argument<Index, Candidate>::type;
} // namespace entt

View File

@@ -6,23 +6,6 @@
namespace entt {
/*! @brief Identity function object (waiting for C++20). */
struct identity {
/*! @brief Indicates that this is a transparent function object. */
using is_transparent = void;
/**
* @brief Returns its argument unchanged.
* @tparam Type Type of the argument.
* @param value The actual argument.
* @return The submitted value as-is.
*/
template<typename Type>
[[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept {
return std::forward<Type>(value);
}
};
/**
* @brief Constant utility to disambiguate overloaded members of a class.
* @tparam Type Type of the desired overload.

View File

@@ -1,35 +1,37 @@
#ifndef ENTT_ENTITY_COMPONENT_HPP
#define ENTT_ENTITY_COMPONENT_HPP
#include <concepts>
#include <cstddef>
#include <type_traits>
#include "../config/config.h"
#include "../core/concepts.hpp"
#include "fwd.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename Type, typename = void>
template<typename Type>
struct in_place_delete: std::bool_constant<!(std::is_move_constructible_v<Type> && std::is_move_assignable_v<Type>)> {};
template<>
struct in_place_delete<void>: std::false_type {};
template<typename Type>
struct in_place_delete<Type, std::enable_if_t<Type::in_place_delete>>
: std::true_type {};
requires Type::in_place_delete
struct in_place_delete<Type>: std::true_type {};
template<typename Type, typename = void>
template<typename Type>
struct page_size: std::integral_constant<std::size_t, !std::is_empty_v<ENTT_ETO_TYPE(Type)> * ENTT_PACKED_PAGE> {};
template<>
struct page_size<void>: std::integral_constant<std::size_t, 0u> {};
template<typename Type>
struct page_size<Type, std::void_t<decltype(Type::page_size)>>
: std::integral_constant<std::size_t, Type::page_size> {};
requires std::is_convertible_v<decltype(Type::page_size), std::size_t>
struct page_size<Type>: std::integral_constant<std::size_t, Type::page_size> {};
} // namespace internal
/*! @endcond */
@@ -39,10 +41,8 @@ struct page_size<Type, std::void_t<decltype(Type::page_size)>>
* @tparam Type Element type.
* @tparam Entity A valid entity type.
*/
template<typename Type, typename Entity, typename>
template<cvref_unqualified Type, typename Entity>
struct component_traits {
static_assert(std::is_same_v<std::decay_t<Type>, Type>, "Unsupported type");
/*! @brief Element type. */
using element_type = Type;
/*! @brief Underlying entity identifier. */

View File

@@ -1,6 +1,8 @@
#ifndef ENTT_ENTITY_ENTITY_HPP
#define ENTT_ENTITY_ENTITY_HPP
#include <bit>
#include <concepts>
#include <cstddef>
#include <cstdint>
#include <type_traits>
@@ -10,20 +12,24 @@
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename, typename = void>
template<typename>
struct entt_traits;
template<typename Type>
struct entt_traits<Type, std::enable_if_t<std::is_enum_v<Type>>>
: entt_traits<std::underlying_type_t<Type>> {
requires requires {
requires std::is_enum_v<Type>;
typename internal::entt_traits<std::underlying_type_t<Type>>::value_type;
}
struct entt_traits<Type>: entt_traits<std::underlying_type_t<Type>> {
using value_type = Type;
};
template<typename Type>
struct entt_traits<Type, std::enable_if_t<std::is_class_v<Type>>>
requires requires { typename Type::entity_type; }
struct entt_traits<Type>
: entt_traits<typename Type::entity_type> {
using value_type = Type;
};
@@ -53,24 +59,33 @@ struct entt_traits<std::uint64_t> {
} // namespace internal
/*! @endcond */
/**
* @brief Specifies that a type is an entity-like type.
* @tparam Type Type to check.
*/
template<typename Type>
concept entity_like = requires {
typename internal::entt_traits<Type>::value_type;
};
/**
* @brief Common basic entity traits implementation.
* @tparam Traits Actual entity traits to use.
*/
template<typename Traits>
class basic_entt_traits {
static constexpr auto length = popcount(Traits::entity_mask);
static constexpr auto length = std::popcount(Traits::entity_mask);
static_assert(Traits::entity_mask && ((Traits::entity_mask & (Traits::entity_mask + 1)) == 0), "Invalid entity mask");
static_assert((Traits::version_mask & (Traits::version_mask + 1)) == 0, "Invalid version mask");
public:
/*! @brief Value type. */
using value_type = typename Traits::value_type;
using value_type = Traits::value_type;
/*! @brief Underlying entity type. */
using entity_type = typename Traits::entity_type;
using entity_type = Traits::entity_type;
/*! @brief Underlying version type. */
using version_type = typename Traits::version_type;
using version_type = Traits::version_type;
/*! @brief Entity mask size. */
static constexpr entity_type entity_mask = Traits::entity_mask;
@@ -159,7 +174,7 @@ public:
* @brief Entity traits.
* @tparam Type Type of identifier.
*/
template<typename Type>
template<entity_like Type>
struct entt_traits: basic_entt_traits<internal::entt_traits<Type>> {
/*! @brief Base type. */
using base_type = basic_entt_traits<internal::entt_traits<Type>>;
@@ -174,7 +189,7 @@ struct entt_traits: basic_entt_traits<internal::entt_traits<Type>> {
* @return The integral representation of the given value.
*/
template<typename Entity>
[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_integral(const Entity value) noexcept {
[[nodiscard]] constexpr entt_traits<Entity>::entity_type to_integral(const Entity value) noexcept {
return entt_traits<Entity>::to_integral(value);
}
@@ -185,7 +200,7 @@ template<typename Entity>
* @return The integral representation of the entity part.
*/
template<typename Entity>
[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_entity(const Entity value) noexcept {
[[nodiscard]] constexpr entt_traits<Entity>::entity_type to_entity(const Entity value) noexcept {
return entt_traits<Entity>::to_entity(value);
}
@@ -196,7 +211,7 @@ template<typename Entity>
* @return The integral representation of the version part.
*/
template<typename Entity>
[[nodiscard]] constexpr typename entt_traits<Entity>::version_type to_version(const Entity value) noexcept {
[[nodiscard]] constexpr entt_traits<Entity>::version_type to_version(const Entity value) noexcept {
return entt_traits<Entity>::to_version(value);
}
@@ -207,11 +222,10 @@ struct null_t {
* @tparam Entity Type of identifier.
* @return The null representation for the given type.
*/
template<typename Entity>
template<entity_like Entity>
[[nodiscard]] constexpr operator Entity() const noexcept {
using traits_type = entt_traits<Entity>;
constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask);
return value;
return traits_type::construct(traits_type::entity_mask, traits_type::version_mask);
}
/**
@@ -223,63 +237,19 @@ struct null_t {
return true;
}
/**
* @brief Compares two null objects.
* @param other A null object.
* @return False in all cases.
*/
[[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const noexcept {
return false;
}
/**
* @brief Compares a null object and an identifier of any type.
* @tparam Entity Type of identifier.
* @param entity Identifier with which to compare.
* @return False if the two elements differ, true otherwise.
*/
template<typename Entity>
template<entity_like Entity>
[[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept {
using traits_type = entt_traits<Entity>;
return traits_type::to_entity(entity) == traits_type::to_entity(*this);
}
/**
* @brief Compares a null object and an identifier of any type.
* @tparam Entity Type of identifier.
* @param entity Identifier with which to compare.
* @return True if the two elements differ, false otherwise.
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept {
return !(entity == *this);
}
};
/**
* @brief Compares a null object and an identifier of any type.
* @tparam Entity Type of identifier.
* @param lhs Identifier with which to compare.
* @param rhs A null object yet to be converted.
* @return False if the two elements differ, true otherwise.
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator==(const Entity lhs, const null_t rhs) noexcept {
return rhs.operator==(lhs);
}
/**
* @brief Compares a null object and an identifier of any type.
* @tparam Entity Type of identifier.
* @param lhs Identifier with which to compare.
* @param rhs A null object yet to be converted.
* @return True if the two elements differ, false otherwise.
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator!=(const Entity lhs, const null_t rhs) noexcept {
return !(rhs == lhs);
}
/*! @brief Tombstone object for all identifiers. */
struct tombstone_t {
/**
@@ -287,11 +257,10 @@ struct tombstone_t {
* @tparam Entity Type of identifier.
* @return The tombstone representation for the given type.
*/
template<typename Entity>
template<entity_like Entity>
[[nodiscard]] constexpr operator Entity() const noexcept {
using traits_type = entt_traits<Entity>;
constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask);
return value;
return traits_type::construct(traits_type::entity_mask, traits_type::version_mask);
}
/**
@@ -303,22 +272,13 @@ struct tombstone_t {
return true;
}
/**
* @brief Compares two tombstone objects.
* @param other A tombstone object.
* @return False in all cases.
*/
[[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const noexcept {
return false;
}
/**
* @brief Compares a tombstone object and an identifier of any type.
* @tparam Entity Type of identifier.
* @param entity Identifier with which to compare.
* @return False if the two elements differ, true otherwise.
*/
template<typename Entity>
template<entity_like Entity>
[[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept {
using traits_type = entt_traits<Entity>;
@@ -328,43 +288,8 @@ struct tombstone_t {
return (traits_type::to_version(entity) == traits_type::to_version(*this));
}
}
/**
* @brief Compares a tombstone object and an identifier of any type.
* @tparam Entity Type of identifier.
* @param entity Identifier with which to compare.
* @return True if the two elements differ, false otherwise.
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept {
return !(entity == *this);
}
};
/**
* @brief Compares a tombstone object and an identifier of any type.
* @tparam Entity Type of identifier.
* @param lhs Identifier with which to compare.
* @param rhs A tombstone object yet to be converted.
* @return False if the two elements differ, true otherwise.
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator==(const Entity lhs, const tombstone_t rhs) noexcept {
return rhs.operator==(lhs);
}
/**
* @brief Compares a tombstone object and an identifier of any type.
* @tparam Entity Type of identifier.
* @param lhs Identifier with which to compare.
* @param rhs A tombstone object yet to be converted.
* @return True if the two elements differ, false otherwise.
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator!=(const Entity lhs, const tombstone_t rhs) noexcept {
return !(rhs == lhs);
}
/**
* @brief Compile-time constant for null entities.
*

View File

@@ -5,6 +5,7 @@
#include <memory>
#include <type_traits>
#include "../config/config.h"
#include "../core/concepts.hpp"
#include "../core/fwd.hpp"
#include "../core/type_traits.hpp"
@@ -25,13 +26,13 @@ enum class deletion_policy : std::uint8_t {
unspecified = swap_and_pop
};
template<typename Type, typename Entity = entity, typename = void>
template<cvref_unqualified Type, typename Entity = entity>
struct component_traits;
template<typename Entity = entity, typename = std::allocator<Entity>>
class basic_sparse_set;
template<typename Type, typename = entity, typename = std::allocator<Type>, typename = void>
template<typename Type, typename = entity, typename = std::allocator<Type>>
class basic_storage;
template<typename, typename>
@@ -43,7 +44,7 @@ class basic_reactive_mixin;
template<typename Entity = entity, typename = std::allocator<Entity>>
class basic_registry;
template<typename, typename, typename = void>
template<typename, typename>
class basic_view;
template<typename Type, typename = std::allocator<Type *>>
@@ -139,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() = default;
explicit ENTT_CONSTEVAL exclude_t() = default;
};
/**
@@ -156,7 +157,7 @@ inline constexpr exclude_t<Type...> exclude{};
template<typename... Type>
struct get_t final: type_list<Type...> {
/*! @brief Default constructor. */
explicit constexpr get_t() = default;
explicit ENTT_CONSTEVAL get_t() = default;
};
/**
@@ -173,7 +174,7 @@ inline constexpr get_t<Type...> get{};
template<typename... Type>
struct owned_t final: type_list<Type...> {
/*! @brief Default constructor. */
explicit constexpr owned_t() = default;
explicit ENTT_CONSTEVAL owned_t() = default;
};
/**
@@ -222,7 +223,7 @@ struct type_list_transform<owned_t<Type...>, Op> {
* @tparam Entity A valid entity type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Type, typename Entity = entity, typename Allocator = std::allocator<Type>, typename = void>
template<typename Type, typename Entity = entity, typename Allocator = std::allocator<Type>>
struct storage_type {
/*! @brief Type-to-storage conversion result. */
using type = ENTT_STORAGE(sigh_mixin, basic_storage<Type, Entity, Allocator>);
@@ -247,7 +248,7 @@ struct storage_type<reactive, Entity, Allocator> {
* @tparam Args Arguments to forward.
*/
template<typename... Args>
using storage_type_t = typename storage_type<Args...>::type;
using storage_type_t = storage_type<Args...>::type;
/**
* Type-to-storage conversion utility that preserves constness.
@@ -266,7 +267,7 @@ struct storage_for {
* @tparam Args Arguments to forward.
*/
template<typename... Args>
using storage_for_t = typename storage_for<Args...>::type;
using storage_for_t = storage_for<Args...>::type;
/**
* @brief Alias declaration for the most common use case.

View File

@@ -2,6 +2,7 @@
#define ENTT_ENTITY_GROUP_HPP
#include <array>
#include <concepts>
#include <cstddef>
#include <iterator>
#include <tuple>
@@ -13,12 +14,13 @@
#include "../core/iterator.hpp"
#include "../core/type_info.hpp"
#include "../core/type_traits.hpp"
#include "../stl/iterator.hpp"
#include "entity.hpp"
#include "fwd.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename, typename, typename>
@@ -73,24 +75,16 @@ public:
return it;
}
template<typename... Lhs, typename... Rhs>
friend constexpr bool operator==(const extended_group_iterator<Lhs...> &, const extended_group_iterator<Rhs...> &) noexcept;
template<typename... Args>
[[nodiscard]] constexpr bool operator==(const extended_group_iterator<Args...> &other) const noexcept {
return it == other.it;
}
private:
It it;
std::tuple<Owned *..., Get *...> pools;
};
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator==(const extended_group_iterator<Lhs...> &lhs, const extended_group_iterator<Rhs...> &rhs) noexcept {
return lhs.it == rhs.it;
}
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator!=(const extended_group_iterator<Lhs...> &lhs, const extended_group_iterator<Rhs...> &rhs) noexcept {
return !(lhs == rhs);
}
struct group_descriptor {
using size_type = std::size_t;
virtual ~group_descriptor() = default;
@@ -101,7 +95,7 @@ struct group_descriptor {
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;
using entity_type = Type::entity_type;
void swap_elements(const std::size_t pos, const entity_type entt) {
for(size_type next{}; next < Owned; ++next) {
@@ -131,14 +125,14 @@ class group_handler final: public group_descriptor {
void common_setup() {
// we cannot iterate backwards because we want to leave behind valid entities in case of owned types
for(auto first = pools[0u]->rbegin(), last = first + static_cast<typename decltype(pools)::difference_type>(pools[0u]->size()); first != last; ++first) {
for(auto first = pools[0u]->rbegin(), last = first + static_cast<decltype(pools)::difference_type>(pools[0u]->size()); first != last; ++first) {
push_on_construct(*first);
}
}
public:
using common_type = Type;
using size_type = typename Type::size_type;
using size_type = Type::size_type;
template<typename... OGType, typename... EType>
group_handler(std::tuple<OGType &...> ogpool, std::tuple<EType &...> epool)
@@ -151,7 +145,7 @@ public:
[[nodiscard]] bool owned(const id_type hash) const noexcept override {
for(size_type pos{}; pos < Owned; ++pos) {
if(pools[pos]->type().hash() == hash) {
if(pools[pos]->info().hash() == hash) {
return true;
}
}
@@ -180,7 +174,7 @@ private:
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;
using entity_type = Type::entity_type;
void push_on_construct(const entity_type entt) {
if(!elem.contains(entt)
@@ -281,7 +275,7 @@ class basic_group;
template<typename... Get, typename... Exclude>
class basic_group<owned_t<>, get_t<Get...>, exclude_t<Exclude...>> {
using base_type = std::common_type_t<typename Get::base_type..., typename Exclude::base_type...>;
using underlying_type = typename base_type::entity_type;
using underlying_type = base_type::entity_type;
template<typename Type>
static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Get::element_type..., typename Exclude::element_type...>>;
@@ -302,9 +296,9 @@ public:
/*! @brief Common type among all storage types. */
using common_type = base_type;
/*! @brief Random access iterator type. */
using iterator = typename common_type::iterator;
using iterator = common_type::iterator;
/*! @brief Reverse iterator type. */
using reverse_iterator = typename common_type::reverse_iterator;
using reverse_iterator = 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. */
@@ -646,12 +640,10 @@ public:
* The shared pool of entities and thus its order is affected by the changes
* to each and every pool that it tracks.
*
* @tparam It Type of input iterator.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
*/
template<typename It>
void sort_as(It first, It last) const {
void sort_as(stl::input_iterator auto first, stl::input_iterator auto last) const {
if(*this) {
descriptor->handle().sort_as(first, last);
}
@@ -697,7 +689,7 @@ 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;
using underlying_type = base_type::entity_type;
template<typename Type>
static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Owned::element_type..., typename Get::element_type..., typename Exclude::element_type...>>;
@@ -718,9 +710,9 @@ public:
/*! @brief Common type among all storage types. */
using common_type = base_type;
/*! @brief Random access iterator type. */
using iterator = typename common_type::iterator;
using iterator = common_type::iterator;
/*! @brief Reverse iterator type. */
using reverse_iterator = typename common_type::reverse_iterator;
using reverse_iterator = 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. */

View File

@@ -13,19 +13,19 @@
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename It>
class handle_storage_iterator final {
template<typename Other>
template<typename>
friend class handle_storage_iterator;
using underlying_type = std::remove_reference_t<typename It::value_type::second_type>;
using entity_type = typename underlying_type::entity_type;
using entity_type = underlying_type::entity_type;
public:
using value_type = typename std::iterator_traits<It>::value_type;
using value_type = std::iterator_traits<It>::value_type;
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using difference_type = std::ptrdiff_t;
@@ -64,8 +64,10 @@ public:
return operator*();
}
template<typename ILhs, typename IRhs>
friend constexpr bool operator==(const handle_storage_iterator<ILhs> &, const handle_storage_iterator<IRhs> &) noexcept;
template<typename Other>
[[nodiscard]] constexpr bool operator==(const handle_storage_iterator<Other> &other) const noexcept {
return it == other.it;
}
private:
entity_type entt;
@@ -73,16 +75,6 @@ private:
It last;
};
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator==(const handle_storage_iterator<ILhs> &lhs, const handle_storage_iterator<IRhs> &rhs) noexcept {
return lhs.it == rhs.it;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator!=(const handle_storage_iterator<ILhs> &lhs, const handle_storage_iterator<IRhs> &rhs) noexcept {
return !(lhs == rhs);
}
} // namespace internal
/*! @endcond */
@@ -107,9 +99,9 @@ public:
/*! @brief Type of registry accepted by the handle. */
using registry_type = Registry;
/*! @brief Underlying entity identifier. */
using entity_type = typename traits_type::value_type;
using entity_type = traits_type::value_type;
/*! @brief Underlying version type. */
using version_type = typename traits_type::version_type;
using version_type = traits_type::version_type;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Iterable handle type. */
@@ -331,6 +323,27 @@ public:
return owner_or_assert().orphan(entt);
}
/**
* @brief Compares two handles.
* @tparam Other Scope of the other handle.
* @param other A valid handle.
* @return True if both handles refer to the same registry and the same
* entity, false otherwise.
*/
template<typename... Other>
[[nodiscard]] bool operator==(const basic_handle<Other...> &other) const noexcept {
return owner == other.registry() && entt == other.entity();
}
/**
* @brief Compares a handle with the null object.
* @param other A null object yet to be converted.
* @return False if the two elements differ, true otherwise.
*/
[[nodiscard]] constexpr bool operator==(const null_t other) const noexcept {
return (entt == other);
}
/**
* @brief Returns a const handle from a non-const one.
* @tparam Other A valid entity type.
@@ -350,82 +363,6 @@ private:
entity_type entt;
};
/**
* @brief Compares two handles.
* @tparam Args Scope of the first handle.
* @tparam Other Scope of the second handle.
* @param lhs A valid handle.
* @param rhs A valid handle.
* @return True if both handles refer to the same registry and the same
* entity, false otherwise.
*/
template<typename... Args, typename... Other>
[[nodiscard]] bool operator==(const basic_handle<Args...> &lhs, const basic_handle<Other...> &rhs) noexcept {
return lhs.registry() == rhs.registry() && lhs.entity() == rhs.entity();
}
/**
* @brief Compares two handles.
* @tparam Args Scope of the first handle.
* @tparam Other Scope of the second handle.
* @param lhs A valid handle.
* @param rhs A valid handle.
* @return False if both handles refer to the same registry and the same
* entity, true otherwise.
*/
template<typename... Args, typename... Other>
[[nodiscard]] bool operator!=(const basic_handle<Args...> &lhs, const basic_handle<Other...> &rhs) noexcept {
return !(lhs == rhs);
}
/**
* @brief Compares a handle with the null object.
* @tparam Args Scope of the handle.
* @param lhs A valid handle.
* @param rhs A null object yet to be converted.
* @return False if the two elements differ, true otherwise.
*/
template<typename... Args>
[[nodiscard]] constexpr bool operator==(const basic_handle<Args...> &lhs, const null_t rhs) noexcept {
return (lhs.entity() == rhs);
}
/**
* @brief Compares a handle with the null object.
* @tparam Args Scope of the handle.
* @param lhs A null object yet to be converted.
* @param rhs A valid handle.
* @return False if the two elements differ, true otherwise.
*/
template<typename... Args>
[[nodiscard]] constexpr bool operator==(const null_t lhs, const basic_handle<Args...> &rhs) noexcept {
return (rhs == lhs);
}
/**
* @brief Compares a handle with the null object.
* @tparam Args Scope of the handle.
* @param lhs A valid handle.
* @param rhs A null object yet to be converted.
* @return True if the two elements differ, false otherwise.
*/
template<typename... Args>
[[nodiscard]] constexpr bool operator!=(const basic_handle<Args...> &lhs, const null_t rhs) noexcept {
return (lhs.entity() != rhs);
}
/**
* @brief Compares a handle with the null object.
* @tparam Args Scope of the handle.
* @param lhs A null object yet to be converted.
* @param rhs A valid handle.
* @return True if the two elements differ, false otherwise.
*/
template<typename... Args>
[[nodiscard]] constexpr bool operator!=(const null_t lhs, const basic_handle<Args...> &rhs) noexcept {
return (rhs != lhs);
}
} // namespace entt
#endif

View File

@@ -29,7 +29,7 @@ public:
/*! @brief Type of registry to convert. */
using registry_type = Registry;
/*! @brief Underlying entity identifier. */
using entity_type = typename registry_type::entity_type;
using entity_type = registry_type::entity_type;
/**
* @brief Constructs a converter for a given registry.
@@ -72,7 +72,7 @@ public:
/*! @brief Type of registry to convert. */
using registry_type = Registry;
/*! @brief Underlying entity identifier. */
using entity_type = typename registry_type::entity_type;
using entity_type = registry_type::entity_type;
/**
* @brief Constructs a converter for a given registry.
@@ -123,7 +123,7 @@ void invoke(Registry &reg, const typename Registry::entity_type entt) {
* @return The entity associated with the given element.
*/
template<typename... Args>
typename basic_storage<Args...>::entity_type to_entity(const basic_storage<Args...> &storage, const typename basic_storage<Args...>::value_type &instance) {
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, typename basic_storage<Args...>::entity_type>;
static_assert(traits_type::page_size != 0u, "Unexpected page size");
const auto *page = storage.raw();
@@ -131,7 +131,7 @@ typename basic_storage<Args...>::entity_type to_entity(const basic_storage<Args.
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic)
for(std::size_t pos{}, count = storage.size(); pos < count; pos += traits_type::page_size, ++page) {
if(const auto dist = (std::addressof(instance) - *page); dist >= 0 && dist < static_cast<decltype(dist)>(traits_type::page_size)) {
return *(static_cast<const typename basic_storage<Args...>::base_type &>(storage).rbegin() + static_cast<decltype(dist)>(pos) + dist);
return *(static_cast<const basic_storage<Args...>::base_type &>(storage).rbegin() + static_cast<decltype(dist)>(pos) + dist);
}
}
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)

View File

@@ -1,40 +1,42 @@
#ifndef ENTT_ENTITY_MIXIN_HPP
#define ENTT_ENTITY_MIXIN_HPP
#include <concepts>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/any.hpp"
#include "../core/type_info.hpp"
#include "../signal/sigh.hpp"
#include "../stl/iterator.hpp"
#include "entity.hpp"
#include "fwd.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename, typename, typename = void>
template<typename, typename>
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 {};
requires std::invocable<decltype(&Type::on_construct), Registry &, typename Registry::entity_type>
struct has_on_construct<Type, Registry>: std::true_type {};
template<typename, typename, typename = void>
template<typename, typename>
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 {};
requires std::invocable<decltype(&Type::on_update), Registry &, typename Registry::entity_type>
struct has_on_update<Type, Registry>: std::true_type {};
template<typename, typename, typename = void>
template<typename, typename>
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 {};
requires std::invocable<decltype(&Type::on_destroy), Registry &, typename Registry::entity_type>
struct has_on_destroy<Type, Registry>: std::true_type {};
} // namespace internal
/*! @endcond */
@@ -60,7 +62,7 @@ class basic_sigh_mixin final: public 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;
using underlying_iterator = underlying_type::base_type::basic_iterator;
static_assert(std::is_base_of_v<basic_registry_type, owner_type>, "Invalid registry type");
@@ -90,7 +92,7 @@ private:
destruction.publish(reg, underlying_type::base_type::operator[](pos));
}
} else {
for(auto entt: static_cast<typename underlying_type::base_type &>(*this)) {
for(auto entt: static_cast<underlying_type::base_type &>(*this)) {
if constexpr(underlying_type::storage_policy == deletion_policy::in_place) {
if(entt != tombstone) {
destruction.publish(reg, entt);
@@ -105,7 +107,7 @@ private:
underlying_type::pop_all();
}
underlying_iterator try_emplace(const typename underlying_type::entity_type entt, const bool force_back, const void *value) final {
underlying_iterator try_emplace(const underlying_type::entity_type entt, const bool force_back, const void *value) final {
const auto it = underlying_type::try_emplace(entt, force_back, value);
if(auto &reg = owner_or_assert(); it != underlying_type::base_type::end()) {
@@ -129,9 +131,9 @@ private:
public:
/*! @brief Allocator type. */
using allocator_type = typename underlying_type::allocator_type;
using allocator_type = underlying_type::allocator_type;
/*! @brief Underlying entity identifier. */
using entity_type = typename underlying_type::entity_type;
using entity_type = underlying_type::entity_type;
/*! @brief Expected registry type. */
using registry_type = owner_type;
@@ -150,15 +152,15 @@ public:
destruction{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>();
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>();
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>();
sink{destruction}.template connect<&underlying_type::element_type::on_destroy>();
}
}
@@ -169,28 +171,24 @@ public:
* @brief Move constructor.
* @param other The instance to move from.
*/
// NOLINTBEGIN(bugprone-use-after-move)
basic_sigh_mixin(basic_sigh_mixin &&other) noexcept
: underlying_type{std::move(other)},
: underlying_type{static_cast<underlying_type &&>(other)},
owner{other.owner},
construction{std::move(other.construction)},
destruction{std::move(other.destruction)},
update{std::move(other.update)} {}
// NOLINTEND(bugprone-use-after-move)
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
// NOLINTBEGIN(bugprone-use-after-move)
basic_sigh_mixin(basic_sigh_mixin &&other, const allocator_type &allocator)
: underlying_type{std::move(other), allocator},
: underlying_type{static_cast<underlying_type &&>(other), allocator},
owner{other.owner},
construction{std::move(other.construction), allocator},
destruction{std::move(other.destruction), allocator},
update{std::move(other.update), allocator} {}
// NOLINTEND(bugprone-use-after-move)
/*! @brief Default destructor. */
~basic_sigh_mixin() override = default;
@@ -313,11 +311,11 @@ public:
/**
* @brief Assigns each element in a range an identifier.
* @tparam It Type of mutable forward iterator.
* @tparam It Type of output iterator.
* @param first An iterator to the first element of the range to generate.
* @param last An iterator past the last element of the range to generate.
*/
template<typename It>
template<stl::output_iterator<entity_type> It>
void generate(It first, It last) {
underlying_type::generate(first, last);
@@ -359,14 +357,13 @@ public:
/**
* @brief Assigns one or more entities to a storage and constructs their
* objects from a given instance.
* @tparam It Type of input iterator.
* @tparam Args Types of arguments to forward to the underlying storage.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @param args Parameters to use to forward to the underlying storage.
*/
template<typename It, typename... Args>
void insert(It first, It last, Args &&...args) {
template<typename... Args>
void insert(stl::input_iterator auto first, stl::input_iterator auto last, Args &&...args) {
auto from = underlying_type::size();
underlying_type::insert(first, last, std::forward<Args>(args)...);
@@ -406,7 +403,7 @@ class basic_reactive_mixin final: public Type {
return static_cast<owner_type &>(*owner);
}
void emplace_element(const Registry &, typename underlying_type::entity_type entity) {
void emplace_element(const Registry &, underlying_type::entity_type entity) {
if(!underlying_type::contains(entity)) {
underlying_type::emplace(entity);
}
@@ -427,9 +424,9 @@ private:
public:
/*! @brief Allocator type. */
using allocator_type = typename underlying_type::allocator_type;
using allocator_type = underlying_type::allocator_type;
/*! @brief Underlying entity identifier. */
using entity_type = typename underlying_type::entity_type;
using entity_type = underlying_type::entity_type;
/*! @brief Expected registry type. */
using registry_type = owner_type;
@@ -454,26 +451,22 @@ public:
* @brief Move constructor.
* @param other The instance to move from.
*/
// NOLINTBEGIN(bugprone-use-after-move)
basic_reactive_mixin(basic_reactive_mixin &&other) noexcept
: underlying_type{std::move(other)},
: underlying_type{static_cast<underlying_type &&>(other)},
owner{other.owner},
conn{} {
conn{std::move(other.conn)} {
}
// NOLINTEND(bugprone-use-after-move)
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
// NOLINTBEGIN(bugprone-use-after-move)
basic_reactive_mixin(basic_reactive_mixin &&other, const allocator_type &allocator)
: underlying_type{std::move(other), allocator},
: underlying_type{static_cast<underlying_type &&>(other), allocator},
owner{other.owner},
conn{allocator} {
conn{std::move(other.conn), allocator} {
}
// NOLINTEND(bugprone-use-after-move)
/*! @brief Default destructor. */
~basic_reactive_mixin() override = default;

View File

@@ -15,7 +15,7 @@
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename>
@@ -79,27 +79,28 @@ template<typename... Owned, typename... Get, typename... Exclude, typename... Ov
struct unpack_type<const basic_group<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>>
: unpack_type<basic_group<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>> {};
template<typename, typename>
template<typename, typename, typename>
struct resource_traits;
template<typename... Args, typename... Req>
struct resource_traits<type_list<Args...>, type_list<Req...>> {
template<typename Registry, typename... Args, typename... Req>
struct resource_traits<Registry, type_list<Args...>, type_list<Req...>> {
using args = type_list<std::remove_const_t<Args>...>;
using ro = type_list_cat_t<typename unpack_type<Args, type_list<Req...>>::ro..., typename unpack_type<Req, type_list<>>::ro...>;
using rw = type_list_cat_t<typename unpack_type<Args, type_list<Req...>>::rw..., typename unpack_type<Req, type_list<>>::rw...>;
static constexpr auto sync_point = (std::is_same_v<Args, Registry> || ...);
};
template<typename... Req, typename Ret, typename... Args>
resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> free_function_to_resource_traits(Ret (*)(Args...));
template<typename Registry, typename... Req, typename Ret, typename... Args>
resource_traits<Registry, type_list<std::remove_reference_t<Args>...>, type_list<Req...>> free_function_to_resource_traits(Ret (*)(Args...));
template<typename... Req, typename Ret, typename Type, typename... Args>
resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (*)(Type &, Args...));
template<typename Registry, typename... Req, typename Ret, typename Type, typename... Args>
resource_traits<Registry, type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (*)(Type &, Args...));
template<typename... Req, typename Ret, typename Class, typename... Args>
resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...));
template<typename Registry, typename... Req, typename Ret, typename Class, typename... Args>
resource_traits<Registry, type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...));
template<typename... Req, typename Ret, typename Class, typename... Args>
resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...) const);
template<typename Registry, typename... Req, typename Ret, typename Class, typename... Args>
resource_traits<Registry, type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...) const);
} // namespace internal
/*! @endcond */
@@ -169,9 +170,9 @@ class basic_organizer final {
}
template<typename... RO, typename... RW>
void track_dependencies(std::size_t index, const bool requires_registry, type_list<RO...>, type_list<RW...>) {
void track_dependencies(std::size_t index, const bool sync_point, type_list<RO...>, type_list<RW...>) {
builder.bind(static_cast<id_type>(index));
builder.set(type_hash<Registry>::value(), requires_registry || (sizeof...(RO) + sizeof...(RW) == 0u));
builder.set(type_hash<Registry>::value(), sync_point || (sizeof...(RO) + sizeof...(RW) == 0u));
(builder.ro(type_hash<RO>::value()), ...);
(builder.rw(type_hash<RW>::value()), ...);
}
@@ -180,7 +181,7 @@ public:
/*! Basic registry type. */
using registry_type = Registry;
/*! @brief Underlying entity identifier. */
using entity_type = typename registry_type::entity_type;
using entity_type = registry_type::entity_type;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Raw task function type. */
@@ -316,8 +317,7 @@ public:
*/
template<auto Candidate, typename... Req>
void emplace(const char *name = nullptr) {
using resource_type = decltype(internal::free_function_to_resource_traits<Req...>(Candidate));
constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, registry_type>;
using resource_type = decltype(internal::free_function_to_resource_traits<registry_type, Req...>(Candidate));
callback_type *callback = +[](const void *, registry_type &reg) {
std::apply(Candidate, to_args(reg, typename resource_type::args{}));
@@ -333,7 +333,7 @@ public:
+[](registry_type &reg) { void(to_args(reg, typename resource_type::args{})); },
&type_id<std::integral_constant<decltype(Candidate), Candidate>>()};
track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{});
track_dependencies(vertices.size(), resource_type::sync_point, typename resource_type::ro{}, typename resource_type::rw{});
vertices.push_back(std::move(vdata));
}
@@ -348,8 +348,7 @@ public:
*/
template<auto Candidate, typename... Req, typename Type>
void emplace(Type &value_or_instance, const char *name = nullptr) {
using resource_type = decltype(internal::constrained_function_to_resource_traits<Req...>(Candidate));
constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, registry_type>;
using resource_type = decltype(internal::constrained_function_to_resource_traits<registry_type, Req...>(Candidate));
callback_type *callback = +[](const void *payload, registry_type &reg) {
Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
@@ -366,7 +365,7 @@ public:
+[](registry_type &reg) { void(to_args(reg, typename resource_type::args{})); },
&type_id<std::integral_constant<decltype(Candidate), Candidate>>()};
track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{});
track_dependencies(vertices.size(), resource_type::sync_point, typename resource_type::ro{}, typename resource_type::rw{});
vertices.push_back(std::move(vdata));
}
@@ -380,7 +379,7 @@ public:
*/
template<typename... Req>
void emplace(function_type *func, const void *payload = nullptr, const char *name = nullptr) {
using resource_type = internal::resource_traits<type_list<>, type_list<Req...>>;
using resource_type = internal::resource_traits<registry_type, type_list<>, type_list<Req...>>;
track_dependencies(vertices.size(), true, typename resource_type::ro{}, typename resource_type::rw{});
vertex_data vdata{
@@ -400,12 +399,11 @@ public:
* @brief Generates a task graph for the current content.
* @return The adjacency list of the task graph.
*/
[[nodiscard]] std::vector<vertex> graph() {
[[nodiscard]] std::vector<vertex> graph() const {
std::vector<vertex> adjacency_list{};
adjacency_list.reserve(vertices.size());
auto adjacency_matrix = builder.graph();
for(auto curr: adjacency_matrix.vertices()) {
for(auto adjacency_matrix = builder.graph(); auto curr: adjacency_matrix.vertices()) {
std::vector<std::size_t> in{};
std::vector<std::size_t> out{};

View File

@@ -3,6 +3,8 @@
#include <algorithm>
#include <array>
#include <compare>
#include <concepts>
#include <cstddef>
#include <functional>
#include <iterator>
@@ -15,12 +17,14 @@
#include "../container/dense_map.hpp"
#include "../core/algorithm.hpp"
#include "../core/any.hpp"
#include "../core/concepts.hpp"
#include "../core/fwd.hpp"
#include "../core/iterator.hpp"
#include "../core/memory.hpp"
#include "../core/type_info.hpp"
#include "../core/type_traits.hpp"
#include "../core/utility.hpp"
#include "../stl/functional.hpp"
#include "../stl/iterator.hpp"
#include "entity.hpp"
#include "fwd.hpp"
#include "group.hpp"
@@ -31,12 +35,12 @@
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename It>
class registry_storage_iterator final {
template<typename Other>
template<typename>
friend class registry_storage_iterator;
using mapped_type = std::remove_reference_t<decltype(std::declval<It>()->second)>;
@@ -55,7 +59,8 @@ public:
constexpr registry_storage_iterator(It iter) noexcept
: it{iter} {}
template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
template<typename Other>
requires (!std::same_as<It, Other> && std::constructible_from<It, Other>)
constexpr registry_storage_iterator(const registry_storage_iterator<Other> &other) noexcept
: registry_storage_iterator{other.it} {}
@@ -107,63 +112,38 @@ public:
return operator*();
}
template<typename Lhs, typename Rhs>
friend constexpr std::ptrdiff_t operator-(const registry_storage_iterator<Lhs> &, const registry_storage_iterator<Rhs> &) noexcept;
template<typename Other>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const registry_storage_iterator<Other> &other) const noexcept {
return it - other.it;
}
template<typename Lhs, typename Rhs>
friend constexpr bool operator==(const registry_storage_iterator<Lhs> &, const registry_storage_iterator<Rhs> &) noexcept;
template<typename Other>
[[nodiscard]] constexpr bool operator==(const registry_storage_iterator<Other> &other) const noexcept {
return it == other.it;
}
template<typename Lhs, typename Rhs>
friend constexpr bool operator<(const registry_storage_iterator<Lhs> &, const registry_storage_iterator<Rhs> &) noexcept;
template<typename Other>
[[nodiscard]] constexpr auto operator<=>(const registry_storage_iterator<Other> &other) const noexcept {
return it <=> other.it;
}
private:
It it;
};
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const registry_storage_iterator<Lhs> &lhs, const registry_storage_iterator<Rhs> &rhs) noexcept {
return lhs.it - rhs.it;
}
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator==(const registry_storage_iterator<Lhs> &lhs, const registry_storage_iterator<Rhs> &rhs) noexcept {
return lhs.it == rhs.it;
}
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator!=(const registry_storage_iterator<Lhs> &lhs, const registry_storage_iterator<Rhs> &rhs) noexcept {
return !(lhs == rhs);
}
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator<(const registry_storage_iterator<Lhs> &lhs, const registry_storage_iterator<Rhs> &rhs) noexcept {
return lhs.it < rhs.it;
}
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator>(const registry_storage_iterator<Lhs> &lhs, const registry_storage_iterator<Rhs> &rhs) noexcept {
return rhs < lhs;
}
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator<=(const registry_storage_iterator<Lhs> &lhs, const registry_storage_iterator<Rhs> &rhs) noexcept {
return !(lhs > rhs);
}
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator>=(const registry_storage_iterator<Lhs> &lhs, const registry_storage_iterator<Rhs> &rhs) noexcept {
return !(lhs < rhs);
}
template<typename Allocator>
class registry_context {
using alloc_traits = std::allocator_traits<Allocator>;
using allocator_type = typename alloc_traits::template rebind_alloc<std::pair<const id_type, basic_any<0u>>>;
using allocator_type = alloc_traits::template rebind_alloc<std::pair<const id_type, basic_any<0u>>>;
public:
explicit registry_context(const allocator_type &allocator)
: ctx{allocator} {}
void clear() noexcept {
ctx.clear();
}
template<typename Type, typename... Args>
Type &emplace_as(const id_type id, Args &&...args) {
return any_cast<Type &>(ctx.try_emplace(id, std::in_place_type<Type>, std::forward<Args>(args)...).first->second);
@@ -176,7 +156,7 @@ public:
template<typename Type>
Type &insert_or_assign(const id_type id, Type &&value) {
return any_cast<std::remove_cv_t<std::remove_reference_t<Type>> &>(ctx.insert_or_assign(id, std::forward<Type>(value)).first->second);
return any_cast<std::remove_cvref_t<Type> &>(ctx.insert_or_assign(id, std::forward<Type>(value)).first->second);
}
template<typename Type>
@@ -187,7 +167,7 @@ public:
template<typename Type>
bool erase(const id_type id = type_id<Type>().hash()) {
const auto it = ctx.find(id);
return it != ctx.end() && it->second.type() == type_id<Type>() ? (ctx.erase(it), true) : false;
return it != ctx.end() && it->second.info() == type_id<Type>() ? (ctx.erase(it), true) : false;
}
template<typename Type>
@@ -215,11 +195,11 @@ public:
template<typename Type>
[[nodiscard]] bool contains(const id_type id = type_id<Type>().hash()) const {
const auto it = ctx.find(id);
return it != ctx.cend() && it->second.type() == type_id<Type>();
return it != ctx.cend() && it->second.info() == type_id<Type>();
}
private:
dense_map<id_type, basic_any<0u>, identity, std::equal_to<>, allocator_type> ctx;
dense_map<id_type, basic_any<0u>, stl::identity, std::equal_to<>, allocator_type> ctx;
};
} // namespace internal
@@ -236,14 +216,12 @@ class basic_registry {
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, Entity>, "Invalid value type");
// std::shared_ptr because of its type erased allocator which is useful here
using pool_container_type = dense_map<id_type, std::shared_ptr<base_type>, identity, std::equal_to<>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, std::shared_ptr<base_type>>>>;
using group_container_type = dense_map<id_type, std::shared_ptr<internal::group_descriptor>, identity, std::equal_to<>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, std::shared_ptr<internal::group_descriptor>>>>;
using pool_container_type = dense_map<id_type, std::shared_ptr<base_type>, stl::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>, stl::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>
template<cvref_unqualified 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;
@@ -251,20 +229,11 @@ class basic_registry {
using storage_type = storage_for_type<Type>;
if(auto it = pools.find(id); it != pools.cend()) {
ENTT_ASSERT(it->second->type() == type_id<Type>(), "Unexpected type");
ENTT_ASSERT(it->second->info() == type_id<Type>(), "Unexpected type");
return static_cast<storage_type &>(*it->second);
}
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());
}
typename pool_container_type::mapped_type cpool = std::allocate_shared<storage_type>(get_allocator(), get_allocator());
pools.emplace(id, cpool);
cpool->bind(*this);
@@ -272,16 +241,14 @@ class basic_registry {
}
}
template<typename Type>
template<cvref_unqualified Type>
[[nodiscard]] const auto *assure([[maybe_unused]] const id_type id = type_hash<Type>::value()) const {
static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Non-decayed types not allowed");
if constexpr(std::is_same_v<Type, entity_type>) {
ENTT_ASSERT(id == type_hash<Type>::value(), "User entity storage not allowed");
return &entities;
} else {
if(const auto it = pools.find(id); it != pools.cend()) {
ENTT_ASSERT(it->second->type() == type_id<Type>(), "Unexpected type");
ENTT_ASSERT(it->second->info() == type_id<Type>(), "Unexpected type");
return static_cast<const storage_for_type<Type> *>(it->second.get());
}
@@ -301,9 +268,9 @@ public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Underlying entity identifier. */
using entity_type = typename traits_type::value_type;
using entity_type = traits_type::value_type;
/*! @brief Underlying version type. */
using version_type = typename traits_type::version_type;
using version_type = traits_type::version_type;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Common type among all storage types. */
@@ -320,7 +287,7 @@ public:
* @tparam Type Storage value type, eventually const.
*/
template<typename Type>
using storage_for_type = typename storage_for<Type, Entity, typename alloc_traits::template rebind_alloc<std::remove_const_t<Type>>>::type;
using storage_for_type = storage_for<Type, Entity, typename alloc_traits::template rebind_alloc<std::remove_const_t<Type>>>::type;
/*! @brief Default constructor. */
basic_registry()
@@ -518,11 +485,11 @@ public:
*
* @sa create
*
* @tparam It Type of forward iterator.
* @tparam It Type of output iterator.
* @param first An iterator to the first element of the range to generate.
* @param last An iterator past the last element of the range to generate.
*/
template<typename It>
template<stl::output_iterator<entity_type> It>
void create(It first, It last) {
entities.generate(std::move(first), std::move(last));
}
@@ -539,7 +506,7 @@ public:
*/
version_type destroy(const entity_type entt) {
for(size_type pos = pools.size(); pos != 0u; --pos) {
pools.begin()[static_cast<typename pool_container_type::difference_type>(pos - 1u)].second->remove(entt);
pools.begin()[static_cast<pool_container_type::difference_type>(pos - 1u)].second->remove(entt);
}
entities.erase(entt);
@@ -569,14 +536,12 @@ public:
*
* @sa destroy
*
* @tparam It Type of input iterator.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
*/
template<typename It>
void destroy(It first, It last) {
void destroy(stl::input_iterator auto first, stl::input_iterator auto last) {
const auto to = entities.sort_as(first, last);
const auto from = entities.cend() - static_cast<typename common_type::difference_type>(entities.free_list());
const auto from = entities.cend() - static_cast<common_type::difference_type>(entities.free_list());
for(auto &&curr: pools) {
curr.second->remove(from, to);
@@ -612,13 +577,12 @@ public:
* @sa emplace
*
* @tparam Type Type of element to create.
* @tparam It Type of input iterator.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @param value An instance of the element to assign.
*/
template<typename Type, typename It>
void insert(It first, It last, const Type &value = {}) {
template<typename Type>
void insert(stl::input_iterator auto first, stl::input_iterator auto 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);
}
@@ -635,7 +599,8 @@ public:
* @param last An iterator past the last element of the range of entities.
* @param from An iterator to the first element of the range of elements.
*/
template<typename Type, typename EIt, typename CIt, typename = std::enable_if_t<std::is_same_v<typename std::iterator_traits<CIt>::value_type, Type>>>
template<typename Type, typename EIt, typename CIt>
requires std::same_as<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);
@@ -728,7 +693,7 @@ public:
* @param last An iterator past the last element of the range of entities.
* @return The number of elements actually removed.
*/
template<typename Type, typename... Other, typename It>
template<typename Type, typename... Other, stl::input_iterator It>
size_type remove(It first, It last) {
size_type count{};
@@ -781,7 +746,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.
*/
template<typename Type, typename... Other, typename It>
template<typename Type, typename... Other, stl::input_iterator It>
void erase(It first, It last) {
if constexpr(std::is_same_v<It, typename common_type::iterator>) {
std::array cpools{static_cast<common_type *>(&assure<Type>()), static_cast<common_type *>(&assure<Other>())...};
@@ -960,7 +925,7 @@ public:
void clear() {
if constexpr(sizeof...(Type) == 0u) {
for(size_type pos = pools.size(); pos; --pos) {
pools.begin()[static_cast<typename pool_container_type::difference_type>(pos - 1u)].second->clear();
pools.begin()[static_cast<pool_container_type::difference_type>(pos - 1u)].second->clear();
}
const auto elem = entities.each();
@@ -1084,7 +1049,7 @@ public:
basic_group<owned_t<storage_for_type<Owned>...>, get_t<storage_for_type<Get>...>, exclude_t<storage_for_type<Exclude>...>>
group(get_t<Get...> = get_t{}, exclude_t<Exclude...> = exclude_t{}) {
using group_type = basic_group<owned_t<storage_for_type<Owned>...>, get_t<storage_for_type<Get>...>, exclude_t<storage_for_type<Exclude>...>>;
using handler_type = typename group_type::handler;
using handler_type = group_type::handler;
if(auto it = groups.find(group_type::group_id()); it != groups.cend()) {
return {*std::static_pointer_cast<handler_type>(it->second)};
@@ -1108,7 +1073,7 @@ public:
[[nodiscard]] basic_group<owned_t<storage_for_type<const Owned>...>, get_t<storage_for_type<const Get>...>, exclude_t<storage_for_type<const Exclude>...>>
group_if_exists(get_t<Get...> = get_t{}, exclude_t<Exclude...> = exclude_t{}) const {
using group_type = basic_group<owned_t<storage_for_type<const Owned>...>, get_t<storage_for_type<const Get>...>, exclude_t<storage_for_type<const Exclude>...>>;
using handler_type = typename group_type::handler;
using handler_type = group_type::handler;
if(auto it = groups.find(group_type::group_id()); it != groups.cend()) {
return {*std::static_pointer_cast<handler_type>(it->second)};

View File

@@ -11,12 +11,12 @@
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename Set>
class runtime_view_iterator final {
using iterator_type = typename Set::iterator;
using iterator_type = Set::iterator;
using iterator_traits = std::iterator_traits<iterator_type>;
[[nodiscard]] bool valid() const {
@@ -26,10 +26,10 @@ class runtime_view_iterator final {
}
public:
using value_type = typename iterator_traits::value_type;
using pointer = typename iterator_traits::pointer;
using reference = typename iterator_traits::reference;
using difference_type = typename iterator_traits::difference_type;
using value_type = iterator_traits::value_type;
using pointer = iterator_traits::pointer;
using reference = iterator_traits::reference;
using difference_type = iterator_traits::difference_type;
using iterator_category = std::bidirectional_iterator_tag;
constexpr runtime_view_iterator() noexcept
@@ -38,8 +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
runtime_view_iterator(const std::vector<Set *> &cpools, iterator_type curr, const std::vector<Set *> &ignore) noexcept
: pools{&cpools},
filter{&ignore},
it{curr},
@@ -83,10 +82,6 @@ public:
return it == other.it;
}
[[nodiscard]] constexpr bool operator!=(const runtime_view_iterator &other) const noexcept {
return !(*this == other);
}
private:
const std::vector<Set *> *pools;
const std::vector<Set *> *filter;
@@ -126,13 +121,21 @@ class basic_runtime_view {
static_assert(std::is_same_v<typename alloc_traits::value_type, Type *>, "Invalid value type");
using container_type = std::vector<Type *, Allocator>;
[[nodiscard]] auto offset() const noexcept {
ENTT_ASSERT(!pools.empty(), "Invalid view");
const auto &leading = *pools.front();
return (leading.policy() == deletion_policy::swap_only) ? leading.free_list() : leading.size();
}
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Underlying entity identifier. */
using entity_type = typename Type::entity_type;
using entity_type = Type::entity_type;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Signed integer type. */
using difference_type = std::ptrdiff_t;
/*! @brief Common type among all storage types. */
using common_type = Type;
/*! @brief Bidirectional iterator type. */
@@ -219,10 +222,10 @@ public:
* @return This runtime view.
*/
basic_runtime_view &iterate(common_type &base) {
if(pools.empty() || !(base.size() < pools[0u]->size())) {
if(pools.empty() || !(base.size() < pools.front()->size())) {
pools.push_back(&base);
} else {
pools.push_back(std::exchange(pools[0u], &base));
pools.push_back(std::exchange(pools.front(), &base));
}
return *this;
@@ -243,7 +246,7 @@ public:
* @return Estimated number of entities iterated by the view.
*/
[[nodiscard]] size_type size_hint() const {
return pools.empty() ? size_type{} : pools.front()->size();
return pools.empty() ? size_type{} : offset();
}
/**
@@ -255,7 +258,7 @@ public:
* @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()};
return pools.empty() ? iterator{} : iterator{pools, pools.front()->end() - static_cast<difference_type>(offset()), filter};
}
/**
@@ -265,7 +268,7 @@ public:
* given elements.
*/
[[nodiscard]] iterator end() const {
return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->end()};
return pools.empty() ? iterator{} : iterator{pools, pools.front()->end(), filter};
}
/**
@@ -284,7 +287,8 @@ public:
[[nodiscard]] bool contains(const entity_type entt) const {
return !pools.empty()
&& std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *curr) { return curr->contains(entt); })
&& std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); });
&& std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); })
&& pools.front()->index(entt) < offset();
}
/**

View File

@@ -1,6 +1,7 @@
#ifndef ENTT_ENTITY_SNAPSHOT_HPP
#define ENTT_ENTITY_SNAPSHOT_HPP
#include <concepts>
#include <cstddef>
#include <iterator>
#include <tuple>
@@ -10,20 +11,19 @@
#include "../config/config.h"
#include "../container/dense_map.hpp"
#include "../core/type_traits.hpp"
#include "../stl/iterator.hpp"
#include "entity.hpp"
#include "fwd.hpp"
#include "view.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename Registry>
void orphans(Registry &registry) {
auto &storage = registry.template storage<typename Registry::entity_type>();
for(auto entt: storage) {
for(auto &storage = registry.template storage<typename Registry::entity_type>(); auto entt: storage) {
if(registry.orphan(entt)) {
storage.erase(entt);
}
@@ -52,7 +52,7 @@ public:
/*! Basic registry type. */
using registry_type = Registry;
/*! @brief Underlying entity identifier. */
using entity_type = typename registry_type::entity_type;
using entity_type = registry_type::entity_type;
/**
* @brief Constructs an instance that is bound to a given registry.
@@ -95,10 +95,10 @@ public:
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()));
archive(static_cast<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()));
archive(static_cast<traits_type::entity_type>(storage->free_list()));
for(auto first = base.rbegin(), last = base.rend(); first != last; ++first) {
archive(*first);
@@ -129,19 +129,18 @@ public:
* the entities in a range.
* @tparam Type Type of elements to serialize.
* @tparam Archive Type of output archive.
* @tparam It Type of input iterator.
* @param archive A valid reference to an output archive.
* @param first An iterator to the first element of the range to serialize.
* @param last An iterator past the last element of the range to serialize.
* @param id Optional name used to map the storage within the registry.
* @return An object of this type to continue creating the snapshot.
*/
template<typename Type, typename Archive, typename It>
const basic_snapshot &get(Archive &archive, It first, It last, const id_type id = type_hash<Type>::value()) const {
template<typename Type, typename Archive>
const basic_snapshot &get(Archive &archive, stl::input_iterator auto first, stl::input_iterator auto last, const id_type id = type_hash<Type>::value()) const {
static_assert(!std::is_same_v<Type, entity_type>, "Entity types not supported");
if(const auto *storage = reg->template storage<Type>(id); storage && !storage->empty()) {
archive(static_cast<typename traits_type::entity_type>(std::distance(first, last)));
archive(static_cast<traits_type::entity_type>(std::distance(first, last)));
for(; first != last; ++first) {
if(const auto entt = *first; storage->contains(entt)) {
@@ -181,7 +180,7 @@ public:
/*! Basic registry type. */
using registry_type = Registry;
/*! @brief Underlying entity identifier. */
using entity_type = typename registry_type::entity_type;
using entity_type = registry_type::entity_type;
/**
* @brief Constructs an instance that is bound to a given registry.
@@ -307,7 +306,7 @@ class basic_continuous_loader {
static_assert(!std::is_const_v<Registry>, "Non-const registry type required");
using traits_type = entt_traits<typename Registry::entity_type>;
void restore(typename Registry::entity_type entt) {
void restore(Registry::entity_type entt) {
if(const auto entity = to_entity(entt); remloc.contains(entity) && remloc[entity].first == entt) {
if(!reg->valid(remloc[entity].second)) {
remloc[entity].second = reg->create();
@@ -324,7 +323,7 @@ class basic_continuous_loader {
for(auto &&pair: container) {
using first_type = std::remove_const_t<typename std::decay_t<decltype(pair)>::first_type>;
using second_type = typename std::decay_t<decltype(pair)>::second_type;
using second_type = std::decay_t<decltype(pair)>::second_type;
if constexpr(std::is_same_v<first_type, entity_type> && std::is_same_v<second_type, entity_type>) {
other.emplace(map(pair.first), map(pair.second));
@@ -366,7 +365,7 @@ public:
/*! Basic registry type. */
using registry_type = Registry;
/*! @brief Underlying entity identifier. */
using entity_type = typename registry_type::entity_type;
using entity_type = registry_type::entity_type;
/**
* @brief Constructs an instance that is bound to a given registry.

View File

@@ -1,6 +1,8 @@
#ifndef ENTT_ENTITY_SPARSE_SET_HPP
#define ENTT_ENTITY_SPARSE_SET_HPP
#include <compare>
#include <concepts>
#include <cstddef>
#include <iterator>
#include <memory>
@@ -12,20 +14,21 @@
#include "../core/any.hpp"
#include "../core/bit.hpp"
#include "../core/type_info.hpp"
#include "../stl/iterator.hpp"
#include "entity.hpp"
#include "fwd.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename Container>
struct sparse_set_iterator final {
using value_type = typename Container::value_type;
using pointer = typename Container::const_pointer;
using reference = typename Container::const_reference;
using difference_type = typename Container::difference_type;
using value_type = Container::value_type;
using pointer = Container::const_pointer;
using reference = Container::const_reference;
using difference_type = Container::difference_type;
using iterator_category = std::random_access_iterator_tag;
constexpr sparse_set_iterator() noexcept
@@ -73,7 +76,7 @@ struct sparse_set_iterator final {
}
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
return (*packed)[static_cast<typename Container::size_type>(index() - value)];
return (*packed)[static_cast<Container::size_type>(index() - value)];
}
[[nodiscard]] constexpr pointer operator->() const noexcept {
@@ -84,6 +87,20 @@ struct sparse_set_iterator final {
return operator[](0);
}
[[nodiscard]] constexpr std::ptrdiff_t operator-(const sparse_set_iterator &other) const noexcept {
// intentionally reversed due to backward iteration
return other.offset - offset;
}
[[nodiscard]] constexpr bool operator==(const sparse_set_iterator &other) const noexcept {
return offset == other.offset;
}
[[nodiscard]] constexpr auto operator<=>(const sparse_set_iterator &other) const noexcept {
// intentionally reversed due to backward iteration
return other.offset <=> offset;
}
[[nodiscard]] constexpr pointer data() const noexcept {
return packed ? packed->data() : nullptr;
}
@@ -97,41 +114,6 @@ private:
difference_type offset;
};
template<typename Container>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
return rhs.index() - lhs.index();
}
template<typename Container>
[[nodiscard]] constexpr bool operator==(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
return lhs.index() == rhs.index();
}
template<typename Container>
[[nodiscard]] constexpr bool operator!=(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
return !(lhs == rhs);
}
template<typename Container>
[[nodiscard]] constexpr bool operator<(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
return lhs.index() > rhs.index();
}
template<typename Container>
[[nodiscard]] constexpr bool operator>(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
return rhs < lhs;
}
template<typename Container>
[[nodiscard]] constexpr bool operator<=(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
return !(lhs > rhs);
}
template<typename Container>
[[nodiscard]] constexpr bool operator>=(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
return !(lhs < rhs);
}
} // namespace internal
/*! @endcond */
@@ -212,9 +194,7 @@ class basic_sparse_set {
}
void release_sparse_pages() {
auto page_allocator{packed.get_allocator()};
for(auto &&page: sparse) {
for(auto page_allocator{packed.get_allocator()}; auto &&page: sparse) {
if(page != nullptr) {
std::destroy(page, page + traits_type::page_size);
alloc_traits::deallocate(page_allocator, page, traits_type::page_size);
@@ -227,8 +207,8 @@ class basic_sparse_set {
auto &from = packed[lhs];
auto &to = packed[rhs];
sparse_ref(from) = traits_type::combine(static_cast<typename traits_type::entity_type>(rhs), traits_type::to_integral(from));
sparse_ref(to) = traits_type::combine(static_cast<typename traits_type::entity_type>(lhs), traits_type::to_integral(to));
sparse_ref(from) = traits_type::combine(static_cast<traits_type::entity_type>(rhs), traits_type::to_integral(from));
sparse_ref(to) = traits_type::combine(static_cast<traits_type::entity_type>(lhs), traits_type::to_integral(to));
std::swap(from, to);
}
@@ -248,25 +228,25 @@ protected:
/**
* @brief Erases an entity from a sparse set.
* @param it An iterator to the element to pop.
* @param entt A valid identifier for the element to pop.
*/
void swap_only(const basic_iterator it) {
void swap_only(const Entity entt) {
ENTT_ASSERT(mode == deletion_policy::swap_only, "Deletion policy mismatch");
const auto pos = index(*it);
bump(traits_type::next(*it));
const auto pos = index(entt);
bump(traits_type::next(entt));
swap_at(pos, head -= (pos < head));
}
/**
* @brief Erases an entity from a sparse set.
* @param it An iterator to the element to pop.
* @param entt A valid identifier for the element to pop.
*/
void swap_and_pop(const basic_iterator it) {
void swap_and_pop(const Entity entt) {
ENTT_ASSERT(mode == deletion_policy::swap_and_pop, "Deletion policy mismatch");
auto &self = sparse_ref(*it);
const auto entt = traits_type::to_entity(self);
sparse_ref(packed.back()) = traits_type::combine(entt, traits_type::to_integral(packed.back()));
packed[static_cast<size_type>(entt)] = packed.back();
auto &self = sparse_ref(entt);
const auto pos = traits_type::to_entity(self);
sparse_ref(packed.back()) = traits_type::combine(pos, traits_type::to_integral(packed.back()));
packed[static_cast<size_type>(pos)] = packed.back();
// unnecessary but it helps to detect nasty bugs
// NOLINTNEXTLINE(bugprone-assert-side-effect)
ENTT_ASSERT((packed.back() = null, true), "");
@@ -277,12 +257,12 @@ protected:
/**
* @brief Erases an entity from a sparse set.
* @param it An iterator to the element to pop.
* @param entt A valid identifier for the element to pop.
*/
void in_place_pop(const basic_iterator it) {
void in_place_pop(const Entity entt) {
ENTT_ASSERT(mode == deletion_policy::in_place, "Deletion policy mismatch");
const auto pos = entity_to_pos(std::exchange(sparse_ref(*it), null));
packed[pos] = traits_type::combine(static_cast<typename traits_type::entity_type>(std::exchange(head, pos)), tombstone);
const auto pos = entity_to_pos(std::exchange(sparse_ref(entt), null));
packed[pos] = traits_type::combine(static_cast<traits_type::entity_type>(std::exchange(head, pos)), tombstone);
}
/**
@@ -294,17 +274,17 @@ protected:
switch(mode) {
case deletion_policy::swap_and_pop:
for(; first != last; ++first) {
swap_and_pop(first);
swap_and_pop(*first);
}
break;
case deletion_policy::in_place:
for(; first != last; ++first) {
in_place_pop(first);
in_place_pop(*first);
}
break;
case deletion_policy::swap_only:
for(; first != last; ++first) {
swap_only(first);
swap_only(*first);
}
break;
}
@@ -312,23 +292,15 @@ protected:
/*! @brief Erases all entities of a sparse set. */
virtual void pop_all() {
switch(mode) {
case deletion_policy::in_place:
if(head != max_size) {
for(auto &&elem: packed) {
if(elem != tombstone) {
sparse_ref(elem) = null;
if(!packed.empty()) {
// suboptimal with few entities, but exploits cache way more with many
for(auto &&elem: sparse) {
if(elem) {
for(size_type pos{}; pos < traits_type::page_size; ++pos) {
elem[pos] = null;
}
}
break;
}
[[fallthrough]];
case deletion_policy::swap_only:
case deletion_policy::swap_and_pop:
for(auto &&elem: packed) {
sparse_ref(elem) = null;
}
break;
}
head = policy_to_head();
@@ -351,7 +323,7 @@ protected:
if(head != max_size && !force_back) {
pos = head;
ENTT_ASSERT(elem == null, "Slot not available");
elem = traits_type::combine(static_cast<typename traits_type::entity_type>(head), traits_type::to_integral(entt));
elem = traits_type::combine(static_cast<traits_type::entity_type>(head), traits_type::to_integral(entt));
head = entity_to_pos(std::exchange(packed[pos], entt));
break;
}
@@ -359,12 +331,12 @@ protected:
case deletion_policy::swap_and_pop:
packed.push_back(entt);
ENTT_ASSERT(elem == null, "Slot not available");
elem = traits_type::combine(static_cast<typename traits_type::entity_type>(packed.size() - 1u), traits_type::to_integral(entt));
elem = traits_type::combine(static_cast<traits_type::entity_type>(packed.size() - 1u), traits_type::to_integral(entt));
break;
case deletion_policy::swap_only:
if(elem == null) {
packed.push_back(entt);
elem = traits_type::combine(static_cast<typename traits_type::entity_type>(packed.size() - 1u), traits_type::to_integral(entt));
elem = traits_type::combine(static_cast<traits_type::entity_type>(packed.size() - 1u), traits_type::to_integral(entt));
} else {
ENTT_ASSERT(!(entity_to_pos(elem) < head), "Slot not available");
bump(entt);
@@ -375,7 +347,7 @@ protected:
break;
}
return --(end() - static_cast<difference_type>(pos));
return iterator{packed, static_cast<difference_type>(++pos)};
}
/*! @brief Forwards variables to derived classes, if any. */
@@ -386,15 +358,15 @@ public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Underlying entity identifier. */
using entity_type = typename traits_type::value_type;
using entity_type = traits_type::value_type;
/*! @brief Underlying version type. */
using version_type = typename traits_type::version_type;
using version_type = traits_type::version_type;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Signed integer type. */
using difference_type = std::ptrdiff_t;
/*! @brief Pointer type to contained entities. */
using pointer = typename packed_container_type::const_pointer;
using pointer = packed_container_type::const_pointer;
/*! @brief Random access iterator type. */
using iterator = basic_iterator;
/*! @brief Constant random access iterator type. */
@@ -433,7 +405,7 @@ public:
explicit basic_sparse_set(const type_info &elem, deletion_policy pol = deletion_policy::swap_and_pop, const allocator_type &allocator = {})
: sparse{allocator},
packed{allocator},
info{&elem},
descriptor{&elem},
mode{pol},
head{policy_to_head()} {
ENTT_ASSERT(traits_type::version_mask || mode != deletion_policy::in_place, "Policy does not support zero-sized versions");
@@ -449,7 +421,7 @@ public:
basic_sparse_set(basic_sparse_set &&other) noexcept
: sparse{std::move(other.sparse)},
packed{std::move(other.packed)},
info{other.info},
descriptor{other.descriptor},
mode{other.mode},
head{std::exchange(other.head, policy_to_head())} {}
@@ -461,7 +433,7 @@ public:
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},
descriptor{other.descriptor},
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");
@@ -497,7 +469,7 @@ public:
using std::swap;
swap(sparse, other.sparse);
swap(packed, other.packed);
swap(info, other.info);
swap(descriptor, other.descriptor);
swap(mode, other.mode);
swap(head, other.head);
}
@@ -560,11 +532,9 @@ public:
virtual void shrink_to_fit() {
sparse_container_type other{sparse.get_allocator()};
const auto len = sparse.size();
size_type cnt{};
other.reserve(len);
for(auto &&elem: std::as_const(packed)) {
for(size_type cnt{}; auto &&elem: std::as_const(packed)) {
if(elem != tombstone) {
if(const auto page = pos_to_page(entity_to_pos(elem)); sparse[page] != nullptr) {
if(const auto sz = page + 1u; sz > other.size()) {
@@ -805,14 +775,12 @@ public:
* Attempting to assign an entity that already belongs to the sparse set
* results in undefined behavior.
*
* @tparam It Type of input iterator.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @return Iterator pointing to the first element inserted in case of
* success, the `end()` iterator otherwise.
*/
template<typename It>
iterator push(It first, It last) {
iterator push(stl::input_iterator auto first, stl::input_iterator auto last) {
auto curr = end();
for(; first != last; ++first) {
@@ -863,7 +831,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.
*/
template<typename It>
template<stl::input_iterator It>
void erase(It first, It last) {
if constexpr(std::is_same_v<It, basic_iterator>) {
pop(first, last);
@@ -890,7 +858,7 @@ public:
* @param last An iterator past the last element of the range of entities.
* @return The number of entities actually removed.
*/
template<typename It>
template<stl::input_iterator It>
size_type remove(It first, It last) {
size_type count{};
@@ -932,7 +900,7 @@ public:
swap_or_move(from, to);
packed[to] = packed[from];
const auto elem = static_cast<typename traits_type::entity_type>(to);
const auto elem = static_cast<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) {}
@@ -1011,7 +979,7 @@ public:
const auto entt = packed[curr];
swap_or_move(next, idx);
const auto elem = static_cast<typename traits_type::entity_type>(curr);
const auto elem = static_cast<traits_type::entity_type>(curr);
sparse_ref(entt) = traits_type::combine(elem, traits_type::to_integral(packed[curr]));
curr = std::exchange(next, idx);
}
@@ -1049,7 +1017,7 @@ public:
* @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>
template<stl::input_iterator It>
iterator sort_as(It first, It last) {
ENTT_ASSERT((mode != deletion_policy::in_place) || (head == max_size), "Sorting with tombstones not allowed");
const size_type len = (mode == deletion_policy::swap_only) ? head : packed.size();
@@ -1079,11 +1047,11 @@ public:
}
/**
* @brief Returned value type, if any.
* @return Returned value type, if any.
* @brief Returns a type info object for the value type, if any.
* @return A type info object for the value type, if any.
*/
[[nodiscard]] const type_info &type() const noexcept {
return *info;
[[nodiscard]] const type_info &info() const noexcept {
return *descriptor;
}
/**
@@ -1099,7 +1067,7 @@ public:
private:
sparse_container_type sparse;
packed_container_type packed;
const type_info *info;
const type_info *descriptor;
deletion_policy mode;
size_type head;
};

View File

@@ -1,6 +1,8 @@
#ifndef ENTT_ENTITY_STORAGE_HPP
#define ENTT_ENTITY_STORAGE_HPP
#include <compare>
#include <concepts>
#include <cstddef>
#include <iterator>
#include <memory>
@@ -13,6 +15,8 @@
#include "../core/iterator.hpp"
#include "../core/memory.hpp"
#include "../core/type_info.hpp"
#include "../stl/iterator.hpp"
#include "../stl/memory.hpp"
#include "component.hpp"
#include "entity.hpp"
#include "fwd.hpp"
@@ -20,12 +24,13 @@
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename Container, auto Page>
class storage_iterator final {
friend storage_iterator<const Container, Page>;
template<typename, auto>
friend class storage_iterator;
using container_type = std::remove_const_t<Container>;
using alloc_traits = std::allocator_traits<typename container_type::allocator_type>;
@@ -36,10 +41,10 @@ class storage_iterator final {
typename alloc_traits::template rebind_traits<typename std::pointer_traits<typename container_type::value_type>::element_type>::pointer>>;
public:
using value_type = typename iterator_traits::value_type;
using pointer = typename iterator_traits::pointer;
using reference = typename iterator_traits::reference;
using difference_type = typename iterator_traits::difference_type;
using value_type = iterator_traits::value_type;
using pointer = iterator_traits::pointer;
using reference = iterator_traits::reference;
using difference_type = iterator_traits::difference_type;
using iterator_category = std::random_access_iterator_tag;
constexpr storage_iterator() noexcept = default;
@@ -48,8 +53,9 @@ public:
: payload{ref},
offset{idx} {}
template<bool Const = std::is_const_v<Container>, typename = std::enable_if_t<Const>>
constexpr storage_iterator(const storage_iterator<std::remove_const_t<Container>, Page> &other) noexcept
template<std::same_as<std::remove_const_t<Container>> Other>
requires std::is_const_v<Container>
constexpr storage_iterator(const storage_iterator<Other, Page> &other) noexcept
: storage_iterator{other.payload, other.offset} {}
constexpr storage_iterator &operator++() noexcept {
@@ -89,7 +95,7 @@ public:
}
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
const auto pos = static_cast<typename Container::size_type>(index() - value);
const auto pos = static_cast<Container::size_type>(index() - value);
return (*payload)[pos / Page][fast_mod(static_cast<std::size_t>(pos), Page)];
}
@@ -101,6 +107,23 @@ public:
return operator[](0);
}
template<typename Other, auto Arg>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator<Other, Arg> &other) const noexcept {
// intentionally reversed due to backward iteration
return other.offset - offset;
}
template<typename Other, auto Arg>
[[nodiscard]] constexpr bool operator==(const storage_iterator<Other, Arg> &other) const noexcept {
return offset == other.offset;
}
template<typename Other, auto Arg>
[[nodiscard]] constexpr auto operator<=>(const storage_iterator<Other, Arg> &other) const noexcept {
// intentionally reversed due to backward iteration
return other.offset <=> offset;
}
[[nodiscard]] constexpr difference_type index() const noexcept {
return offset - 1;
}
@@ -110,44 +133,9 @@ private:
difference_type offset;
};
template<typename Lhs, typename Rhs, auto Page>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator<Lhs, Page> &lhs, const storage_iterator<Rhs, Page> &rhs) noexcept {
return rhs.index() - lhs.index();
}
template<typename Lhs, typename Rhs, auto Page>
[[nodiscard]] constexpr bool operator==(const storage_iterator<Lhs, Page> &lhs, const storage_iterator<Rhs, Page> &rhs) noexcept {
return lhs.index() == rhs.index();
}
template<typename Lhs, typename Rhs, auto Page>
[[nodiscard]] constexpr bool operator!=(const storage_iterator<Lhs, Page> &lhs, const storage_iterator<Rhs, Page> &rhs) noexcept {
return !(lhs == rhs);
}
template<typename Lhs, typename Rhs, auto Page>
[[nodiscard]] constexpr bool operator<(const storage_iterator<Lhs, Page> &lhs, const storage_iterator<Rhs, Page> &rhs) noexcept {
return lhs.index() > rhs.index();
}
template<typename Lhs, typename Rhs, auto Page>
[[nodiscard]] constexpr bool operator>(const storage_iterator<Lhs, Page> &lhs, const storage_iterator<Rhs, Page> &rhs) noexcept {
return rhs < lhs;
}
template<typename Lhs, typename Rhs, auto Page>
[[nodiscard]] constexpr bool operator<=(const storage_iterator<Lhs, Page> &lhs, const storage_iterator<Rhs, Page> &rhs) noexcept {
return !(lhs > rhs);
}
template<typename Lhs, typename Rhs, auto Page>
[[nodiscard]] constexpr bool operator>=(const storage_iterator<Lhs, Page> &lhs, const storage_iterator<Rhs, Page> &rhs) noexcept {
return !(lhs < rhs);
}
template<typename It, typename... Other>
class extended_storage_iterator final {
template<typename Iter, typename... Args>
template<typename, typename...>
friend class extended_storage_iterator;
public:
@@ -165,7 +153,8 @@ public:
constexpr extended_storage_iterator(iterator_type base, Other... other)
: it{base, other...} {}
template<typename... Args, typename = std::enable_if_t<(!std::is_same_v<Other, Args> && ...) && (std::is_constructible_v<Other, Args> && ...)>>
template<typename... Args>
requires (!std::same_as<Other, Args> && ...) && (std::constructible_from<Other, Args> && ...)
constexpr extended_storage_iterator(const extended_storage_iterator<It, Args...> &other)
: it{other.it} {}
@@ -190,23 +179,15 @@ public:
return std::get<It>(it);
}
template<typename... Lhs, typename... Rhs>
friend constexpr bool operator==(const extended_storage_iterator<Lhs...> &, const extended_storage_iterator<Rhs...> &) noexcept;
template<typename... Args>
[[nodiscard]] constexpr bool operator==(const extended_storage_iterator<Args...> &other) const noexcept {
return std::get<0>(it) == std::get<0>(other.it);
}
private:
std::tuple<It, Other...> it;
};
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator==(const extended_storage_iterator<Lhs...> &lhs, const extended_storage_iterator<Rhs...> &rhs) noexcept {
return std::get<0>(lhs.it) == std::get<0>(rhs.it);
}
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator!=(const extended_storage_iterator<Lhs...> &lhs, const extended_storage_iterator<Rhs...> &rhs) noexcept {
return !(lhs == rhs);
}
} // namespace internal
/*! @endcond */
@@ -225,13 +206,13 @@ template<typename... Lhs, typename... Rhs>
* @tparam Entity A valid entity type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Type, typename Entity, typename Allocator, typename>
template<typename Type, typename Entity, typename Allocator>
class basic_storage: public basic_sparse_set<Entity, typename std::allocator_traits<Allocator>::template rebind_alloc<Entity>> {
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
using container_type = std::vector<typename alloc_traits::pointer, typename alloc_traits::template rebind_alloc<typename alloc_traits::pointer>>;
using underlying_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
using underlying_iterator = typename underlying_type::basic_iterator;
using underlying_iterator = underlying_type::basic_iterator;
using traits_type = component_traits<Type, Entity>;
[[nodiscard]] auto &element_at(const std::size_t pos) const {
@@ -265,7 +246,7 @@ 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 = to_address(assure_at_least(static_cast<size_type>(it.index())));
auto *elem = stl::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 {
@@ -280,13 +261,15 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
const auto from = (sz + traits_type::page_size - 1u) / traits_type::page_size;
allocator_type allocator{get_allocator()};
for(auto pos = sz, length = base_type::size(); pos < length; ++pos) {
if constexpr(traits_type::in_place_delete) {
if(base_type::data()[pos] != tombstone) {
if constexpr(!std::is_trivially_destructible_v<element_type>) {
for(auto pos = sz, length = base_type::size(); pos < length; ++pos) {
if constexpr(traits_type::in_place_delete) {
if(base_type::data()[pos] != tombstone) {
alloc_traits::destroy(allocator, std::addressof(element_at(pos)));
}
} else {
alloc_traits::destroy(allocator, std::addressof(element_at(pos)));
}
} else {
alloc_traits::destroy(allocator, std::addressof(element_at(pos)));
}
}
@@ -306,7 +289,7 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
void move_to(const std::size_t lhs, const std::size_t rhs) {
auto &elem = element_at(lhs);
allocator_type allocator{get_allocator()};
entt::uninitialized_construct_using_allocator(to_address(assure_at_least(rhs)), allocator, std::move(elem));
entt::uninitialized_construct_using_allocator(stl::to_address(assure_at_least(rhs)), allocator, std::move(elem));
alloc_traits::destroy(allocator, std::addressof(elem));
}
@@ -341,31 +324,38 @@ protected:
auto &elem = element_at(base_type::index(*first));
if constexpr(traits_type::in_place_delete) {
base_type::in_place_pop(first);
base_type::in_place_pop(*first);
alloc_traits::destroy(allocator, std::addressof(elem));
} else if constexpr(std::is_trivially_destructible_v<element_type>) {
elem = std::move(element_at(base_type::size() - 1u));
base_type::swap_and_pop(*first);
} else {
auto &other = element_at(base_type::size() - 1u);
// destroying on exit allows reentrant destructors
[[maybe_unused]] auto unused = std::exchange(elem, std::move(other));
alloc_traits::destroy(allocator, std::addressof(other));
base_type::swap_and_pop(first);
base_type::swap_and_pop(*first);
}
}
}
/*! @brief Erases all entities of a storage. */
void pop_all() override {
allocator_type allocator{get_allocator()};
if constexpr(std::is_trivially_destructible_v<element_type>) {
base_type::pop_all();
} else {
allocator_type allocator{get_allocator()};
for(auto first = base_type::begin(); !(first.index() < 0); ++first) {
if constexpr(traits_type::in_place_delete) {
if(*first != tombstone) {
base_type::in_place_pop(first);
for(auto first = base_type::begin(); !(first.index() < 0); ++first) {
if constexpr(traits_type::in_place_delete) {
if(*first != tombstone) {
base_type::in_place_pop(*first);
alloc_traits::destroy(allocator, std::addressof(element_at(static_cast<size_type>(first.index()))));
}
} else {
base_type::swap_and_pop(*first);
alloc_traits::destroy(allocator, std::addressof(element_at(static_cast<size_type>(first.index()))));
}
} else {
base_type::swap_and_pop(first);
alloc_traits::destroy(allocator, std::addressof(element_at(static_cast<size_type>(first.index()))));
}
}
}
@@ -409,9 +399,9 @@ public:
/*! @brief Signed integer type. */
using difference_type = std::ptrdiff_t;
/*! @brief Pointer type to contained elements. */
using pointer = typename container_type::pointer;
using pointer = 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;
using const_pointer = 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>;
/*! @brief Constant random access iterator type. */
@@ -450,24 +440,20 @@ public:
* @brief Move constructor.
* @param other The instance to move from.
*/
// NOLINTBEGIN(bugprone-use-after-move)
basic_storage(basic_storage &&other) noexcept
: base_type{std::move(other)},
: base_type{static_cast<base_type &&>(other)},
payload{std::move(other.payload)} {}
// NOLINTEND(bugprone-use-after-move)
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
// NOLINTBEGIN(bugprone-use-after-move)
basic_storage(basic_storage &&other, const allocator_type &allocator)
: base_type{std::move(other), allocator},
: base_type{static_cast<base_type &&>(other), allocator},
payload{std::move(other.payload), allocator} {
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a storage is not allowed");
}
// NOLINTEND(bugprone-use-after-move)
/*! @brief Default destructor. */
// NOLINTNEXTLINE(bugprone-exception-escape)
@@ -714,14 +700,12 @@ public:
* Attempting to assign an entity that already belongs to the storage
* results in undefined behavior.
*
* @tparam It Type of input iterator.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @param value An instance of the object to construct.
* @return Iterator pointing to the first element inserted, if any.
*/
template<typename It>
iterator insert(It first, It last, const value_type &value = {}) {
iterator insert(stl::input_iterator auto first, stl::input_iterator auto last, const value_type &value = {}) {
for(; first != last; ++first) {
emplace_element(*first, true, value);
}
@@ -735,15 +719,15 @@ public:
*
* @sa construct
*
* @tparam EIt Type of input iterator.
* @tparam CIt Type of input iterator.
* @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 from An iterator to the first element of the range of objects.
* @return Iterator pointing to the first element inserted, if any.
*/
template<typename EIt, typename CIt, typename = std::enable_if_t<std::is_same_v<typename std::iterator_traits<CIt>::value_type, value_type>>>
iterator insert(EIt first, EIt last, CIt from) {
template<stl::input_iterator It>
requires std::same_as<typename std::iterator_traits<It>::value_type, value_type>
iterator insert(stl::input_iterator auto first, stl::input_iterator auto last, It from) {
for(; first != last; ++first, ++from) {
emplace_element(*first, true, *from);
}
@@ -790,7 +774,8 @@ private:
/*! @copydoc basic_storage */
template<typename Type, typename Entity, typename Allocator>
class basic_storage<Type, Entity, Allocator, std::enable_if_t<component_traits<Type, Entity>::page_size == 0u>>
requires (component_traits<Type, Entity>::page_size == 0u)
class basic_storage<Type, Entity, Allocator>
: 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");
@@ -871,12 +856,7 @@ public:
* @return The associated allocator.
*/
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
// std::allocator<void> has no cross constructors (waiting for C++20)
if constexpr(std::is_void_v<element_type> && !std::is_constructible_v<allocator_type, typename base_type::allocator_type>) {
return allocator_type{};
} else {
return allocator_type{base_type::get_allocator()};
}
return allocator_type{base_type::get_allocator()};
}
/**
@@ -909,12 +889,9 @@ public:
* Attempting to use an entity that already belongs to the storage results
* in undefined behavior.
*
* @tparam Args Types of arguments to use to construct the object.
* @param entt A valid identifier.
*/
template<typename... Args>
// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
void emplace(const entity_type entt, Args &&...) {
void emplace(const entity_type entt, const auto &...) {
base_type::try_emplace(entt, false);
}
@@ -932,14 +909,10 @@ public:
/**
* @brief Assigns entities to a storage.
* @tparam It Type of input iterator.
* @tparam Args Types of optional arguments.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
*/
template<typename It, typename... Args>
// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
void insert(It first, It last, Args &&...) {
void insert(stl::input_iterator auto first, stl::input_iterator auto last, const auto &...) {
for(; first != last; ++first) {
base_type::try_emplace(*first, true);
}
@@ -988,11 +961,11 @@ class basic_storage<Entity, Entity, Allocator>
: public basic_sparse_set<Entity, Allocator> {
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, Entity>, "Invalid value type");
using underlying_iterator = typename basic_sparse_set<Entity, Allocator>::basic_iterator;
using underlying_iterator = basic_sparse_set<Entity, Allocator>::basic_iterator;
using traits_type = entt_traits<Entity>;
auto from_placeholder() noexcept {
const auto entt = traits_type::combine(static_cast<typename traits_type::entity_type>(placeholder), {});
const auto entt = traits_type::combine(static_cast<traits_type::entity_type>(placeholder), {});
ENTT_ASSERT(entt != null, "No more entities available");
placeholder += static_cast<size_type>(entt != null);
return entt;
@@ -1069,22 +1042,20 @@ public:
* @brief Move constructor.
* @param other The instance to move from.
*/
// NOLINTBEGIN(bugprone-use-after-move)
// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
basic_storage(basic_storage &&other) noexcept
: base_type{std::move(other)},
: base_type{static_cast<base_type &&>(other)},
placeholder{other.placeholder} {}
// NOLINTEND(bugprone-use-after-move)
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
// NOLINTBEGIN(bugprone-use-after-move)
// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
basic_storage(basic_storage &&other, const allocator_type &allocator)
: base_type{std::move(other), allocator},
: base_type{static_cast<base_type &&>(other), allocator},
placeholder{other.placeholder} {}
// NOLINTEND(bugprone-use-after-move)
/*! @brief Default destructor. */
~basic_storage() override = default;
@@ -1106,6 +1077,16 @@ public:
return *this;
}
/**
* @brief Exchanges the contents with those of a given storage.
* @param other Storage to exchange the content with.
*/
void swap(basic_storage &other) noexcept {
using std::swap;
swap(placeholder, other.placeholder);
base_type::swap(other);
}
/**
* @brief Returns the object assigned to an entity, that is `void`.
*
@@ -1160,11 +1141,11 @@ public:
/**
* @brief Assigns each element in a range an identifier.
* @tparam It Type of mutable forward iterator.
* @tparam It Type of output iterator.
* @param first An iterator to the first element of the range to generate.
* @param last An iterator past the last element of the range to generate.
*/
template<typename It>
template<stl::output_iterator<entity_type> It>
void generate(It first, It last) {
for(const auto sz = base_type::size(); first != last && base_type::free_list() != sz; ++first) {
*first = *base_type::try_emplace(base_type::data()[base_type::free_list()], true);
@@ -1175,27 +1156,6 @@ public:
}
}
/**
* @brief Creates a new identifier or recycles a destroyed one.
* @return A valid identifier.
*/
[[deprecated("use ::generate() instead")]] entity_type emplace() {
return generate();
}
/**
* @brief Creates a new identifier or recycles a destroyed one.
*
* If the requested identifier isn't in use, the suggested one is used.
* Otherwise, a new identifier is returned.
*
* @param hint Required identifier.
* @return A valid identifier.
*/
[[deprecated("use ::generate(hint) instead")]] entity_type emplace(const entity_type hint) {
return generate(hint);
}
/**
* @brief Updates a given identifier.
* @tparam Func Types of the function objects to invoke.
@@ -1208,17 +1168,6 @@ public:
(std::forward<Func>(func)(), ...);
}
/**
* @brief Assigns each element in a range an identifier.
* @tparam It Type of mutable forward iterator.
* @param first An iterator to the first element of the range to generate.
* @param last An iterator past the last element of the range to generate.
*/
template<typename It>
[[deprecated("use ::generate(first, last) instead")]] void insert(It first, It last) {
generate(std::move(first), std::move(last));
}
/**
* @brief Returns an iterable object to use to _visit_ a storage.
*

View File

@@ -2,12 +2,14 @@
#define ENTT_ENTITY_VIEW_HPP
#include <array>
#include <concepts>
#include <cstddef>
#include <iterator>
#include <tuple>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/concepts.hpp"
#include "../core/iterator.hpp"
#include "../core/type_traits.hpp"
#include "entity.hpp"
@@ -15,28 +17,25 @@
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename... Type>
// NOLINTNEXTLINE(misc-redundant-expression)
static constexpr bool tombstone_check_v = ((sizeof...(Type) == 1u) && ... && (Type::storage_policy == deletion_policy::in_place));
template<typename Type>
template<cvref_unqualified Type>
const Type *view_placeholder() {
static_assert(std::is_same_v<std::remove_const_t<std::remove_reference_t<Type>>, Type>, "Unexpected type");
static const Type placeholder{};
return &placeholder;
}
template<typename It, typename Entity>
[[nodiscard]] bool all_of(It first, const It last, const Entity entt) noexcept {
[[nodiscard]] bool all_of(auto first, const auto last, const auto entt) noexcept {
for(; (first != last) && (*first)->contains(entt); ++first) {}
return first == last;
}
template<typename It, typename Entity>
[[nodiscard]] bool none_of(It first, const It last, const Entity entt) noexcept {
[[nodiscard]] bool none_of(auto first, const auto last, const auto entt) noexcept {
for(; (first != last) && !(*first)->contains(entt); ++first) {}
return first == last;
}
@@ -52,7 +51,7 @@ template<typename Result, typename View, typename Other, std::size_t... GLhs, st
Result elem{};
// friend-initialization, avoid multiple calls to refresh
elem.pools = {view.template storage<GLhs>()..., other.template storage<GRhs>()...};
auto filter_or_placeholder = [placeholder = elem.placeholder](auto *value) { return (value == nullptr) ? placeholder : value; };
[[maybe_unused]] const auto filter_or_placeholder = [placeholder = elem.placeholder](auto *value) { return (value == nullptr) ? placeholder : value; };
elem.filter = {filter_or_placeholder(view.template storage<sizeof...(GLhs) + ELhs>())..., filter_or_placeholder(other.template storage<sizeof...(GRhs) + ERhs>())...};
elem.refresh();
return elem;
@@ -61,12 +60,12 @@ template<typename Result, typename View, typename Other, std::size_t... GLhs, st
template<typename Type, bool Checked, std::size_t Get, std::size_t Exclude>
class view_iterator final {
template<typename, typename...>
friend class extended_view_iterator;
friend struct extended_view_iterator;
using iterator_type = typename Type::const_iterator;
using iterator_type = Type::const_iterator;
using iterator_traits = std::iterator_traits<iterator_type>;
[[nodiscard]] bool valid(const typename iterator_traits::value_type entt) const noexcept {
[[nodiscard]] bool valid(const iterator_traits::value_type entt) const noexcept {
return (!Checked || (entt != tombstone))
&& ((Get == 1u) || (internal::all_of(pools.begin(), pools.begin() + index, entt) && internal::all_of(pools.begin() + index + 1, pools.end(), entt)))
&& ((Exclude == 0u) || internal::none_of(filter.begin(), filter.end(), entt));
@@ -77,10 +76,10 @@ class view_iterator final {
}
public:
using value_type = typename iterator_traits::value_type;
using pointer = typename iterator_traits::pointer;
using reference = typename iterator_traits::reference;
using difference_type = typename iterator_traits::difference_type;
using value_type = iterator_traits::value_type;
using pointer = iterator_traits::pointer;
using reference = iterator_traits::reference;
using difference_type = iterator_traits::difference_type;
using iterator_category = std::forward_iterator_tag;
constexpr view_iterator() noexcept
@@ -117,8 +116,10 @@ public:
return *operator->();
}
template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs>
friend constexpr bool operator==(const view_iterator<LhsType, LhsArgs...> &, const view_iterator<RhsType, RhsArgs...> &) noexcept;
template<typename Other, auto... Args>
[[nodiscard]] constexpr bool operator==(const view_iterator<Other, Args...> &other) const noexcept {
return it == other.it;
}
private:
iterator_type it;
@@ -127,24 +128,8 @@ private:
difference_type index;
};
template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs>
[[nodiscard]] constexpr bool operator==(const view_iterator<LhsType, LhsArgs...> &lhs, const view_iterator<RhsType, RhsArgs...> &rhs) noexcept {
return lhs.it == rhs.it;
}
template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs>
[[nodiscard]] constexpr bool operator!=(const view_iterator<LhsType, LhsArgs...> &lhs, const view_iterator<RhsType, RhsArgs...> &rhs) noexcept {
return !(lhs == rhs);
}
template<typename It, typename... Get>
class extended_view_iterator final {
template<std::size_t... Index>
[[nodiscard]] auto dereference(std::index_sequence<Index...>) const noexcept {
return std::tuple_cat(std::make_tuple(*it), static_cast<Get *>(const_cast<constness_as_t<typename Get::base_type, Get> *>(std::get<Index>(it.pools)))->get_as_tuple(*it)...);
}
public:
struct extended_view_iterator final {
using iterator_type = It;
using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::declval<Get>().get_as_tuple({})...));
using pointer = input_iterator_pointer<value_type>;
@@ -169,7 +154,9 @@ public:
}
[[nodiscard]] reference operator*() const noexcept {
return dereference(std::index_sequence_for<Get...>{});
return [this]<auto... Index>(std::index_sequence<Index...>) {
return std::tuple_cat(std::make_tuple(*it), static_cast<Get *>(const_cast<constness_as_t<typename Get::base_type, Get> *>(std::get<Index>(it.pools)))->get_as_tuple(*it)...);
}(std::index_sequence_for<Get...>{});
}
[[nodiscard]] pointer operator->() const noexcept {
@@ -180,23 +167,15 @@ public:
return it;
}
template<typename... Lhs, typename... Rhs>
friend bool constexpr operator==(const extended_view_iterator<Lhs...> &, const extended_view_iterator<Rhs...> &) noexcept;
template<typename... Other>
[[nodiscard]] constexpr bool operator==(const extended_view_iterator<Other...> &other) const noexcept {
return it == other.it;
}
private:
It it;
};
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator==(const extended_view_iterator<Lhs...> &lhs, const extended_view_iterator<Rhs...> &rhs) noexcept {
return lhs.it == rhs.it;
}
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator!=(const extended_view_iterator<Lhs...> &lhs, const extended_view_iterator<Rhs...> &rhs) noexcept {
return !(lhs == rhs);
}
} // namespace internal
/*! @endcond */
@@ -218,7 +197,7 @@ template<typename... Lhs, typename... Rhs>
* In all other cases, modifying the storage iterated by a view in any way can
* invalidate all iterators.
*/
template<typename, typename, typename>
template<typename, typename>
class basic_view;
/**
@@ -229,10 +208,8 @@ class basic_view;
* @tparam Get Number of storage iterated by the view.
* @tparam Exclude Number of storage used to filter the view.
*/
template<typename Type, bool Checked, std::size_t Get, std::size_t Exclude>
template<cvref_unqualified Type, bool Checked, std::size_t Get, std::size_t Exclude>
class basic_common_view {
static_assert(std::is_same_v<std::remove_const_t<std::remove_reference_t<Type>>, Type>, "Unexpected type");
template<typename Return, typename View, typename Other, std::size_t... GLhs, std::size_t... ELhs, std::size_t... GRhs, std::size_t... ERhs>
friend Return internal::view_pack(const View &, const Other &, std::index_sequence<GLhs...>, std::index_sequence<ELhs...>, std::index_sequence<GRhs...>, std::index_sequence<ERhs...>);
@@ -254,7 +231,7 @@ class basic_common_view {
}
protected:
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
basic_common_view() noexcept {
for(size_type pos{}, last = filter.size(); pos < last; ++pos) {
filter[pos] = placeholder;
@@ -287,7 +264,7 @@ protected:
filter[pos] = elem;
}
[[nodiscard]] bool none_of(const typename Type::entity_type entt) const noexcept {
[[nodiscard]] bool none_of(const Type::entity_type entt) const noexcept {
return internal::none_of(filter.begin(), filter.end(), entt);
}
@@ -300,7 +277,7 @@ public:
/*! @brief Common type among all storage types. */
using common_type = Type;
/*! @brief Underlying entity identifier. */
using entity_type = typename Type::entity_type;
using entity_type = Type::entity_type;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Signed integer type. */
@@ -429,7 +406,8 @@ private:
* @tparam Exclude Types of storage used to filter the view.
*/
template<typename... Get, typename... Exclude>
class basic_view<get_t<Get...>, exclude_t<Exclude...>, std::enable_if_t<(sizeof...(Get) != 0u)>>
requires (sizeof...(Get) != 0u)
class basic_view<get_t<Get...>, exclude_t<Exclude...>>
: public basic_common_view<std::common_type_t<typename Get::base_type...>, internal::tombstone_check_v<Get...>, sizeof...(Get), sizeof...(Exclude)> {
using base_type = basic_common_view<std::common_type_t<typename Get::base_type...>, internal::tombstone_check_v<Get...>, sizeof...(Get), sizeof...(Exclude)>;
@@ -439,11 +417,6 @@ class basic_view<get_t<Get...>, exclude_t<Exclude...>, std::enable_if_t<(sizeof.
template<typename Type>
static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Get::element_type..., typename Exclude::element_type...>>;
template<std::size_t... Index>
[[nodiscard]] auto get(const typename base_type::entity_type entt, std::index_sequence<Index...>) const noexcept {
return std::tuple_cat(storage<Index>()->get_as_tuple(entt)...);
}
template<std::size_t Curr, std::size_t Other, typename... Args>
[[nodiscard]] auto dispatch_get(const std::tuple<typename base_type::entity_type, Args...> &curr) const {
if constexpr(Curr == Other) {
@@ -454,7 +427,7 @@ class basic_view<get_t<Get...>, exclude_t<Exclude...>, std::enable_if_t<(sizeof.
}
template<std::size_t Curr, typename Func, std::size_t... Index>
void each(Func &func, std::index_sequence<Index...>) const {
void each(Func func, std::index_sequence<Index...>) const {
for(const auto curr: storage<Curr>()->each()) {
if(const auto entt = std::get<0>(curr); (!internal::tombstone_check_v<Get...> || (entt != tombstone)) && ((Curr == Index || base_type::pool_at(Index)->contains(entt)) && ...) && base_type::none_of(entt)) {
if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_view>().get({})))>) {
@@ -466,24 +439,24 @@ class basic_view<get_t<Get...>, exclude_t<Exclude...>, std::enable_if_t<(sizeof.
}
}
template<typename Func, std::size_t... Index>
void pick_and_each(Func &func, std::index_sequence<Index...> seq) const {
if(const auto *view = base_type::handle(); view != nullptr) {
((view == base_type::pool_at(Index) ? each<Index>(func, seq) : void()), ...);
template<typename Type>
void storage_if(Type *elem) noexcept {
if(elem != nullptr) {
storage<index_of<typename Type::element_type>>(*elem);
}
}
public:
/*! @brief Common type among all storage types. */
using common_type = typename base_type::common_type;
using common_type = base_type::common_type;
/*! @brief Underlying entity identifier. */
using entity_type = typename base_type::entity_type;
using entity_type = base_type::entity_type;
/*! @brief Unsigned integer type. */
using size_type = typename base_type::size_type;
using size_type = base_type::size_type;
/*! @brief Signed integer type. */
using difference_type = std::ptrdiff_t;
/*! @brief Forward iterator type. */
using iterator = typename base_type::iterator;
using iterator = base_type::iterator;
/*! @brief Iterable view type. */
using iterable = iterable_adaptor<internal::extended_view_iterator<iterator, Get...>>;
@@ -508,6 +481,19 @@ public:
basic_view(std::tuple<Get &...> value, std::tuple<Exclude &...> excl = {}) noexcept
: basic_view{std::make_from_tuple<basic_view>(std::tuple_cat(value, excl))} {}
/**
* @brief Constructs a view from a convertible counterpart.
* @tparam Args Storage types managed by the other view.
* @param other A view to convert from.
*/
template<typename... Args>
requires (!std::same_as<basic_view, basic_view<Args...>>)
basic_view(const basic_view<Args...> &other) noexcept
: basic_view{} {
(storage_if(other.template storage<typename Get::element_type>()), ...);
(storage_if(other.template storage<typename Exclude::element_type>()), ...);
}
/**
* @brief Forces a view to use a given element to drive iterations
* @tparam Type Type of element to use to drive iterations.
@@ -607,7 +593,9 @@ public:
template<std::size_t... Index>
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
if constexpr(sizeof...(Index) == 0) {
return get(entt, std::index_sequence_for<Get...>{});
return [this, entt]<auto... Idx>(std::index_sequence<Idx...>) {
return std::tuple_cat(this->storage<Idx>()->get_as_tuple(entt)...);
}(std::index_sequence_for<Get...>{});
} else if constexpr(sizeof...(Index) == 1) {
return (storage<Index>()->get(entt), ...);
} else {
@@ -632,7 +620,11 @@ public:
*/
template<typename Func>
void each(Func func) const {
pick_and_each(func, std::index_sequence_for<Get...>{});
[this, &func]<auto... Index>(std::index_sequence<Index...> seq) {
if(const auto *view = base_type::handle(); view != nullptr) {
((view == base_type::pool_at(Index) ? each<Index>(std::move(func), seq) : void()), ...);
}
}(std::index_sequence_for<Get...>{});
}
/**
@@ -654,8 +646,8 @@ public:
* @param other The storage for the type to combine the view with.
* @return A more specific view.
*/
template<typename OGet>
[[nodiscard]] std::enable_if_t<std::is_base_of_v<common_type, OGet>, basic_view<get_t<Get..., OGet>, exclude_t<Exclude...>>> operator|(OGet &other) const noexcept {
template<std::derived_from<common_type> OGet>
[[nodiscard]] basic_view<get_t<Get..., OGet>, exclude_t<Exclude...>> operator|(OGet &other) const noexcept {
return *this | basic_view<get_t<OGet>, exclude_t<>>{other};
}
@@ -666,7 +658,7 @@ public:
* @param other The view to combine with.
* @return A more specific view.
*/
template<typename... OGet, typename... OExclude>
template<std::derived_from<common_type>... OGet, std::derived_from<common_type>... OExclude>
[[nodiscard]] auto operator|(const basic_view<get_t<OGet...>, exclude_t<OExclude...>> &other) const noexcept {
return internal::view_pack<basic_view<get_t<Get..., OGet...>, exclude_t<Exclude..., OExclude...>>>(
*this, other, std::index_sequence_for<Get...>{}, std::index_sequence_for<Exclude...>{}, std::index_sequence_for<OGet...>{}, std::index_sequence_for<OExclude...>{});
@@ -679,12 +671,10 @@ public:
* @tparam Type Common type among all storage types.
* @tparam Policy Storage policy.
*/
template<typename Type, deletion_policy Policy>
template<cvref_unqualified Type, deletion_policy Policy>
class basic_storage_view {
static_assert(std::is_same_v<std::remove_const_t<std::remove_reference_t<Type>>, Type>, "Unexpected type");
protected:
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
basic_storage_view() noexcept = default;
basic_storage_view(const Type *value) noexcept
@@ -697,7 +687,7 @@ public:
/*! @brief Common type among all storage types. */
using common_type = Type;
/*! @brief Underlying entity identifier. */
using entity_type = typename common_type::entity_type;
using entity_type = common_type::entity_type;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Signed integer type. */
@@ -717,11 +707,10 @@ public:
/**
* @brief Returns the number of entities that have the given element.
* @tparam Pol Dummy template parameter used for sfinae purposes only.
* @return Number of entities that have the given element.
*/
template<typename..., deletion_policy Pol = Policy>
[[nodiscard]] std::enable_if_t<Pol != deletion_policy::in_place, size_type> size() const noexcept {
[[nodiscard]] size_type size() const noexcept
requires (Policy != deletion_policy::in_place) {
if constexpr(Policy == deletion_policy::swap_and_pop) {
return leading ? leading->size() : size_type{};
} else {
@@ -732,21 +721,19 @@ public:
/**
* @brief Estimates the number of entities iterated by the view.
* @tparam Pol Dummy template parameter used for sfinae purposes only.
* @return Estimated number of entities iterated by the view.
*/
template<typename..., deletion_policy Pol = Policy>
[[nodiscard]] std::enable_if_t<Pol == deletion_policy::in_place, size_type> size_hint() const noexcept {
[[nodiscard]] size_type size_hint() const noexcept
requires (Policy == deletion_policy::in_place) {
return leading ? leading->size() : size_type{};
}
/**
* @brief Checks whether a view is empty.
* @tparam Pol Dummy template parameter used for sfinae purposes only.
* @return True if the view is empty, false otherwise.
*/
template<typename..., deletion_policy Pol = Policy>
[[nodiscard]] std::enable_if_t<Pol != deletion_policy::in_place, bool> empty() const noexcept {
[[nodiscard]] bool empty() const noexcept
requires (Policy != deletion_policy::in_place) {
if constexpr(Policy == deletion_policy::swap_and_pop) {
return !leading || leading->empty();
} else {
@@ -791,23 +778,21 @@ public:
*
* If the view is empty, the returned iterator will be equal to `rend()`.
*
* @tparam Pol Dummy template parameter used for sfinae purposes only.
* @return An iterator to the first entity of the reversed view.
*/
template<typename..., deletion_policy Pol = Policy>
[[nodiscard]] std::enable_if_t<Pol != deletion_policy::in_place, reverse_iterator> rbegin() const noexcept {
[[nodiscard]] reverse_iterator rbegin() const noexcept
requires (Policy != deletion_policy::in_place) {
return leading ? leading->rbegin() : reverse_iterator{};
}
/**
* @brief Returns an iterator that is past the last entity of the reversed
* view.
* @tparam Pol Dummy template parameter used for sfinae purposes only.
* @return An iterator to the entity following the last entity of the
* reversed view.
*/
template<typename..., deletion_policy Pol = Policy>
[[nodiscard]] std::enable_if_t<Pol != deletion_policy::in_place, reverse_iterator> rend() const noexcept {
[[nodiscard]] reverse_iterator rend() const noexcept
requires (Policy != deletion_policy::in_place) {
if constexpr(Policy == deletion_policy::swap_and_pop) {
return leading ? leading->rend() : reverse_iterator{};
} else {
@@ -913,19 +898,25 @@ class basic_view<get_t<Get>, exclude_t<>>
: public basic_storage_view<typename Get::base_type, Get::storage_policy> {
using base_type = basic_storage_view<typename Get::base_type, Get::storage_policy>;
void storage_if(Get *value) noexcept {
if(value != nullptr) {
storage(*value);
}
}
public:
/*! @brief Common type among all storage types. */
using common_type = typename base_type::common_type;
using common_type = base_type::common_type;
/*! @brief Underlying entity identifier. */
using entity_type = typename base_type::entity_type;
using entity_type = base_type::entity_type;
/*! @brief Unsigned integer type. */
using size_type = typename base_type::size_type;
using size_type = base_type::size_type;
/*! @brief Signed integer type. */
using difference_type = std::ptrdiff_t;
/*! @brief Random access iterator type. */
using iterator = typename base_type::iterator;
using iterator = base_type::iterator;
/*! @brief Reverse iterator type. */
using reverse_iterator = typename base_type::reverse_iterator;
using reverse_iterator = base_type::reverse_iterator;
/*! @brief Iterable view type. */
using iterable = std::conditional_t<Get::storage_policy == deletion_policy::in_place, iterable_adaptor<internal::extended_view_iterator<iterator, Get>>, decltype(std::declval<Get>().each())>;
@@ -948,12 +939,24 @@ public:
basic_view(std::tuple<Get &> value, std::tuple<> = {}) noexcept
: basic_view{std::get<0>(value)} {}
/**
* @brief Constructs a view from a convertible counterpart.
* @tparam Args Storage types managed by the other view.
* @param other A view to convert from.
*/
template<typename... Args>
requires (!std::same_as<basic_view, basic_view<Args...>>)
basic_view(const basic_view<Args...> &other) noexcept
: base_type{} {
storage_if(other.template storage<typename Get::element_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 = typename Get::element_type>
template<typename Type = Get::element_type>
[[nodiscard]] auto *storage() const noexcept {
static_assert(std::is_same_v<std::remove_const_t<Type>, typename Get::element_type>, "Invalid element type");
return storage<0>();
@@ -1042,7 +1045,7 @@ public:
*
* @code{.cpp}
* void(const entity_type, Type &);
* void(typename Type &);
* void(Type &);
* @endcode
*
* @tparam Func Type of the function object to invoke.
@@ -1099,8 +1102,8 @@ public:
* @param other The storage for the type to combine the view with.
* @return A more specific view.
*/
template<typename OGet>
[[nodiscard]] std::enable_if_t<std::is_base_of_v<common_type, OGet>, basic_view<get_t<Get, OGet>, exclude_t<>>> operator|(OGet &other) const noexcept {
template<std::derived_from<common_type> OGet>
[[nodiscard]] basic_view<get_t<Get, OGet>, exclude_t<>> operator|(OGet &other) const noexcept {
return *this | basic_view<get_t<OGet>, exclude_t<>>{other};
}
@@ -1111,7 +1114,7 @@ public:
* @param other The view to combine with.
* @return A more specific view.
*/
template<typename... OGet, typename... OExclude>
template<std::derived_from<common_type>... OGet, std::derived_from<common_type>... OExclude>
[[nodiscard]] auto operator|(const basic_view<get_t<OGet...>, exclude_t<OExclude...>> &other) const noexcept {
return internal::view_pack<basic_view<get_t<Get, OGet...>, exclude_t<OExclude...>>>(
*this, other, std::index_sequence_for<Get>{}, std::index_sequence_for<>{}, std::index_sequence_for<OGet...>{}, std::index_sequence_for<OExclude...>{});

View File

@@ -10,9 +10,9 @@ namespace entt {}
#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/concepts.hpp"
#include "core/enum.hpp"
#include "core/family.hpp"
#include "core/hashed_string.hpp"
@@ -66,4 +66,7 @@ namespace entt {}
#include "signal/dispatcher.hpp"
#include "signal/emitter.hpp"
#include "signal/sigh.hpp"
#include "stl/functional.hpp"
#include "stl/iterator.hpp"
#include "stl/memory.hpp"
// IWYU pragma: end_exports

View File

@@ -1,6 +1,7 @@
#ifndef ENTT_GRAPH_ADJACENCY_MATRIX_HPP
#define ENTT_GRAPH_ADJACENCY_MATRIX_HPP
#include <concepts>
#include <cstddef>
#include <iterator>
#include <memory>
@@ -13,7 +14,7 @@
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename It>
@@ -21,7 +22,7 @@ class edge_iterator {
using size_type = std::size_t;
void find_next() noexcept {
for(; pos != last && !it[static_cast<typename It::difference_type>(pos)]; pos += offset) {}
for(; pos != last && !it[static_cast<It::difference_type>(pos)]; pos += offset) {}
}
public:
@@ -63,8 +64,9 @@ public:
return std::make_pair<size_type>(pos / vert, pos % vert);
}
template<typename Type>
friend constexpr bool operator==(const edge_iterator<Type> &, const edge_iterator<Type> &) noexcept;
[[nodiscard]] constexpr bool operator==(const edge_iterator &other) const noexcept {
return pos == other.pos;
}
private:
It it{};
@@ -74,16 +76,6 @@ private:
size_type offset{};
};
template<typename Container>
[[nodiscard]] constexpr bool operator==(const edge_iterator<Container> &lhs, const edge_iterator<Container> &rhs) noexcept {
return lhs.pos == rhs.pos;
}
template<typename Container>
[[nodiscard]] constexpr bool operator!=(const edge_iterator<Container> &lhs, const edge_iterator<Container> &rhs) noexcept {
return !(lhs == rhs);
}
} // namespace internal
/*! @endcond */
@@ -92,10 +84,9 @@ template<typename Container>
* @tparam Category Either a directed or undirected category tag.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Category, typename Allocator>
template<std::derived_from<directed_tag> Category, typename Allocator>
class adjacency_matrix {
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_base_of_v<directed_tag, Category>, "Invalid graph category");
static_assert(std::is_same_v<typename alloc_traits::value_type, std::size_t>, "Invalid value type");
using container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;

View File

@@ -1,8 +1,8 @@
#ifndef ENTT_GRAPH_DOT_HPP
#define ENTT_GRAPH_DOT_HPP
#include <concepts>
#include <ostream>
#include <type_traits>
#include "fwd.hpp"
namespace entt {
@@ -10,16 +10,14 @@ namespace entt {
/**
* @brief Outputs a graph in dot format.
* @tparam Graph Graph type, valid as long as it exposes edges and vertices.
* @tparam Writer Vertex decorator type.
* @param out A standard output stream.
* @param graph The graph to output.
* @param writer Vertex decorator object.
*/
template<typename Graph, typename Writer>
void dot(std::ostream &out, const Graph &graph, Writer writer) {
static_assert(std::is_base_of_v<directed_tag, typename Graph::graph_category>, "Invalid graph category");
if constexpr(std::is_same_v<typename Graph::graph_category, undirected_tag>) {
template<typename Graph>
requires std::derived_from<typename Graph::graph_category, directed_tag>
void dot(std::ostream &out, const Graph &graph, std::invocable<std::ostream &, typename Graph::vertex_type> auto writer) {
if constexpr(std::same_as<typename Graph::graph_category, undirected_tag>) {
out << "graph{";
} else {
out << "digraph{";
@@ -32,7 +30,7 @@ void dot(std::ostream &out, const Graph &graph, Writer writer) {
}
for(auto [lhs, rhs]: graph.edges()) {
if constexpr(std::is_same_v<typename Graph::graph_category, undirected_tag>) {
if constexpr(std::same_as<typename Graph::graph_category, undirected_tag>) {
out << lhs << "--" << rhs << ";";
} else {
out << lhs << "->" << rhs << ";";

View File

@@ -2,6 +2,7 @@
#define ENTT_GRAPH_FLOW_HPP
#include <algorithm>
#include <concepts>
#include <cstddef>
#include <functional>
#include <iterator>
@@ -15,7 +16,8 @@
#include "../core/compressed_pair.hpp"
#include "../core/fwd.hpp"
#include "../core/iterator.hpp"
#include "../core/utility.hpp"
#include "../stl/functional.hpp"
#include "../stl/iterator.hpp"
#include "adjacency_matrix.hpp"
#include "fwd.hpp"
@@ -29,9 +31,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<>, typename alloc_traits::template rebind_alloc<id_type>>;
using task_container_type = dense_set<id_type, stl::identity, std::equal_to<>, typename alloc_traits::template rebind_alloc<id_type>>;
using ro_rw_container_type = std::vector<std::pair<std::size_t, bool>, typename alloc_traits::template rebind_alloc<std::pair<std::size_t, bool>>>;
using deps_container_type = dense_map<id_type, ro_rw_container_type, identity, std::equal_to<>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, ro_rw_container_type>>>;
using deps_container_type = dense_map<id_type, ro_rw_container_type, stl::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) {
@@ -206,7 +208,7 @@ public:
* @return The requested identifier.
*/
[[nodiscard]] id_type operator[](const size_type pos) const {
return vertices.cbegin()[static_cast<typename task_container_type::difference_type>(pos)];
return vertices.cbegin()[static_cast<task_container_type::difference_type>(pos)];
}
/*! @brief Clears the flow builder. */
@@ -283,14 +285,11 @@ public:
/**
* @brief Assigns a range of read-only resources to the current task.
* @tparam It Type of input iterator.
* @param first An iterator to the first element of the range of elements.
* @param last An iterator past the last element of the range of elements.
* @return This flow builder.
*/
template<typename It>
std::enable_if_t<std::is_same_v<std::remove_const_t<typename std::iterator_traits<It>::value_type>, id_type>, basic_flow &>
ro(It first, It last) {
basic_flow &ro(stl::input_iterator auto first, stl::input_iterator auto last) {
for(; first != last; ++first) {
emplace(*first, false);
}
@@ -310,14 +309,11 @@ public:
/**
* @brief Assigns a range of writable resources to the current task.
* @tparam It Type of input iterator.
* @param first An iterator to the first element of the range of elements.
* @param last An iterator past the last element of the range of elements.
* @return This flow builder.
*/
template<typename It>
std::enable_if_t<std::is_same_v<std::remove_const_t<typename std::iterator_traits<It>::value_type>, id_type>, basic_flow &>
rw(It first, It last) {
basic_flow &rw(stl::input_iterator auto first, stl::input_iterator auto last) {
for(; first != last; ++first) {
emplace(*first, true);
}

View File

@@ -1,6 +1,7 @@
#ifndef ENTT_GRAPH_FWD_HPP
#define ENTT_GRAPH_FWD_HPP
#include <concepts>
#include <cstddef>
#include <memory>
#include "../core/fwd.hpp"
@@ -13,7 +14,7 @@ struct directed_tag {};
/*! @brief Directed graph category tag. */
struct undirected_tag: directed_tag {};
template<typename, typename = std::allocator<std::size_t>>
template<std::derived_from<directed_tag>, typename = std::allocator<std::size_t>>
class adjacency_matrix;
template<typename = std::allocator<id_type>>

View File

@@ -1,6 +1,7 @@
#ifndef ENTT_LOCATOR_LOCATOR_HPP
#define ENTT_LOCATOR_LOCATOR_HPP
#include <concepts>
#include <memory>
#include <utility>
#include "../config/config.h"
@@ -84,7 +85,8 @@ public:
* @param args Parameters to use to construct the fallback service.
* @return A reference to a valid service.
*/
template<typename Type = Service, typename... Args>
template<std::derived_from<Service> Type = Service, typename... Args>
requires std::constructible_from<Type, Args...>
[[nodiscard]] static Service &value_or(Args &&...args) {
return service ? *service : emplace<Type>(std::forward<Args>(args)...);
}
@@ -96,7 +98,8 @@ public:
* @param args Parameters to use to construct the service.
* @return A reference to a valid service.
*/
template<typename Type = Service, typename... Args>
template<std::derived_from<Service> Type = Service, typename... Args>
requires std::constructible_from<Type, Args...>
static Service &emplace(Args &&...args) {
service = std::make_shared<Type>(std::forward<Args>(args)...);
return *service;
@@ -105,14 +108,14 @@ public:
/**
* @brief Sets or replaces a service using a given allocator.
* @tparam Type Service type.
* @tparam Allocator Type of allocator used to manage memory and elements.
* @tparam Args Types of arguments to use to construct the service.
* @param alloc The allocator to use.
* @param args Parameters to use to construct the service.
* @return A reference to a valid service.
*/
template<typename Type = Service, typename Allocator, typename... Args>
static Service &emplace(std::allocator_arg_t, Allocator alloc, Args &&...args) {
template<std::derived_from<Service> Type = Service, typename... Args>
requires std::constructible_from<Type, Args...>
static Service &emplace(std::allocator_arg_t, auto alloc, Args &&...args) {
service = std::allocate_shared<Type>(alloc, std::forward<Args>(args)...);
return *service;
}
@@ -142,7 +145,7 @@ public:
* @param elem A pointer to a service to manage.
* @param deleter A deleter to use to destroy the service.
*/
template<typename Type, typename Deleter = std::default_delete<Type>>
template<std::derived_from<Service> Type, typename Deleter = std::default_delete<Type>>
static void reset(Type *elem, Deleter deleter = {}) {
service = std::shared_ptr<Service>{elem, std::move(deleter)};
}

View File

@@ -3,53 +3,55 @@
#ifndef ENTT_META_CONTAINER_HPP
#define ENTT_META_CONTAINER_HPP
#include <array>
#include <deque>
#include <concepts>
#include <cstddef>
#include <iterator>
#include <list>
#include <map>
#include <set>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "../container/dense_map.hpp"
#include "../container/dense_set.hpp"
#include <utility>
#include "../core/concepts.hpp"
#include "../core/type_traits.hpp"
#include "../stl/iterator.hpp"
#include "context.hpp"
#include "fwd.hpp"
#include "meta.hpp"
#include "type_traits.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename, typename = void>
struct fixed_size_sequence_container: std::true_type {};
template<typename Type>
struct sequence_container_extent: integral_constant<meta_dynamic_extent> {};
template<typename Type>
struct fixed_size_sequence_container<Type, std::void_t<decltype(&Type::clear)>>: std::false_type {};
requires is_complete_v<std::tuple_size<Type>>
struct sequence_container_extent<Type>: integral_constant<std::tuple_size_v<Type>> {};
template<typename Type>
inline constexpr bool fixed_size_sequence_container_v = fixed_size_sequence_container<Type>::value;
template<typename, typename = void>
struct key_only_associative_container: std::true_type {};
inline constexpr std::size_t sequence_container_extent_v = sequence_container_extent<Type>::value;
template<typename Type>
struct key_only_associative_container<Type, std::void_t<typename Type::mapped_type>>: std::false_type {};
concept meta_sequence_container_like = requires(Type elem) {
typename Type::value_type;
typename Type::iterator;
requires entt::stl::forward_iterator<typename Type::iterator>;
{ elem.begin() } -> std::same_as<typename Type::iterator>;
{ elem.end() } -> std::same_as<typename Type::iterator>;
requires !requires { typename Type::key_type; };
requires !requires { elem.substr(); };
};
template<typename Type>
inline constexpr bool key_only_associative_container_v = key_only_associative_container<Type>::value;
template<typename, typename = void>
struct reserve_aware_container: std::false_type {};
template<typename Type>
struct reserve_aware_container<Type, std::void_t<decltype(&Type::reserve)>>: std::true_type {};
template<typename Type>
inline constexpr bool reserve_aware_container_v = reserve_aware_container<Type>::value;
concept meta_associative_container_like = requires(Type value) {
typename Type::key_type;
typename Type::value_type;
typename Type::iterator;
requires entt::stl::forward_iterator<typename Type::iterator>;
{ value.begin() } -> std::same_as<typename Type::iterator>;
{ value.end() } -> std::same_as<typename Type::iterator>;
value.find(std::declval<typename Type::key_type>());
};
} // namespace internal
/*! @endcond */
@@ -58,17 +60,15 @@ inline constexpr bool reserve_aware_container_v = reserve_aware_container<Type>:
* @brief General purpose implementation of meta sequence container traits.
* @tparam Type Type of underlying sequence container.
*/
template<typename Type>
template<cvref_unqualified Type>
struct basic_meta_sequence_container_traits {
static_assert(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>, "Unexpected type");
/*! @brief Unsigned integer type. */
using size_type = typename meta_sequence_container::size_type;
using size_type = meta_sequence_container::size_type;
/*! @brief Meta iterator type. */
using iterator = typename meta_sequence_container::iterator;
using iterator = 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 Number of elements, or `meta_dynamic_extent` if dynamic. */
static constexpr std::size_t extent = internal::sequence_container_extent_v<Type>;
/**
* @brief Returns the number of elements in a container.
@@ -85,11 +85,11 @@ struct basic_meta_sequence_container_traits {
* @return True in case of success, false otherwise.
*/
[[nodiscard]] static bool clear([[maybe_unused]] void *container) {
if constexpr(fixed_size) {
return false;
} else {
if constexpr(requires(Type elem) { elem.clear(); }) {
static_cast<Type *>(container)->clear();
return true;
} else {
return false;
}
}
@@ -100,7 +100,7 @@ struct basic_meta_sequence_container_traits {
* @return True in case of success, false otherwise.
*/
[[nodiscard]] static bool reserve([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) {
if constexpr(internal::reserve_aware_container_v<Type>) {
if constexpr(requires(Type elem) { elem.reserve(sz); }) {
static_cast<Type *>(container)->reserve(sz);
return true;
} else {
@@ -115,36 +115,27 @@ struct basic_meta_sequence_container_traits {
* @return True in case of success, false otherwise.
*/
[[nodiscard]] static bool resize([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) {
if constexpr(fixed_size || !std::is_default_constructible_v<typename Type::value_type>) {
return false;
} else {
if constexpr(std::is_default_constructible_v<typename Type::value_type> && requires(Type elem) { elem.resize(sz); }) {
static_cast<Type *>(container)->resize(sz);
return true;
} else {
return false;
}
}
/**
* @brief Returns a possibly const iterator to the beginning.
* @brief Returns a possibly const iterator to the beginning or the end.
* @param area The context to pass to the newly created iterator.
* @param container Opaque pointer to a container of the given type.
* @param as_const Const opaque pointer fallback.
* @return An iterator to the first element of the container.
* @param end False to get a pointer that is past the last element.
* @return An iterator to the first or past the last element of the
* container.
*/
static iterator begin(const meta_ctx &area, void *container, const void *as_const) {
return (container != nullptr) ? iterator{area, static_cast<Type *>(container)->begin()}
: iterator{area, static_cast<const Type *>(as_const)->begin()};
}
/**
* @brief Returns a possibly const iterator to the end.
* @param area The context to pass to the newly created iterator.
* @param container Opaque pointer to a container of the given type.
* @param as_const Const opaque pointer fallback.
* @return An iterator that is past the last element of the container.
*/
static iterator end(const meta_ctx &area, void *container, const void *as_const) {
return (container != nullptr) ? iterator{area, static_cast<Type *>(container)->end()}
: iterator{area, static_cast<const Type *>(as_const)->end()};
static iterator iter(const meta_ctx &area, void *container, const void *as_const, const bool end) {
return (container == nullptr)
? iterator{area, end ? static_cast<const Type *>(as_const)->cend() : static_cast<const Type *>(as_const)->cbegin()}
: iterator{area, end ? static_cast<Type *>(container)->end() : static_cast<Type *>(container)->begin()};
}
/**
@@ -160,13 +151,13 @@ struct basic_meta_sequence_container_traits {
* @return A possibly invalid iterator to the inserted element.
*/
[[nodiscard]] static iterator insert([[maybe_unused]] const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const void *value, [[maybe_unused]] const void *cref, [[maybe_unused]] const iterator &it) {
if constexpr(fixed_size) {
return iterator{};
} else {
if constexpr(requires(Type elem, typename Type::const_iterator iter, Type::value_type instance) { elem.insert(iter, instance); }) {
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 != nullptr) ? *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 Type::value_type *>(value) : *static_cast<const std::remove_reference_t<typename Type::const_reference> *>(cref))};
} else {
return iterator{};
}
}
@@ -178,11 +169,11 @@ struct basic_meta_sequence_container_traits {
* @return A possibly invalid iterator following the last removed element.
*/
[[nodiscard]] static iterator erase([[maybe_unused]] const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const iterator &it) {
if constexpr(fixed_size) {
return iterator{};
} else {
if constexpr(requires(Type elem, typename Type::const_iterator iter) { elem.erase(iter); }) {
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()))};
} else {
return iterator{};
}
}
};
@@ -191,17 +182,15 @@ struct basic_meta_sequence_container_traits {
* @brief General purpose implementation of meta associative container traits.
* @tparam Type Type of underlying associative container.
*/
template<typename Type>
template<cvref_unqualified Type>
struct basic_meta_associative_container_traits {
static_assert(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>, "Unexpected type");
/*! @brief Unsigned integer type. */
using size_type = typename meta_associative_container::size_type;
using size_type = meta_associative_container::size_type;
/*! @brief Meta iterator type. */
using iterator = typename meta_associative_container::iterator;
using iterator = 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>;
static constexpr bool key_only = !requires { typename Type::mapped_type; };
/**
* @brief Returns the number of elements in a container.
@@ -229,7 +218,7 @@ struct basic_meta_associative_container_traits {
* @return True in case of success, false otherwise.
*/
[[nodiscard]] static bool reserve([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) {
if constexpr(internal::reserve_aware_container_v<Type>) {
if constexpr(requires(Type elem) { elem.reserve(sz); }) {
static_cast<Type *>(container)->reserve(sz);
return true;
} else {
@@ -238,27 +227,18 @@ struct basic_meta_associative_container_traits {
}
/**
* @brief Returns a possibly const iterator to the beginning.
* @brief Returns a possibly const iterator to the beginning or the end.
* @param area The context to pass to the newly created iterator.
* @param container Opaque pointer to a container of the given type.
* @param as_const Const opaque pointer fallback.
* @return An iterator to the first element of the container.
* @param end False to get a pointer that is past the last element.
* @return An iterator to the first or past the last element of the
* container.
*/
static iterator begin(const meta_ctx &area, void *container, const void *as_const) {
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()};
}
/**
* @brief Returns a possibly const iterator to the end.
* @param area The context to pass to the newly created iterator.
* @param container Opaque pointer to a container of the given type.
* @param as_const Const opaque pointer fallback.
* @return An iterator that is past the last element of the container.
*/
static iterator end(const meta_ctx &area, void *container, const void *as_const) {
return (container != 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()};
static iterator iter(const meta_ctx &area, void *container, const void *as_const, const bool end) {
return (container == nullptr)
? iterator{area, std::bool_constant<key_only>{}, end ? static_cast<const Type *>(as_const)->cend() : static_cast<const Type *>(as_const)->cbegin()}
: iterator{area, std::bool_constant<key_only>{}, end ? static_cast<Type *>(container)->end() : static_cast<Type *>(container)->begin()};
}
/**
@@ -270,9 +250,9 @@ struct basic_meta_associative_container_traits {
*/
[[nodiscard]] static bool insert(void *container, const void *key, [[maybe_unused]] const void *value) {
if constexpr(key_only) {
return static_cast<Type *>(container)->insert(*static_cast<const typename Type::key_type *>(key)).second;
return static_cast<Type *>(container)->insert(*static_cast<const Type::key_type *>(key)).second;
} else {
return static_cast<Type *>(container)->emplace(*static_cast<const typename Type::key_type *>(key), *static_cast<const typename Type::mapped_type *>(value)).second;
return static_cast<Type *>(container)->emplace(*static_cast<const Type::key_type *>(key), *static_cast<const Type::mapped_type *>(value)).second;
}
}
@@ -283,7 +263,7 @@ struct basic_meta_associative_container_traits {
* @return Number of elements removed (either 0 or 1).
*/
[[nodiscard]] static size_type erase(void *container, const void *key) {
return static_cast<Type *>(container)->erase(*static_cast<const typename Type::key_type *>(key));
return static_cast<Type *>(container)->erase(*static_cast<const Type::key_type *>(key));
}
/**
@@ -295,93 +275,24 @@ 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 != 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))};
return (container != nullptr) ? iterator{area, std::bool_constant<key_only>{}, static_cast<Type *>(container)->find(*static_cast<const Type::key_type *>(key))}
: iterator{area, std::bool_constant<key_only>{}, static_cast<const Type *>(as_const)->find(*static_cast<const Type::key_type *>(key))};
}
};
/**
* @brief Meta sequence container traits for `std::vector`s of any type.
* @tparam Args Template arguments for the container.
* @brief Traits meta sequence container like types.
* @tparam Type Container type to inspect.
*/
template<typename... Args>
struct meta_sequence_container_traits<std::vector<Args...>>
: basic_meta_sequence_container_traits<std::vector<Args...>> {};
template<internal::meta_sequence_container_like Type>
struct meta_sequence_container_traits<Type>: basic_meta_sequence_container_traits<Type> {};
/**
* @brief Meta sequence container traits for `std::array`s of any type.
* @tparam Type Template arguments for the container.
* @tparam N Template arguments for the container.
* @brief Traits for meta associative container like types.
* @tparam Type Container type to inspect.
*/
template<typename Type, auto N>
struct meta_sequence_container_traits<std::array<Type, N>>
: basic_meta_sequence_container_traits<std::array<Type, N>> {};
/**
* @brief Meta sequence container traits for `std::list`s of any type.
* @tparam Args Template arguments for the container.
*/
template<typename... Args>
struct meta_sequence_container_traits<std::list<Args...>>
: basic_meta_sequence_container_traits<std::list<Args...>> {};
/**
* @brief Meta sequence container traits for `std::deque`s of any type.
* @tparam Args Template arguments for the container.
*/
template<typename... Args>
struct meta_sequence_container_traits<std::deque<Args...>>
: basic_meta_sequence_container_traits<std::deque<Args...>> {};
/**
* @brief Meta associative container traits for `std::map`s of any type.
* @tparam Args Template arguments for the container.
*/
template<typename... Args>
struct meta_associative_container_traits<std::map<Args...>>
: basic_meta_associative_container_traits<std::map<Args...>> {};
/**
* @brief Meta associative container traits for `std::unordered_map`s of any
* type.
* @tparam Args Template arguments for the container.
*/
template<typename... Args>
struct meta_associative_container_traits<std::unordered_map<Args...>>
: basic_meta_associative_container_traits<std::unordered_map<Args...>> {};
/**
* @brief Meta associative container traits for `std::set`s of any type.
* @tparam Args Template arguments for the container.
*/
template<typename... Args>
struct meta_associative_container_traits<std::set<Args...>>
: basic_meta_associative_container_traits<std::set<Args...>> {};
/**
* @brief Meta associative container traits for `std::unordered_set`s of any
* type.
* @tparam Args Template arguments for the container.
*/
template<typename... Args>
struct meta_associative_container_traits<std::unordered_set<Args...>>
: basic_meta_associative_container_traits<std::unordered_set<Args...>> {};
/**
* @brief Meta associative container traits for `dense_map`s of any type.
* @tparam Args Template arguments for the container.
*/
template<typename... Args>
struct meta_associative_container_traits<dense_map<Args...>>
: basic_meta_associative_container_traits<dense_map<Args...>> {};
/**
* @brief Meta associative container traits for `dense_set`s of any type.
* @tparam Args Template arguments for the container.
*/
template<typename... Args>
struct meta_associative_container_traits<dense_set<Args...>>
: basic_meta_associative_container_traits<dense_set<Args...>> {};
template<internal::meta_associative_container_like Type>
struct meta_associative_container_traits<Type>: basic_meta_associative_container_traits<Type> {};
} // namespace entt

View File

@@ -1,24 +1,26 @@
#ifndef ENTT_META_CTX_HPP
#define ENTT_META_CTX_HPP
#include <memory>
#include "../container/dense_map.hpp"
#include "../core/fwd.hpp"
#include "../core/utility.hpp"
#include "../stl/functional.hpp"
#include "fwd.hpp"
namespace entt {
class meta_ctx;
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
struct meta_type_node;
struct meta_context {
dense_map<id_type, meta_type_node, identity> value;
using container_type = dense_map<id_type, std::unique_ptr<meta_type_node>, stl::identity>;
[[nodiscard]] inline static meta_context &from(meta_ctx &ctx);
[[nodiscard]] inline static const meta_context &from(const meta_ctx &ctx);
container_type bucket;
[[nodiscard]] inline static meta_context &from(meta_ctx &);
[[nodiscard]] inline static const meta_context &from(const meta_ctx &);
};
} // namespace internal
@@ -36,7 +38,7 @@ class meta_ctx: private internal::meta_context {
friend struct internal::meta_context;
};
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
[[nodiscard]] inline internal::meta_context &internal::meta_context::from(meta_ctx &ctx) {
return ctx;
}

View File

@@ -1,6 +1,7 @@
#ifndef ENTT_META_FACTORY_HPP
#define ENTT_META_FACTORY_HPP
#include <concepts>
#include <cstddef>
#include <cstdint>
#include <functional>
@@ -11,10 +12,12 @@
#include "../config/config.h"
#include "../core/bit.hpp"
#include "../core/fwd.hpp"
#include "../core/hashed_string.hpp"
#include "../core/type_info.hpp"
#include "../core/type_traits.hpp"
#include "../locator/locator.hpp"
#include "context.hpp"
#include "fwd.hpp"
#include "meta.hpp"
#include "node.hpp"
#include "policy.hpp"
@@ -24,119 +27,131 @@
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
class basic_meta_factory {
using invoke_type = std::remove_pointer_t<decltype(meta_func_node::invoke)>;
auto *find_member_or_assert() {
auto *member = find_member<&meta_data_node::id>(details->data, bucket);
enum class mode {
type,
data,
func
};
[[nodiscard]] auto *find_member_or_assert() {
auto *member = find_member(parent->details->data, bucket);
ENTT_ASSERT(member != nullptr, "Cannot find member");
return member;
}
auto *find_overload_or_assert() {
auto *overload = find_overload(find_member<&meta_func_node::id>(details->func, bucket), invoke);
[[nodiscard]] auto *find_overload_or_assert() {
ENTT_ASSERT(invoke != nullptr, "Invoke function not available");
auto *overload = find_overload(find_member(parent->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;
void type(const id_type id, const char *name) noexcept {
state = mode::type;
ENTT_ASSERT(parent->id == id || !resolve(*ctx, id), "Duplicate identifier");
parent->name = name;
parent->id = id;
}
template<typename Type>
void insert_or_assign(Type node) {
reset_bucket(parent);
state = mode::type;
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);
auto *member = find_member(parent->details->base, node.id);
member ? (*member = node) : parent->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);
auto *member = find_member(parent->details->conv, node.id);
member ? (*member = node) : parent->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);
auto *member = find_member(parent->details->ctor, node.id);
member ? (*member = node) : parent->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);
state = mode::data;
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));
if(auto *member = find_member(parent->details->data, node.id); member == nullptr) {
parent->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);
state = mode::func;
bucket = node.id;
invoke = node.invoke;
if(auto *member = find_member<&meta_func_node::id>(details->func, node.id); member == nullptr) {
details->func.emplace_back(std::move(node));
if(auto *member = find_member(parent->details->func, node.id); member == nullptr) {
parent->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));
member->next = std::make_unique<meta_func_node>(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 traits(const meta_traits value, const bool unset) {
const auto set_or_unset_on = [=](auto &node) {
node.traits = (unset ? (node.traits & ~value) : (node.traits | value));
};
switch(state) {
case mode::type:
set_or_unset_on(*parent);
break;
case mode::data:
set_or_unset_on(*find_member_or_assert());
break;
case mode::func:
set_or_unset_on(*find_overload_or_assert());
break;
}
}
void custom(meta_custom_node node) {
if(bucket == parent) {
meta_context::from(*ctx).value[bucket].custom = std::move(node);
} else if(invoke == nullptr) {
switch(state) {
case mode::type:
parent->custom = std::move(node);
break;
case mode::data:
find_member_or_assert()->custom = std::move(node);
} else {
break;
case mode::func:
find_overload_or_assert()->custom = std::move(node);
break;
}
}
public:
basic_meta_factory(meta_ctx &area, meta_type_node node)
: ctx{&area},
parent{node.info->hash()},
bucket{parent},
details{node.details.get()} {
if(details == nullptr) {
node.details = std::make_shared<meta_type_descriptor>();
meta_context::from(*ctx).value[parent] = node;
details = node.details.get();
bucket{node.info->hash()},
state{mode::type} {
if(const auto it = meta_context::from(*ctx).bucket.find(bucket); it == meta_context::from(*ctx).bucket.cend()) {
parent = meta_context::from(*ctx).bucket.emplace(node.info->hash(), std::make_unique<meta_type_node>(std::move(node))).first->second.get();
parent->details = std::make_unique<meta_type_descriptor>();
} else {
parent = it->second.get();
}
}
private:
meta_ctx *ctx{};
id_type parent{};
id_type bucket{};
invoke_type *invoke{};
meta_type_descriptor *details{};
meta_type_node *parent{};
mode state{};
};
} // namespace internal
@@ -144,32 +159,16 @@ private:
/**
* @brief Meta factory to be used for reflection purposes.
* @tparam Type Reflected type for which the factory was created.
* @tparam Type Type for which the factory was created.
*/
template<typename Type>
class meta_factory: private internal::basic_meta_factory {
using base_type = internal::basic_meta_factory;
template<typename Setter, auto Getter, typename Policy, std::size_t... Index>
[[deprecated("use variant types or conversion support")]]
void data(const id_type id, std::index_sequence<Index...>) noexcept {
using data_type = std::invoke_result_t<decltype(Getter), Type &>;
using args_type = type_list<typename meta_function_helper_t<Type, decltype(value_list_element_v<Index, Setter>)>::args_type...>;
static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
base_type::data(
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,
&internal::resolve<std::remove_cv_t<std::remove_reference_t<data_type>>>,
&meta_arg<type_list<type_list_element_t<static_cast<std::size_t>(type_list_element_t<Index, args_type>::size != 1u), type_list_element_t<Index, args_type>>...>>,
+[](meta_handle instance, meta_any value) { return (meta_setter<Type, value_list_element_v<Index, Setter>>(*instance.operator->(), value.as_ref()) || ...); },
&meta_getter<Type, Getter, Policy>});
}
public:
/*! @brief Type of object for which this factory builds a meta type. */
using element_type = Type;
/*! @brief Default constructor. */
meta_factory() noexcept
: meta_factory{locator<meta_ctx>::value_or()} {}
@@ -179,15 +178,25 @@ public:
* @param area The context into which to construct meta types.
*/
meta_factory(meta_ctx &area) noexcept
: internal::basic_meta_factory{area, internal::resolve<Type>(internal::meta_context::from(area))} {}
: internal::basic_meta_factory{area, internal::setup_node_for<Type>()} {}
/**
* @brief Assigns a custom unique identifier to a meta type.
* @param name A custom unique identifier as a **string literal**.
* @return A meta factory for the given type.
*/
meta_factory type(const char *name) noexcept {
return type(hashed_string::value(name), name);
}
/**
* @brief Assigns a custom unique identifier to a meta type.
* @param id A custom unique identifier.
* @param name An optional name for the type as a **string literal**.
* @return A meta factory for the given type.
*/
meta_factory type(const id_type id) noexcept {
base_type::type(id);
meta_factory type(const id_type id, const char *name = nullptr) noexcept {
base_type::type(id, name);
return *this;
}
@@ -200,10 +209,18 @@ public:
* @return A meta factory for the parent type.
*/
template<typename Base>
requires std::derived_from<Type, Base>
meta_factory base() noexcept {
static_assert(!std::is_same_v<Type, Base> && std::is_base_of_v<Base, Type>, "Invalid base type");
auto *const op = +[](const void *instance) noexcept { return static_cast<const void *>(static_cast<const Base *>(static_cast<const Type *>(instance))); };
base_type::insert_or_assign(internal::meta_base_node{type_id<Base>().hash(), &internal::resolve<Base>, op});
if constexpr(!std::same_as<Type, Base>) {
auto *const op = +[](const void *instance) noexcept { return static_cast<const void *>(static_cast<const Base *>(static_cast<const Type *>(instance))); };
base_type::insert_or_assign(
internal::meta_base_node{
type_id<Base>().hash(),
&internal::resolve<Base>,
op});
}
return *this;
}
@@ -221,9 +238,14 @@ public:
*/
template<auto Candidate>
auto conv() noexcept {
using conv_type = std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(Candidate), Type &>>>;
using conv_type = std::remove_cvref_t<std::invoke_result_t<decltype(Candidate), Type &>>;
auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, std::invoke(Candidate, *static_cast<const Type *>(instance))); };
base_type::insert_or_assign(internal::meta_conv_node{type_id<conv_type>().hash(), op});
base_type::insert_or_assign(
internal::meta_conv_node{
type_id<conv_type>().hash(),
op});
return *this;
}
@@ -238,9 +260,14 @@ public:
*/
template<typename To>
meta_factory conv() noexcept {
using conv_type = std::remove_cv_t<std::remove_reference_t<To>>;
using conv_type = std::remove_cvref_t<To>;
auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, static_cast<To>(*static_cast<const Type *>(instance))); };
base_type::insert_or_assign(internal::meta_conv_node{type_id<conv_type>().hash(), op});
base_type::insert_or_assign(
internal::meta_conv_node{
type_id<conv_type>().hash(),
op});
return *this;
}
@@ -257,12 +284,19 @@ public:
* @tparam Policy Optional policy (no policy set by default).
* @return A meta factory for the parent type.
*/
template<auto Candidate, typename Policy = as_is_t>
template<auto Candidate, typename Policy = as_value_t>
meta_factory ctor() noexcept {
using descriptor = meta_function_helper_t<Type, decltype(Candidate)>;
static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy");
static_assert(std::is_same_v<std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>, Type>, "The function doesn't return an object of the required type");
base_type::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>});
static_assert(std::is_same_v<std::remove_cvref_t<typename descriptor::return_type>, Type>, "The function doesn't return an object of the required type");
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;
}
@@ -281,36 +315,28 @@ public:
// default constructor is already implicitly generated, no need for redundancy
if constexpr(sizeof...(Args) != 0u) {
using descriptor = meta_function_helper_t<Type, Type (*)(Args...)>;
base_type::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...>});
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...>});
}
return *this;
}
/**
* @brief Assigns a meta destructor to a meta type.
*
* Both free functions and member functions can be assigned to meta types in
* the role of destructors.<br/>
* The signature of a free function should be identical to the following:
*
* @code{.cpp}
* void(Type &);
* @endcode
*
* Member functions should not take arguments instead.<br/>
* The purpose is to give users the ability to free up resources that
* require special treatment before an object is actually destroyed.
*
* @tparam Func The actual function to use as a destructor.
* @return A meta factory for the parent type.
* @brief Assigns a meta data to a meta type.
* @tparam Data The actual variable to attach to the meta type.
* @tparam Policy Optional policy (no policy set by default).
* @param name A custom unique identifier as a **string literal**.
* @return A meta factory for the given type.
*/
template<auto Func>
meta_factory dtor() noexcept {
static_assert(std::is_invocable_v<decltype(Func), Type &>, "The function doesn't accept an object of the type provided");
auto *const op = +[](void *instance) { std::invoke(Func, *static_cast<Type *>(instance)); };
base_type::dtor(internal::meta_dtor_node{op});
return *this;
template<auto Data, typename Policy = as_value_t>
meta_factory data(const char *name) noexcept {
return data<Data, Policy>(hashed_string::value(name), name);
}
/**
@@ -324,10 +350,11 @@ public:
* @tparam Data The actual variable to attach to the meta type.
* @tparam Policy Optional policy (no policy set by default).
* @param id Unique identifier.
* @param name An optional name for the meta data as a **string literal**.
* @return A meta factory for the parent type.
*/
template<auto Data, typename Policy = as_is_t>
meta_factory data(const id_type id) noexcept {
template<auto Data, typename Policy = as_value_t>
meta_factory data(const id_type id, const char *name = nullptr) 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");
@@ -335,11 +362,12 @@ public:
base_type::data(
internal::meta_data_node{
id,
name,
/* this is never static */
std::is_const_v<std::remove_reference_t<data_type>> ? internal::meta_traits::is_const : internal::meta_traits::is_none,
1u,
&internal::resolve<std::remove_cv_t<std::remove_reference_t<data_type>>>,
&meta_arg<type_list<std::remove_cv_t<std::remove_reference_t<data_type>>>>,
&internal::resolve<std::remove_cvref_t<data_type>>,
&meta_arg<type_list<std::remove_cvref_t<data_type>>>,
&meta_setter<Type, Data>,
&meta_getter<Type, Data, Policy>});
} else {
@@ -354,10 +382,11 @@ public:
base_type::data(
internal::meta_data_node{
id,
name,
((!std::is_pointer_v<decltype(Data)> || std::is_const_v<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>>>>,
&internal::resolve<std::remove_cvref_t<data_type>>,
&meta_arg<type_list<std::remove_cvref_t<data_type>>>,
&meta_setter<Type, Data>,
&meta_getter<Type, Data, Policy>});
}
@@ -365,6 +394,20 @@ public:
return *this;
}
/**
* @brief Assigns a meta data to a meta type by means of its setter and
* getter.
* @tparam Setter The actual function to use as a setter.
* @tparam Getter The actual function to use as a getter.
* @tparam Policy Optional policy (no policy set by default).
* @param name A custom unique identifier as a **string literal**.
* @return A meta factory for the given type.
*/
template<auto Setter, auto Getter, typename Policy = as_value_t>
meta_factory data(const char *name) noexcept {
return data<Setter, Getter, Policy>(hashed_string::value(name), name);
}
/**
* @brief Assigns a meta data to a meta type by means of its setter and
* getter.
@@ -383,10 +426,11 @@ public:
* @tparam Getter The actual function to use as a getter.
* @tparam Policy Optional policy (no policy set by default).
* @param id Unique identifier.
* @param name An optional name for the meta data as a **string literal**.
* @return A meta factory for the parent type.
*/
template<auto Setter, auto Getter, typename Policy = as_is_t>
meta_factory data(const id_type id) noexcept {
template<auto Setter, auto Getter, typename Policy = as_value_t>
meta_factory data(const id_type id, const char *name = nullptr) noexcept {
using descriptor = meta_function_helper_t<Type, decltype(Getter)>;
static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy");
@@ -394,23 +438,25 @@ public:
base_type::data(
internal::meta_data_node{
id,
name,
/* this is never static */
internal::meta_traits::is_const,
0u,
&internal::resolve<std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>>,
&internal::resolve<std::remove_cvref_t<typename descriptor::return_type>>,
&meta_arg<type_list<>>,
&meta_setter<Type, Setter>,
&meta_getter<Type, Getter, Policy>});
} else {
using args_type = typename meta_function_helper_t<Type, decltype(Setter)>::args_type;
using args_type = meta_function_helper_t<Type, decltype(Setter)>::args_type;
base_type::data(
internal::meta_data_node{
id,
name,
/* this is never static nor const */
internal::meta_traits::is_none,
1u,
&internal::resolve<std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>>,
&internal::resolve<std::remove_cvref_t<typename descriptor::return_type>>,
&meta_arg<type_list<type_list_element_t<static_cast<std::size_t>(args_type::size != 1u), args_type>>>,
&meta_setter<Type, Setter>,
&meta_getter<Type, Getter, Policy>});
@@ -420,27 +466,15 @@ public:
}
/**
* @brief Assigns a meta data to a meta type by means of its setters and
* getter.
*
* Multi-setter support for meta data members. All setters are tried in the
* order of definition before returning to the caller.<br/>
* Setters can be either free functions, member functions or a mix of them
* and are provided via a `value_list` type.
*
* @sa data
*
* @tparam Setter The actual functions to use as setters.
* @tparam Getter The actual getter function.
* @brief Assigns a meta function to a meta type.
* @tparam Candidate The actual function to attach to the meta function.
* @tparam Policy Optional policy (no policy set by default).
* @param id Unique identifier.
* @return A meta factory for the parent type.
* @param name A custom unique identifier as a **string literal**.
* @return A meta factory for the given type.
*/
template<typename Setter, auto Getter, typename Policy = as_is_t>
[[deprecated("use variant types or conversion support")]]
meta_factory data(const id_type id) noexcept {
data<Setter, Getter, Policy>(id, std::make_index_sequence<Setter::size>{});
return *this;
template<auto Candidate, typename Policy = as_value_t>
meta_factory func(const char *name) noexcept {
return func<Candidate, Policy>(hashed_string::value(name), name);
}
/**
@@ -454,19 +488,21 @@ public:
* @tparam Candidate The actual function to attach to the meta type.
* @tparam Policy Optional policy (no policy set by default).
* @param id Unique identifier.
* @param name An optional name for the function as a **string literal**.
* @return A meta factory for the parent type.
*/
template<auto Candidate, typename Policy = as_is_t>
meta_factory func(const id_type id) noexcept {
template<auto Candidate, typename Policy = as_value_t>
meta_factory func(const id_type id, const char *name = nullptr) noexcept {
using descriptor = meta_function_helper_t<Type, decltype(Candidate)>;
static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy");
base_type::func(
internal::meta_func_node{
id,
name,
(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>>>>,
&internal::resolve<std::conditional_t<std::is_same_v<Policy, as_void_t>, void, std::remove_cvref_t<typename descriptor::return_type>>>,
&meta_arg<typename descriptor::args_type>,
&meta_invoke<Type, Candidate, Policy>});
@@ -480,12 +516,13 @@ public:
*
* @tparam Value Type of the traits value.
* @param value Traits value.
* @param unset True to unset the given traits, false otherwise.
* @return A meta factory for the parent type.
*/
template<typename Value>
meta_factory traits(const Value value) {
meta_factory traits(const Value value, const bool unset = false) {
static_assert(std::is_enum_v<Value>, "Invalid enum type");
base_type::traits(internal::user_to_meta_traits(value));
base_type::traits(internal::user_to_meta_traits(value), unset);
return *this;
}
@@ -503,39 +540,6 @@ public:
}
};
/**
* @brief Utility function to use for reflection.
*
* This is the point from which everything starts.<br/>
* By invoking this function with a type that is not yet reflected, a meta type
* is created to which it will be possible to attach meta objects through a
* dedicated factory.
*
* @tparam Type Type to reflect.
* @param ctx The context into which to construct meta types.
* @return A meta factory for the given type.
*/
template<typename Type>
[[nodiscard]] [[deprecated("use meta_factory directly instead")]] auto meta(meta_ctx &ctx) noexcept {
return meta_factory<Type>{ctx};
}
/**
* @brief Utility function to use for reflection.
*
* This is the point from which everything starts.<br/>
* By invoking this function with a type that is not yet reflected, a meta type
* is created to which it will be possible to attach meta objects through a
* dedicated factory.
*
* @tparam Type Type to reflect.
* @return A meta factory for the given type.
*/
template<typename Type>
[[nodiscard]] [[deprecated("use meta_factory directly instead")]] auto meta() noexcept {
return meta<Type>(locator<meta_ctx>::value_or());
}
/**
* @brief Resets a type and all its parts.
*
@@ -549,11 +553,11 @@ template<typename Type>
* @param ctx The context from which to reset meta types.
*/
inline void meta_reset(meta_ctx &ctx, const id_type id) noexcept {
auto &&context = internal::meta_context::from(ctx);
auto &context = internal::meta_context::from(ctx);
for(auto it = context.value.begin(); it != context.value.end();) {
if(it->second.id == id) {
it = context.value.erase(it);
for(auto it = context.bucket.begin(); it != context.bucket.end();) {
if(it->second->id == id) {
it = context.bucket.erase(it);
} else {
++it;
}
@@ -585,7 +589,7 @@ inline void meta_reset(const id_type id) noexcept {
*/
template<typename Type>
void meta_reset(meta_ctx &ctx) noexcept {
internal::meta_context::from(ctx).value.erase(type_id<Type>().hash());
internal::meta_context::from(ctx).bucket.erase(type_id<Type>().hash());
}
/**
@@ -608,7 +612,7 @@ void meta_reset() noexcept {
* @param ctx The context from which to reset meta types.
*/
inline void meta_reset(meta_ctx &ctx) noexcept {
internal::meta_context::from(ctx).value.clear();
internal::meta_context::from(ctx).bucket.clear();
}
/**

View File

@@ -1,15 +1,20 @@
#ifndef ENTT_META_FWD_HPP
#define ENTT_META_FWD_HPP
#include <cstddef>
#include <limits>
namespace entt {
class meta_ctx;
class meta_sequence_container;
class meta_associative_container;
class meta_any;
struct meta_handle;
class meta_handle;
struct meta_custom;
@@ -17,8 +22,16 @@ struct meta_data;
struct meta_func;
struct meta_base;
class meta_type;
template<typename>
class meta_factory;
/*! @brief Used to identicate that a sequence container has not a fixed size. */
inline constexpr std::size_t meta_dynamic_extent = (std::numeric_limits<std::size_t>::max)();
} // namespace entt
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,29 +1,28 @@
#ifndef ENTT_META_NODE_HPP
#define ENTT_META_NODE_HPP
#include <array>
#include <bit>
#include <cstddef>
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../core/attribute.h"
#include "../core/bit.hpp"
#include "../core/concepts.hpp"
#include "../core/enum.hpp"
#include "../core/fwd.hpp"
#include "../core/type_info.hpp"
#include "../core/type_traits.hpp"
#include "../core/utility.hpp"
#include "context.hpp"
#include "fwd.hpp"
#include "type_traits.hpp"
namespace entt {
class meta_any;
class meta_type;
struct meta_handle;
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
enum class meta_traits : std::uint32_t {
@@ -45,16 +44,16 @@ enum class meta_traits : std::uint32_t {
};
template<typename Type>
requires std::is_enum_v<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));
constexpr auto shift = std::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>
requires std::is_enum_v<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));
constexpr auto shift = std::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};
@@ -63,18 +62,18 @@ template<typename Type>
struct meta_type_node;
struct meta_custom_node {
id_type type{};
std::shared_ptr<void> value;
id_type id{};
std::shared_ptr<void> value{};
};
struct meta_base_node {
id_type type{};
meta_type_node (*resolve)(const meta_context &) noexcept {};
id_type id{};
const meta_type_node &(*type)(const meta_context &) noexcept {};
const void *(*cast)(const void *) noexcept {};
};
struct meta_conv_node {
id_type type{};
id_type id{};
meta_any (*conv)(const meta_ctx &, const void *){};
};
@@ -87,17 +86,14 @@ struct meta_ctor_node {
meta_any (*invoke)(const meta_ctx &, meta_any *const){};
};
struct meta_dtor_node {
void (*dtor)(void *){};
};
struct meta_data_node {
using size_type = std::size_t;
id_type id{};
const char *name{};
meta_traits traits{meta_traits::is_none};
size_type arity{0u};
meta_type_node (*type)(const meta_context &) noexcept {};
const 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)(meta_handle){};
@@ -108,12 +104,13 @@ struct meta_func_node {
using size_type = std::size_t;
id_type id{};
const char *name{};
meta_traits traits{meta_traits::is_none};
size_type arity{0u};
meta_type_node (*ret)(const meta_context &) noexcept {};
const meta_type_node &(*ret)(const meta_context &) noexcept {};
meta_type (*arg)(const meta_ctx &, const size_type) noexcept {};
meta_any (*invoke)(meta_handle, meta_any *const){};
std::shared_ptr<meta_func_node> next;
std::unique_ptr<meta_func_node> next;
meta_custom_node custom{};
};
@@ -121,16 +118,16 @@ struct meta_template_node {
using size_type = std::size_t;
size_type arity{0u};
meta_type_node (*resolve)(const meta_context &) noexcept {};
meta_type_node (*arg)(const meta_context &, const size_type) noexcept {};
const meta_type_node &(*resolve)(const meta_context &) noexcept {};
const meta_type_node &(*arg)(const meta_context &, const size_type) noexcept {};
};
struct meta_type_descriptor {
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_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{};
};
struct meta_type_node {
@@ -138,28 +135,27 @@ struct meta_type_node {
const type_info *info{};
id_type id{};
const char *name{};
meta_traits traits{meta_traits::is_none};
size_type size_of{0u};
meta_type_node (*resolve)(const meta_context &) noexcept {};
meta_type_node (*remove_pointer)(const meta_context &) noexcept {};
const meta_type_node &(*remove_pointer)(const meta_context &) noexcept {};
meta_any (*default_constructor)(const meta_ctx &){};
double (*conversion_helper)(void *, const void *){};
meta_any (*from_void)(const meta_ctx &, void *, const void *){};
meta_template_node templ{};
meta_dtor_node dtor{};
meta_custom_node custom{};
std::shared_ptr<meta_type_descriptor> details;
std::unique_ptr<meta_type_descriptor> details{};
};
template<auto Member, typename Type, typename Value>
template<typename Type, typename Value>
[[nodiscard]] auto *find_member(Type &from, const Value value) {
for(auto &&elem: from) {
if((elem.*Member) == value) {
if(elem.id == value) {
return &elem;
}
}
return static_cast<typename Type::value_type *>(nullptr);
return static_cast<Type::value_type *>(nullptr);
}
[[nodiscard]] inline auto *find_overload(meta_func_node *curr, std::remove_pointer_t<decltype(meta_func_node::invoke)> *const ref) {
@@ -168,17 +164,19 @@ template<auto Member, typename Type, typename Value>
}
template<auto Member>
[[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;
[[nodiscard]] auto *look_for(const meta_context &context, const meta_type_node &node, const id_type id, bool recursive) {
using value_type = std::remove_reference_t<decltype((node.details.get()->*Member))>::value_type;
if(node.details) {
if(auto *member = find_member<&value_type::id>((node.details.get()->*Member), id); member != nullptr) {
if(auto *member = find_member((node.details.get()->*Member), id); member != nullptr) {
return member;
}
for(auto &&curr: node.details->base) {
if(auto *elem = look_for<Member>(context, curr.resolve(context), id); elem) {
return elem;
if(recursive) {
for(auto &&curr: node.details->base) {
if(auto *elem = look_for<Member>(context, curr.type(context), id, recursive); elem) {
return elem;
}
}
}
}
@@ -186,30 +184,23 @@ template<auto Member>
return static_cast<value_type *>(nullptr);
}
template<typename Type>
meta_type_node resolve(const meta_context &) noexcept;
template<cvref_unqualified Type>
const meta_type_node &resolve(const meta_context &) noexcept;
template<typename... Args>
[[nodiscard]] auto meta_arg_node(const meta_context &context, type_list<Args...>, [[maybe_unused]] const std::size_t index) noexcept {
meta_type_node (*value)(const meta_context &) noexcept = nullptr;
if constexpr(sizeof...(Args) != 0u) {
std::size_t pos{};
((value = (pos++ == index ? &resolve<std::remove_cv_t<std::remove_reference_t<Args>>> : value)), ...);
}
ENTT_ASSERT(value != nullptr, "Out of bounds");
return value(context);
[[nodiscard]] const meta_type_node &meta_arg_node(const meta_context &context, type_list<Args...>, const std::size_t index) noexcept {
using resolve_type = const meta_type_node &(*)(const meta_context &) noexcept;
constexpr std::array<resolve_type, sizeof...(Args)> list{&resolve<std::remove_cvref_t<Args>>...};
ENTT_ASSERT(index < sizeof...(Args), "Out of bounds");
return list[index](context);
}
[[nodiscard]] inline const void *try_cast(const meta_context &context, const meta_type_node &from, const type_info &to, const void *instance) noexcept {
if((from.info != nullptr) && *from.info == to) {
return instance;
}
[[nodiscard]] inline const void *try_cast(const meta_context &context, const meta_type_node &from, const id_type to, const void *instance) noexcept {
if(from.details) {
for(auto &&curr: from.details->base) {
if(const void *elem = try_cast(context, curr.resolve(context), to, curr.cast(instance)); elem) {
if(const void *other = curr.cast(instance); curr.id == to) {
return other;
} else if(const void *elem = try_cast(context, curr.type(context), to, other); elem) {
return elem;
}
}
@@ -218,49 +209,12 @@ template<typename... Args>
return nullptr;
}
template<typename Func>
[[nodiscard]] inline auto try_convert(const meta_context &context, const meta_type_node &from, const type_info &to, const bool arithmetic_or_enum, const void *instance, Func func) {
if(from.info && *from.info == to) {
return func(instance, from);
}
if(from.details) {
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.resolve(context), to, arithmetic_or_enum, curr.cast(instance), func); other) {
return other;
}
}
}
if(from.conversion_helper && arithmetic_or_enum) {
return func(instance, from.conversion_helper);
}
return func(instance);
}
[[nodiscard]] inline const meta_type_node *try_resolve(const meta_context &context, const type_info &info) noexcept {
const auto it = context.value.find(info.hash());
return it != context.value.end() ? &it->second : nullptr;
}
template<typename Type>
[[nodiscard]] meta_type_node resolve(const meta_context &context) noexcept {
static_assert(std::is_same_v<Type, std::remove_const_t<std::remove_reference_t<Type>>>, "Invalid type");
if(auto *elem = try_resolve(context, type_id<Type>()); elem) {
return *elem;
}
auto setup_node_for() noexcept {
meta_type_node node{
&type_id<Type>(),
type_id<Type>().hash(),
nullptr,
(std::is_arithmetic_v<Type> ? meta_traits::is_arithmetic : meta_traits::is_none)
| (std::is_integral_v<Type> ? meta_traits::is_integral : meta_traits::is_none)
| (std::is_signed_v<Type> ? meta_traits::is_signed : meta_traits::is_none)
@@ -272,8 +226,7 @@ template<typename Type>
| (is_complete_v<meta_sequence_container_traits<Type>> ? meta_traits::is_sequence_container : meta_traits::is_none)
| (is_complete_v<meta_associative_container_traits<Type>> ? meta_traits::is_associative_container : meta_traits::is_none),
size_of_v<Type>,
&resolve<Type>,
&resolve<std::remove_cv_t<std::remove_pointer_t<Type>>>};
&resolve<std::remove_const_t<std::remove_pointer_t<Type>>>};
if constexpr(std::is_default_constructible_v<Type>) {
node.default_constructor = +[](const meta_ctx &ctx) {
@@ -310,12 +263,24 @@ template<typename Type>
node.templ = meta_template_node{
meta_template_traits<Type>::args_type::size,
&resolve<typename meta_template_traits<Type>::class_type>,
+[](const meta_context &area, const std::size_t index) noexcept { return meta_arg_node(area, typename meta_template_traits<Type>::args_type{}, index); }};
+[](const meta_context &area, const std::size_t index) noexcept -> decltype(auto) { return meta_arg_node(area, typename meta_template_traits<Type>::args_type{}, index); }};
}
return node;
}
[[nodiscard]] inline const meta_type_node *try_resolve(const meta_context &context, const type_info &info) noexcept {
const auto it = context.bucket.find(info.hash());
return (it != context.bucket.end()) ? it->second.get() : nullptr;
}
template<cvref_unqualified Type>
[[nodiscard]] const meta_type_node &resolve(const meta_context &context) noexcept {
static const meta_type_node node = setup_node_for<Type>();
const auto *elem = try_resolve(context, *node.info);
return (elem == nullptr) ? node : *elem;
}
} // namespace internal
/*! @endcond */

View File

@@ -9,24 +9,6 @@
namespace entt {
/**
* @brief Makes plain pointers pointer-like types for the meta system.
* @tparam Type Element type.
*/
template<typename Type>
struct is_meta_pointer_like<Type *>
: std::true_type {};
/**
* @brief Partial specialization used to reject pointers to arrays.
* @tparam Type Type of elements of the array.
* @tparam N Number of elements of the array.
*/
template<typename Type, std::size_t N>
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
struct is_meta_pointer_like<Type (*)[N]>
: std::false_type {};
/**
* @brief Makes `std::shared_ptr`s of any type pointer-like types for the meta
* system.
@@ -51,7 +33,8 @@ struct is_meta_pointer_like<std::unique_ptr<Type, Args...>>
* @tparam Type Element type.
*/
template<typename Type>
struct is_meta_pointer_like<Type, std::void_t<typename Type::is_meta_pointer_like>>
requires requires { typename Type::is_meta_pointer_like; }
struct is_meta_pointer_like<Type>
: std::true_type {};
} // namespace entt

View File

@@ -5,33 +5,49 @@
namespace entt {
/*! @brief Empty class type used to request the _as ref_ policy. */
struct as_ref_t final {
/*! @cond TURN_OFF_DOXYGEN */
template<typename Type>
static constexpr bool value = std::is_reference_v<Type> && !std::is_const_v<std::remove_reference_t<Type>>;
/*! @endcond */
};
/*! @cond ENTT_INTERNAL */
namespace internal {
/*! @brief Empty class type used to request the _as cref_ policy. */
struct as_cref_t final {
/*! @cond TURN_OFF_DOXYGEN */
template<typename Type>
static constexpr bool value = std::is_reference_v<Type>;
/*! @endcond */
};
struct meta_policy {};
} // namespace internal
/*! @endcond */
/*! @brief Empty class type used to request the _as-is_ policy. */
struct as_is_t final {
/*! @cond TURN_OFF_DOXYGEN */
struct as_value_t final: private internal::meta_policy {
/*! @cond ENTT_INTERNAL */
template<typename>
static constexpr bool value = true;
/*! @endcond */
};
/*! @brief Empty class type used to request the _as void_ policy. */
struct as_void_t final {
/*! @cond TURN_OFF_DOXYGEN */
struct as_void_t final: private internal::meta_policy {
/*! @cond ENTT_INTERNAL */
template<typename>
static constexpr bool value = true;
/*! @endcond */
};
/*! @brief Empty class type used to request the _as ref_ policy. */
struct as_ref_t final: private internal::meta_policy {
/*! @cond ENTT_INTERNAL */
template<typename Type>
static constexpr bool value = std::is_reference_v<Type> && !std::is_const_v<std::remove_reference_t<Type>>;
/*! @endcond */
};
/*! @brief Empty class type used to request the _as cref_ policy. */
struct as_cref_t final: private internal::meta_policy {
/*! @cond ENTT_INTERNAL */
template<typename Type>
static constexpr bool value = std::is_reference_v<Type>;
/*! @endcond */
};
/*! @brief Empty class type used to request the _as auto_ policy. */
struct as_is_t final: private internal::meta_policy {
/*! @cond ENTT_INTERNAL */
template<typename>
static constexpr bool value = true;
/*! @endcond */
@@ -44,7 +60,7 @@ struct as_void_t final {
*/
template<typename Type>
struct is_meta_policy
: std::bool_constant<std::is_same_v<Type, as_ref_t> || std::is_same_v<Type, as_cref_t> || std::is_same_v<Type, as_is_t> || std::is_same_v<Type, as_void_t>> {};
: std::bool_constant<std::is_base_of_v<internal::meta_policy, Type>> {};
/**
* @brief Helper variable template.
@@ -53,6 +69,13 @@ struct is_meta_policy
template<typename Type>
inline constexpr bool is_meta_policy_v = is_meta_policy<Type>::value;
/**
* @brief Specifies whether a type is a meta policy.
* @tparam Type Type to check.
*/
template<typename Type>
concept meta_policy = is_meta_policy_v<Type>;
} // namespace entt
#endif

View File

@@ -1,16 +1,19 @@
#ifndef ENTT_META_RANGE_HPP
#define ENTT_META_RANGE_HPP
#include <compare>
#include <concepts>
#include <cstddef>
#include <iterator>
#include <utility>
#include "../core/fwd.hpp"
#include "../core/iterator.hpp"
#include "../stl/iterator.hpp"
#include "context.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
struct meta_base_node;
@@ -69,10 +72,8 @@ struct meta_range_iterator final {
}
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
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]}};
if constexpr(std::is_same_v<It, typename meta_context::container_type::const_iterator>) {
return {it[value].first, Type{*ctx, *it[value].second}};
} else {
return {it[value].id, Type{*ctx, it[value]}};
}
@@ -86,55 +87,23 @@ struct meta_range_iterator final {
return operator[](0);
}
template<typename... Args>
friend constexpr std::ptrdiff_t operator-(const meta_range_iterator<Args...> &, const meta_range_iterator<Args...> &) noexcept;
[[nodiscard]] constexpr std::ptrdiff_t operator-(const meta_range_iterator &other) const noexcept {
return it - other.it;
}
template<typename... Args>
friend constexpr bool operator==(const meta_range_iterator<Args...> &, const meta_range_iterator<Args...> &) noexcept;
[[nodiscard]] constexpr bool operator==(const meta_range_iterator &other) const noexcept {
return it == other.it;
}
template<typename... Args>
friend constexpr bool operator<(const meta_range_iterator<Args...> &, const meta_range_iterator<Args...> &) noexcept;
[[nodiscard]] constexpr auto operator<=>(const meta_range_iterator &other) const noexcept {
return it <=> other.it;
}
private:
It it;
const meta_ctx *ctx;
};
template<typename... Args>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const meta_range_iterator<Args...> &lhs, const meta_range_iterator<Args...> &rhs) noexcept {
return lhs.it - rhs.it;
}
template<typename... Args>
[[nodiscard]] constexpr bool operator==(const meta_range_iterator<Args...> &lhs, const meta_range_iterator<Args...> &rhs) noexcept {
return lhs.it == rhs.it;
}
template<typename... Args>
[[nodiscard]] constexpr bool operator!=(const meta_range_iterator<Args...> &lhs, const meta_range_iterator<Args...> &rhs) noexcept {
return !(lhs == rhs);
}
template<typename... Args>
[[nodiscard]] constexpr bool operator<(const meta_range_iterator<Args...> &lhs, const meta_range_iterator<Args...> &rhs) noexcept {
return lhs.it < rhs.it;
}
template<typename... Args>
[[nodiscard]] constexpr bool operator>(const meta_range_iterator<Args...> &lhs, const meta_range_iterator<Args...> &rhs) noexcept {
return rhs < lhs;
}
template<typename... Args>
[[nodiscard]] constexpr bool operator<=(const meta_range_iterator<Args...> &lhs, const meta_range_iterator<Args...> &rhs) noexcept {
return !(lhs > rhs);
}
template<typename... Args>
[[nodiscard]] constexpr bool operator>=(const meta_range_iterator<Args...> &lhs, const meta_range_iterator<Args...> &rhs) noexcept {
return !(lhs < rhs);
}
} // namespace internal
/*! @endcond */
@@ -143,7 +112,7 @@ template<typename... Args>
* @tparam Type Type of meta objects returned.
* @tparam It Type of forward iterator.
*/
template<typename Type, typename It>
template<typename Type, stl::forward_iterator It>
using meta_range = iterable_adaptor<internal::meta_range_iterator<Type, It>>;
} // namespace entt

View File

@@ -19,8 +19,8 @@ namespace entt {
*/
template<typename Type>
[[nodiscard]] meta_type resolve(const meta_ctx &ctx) noexcept {
auto &&context = internal::meta_context::from(ctx);
return {ctx, internal::resolve<std::remove_cv_t<std::remove_reference_t<Type>>>(context)};
const auto &context = internal::meta_context::from(ctx);
return {ctx, internal::resolve<std::remove_cvref_t<Type>>(context)};
}
/**
@@ -38,16 +38,16 @@ template<typename Type>
* @param ctx The context from which to search for meta types.
* @return An iterable range to use to visit all meta types.
*/
[[nodiscard]] inline meta_range<meta_type, typename decltype(internal::meta_context::value)::const_iterator> resolve(const meta_ctx &ctx) noexcept {
auto &&context = internal::meta_context::from(ctx);
return {{ctx, context.value.cbegin()}, {ctx, context.value.cend()}};
[[nodiscard]] inline meta_range<meta_type, typename internal::meta_context::container_type::const_iterator> resolve(const meta_ctx &ctx) noexcept {
const auto &context = internal::meta_context::from(ctx);
return {{ctx, context.bucket.cbegin()}, {ctx, context.bucket.cend()}};
}
/**
* @brief Returns a range to use to visit all meta types.
* @return An iterable range to use to visit all meta types.
*/
[[nodiscard]] inline meta_range<meta_type, typename decltype(internal::meta_context::value)::const_iterator> resolve() noexcept {
[[nodiscard]] inline meta_range<meta_type, typename internal::meta_context::container_type::const_iterator> resolve() noexcept {
return resolve(locator<meta_ctx>::value_or());
}
@@ -83,7 +83,7 @@ template<typename Type>
* @return The meta type associated with the given type info object, if any.
*/
[[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const type_info &info) noexcept {
auto &&context = internal::meta_context::from(ctx);
const auto &context = internal::meta_context::from(ctx);
const auto *elem = internal::try_resolve(context, info);
return (elem != nullptr) ? meta_type{ctx, *elem} : meta_type{};
}

View File

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

View File

@@ -93,11 +93,11 @@ struct meta_function_descriptor<Type, Ret (*)(MaybeType, Args...)>
: meta_function_descriptor_traits<
Ret,
std::conditional_t<
std::is_same_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>,
std::is_same_v<std::remove_cvref_t<MaybeType>, Type> || std::is_base_of_v<std::remove_cvref_t<MaybeType>, Type>,
type_list<Args...>,
type_list<MaybeType, Args...>>,
!(std::is_same_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>),
std::is_const_v<std::remove_reference_t<MaybeType>> && (std::is_same_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>)> {};
!(std::is_same_v<std::remove_cvref_t<MaybeType>, Type> || std::is_base_of_v<std::remove_cvref_t<MaybeType>, Type>),
std::is_const_v<std::remove_reference_t<MaybeType>> && (std::is_same_v<std::remove_cvref_t<MaybeType>, Type> || std::is_base_of_v<std::remove_cvref_t<MaybeType>, Type>)> {};
/**
* @brief Meta function descriptor.
@@ -124,19 +124,20 @@ struct meta_function_descriptor<Type, Ret (*)()>
template<typename Type, typename Candidate>
class meta_function_helper {
template<typename Ret, typename... Args, typename Class>
static constexpr meta_function_descriptor<Type, Ret (Class::*)(Args...) const> get_rid_of_noexcept(Ret (Class::*)(Args...) const);
static meta_function_descriptor<Type, Ret (Class::*)(Args...) const> get_rid_of_noexcept(Ret (Class::*)(Args...) const);
template<typename Ret, typename... Args, typename Class>
static constexpr meta_function_descriptor<Type, Ret (Class::*)(Args...)> get_rid_of_noexcept(Ret (Class::*)(Args...));
static meta_function_descriptor<Type, Ret (Class::*)(Args...)> get_rid_of_noexcept(Ret (Class::*)(Args...));
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 Class>
requires std::is_member_object_pointer_v<Ret Class::*>
static meta_function_descriptor<Type, Ret Class::*> get_rid_of_noexcept(Ret Class::*);
template<typename Ret, typename... Args>
static constexpr meta_function_descriptor<Type, Ret (*)(Args...)> get_rid_of_noexcept(Ret (*)(Args...));
static meta_function_descriptor<Type, Ret (*)(Args...)> get_rid_of_noexcept(Ret (*)(Args...));
template<typename Class>
static constexpr meta_function_descriptor<Class, decltype(&Class::operator())> get_rid_of_noexcept(Class);
static meta_function_descriptor<Class, decltype(&Class::operator())> get_rid_of_noexcept(Class);
public:
/*! @brief The meta function descriptor of the given function. */
@@ -149,7 +150,7 @@ public:
* @tparam Candidate The actual function to associate with the reflected type.
*/
template<typename Type, typename Candidate>
using meta_function_helper_t = typename meta_function_helper<Type, Candidate>::type;
using meta_function_helper_t = meta_function_helper<Type, Candidate>::type;
/**
* @brief Wraps a value depending on the given policy.
@@ -164,15 +165,15 @@ using meta_function_helper_t = typename meta_function_helper<Type, Candidate>::t
* @param value Value to wrap.
* @return A meta any containing the returned value, if any.
*/
template<typename Policy = as_is_t, typename Type>
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) {
if constexpr(std::is_same_v<Policy, as_void_t>) {
return meta_any{ctx, std::in_place_type<void>};
} else if constexpr(std::is_same_v<Policy, as_ref_t>) {
return meta_any{ctx, std::in_place_type<Type>, value};
} else if constexpr(std::is_same_v<Policy, as_cref_t>) {
template<meta_policy Policy = as_value_t, typename Type>
[[nodiscard]] meta_any meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) {
if constexpr(std::is_same_v<Policy, as_cref_t>) {
static_assert(std::is_lvalue_reference_v<Type>, "Invalid type");
return meta_any{ctx, std::in_place_type<const std::remove_reference_t<Type> &>, std::as_const(value)};
} else if constexpr(std::is_same_v<Policy, as_ref_t> || (std::is_same_v<Policy, as_is_t> && std::is_lvalue_reference_v<Type>)) {
return meta_any{ctx, std::in_place_type<Type>, value};
} else if constexpr(std::is_same_v<Policy, as_void_t>) {
return meta_any{ctx, std::in_place_type<void>};
} else {
return meta_any{ctx, std::forward<Type>(value)};
}
@@ -185,12 +186,12 @@ template<typename Policy = as_is_t, typename Type>
* @param value Value to wrap.
* @return A meta any containing the returned value, if any.
*/
template<typename Policy = as_is_t, typename Type>
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(Type &&value) {
template<meta_policy Policy = as_value_t, typename Type>
[[nodiscard]] meta_any meta_dispatch(Type &&value) {
return meta_dispatch<Policy, Type>(locator<meta_ctx>::value_or(), std::forward<Type>(value));
}
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename Policy, typename Candidate, typename... Args>
@@ -204,26 +205,26 @@ template<typename Policy, typename Candidate, typename... Args>
}
template<typename Type, typename Policy, typename Candidate, std::size_t... Index>
[[nodiscard]] meta_any meta_invoke(meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *const args, std::index_sequence<Index...>) {
[[nodiscard]] meta_any meta_invoke(meta_any &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>(instance->context(), std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<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>(instance.context(), std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
}
} else if constexpr(std::is_invocable_v<std::remove_reference_t<Candidate>, Type &, type_list_element_t<Index, typename descriptor::args_type>...>) {
if(auto *const clazz = instance->try_cast<Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
return meta_invoke_with_args<Policy>(instance->context(), std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
if(auto *const clazz = instance.try_cast<Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
return meta_invoke_with_args<Policy>(instance.context(), std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
}
} else {
if(((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
return meta_invoke_with_args<Policy>(instance->context(), std::forward<Candidate>(candidate), (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
return meta_invoke_with_args<Policy>(instance.context(), 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, instance->context()};
return meta_any{meta_ctx_arg, instance.context()};
}
template<typename Type, typename... Args, std::size_t... Index>
@@ -248,8 +249,8 @@ template<typename Type, typename... Args, std::size_t... Index>
* @return The meta type of the i-th element of the list of arguments.
*/
template<typename Type>
[[nodiscard]] static meta_type meta_arg(const meta_ctx &ctx, const std::size_t index) noexcept {
auto &&context = internal::meta_context::from(ctx);
[[nodiscard]] meta_type meta_arg(const meta_ctx &ctx, const std::size_t index) noexcept {
const auto &context = internal::meta_context::from(ctx);
return {ctx, internal::meta_arg_node(context, Type{}, index)};
}
@@ -260,7 +261,7 @@ template<typename Type>
* @return The meta type of the i-th element of the list of arguments.
*/
template<typename Type>
[[nodiscard]] static meta_type meta_arg(const std::size_t index) noexcept {
[[nodiscard]] meta_type meta_arg(const std::size_t index) noexcept {
return meta_arg<Type>(locator<meta_ctx>::value_or(), index);
}
@@ -313,10 +314,10 @@ template<typename Type, auto Data>
* @param instance An opaque instance of the underlying type, if required.
* @return A meta any containing the value of the underlying variable.
*/
template<typename Type, auto Data, typename Policy = as_is_t>
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_getter(meta_handle instance) {
template<typename Type, auto Data, meta_policy Policy = as_value_t>
[[nodiscard]] meta_any meta_getter(meta_handle instance) {
if constexpr(std::is_member_pointer_v<decltype(Data)> || std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>>) {
if constexpr(!std::is_array_v<std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(Data), Type &>>>>) {
if constexpr(!std::is_array_v<std::remove_cvref_t<std::invoke_result_t<decltype(Data), Type &>>>) {
if constexpr(std::is_invocable_v<decltype(Data), Type &>) {
if(auto *clazz = instance->try_cast<Type>(); clazz) {
return meta_dispatch<Policy>(instance->context(), std::invoke(Data, *clazz));
@@ -342,20 +343,6 @@ template<typename Type, auto Data, typename Policy = as_is_t>
}
}
/**
* @brief Gets the value of a given variable.
* @tparam Type Reflected type to which the variable is associated.
* @tparam Data The actual variable to get.
* @tparam Policy Optional policy (no policy set by default).
* @param ctx The context from which to search for meta types.
* @param instance An opaque instance of the underlying type, if required.
* @return A meta any containing the value of the underlying variable.
*/
template<typename Type, auto Data, typename Policy = as_is_t>
[[deprecated("a context is no longer required, it is inferred from the meta_handle")]] [[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_getter(const meta_ctx &ctx, meta_handle instance) {
return meta_getter<Type, Data, Policy>(meta_handle{ctx, std::move(instance)});
}
/**
* @brief Tries to _invoke_ an object given a list of erased parameters.
* @tparam Type Reflected type to which the object to _invoke_ is associated.
@@ -366,25 +353,9 @@ template<typename Type, auto Data, typename Policy = as_is_t>
* @param args Parameters to use to _invoke_ the object.
* @return A meta any containing the returned value, if any.
*/
template<typename Type, typename Policy = as_is_t, typename Candidate>
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_invoke(meta_handle instance, Candidate &&candidate, meta_any *const args) {
return internal::meta_invoke<Type, Policy>(std::move(instance), std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
}
/**
* @brief Tries to _invoke_ an object given a list of erased parameters.
* @tparam Type Reflected type to which the object to _invoke_ is associated.
* @tparam Policy Optional policy (no policy set by default).
* @param ctx The context from which to search for meta types.
* @tparam Candidate The type of the actual object to _invoke_.
* @param instance An opaque instance of the underlying type, if required.
* @param candidate The actual object to _invoke_.
* @param args Parameters to use to _invoke_ the object.
* @return A meta any containing the returned value, if any.
*/
template<typename Type, typename Policy = as_is_t, typename Candidate>
[[deprecated("a context is no longer required, it is inferred from the meta_handle")]] [[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_invoke(const meta_ctx &ctx, meta_handle instance, Candidate &&candidate, meta_any *const args) {
return meta_invoke<Type, Policy>(meta_handle{ctx, std::move(instance)}, std::forward<Candidate>(candidate), args);
template<typename Type, meta_policy Policy = as_value_t, typename Candidate>
[[nodiscard]] meta_any meta_invoke(meta_handle instance, Candidate &&candidate, meta_any *const args) {
return internal::meta_invoke<Type, Policy>(*instance.operator->(), std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
}
/**
@@ -396,24 +367,9 @@ template<typename Type, typename Policy = as_is_t, typename Candidate>
* @param args Parameters to use to invoke the function.
* @return A meta any containing the returned value, if any.
*/
template<typename Type, auto Candidate, typename Policy = as_is_t>
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_invoke(meta_handle instance, meta_any *const args) {
return internal::meta_invoke<Type, Policy>(std::move(instance), Candidate, args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<decltype(Candidate)>>::args_type::size>{});
}
/**
* @brief Tries to invoke a function given a list of erased parameters.
* @tparam Type Reflected type to which the function is associated.
* @tparam Candidate The actual function to invoke.
* @tparam Policy Optional policy (no policy set by default).
* @param ctx The context from which to search for meta types.
* @param instance An opaque instance of the underlying type, if required.
* @param args Parameters to use to invoke the function.
* @return A meta any containing the returned value, if any.
*/
template<typename Type, auto Candidate, typename Policy = as_is_t>
[[deprecated("a context is no longer required, it is inferred from the meta_handle")]] [[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_invoke(const meta_ctx &ctx, meta_handle instance, meta_any *const args) {
return meta_invoke<Type, Candidate, Policy>(meta_handle{ctx, std::move(instance)}, args);
template<typename Type, auto Candidate, meta_policy Policy = as_value_t>
[[nodiscard]] meta_any meta_invoke(meta_handle instance, meta_any *const args) {
return internal::meta_invoke<Type, Policy>(*instance.operator->(), Candidate, args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<decltype(Candidate)>>::args_type::size>{});
}
/**
@@ -461,14 +417,14 @@ template<typename Type, typename... Args>
* @param args Parameters to use to _invoke_ the object.
* @return A meta any containing the returned value, if any.
*/
template<typename Type, typename Policy = as_is_t, typename Candidate>
template<typename Type, typename Policy = as_value_t, typename Candidate>
[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, Candidate &&candidate, meta_any *const args) {
if constexpr(meta_function_helper_t<Type, Candidate>::is_static || std::is_class_v<std::remove_cv_t<std::remove_reference_t<Candidate>>>) {
return internal::meta_invoke<Type, Policy>(meta_handle{meta_ctx_arg, ctx}, std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
if constexpr(meta_function_helper_t<Type, Candidate>::is_static || std::is_class_v<std::remove_cvref_t<Candidate>>) {
meta_any placeholder{meta_ctx_arg, ctx};
return internal::meta_invoke<Type, Policy>(placeholder, std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
} else {
meta_any handle{ctx, args->as_ref()};
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - waiting for C++20 (and std::span)
return internal::meta_invoke<Type, Policy>(handle, std::forward<Candidate>(candidate), args + 1u, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
return internal::meta_invoke<Type, Policy>(*args, std::forward<Candidate>(candidate), args + 1u, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
}
}
@@ -481,8 +437,8 @@ template<typename Type, typename Policy = as_is_t, typename Candidate>
* @param args Parameters to use to _invoke_ the object.
* @return A meta any containing the returned value, if any.
*/
template<typename Type, typename Policy = as_is_t, typename Candidate>
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_construct(Candidate &&candidate, meta_any *const args) {
template<typename Type, meta_policy Policy = as_value_t, typename Candidate>
[[nodiscard]] meta_any meta_construct(Candidate &&candidate, meta_any *const args) {
return meta_construct<Type, Policy>(locator<meta_ctx>::value_or(), std::forward<Candidate>(candidate), args);
}
@@ -500,8 +456,8 @@ template<typename Type, typename Policy = as_is_t, typename Candidate>
* @param args Parameters to use to invoke the function.
* @return A meta any containing the returned value, if any.
*/
template<typename Type, auto Candidate, typename Policy = as_is_t>
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_construct(const meta_ctx &ctx, meta_any *const args) {
template<typename Type, auto Candidate, meta_policy Policy = as_value_t>
[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, meta_any *const args) {
return meta_construct<Type, Policy>(ctx, Candidate, args);
}
@@ -513,8 +469,8 @@ template<typename Type, auto Candidate, typename Policy = as_is_t>
* @param args Parameters to use to invoke the function.
* @return A meta any containing the returned value, if any.
*/
template<typename Type, auto Candidate, typename Policy = as_is_t>
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_construct(meta_any *const args) {
template<typename Type, auto Candidate, meta_policy Policy = as_value_t>
[[nodiscard]] meta_any meta_construct(meta_any *const args) {
return meta_construct<Type, Candidate, Policy>(locator<meta_ctx>::value_or(), args);
}

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="entt::basic_any&lt;*&gt;">
<DisplayString>{{ type={ info->alias,na }, policy={ mode,en } }}</DisplayString>
<DisplayString>{{ policy={ mode,en } }}</DisplayString>
</Type>
<Type Name="entt::compressed_pair&lt;*&gt;">
<Intrinsic Name="first" Optional="true" Expression="((first_base*)this)->value"/>

View File

@@ -47,7 +47,7 @@
<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>
<DisplayString>{{ size={ packed.size() }, type={ descriptor->alias,na } }}</DisplayString>
<Expand>
<Item Name="[capacity]" ExcludeView="simple">packed.capacity()</Item>
<Item Name="[policy]" ExcludeView="simple">mode,en</Item>
@@ -94,7 +94,7 @@
<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>
<DisplayString>{{ size={ base_type::packed.size() }, type={ base_type::descriptor->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>
@@ -163,7 +163,7 @@
<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>
<Item Name="[{ pool_at(pos)->descriptor->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>

View File

@@ -1,17 +1,17 @@
<?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 Condition="type != nullptr">{{ id={ id } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[type]">type</Item>
<Item Name="[id]">id</Item>
</Expand>
</Type>
<Type Name="entt::internal::meta_conv_node">
<DisplayString Condition="conv != nullptr">{{ type={ type } }}</DisplayString>
<DisplayString Condition="conv != nullptr">{{ id={ id } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[type]">type</Item>
<Item Name="[type]">id</Item>
</Expand>
</Type>
<Type Name="entt::internal::meta_ctor_node">
@@ -23,10 +23,10 @@
</Expand>
</Type>
<Type Name="entt::internal::meta_custom_node">
<DisplayString Condition="value != nullptr">{{ type={ type } }}</DisplayString>
<DisplayString Condition="value != nullptr">{{ id={ id } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[type]">type</Item>
<Item Name="[id]">id</Item>
<Item Name="[value]">value</Item>
</Expand>
</Type>
@@ -34,33 +34,33 @@
<Intrinsic Name="has_trait" Expression="!!(traits &amp; property)">
<Parameter Name="property" Type="int"/>
</Intrinsic>
<DisplayString Condition="name != nullptr">{{ id={ name,na } }}</DisplayString>
<DisplayString Condition="get != nullptr">{{ id={ id } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[id]">id</Item>
<Item Name="[name]" Condition="name != nullptr">name,na</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="[custom]">custom</Item>
<Item Name="[custom]" Condition="custom.value != nullptr">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="name != nullptr">{{ id={ name,na } }}</DisplayString>
<DisplayString Condition="invoke != nullptr">{{ id={ id } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[id]">id</Item>
<Item Name="[name]" Condition="name != nullptr">name,na</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="[custom]">custom</Item>
<Item Name="[custom]" Condition="custom.value != nullptr">custom</Item>
</Expand>
</Type>
<Type Name="entt::internal::meta_template_node">
@@ -84,10 +84,12 @@
<Intrinsic Name="has_trait" Expression="!!(traits &amp; property)">
<Parameter Name="property" Type="int"/>
</Intrinsic>
<DisplayString Condition="name != nullptr">{{ type={ name,na } }}</DisplayString>
<DisplayString Condition="info != nullptr">{{ type={ info->alias,na } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[id]">id</Item>
<Item Name="[name]" Condition="name != nullptr">name,na</Item>
<Item Name="[sizeof]">size_of</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>
@@ -102,17 +104,17 @@
<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="[custom]">custom</Item>
<Item Name="[details]" Condition="!(details == nullptr)">*details</Item>
<Item Name="[template_info]" Condition="templ.arity != 0u">templ</Item>
<Item Name="[custom]" Condition="custom.value != nullptr">custom</Item>
<Item Name="[details]" Condition="details != nullptr">*details</Item>
</Expand>
</Type>
<Type Name="entt::meta_any">
<DisplayString Condition="node.info != nullptr">{{ type={ node.info->alias,na }, policy={ storage.mode,en } }}</DisplayString>
<DisplayString>{ storage }</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<ExpandedItem>node</ExpandedItem>
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
<ExpandedItem Condition="node != nullptr">node,na</ExpandedItem>
<Item Name="[context]" Condition="ctx != nullptr">ctx,na</Item>
</Expand>
</Type>
<Type Name="entt::meta_handle">
@@ -125,7 +127,7 @@
<DisplayString Condition="data != nullptr">{{ const={ const_only } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
<Item Name="[context]" Condition="ctx != nullptr">ctx,na</Item>
<Item Name="[const]">const_only</Item>
<Item Name="[data]">data</Item>
</Expand>
@@ -134,49 +136,59 @@
<DisplayString Condition="data != nullptr">{{ const={ const_only } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
<Item Name="[context]" Condition="ctx != nullptr">ctx,na</Item>
<Item Name="[const]">const_only</Item>
<Item Name="[data]">data</Item>
</Expand>
</Type>
<Type Name="entt::meta_custom">
<DisplayString>{ node }</DisplayString>
<DisplayString Condition="node != nullptr">{ node,na }</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<ExpandedItem>node</ExpandedItem>
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
</Expand>
</Type>
<Type Name="entt::meta_data">
<DisplayString Condition="node.get != nullptr">{ node }</DisplayString>
<DisplayString Condition="node != nullptr">{ node,na }</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<ExpandedItem Condition="node.get != nullptr">node</ExpandedItem>
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
<Item Name="[context]" Condition="ctx != nullptr">ctx,na</Item>
</Expand>
</Type>
<Type Name="entt::meta_func">
<DisplayString Condition="node.invoke != nullptr">{ node }</DisplayString>
<DisplayString Condition="node != nullptr">{ node,na }</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<ExpandedItem Condition="node.invoke != nullptr">node</ExpandedItem>
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
<Item Name="[context]" Condition="ctx != nullptr">ctx,na</Item>
</Expand>
</Type>
<Type Name="entt::meta_base">
<DisplayString Condition="node != nullptr">{ node,na }</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
<Item Name="[context]" Condition="ctx != nullptr">ctx,na</Item>
</Expand>
</Type>
<Type Name="entt::meta_type">
<DisplayString>{ node }</DisplayString>
<DisplayString Condition="node != nullptr">{ node,na }</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<ExpandedItem>node</ExpandedItem>
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
<Item Name="[context]" Condition="ctx != nullptr">ctx,na</Item>
</Expand>
</Type>
<Type Name="entt::meta_ctx">
<Intrinsic Name="element_at" Expression="value.packed.first_base::value[pos].element">
<Intrinsic Name="element_at" Expression="bucket.packed.first_base::value[pos].element">
<Parameter Name="pos" Type="int"/>
</Intrinsic>
<DisplayString>{ value }</DisplayString>
<DisplayString>{ bucket }</DisplayString>
<Expand>
<CustomListItems>
<Variable Name="pos" InitialValue="0"/>
<Variable Name="last" InitialValue="value.size()"/>
<Variable Name="last" InitialValue="bucket.size()"/>
<Loop>
<Break Condition="pos == last"/>
<Item Name="[{ element_at(pos).first }]">element_at(pos).second</Item>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="entt::basic_process&lt;*&gt;">
<DisplayString>{{ state={ current,en } }}</DisplayString>
<Expand>
<Item Name="[state]">current,en</Item>
<Item Name="[child]" Condition="next.first_base::value != nullptr">*next.first_base::value</Item>
</Expand>
</Type>
<Type Name="entt::basic_scheduler&lt;*&gt;">
<Intrinsic Name="size" Expression="handlers.first_base::value.size()"/>
<DisplayString>{{ size={ size() } }}</DisplayString>
<Expand>
<Item Name="[capacity]" ExcludeView="simple">handlers.first_base::value.capacity()</Item>
<IndexListItems>
<Size>size()</Size>
<ValueNode>*handlers.first_base::value[$i]</ValueNode>
</IndexListItems>
</Expand>
</Type>
</AutoVisualizer>

View File

@@ -21,6 +21,7 @@
<DisplayString>{{ size={ events.size() }, event={ "$T1" } }}</DisplayString>
<Expand>
<Item Name="[signal]">signal</Item>
<Item Name="[events]">events,view(simple)</Item>
</Expand>
</Type>
<Type Name="entt::emitter&lt;*&gt;">

View File

@@ -1,12 +1,14 @@
#ifndef ENTT_POLY_POLY_HPP
#define ENTT_POLY_POLY_HPP
#include <concepts>
#include <cstddef>
#include <functional>
#include <tuple>
#include <type_traits>
#include <utility>
#include "../core/any.hpp"
#include "../core/concepts.hpp"
#include "../core/type_info.hpp"
#include "../core/type_traits.hpp"
#include "fwd.hpp"
@@ -45,30 +47,33 @@ struct poly_inspector {
*/
template<typename Concept, std::size_t Len, std::size_t Align>
class poly_vtable {
using inspector = typename Concept::template type<poly_inspector>;
using inspector = Concept::template type<poly_inspector>;
template<typename Ret, typename Clazz, typename... Args>
requires std::derived_from<inspector, std::remove_const_t<Clazz>>
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...)>;
-> Ret (*)(constness_as_t<basic_any<Len, Align>, Clazz> &, 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>
requires std::derived_from<inspector, Clazz>
static auto vtable_entry(Ret (Clazz::*)(Args...))
-> std::enable_if_t<std::is_base_of_v<Clazz, inspector>, Ret (*)(basic_any<Len, Align> &, Args...)>;
-> Ret (*)(basic_any<Len, Align> &, Args...);
template<typename Ret, typename Clazz, typename... Args>
requires std::derived_from<inspector, Clazz>
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...)>;
-> Ret (*)(const basic_any<Len, Align> &, Args...);
template<auto... Candidate>
static auto make_vtable(value_list<Candidate...>) noexcept
-> decltype(std::make_tuple(vtable_entry(Candidate)...));
template<typename... Func>
[[nodiscard]] static constexpr auto make_vtable(type_list<Func...>) noexcept {
[[nodiscard]] static ENTT_CONSTEVAL auto make_vtable(type_list<Func...>) noexcept {
if constexpr(sizeof...(Func) == 0u) {
return decltype(make_vtable(typename Concept::template impl<inspector>{})){};
} else if constexpr((std::is_function_v<Func> && ...)) {
@@ -108,9 +113,8 @@ public:
* @tparam Type The type for which to generate the virtual table.
* @return A static virtual table for the given concept and type.
*/
template<typename Type>
template<cvref_unqualified Type>
[[nodiscard]] static type instance() noexcept {
static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Type differs from its decayed form");
static const vtable_type vtable = fill_vtable<Type>(std::make_index_sequence<Concept::template impl<Type>::size>{});
if constexpr(is_mono) {
@@ -195,9 +199,9 @@ class basic_poly: private Concept::template type<poly_base<basic_poly<Concept, L
public:
/*! @brief Concept type. */
using concept_type = typename Concept::template type<poly_base<basic_poly>>;
using concept_type = Concept::template type<poly_base<basic_poly>>;
/*! @brief Virtual table type. */
using vtable_type = typename poly_vtable<Concept, Len, Align>::type;
using vtable_type = poly_vtable<Concept, Len, Align>::type;
/*! @brief Default constructor. */
basic_poly() noexcept = default;
@@ -211,23 +215,24 @@ public:
template<typename Type, typename... Args>
explicit basic_poly(std::in_place_type_t<Type>, Args &&...args)
: storage{std::in_place_type<Type>, std::forward<Args>(args)...},
vtable{poly_vtable<Concept, Len, Align>::template instance<std::remove_cv_t<std::remove_reference_t<Type>>>()} {}
vtable{poly_vtable<Concept, Len, Align>::template instance<std::remove_cvref_t<Type>>()} {}
/**
* @brief Constructs a poly from a given value.
* @tparam Type Type of object to use to initialize the poly.
* @param value An instance of an object to use to initialize the poly.
*/
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, basic_poly>>>
template<typename Type>
requires (!std::same_as<std::remove_cvref_t<Type>, basic_poly>)
basic_poly(Type &&value) noexcept
: basic_poly{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)} {}
: basic_poly{std::in_place_type<std::remove_cvref_t<Type>>, std::forward<Type>(value)} {}
/**
* @brief Returns the object type if any, `type_id<void>()` otherwise.
* @return The object type if any, `type_id<void>()` otherwise.
* @brief Returns the object type info if any, `type_id<void>()` otherwise.
* @return The object type info if any, `type_id<void>()` otherwise.
*/
[[nodiscard]] const type_info &type() const noexcept {
return storage.type();
[[nodiscard]] const type_info &info() const noexcept {
return storage.info();
}
/**
@@ -252,7 +257,7 @@ public:
template<typename Type, typename... Args>
void emplace(Args &&...args) {
storage.template emplace<Type>(std::forward<Args>(args)...);
vtable = poly_vtable<Concept, Len, Align>::template instance<std::remove_cv_t<std::remove_reference_t<Type>>>();
vtable = poly_vtable<Concept, Len, Align>::template instance<std::remove_cvref_t<Type>>();
}
/*! @brief Destroys contained object */

View File

@@ -6,14 +6,17 @@
namespace entt {
template<typename, typename>
class process;
template<typename, typename = std::allocator<void>>
class basic_process;
template<typename = std::uint32_t, typename = std::allocator<void>>
/*! @brief Alias declaration for the most common use case. */
using process = basic_process<std::uint32_t>;
template<typename, typename = std::allocator<void>>
class basic_scheduler;
/*! @brief Alias declaration for the most common use case. */
using scheduler = basic_scheduler<>;
using scheduler = basic_scheduler<std::uint32_t>;
} // namespace entt

View File

@@ -2,22 +2,32 @@
#define ENTT_PROCESS_PROCESS_HPP
#include <cstdint>
#include <memory>
#include <type_traits>
#include <utility>
#include "../core/compressed_pair.hpp"
#include "../core/type_traits.hpp"
#include "fwd.hpp"
namespace entt {
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename, typename, typename>
struct process_adaptor;
} // namespace internal
/*! @endcond */
/**
* @brief Base class for processes.
*
* This class stays true to the CRTP idiom. Derived classes must specify what's
* the intended type for elapsed times.<br/>
* A process should expose publicly the following member functions whether
* required:
* Derived classes must specify what's the intended type for elapsed times.<br/>
* A process can implement the following member functions whether required:
*
* * @code{.cpp}
* void update(Delta, void *);
* void update(Delta, void *) override;
* @endcode
*
* It's invoked once per tick until a process is explicitly aborted or it
@@ -28,30 +38,21 @@ namespace entt {
* update.
*
* * @code{.cpp}
* void init();
* @endcode
*
* It's invoked when the process joins the running queue of a scheduler. This
* happens as soon as it's attached to the scheduler if the process is a top
* level one, otherwise when it replaces its parent if the process is a
* continuation.
*
* * @code{.cpp}
* void succeeded();
* void succeeded() override;
* @endcode
*
* It's invoked in case of success, immediately after an update and during the
* same tick.
*
* * @code{.cpp}
* void failed();
* void failed() override;
* @endcode
*
* It's invoked in case of errors, immediately after an update and during the
* same tick.
*
* * @code{.cpp}
* void aborted();
* void aborted() override;
* @endcode
*
* It's invoked only if a process is explicitly aborted. There is no guarantee
@@ -59,18 +60,18 @@ namespace entt {
* process is aborted immediately or not.
*
* Derived classes can change the internal state of a process by invoking the
* `succeed` and `fail` protected member functions and even pause or unpause the
* process itself.
* `succeed` and `fail` member functions and even pause or unpause the process
* itself.
*
* @sa scheduler
*
* @tparam Derived Actual type of process that extends the class template.
* @tparam Delta Type to use to provide elapsed time.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Derived, typename Delta>
class process {
template<typename Delta, typename Allocator>
class basic_process: public std::enable_shared_from_this<basic_process<Delta, Allocator>> {
enum class state : std::uint8_t {
uninitialized = 0,
idle = 0,
running,
paused,
succeeded,
@@ -80,45 +81,73 @@ class process {
rejected
};
template<typename Target = Derived>
auto next(std::integral_constant<state, state::uninitialized>)
-> decltype(std::declval<Target>().init(), void()) {
static_cast<Target *>(this)->init();
virtual void update(const Delta, void *) {
abort();
}
template<typename Target = Derived>
auto next(std::integral_constant<state, state::running>, Delta delta, void *data)
-> decltype(std::declval<Target>().update(delta, data), void()) {
static_cast<Target *>(this)->update(delta, data);
}
virtual void succeeded() {}
virtual void failed() {}
virtual void aborted() {}
template<typename Target = Derived>
auto next(std::integral_constant<state, state::succeeded>)
-> decltype(std::declval<Target>().succeeded(), void()) {
static_cast<Target *>(this)->succeeded();
}
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Type used to provide elapsed time. */
using delta_type = Delta;
/*! @brief Handle type. */
using handle_type = std::shared_ptr<basic_process>;
template<typename Target = Derived>
auto next(std::integral_constant<state, state::failed>)
-> decltype(std::declval<Target>().failed(), void()) {
static_cast<Target *>(this)->failed();
}
/*! @brief Default constructor. */
basic_process()
: basic_process{allocator_type{}} {}
template<typename Target = Derived>
auto next(std::integral_constant<state, state::aborted>)
-> decltype(std::declval<Target>().aborted(), void()) {
static_cast<Target *>(this)->aborted();
}
template<typename... Args>
void next(Args...) const noexcept {}
protected:
/**
* @brief Terminates a process with success if it's still alive.
*
* The function is idempotent and it does nothing if the process isn't
* alive.
* @brief Constructs a scheduler with a given allocator.
* @param allocator The allocator to use.
*/
explicit basic_process(const allocator_type &allocator)
: next{nullptr, allocator},
current{state::idle} {}
/*! @brief Default copy constructor, deleted on purpose. */
basic_process(const basic_process &) = delete;
/*! @brief Default move constructor, deleted on purpose. */
basic_process(basic_process &&) = delete;
/*! @brief Default destructor. */
virtual ~basic_process() = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This process scheduler.
*/
basic_process &operator=(const basic_process &) = delete;
/**
* @brief Default move assignment operator, deleted on purpose.
* @return This process scheduler.
*/
basic_process &operator=(basic_process &&) = delete;
/**
* @brief Returns the associated allocator.
* @return The associated allocator.
*/
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
return next.second();
}
/*! @brief Aborts a process if it's still alive, otherwise does nothing. */
void abort() {
if(alive()) {
current = state::aborted;
}
}
/**
* @brief Terminates a process with success if it's still alive, otherwise
* does nothing.
*/
void succeed() noexcept {
if(alive()) {
@@ -127,10 +156,8 @@ protected:
}
/**
* @brief Terminates a process with errors if it's still alive.
*
* The function is idempotent and it does nothing if the process isn't
* alive.
* @brief Terminates a process with errors if it's still alive, otherwise
* does nothing.
*/
void fail() noexcept {
if(alive()) {
@@ -138,75 +165,17 @@ protected:
}
}
/**
* @brief Stops a process if it's in a running state.
*
* The function is idempotent and it does nothing if the process isn't
* running.
*/
/*! @brief Stops a process if it's running, otherwise does nothing. */
void pause() noexcept {
if(current == state::running) {
if(alive()) {
current = state::paused;
}
}
/**
* @brief Restarts a process if it's paused.
*
* The function is idempotent and it does nothing if the process isn't
* paused.
*/
/*! @brief Restarts a process if it's paused, otherwise does nothing. */
void unpause() noexcept {
if(current == state::paused) {
current = state::running;
}
}
public:
/*! @brief Type used to provide elapsed time. */
using delta_type = Delta;
/*! @brief Default constructor. */
constexpr process() = default;
/*! @brief Default copy constructor. */
process(const process &) = default;
/*! @brief Default move constructor. */
process(process &&) noexcept = default;
/**
* @brief Default copy assignment operator.
* @return This process.
*/
process &operator=(const process &) = default;
/**
* @brief Default move assignment operator.
* @return This process.
*/
process &operator=(process &&) noexcept = default;
/*! @brief Default destructor. */
virtual ~process() {
static_assert(std::is_base_of_v<process, Derived>, "Incorrect use of the class template");
}
/**
* @brief Aborts a process if it's still alive.
*
* The function is idempotent and it does nothing if the process isn't
* alive.
*
* @param immediate Requests an immediate operation.
*/
void abort(const bool immediate = false) {
if(alive()) {
current = state::aborted;
if(immediate) {
tick({});
}
current = state::running;
}
}
@@ -243,18 +212,50 @@ public:
}
/**
* @brief Updates a process and its internal state if required.
* @brief Assigns a child process to run in case of success.
* @tparam Type Type of child process to create.
* @tparam Args Types of arguments to use to initialize the child process.
* @param args Parameters to use to initialize the child process.
* @return A reference to the newly created child process.
*/
template<typename Type, typename... Args>
basic_process &then(Args &&...args) {
const auto &allocator = next.second();
return *(next.first() = std::allocate_shared<Type>(allocator, allocator, std::forward<Args>(args)...));
}
/**
* @brief Assigns a child process to run in case of success.
* @tparam Func Type of child process to create.
* @param func Either a lambda or a functor to use as a child process.
* @return A reference to the newly created child process.
*/
template<typename Func>
basic_process &then(Func func) {
const auto &allocator = next.second();
using process_type = internal::process_adaptor<delta_type, Func, allocator_type>;
return *(next.first() = std::allocate_shared<process_type>(allocator, allocator, std::move(func)));
}
/**
* @brief Returns the child process without releasing ownership, if any.
* @return The child process attached to the object, if any.
*/
handle_type peek() {
return next.first();
}
/**
* @brief Updates a process and its internal state, if required.
* @param delta Elapsed time.
* @param data Optional data.
*/
void tick(const Delta delta, void *data = nullptr) {
switch(current) {
case state::uninitialized:
next(std::integral_constant<state, state::uninitialized>{});
current = state::running;
break;
case state::idle:
case state::running:
next(std::integral_constant<state, state::running>{}, delta, data);
current = state::running;
update(delta, data);
break;
default:
// suppress warnings
@@ -264,15 +265,15 @@ public:
// if it's dead, it must be notified and removed immediately
switch(current) {
case state::succeeded:
next(std::integral_constant<state, state::succeeded>{});
succeeded();
current = state::finished;
break;
case state::failed:
next(std::integral_constant<state, state::failed>{});
failed();
current = state::rejected;
break;
case state::aborted:
next(std::integral_constant<state, state::aborted>{});
aborted();
current = state::rejected;
break;
default:
@@ -282,73 +283,34 @@ public:
}
private:
state current{state::uninitialized};
compressed_pair<handle_type, allocator_type> next;
state current;
};
/**
* @brief Adaptor for lambdas and functors to turn them into processes.
*
* Lambdas and functors can't be used directly with a scheduler for they are not
* properly defined processes with managed life cycles.<br/>
* This class helps in filling the gap and turning lambdas and functors into
* full featured processes usable by a scheduler.
*
* The signature of the function call operator should be equivalent to the
* following:
*
* @code{.cpp}
* void(Delta delta, void *data, auto succeed, auto fail);
* @endcode
*
* Where:
*
* * `delta` is the elapsed time.
* * `data` is an opaque pointer to user data if any, `nullptr` otherwise.
* * `succeed` is a function to call when a process terminates with success.
* * `fail` is a function to call when a process terminates with errors.
*
* The signature of the function call operator of both `succeed` and `fail`
* is equivalent to the following:
*
* @code{.cpp}
* void();
* @endcode
*
* Usually users shouldn't worry about creating adaptors. A scheduler will
* create them internally each and avery time a lambda or a functor is used as
* a process.
*
* @sa process
* @sa scheduler
*
* @tparam Func Actual type of process.
* @tparam Delta Type to use to provide elapsed time.
*/
template<typename Func, typename Delta>
struct process_adaptor: process<process_adaptor<Func, Delta>, Delta>, private Func {
/**
* @brief Constructs a process adaptor from a lambda or a functor.
* @tparam Args Types of arguments to use to initialize the actual process.
* @param args Parameters to use to initialize the actual process.
*/
template<typename... Args>
process_adaptor(Args &&...args)
: Func{std::forward<Args>(args)...} {}
/*! @cond ENTT_INTERNAL */
namespace internal {
/**
* @brief Updates a process and its internal state if required.
* @param delta Elapsed time.
* @param data Optional data.
*/
void update(const Delta delta, void *data) {
Func::operator()(
delta,
data,
[this]() { this->succeed(); },
[this]() { this->fail(); });
template<typename Delta, typename Func, typename Allocator>
struct process_adaptor: public basic_process<Delta, Allocator> {
using allocator_type = Allocator;
using base_type = basic_process<Delta, Allocator>;
using delta_type = base_type::delta_type;
process_adaptor(const allocator_type &allocator, Func proc)
: base_type{allocator},
func{std::move(proc)} {}
void update(const delta_type delta, void *data) override {
func(*this, delta, data);
}
private:
Func func;
};
} // namespace internal
/*! @endcond */
} // namespace entt
#endif

View File

@@ -13,44 +13,6 @@
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename Delta>
struct basic_process_handler {
virtual ~basic_process_handler() = default;
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;
};
template<typename Delta, typename Type>
struct process_handler final: basic_process_handler<Delta> {
template<typename... Args>
process_handler(Args &&...args)
: process{std::forward<Args>(args)...} {}
bool update(const Delta delta, void *data) override {
if(process.tick(delta, data); process.rejected()) {
this->next.reset();
}
return (process.rejected() || process.finished());
}
void abort(const bool immediate) override {
process.abort(immediate);
}
Type process;
};
} // namespace internal
/*! @endcond */
/**
* @brief Cooperative scheduler for processes.
*
@@ -62,14 +24,6 @@ struct process_handler final: basic_process_handler<Delta> {
* its child when it terminates if it returns with success. In case of errors,
* both the process and its child are discarded.
*
* Example of use (pseudocode):
*
* @code{.cpp}
* scheduler.attach([](auto delta, void *, auto succeed, auto fail) {
* // code
* }).then<my_process>(arguments...);
* @endcode
*
* In order to invoke all scheduled processes, call the `update` member function
* passing it the elapsed time to forward to the tasks.
*
@@ -80,17 +34,14 @@ struct process_handler final: basic_process_handler<Delta> {
*/
template<typename Delta, typename Allocator>
class basic_scheduler {
template<typename Type>
using handler_type = internal::process_handler<Delta, Type>;
// std::shared_ptr because of its type erased allocator which is useful here
using process_type = std::shared_ptr<internal::basic_process_handler<Delta>>;
using base_type = basic_process<Delta, Allocator>;
using alloc_traits = std::allocator_traits<Allocator>;
using container_allocator = typename alloc_traits::template rebind_alloc<process_type>;
using container_type = std::vector<process_type, container_allocator>;
using container_allocator = alloc_traits::template rebind_alloc<std::shared_ptr<base_type>>;
using container_type = std::vector<std::shared_ptr<base_type>, container_allocator>;
public:
/*! @brief Process type. */
using type = base_type;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Unsigned integer type. */
@@ -194,122 +145,28 @@ public:
/**
* @brief Schedules a process for the next tick.
*
* Returned value can be used to attach a continuation for the last process.
* The continutation is scheduled automatically when the process terminates
* and only if the process returns with success.
*
* Example of use (pseudocode):
*
* @code{.cpp}
* // schedules a task in the form of a process class
* scheduler.attach<my_process>(arguments...)
* // appends a child in the form of a lambda function
* .then([](auto delta, void *, auto succeed, auto fail) {
* // code
* })
* // appends a child in the form of another process class
* .then<my_other_process>();
* @endcode
*
* @tparam Proc Type of process to schedule.
* @tparam Type Type of process to create.
* @tparam Args Types of arguments to use to initialize the process.
* @param args Parameters to use to initialize the process.
* @return This process scheduler.
* @return A reference to the newly created process.
*/
template<typename Proc, typename... Args>
basic_scheduler &attach(Args &&...args) {
static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type");
auto &ref = handlers.first().emplace_back(std::allocate_shared<handler_type<Proc>>(handlers.second(), std::forward<Args>(args)...));
// forces the process to exit the uninitialized state
ref->update({}, nullptr);
return *this;
template<typename Type, typename... Args>
type &attach(Args &&...args) {
const auto &allocator = handlers.second();
return *handlers.first().emplace_back(std::allocate_shared<Type>(allocator, allocator, std::forward<Args>(args)...));
}
/**
* @brief Schedules a process for the next tick.
*
* A process can be either a lambda or a functor. The scheduler wraps both
* of them in a process adaptor internally.<br/>
* The signature of the function call operator should be equivalent to the
* following:
*
* @code{.cpp}
* void(Delta delta, void *data, auto succeed, auto fail);
* @endcode
*
* Where:
*
* * `delta` is the elapsed time.
* * `data` is an opaque pointer to user data if any, `nullptr` otherwise.
* * `succeed` is a function to call when a process terminates with success.
* * `fail` is a function to call when a process terminates with errors.
*
* The signature of the function call operator of both `succeed` and `fail`
* is equivalent to the following:
*
* @code{.cpp}
* void();
* @endcode
*
* Returned value can be used to attach a continuation for the last process.
* The continutation is scheduled automatically when the process terminates
* and only if the process returns with success.
*
* Example of use (pseudocode):
*
* @code{.cpp}
* // schedules a task in the form of a lambda function
* scheduler.attach([](auto delta, void *, auto succeed, auto fail) {
* // code
* })
* // appends a child in the form of another lambda function
* .then([](auto delta, void *, auto succeed, auto fail) {
* // code
* })
* // appends a child in the form of a process class
* .then<my_process>(arguments...);
* @endcode
*
* @sa process_adaptor
*
* @tparam Func Type of process to schedule.
* @tparam Func Type of process to create.
* @param func Either a lambda or a functor to use as a process.
* @return This process scheduler.
* @return A reference to the newly created process.
*/
template<typename Func>
basic_scheduler &attach(Func &&func) {
using Proc = process_adaptor<std::decay_t<Func>, Delta>;
return attach<Proc>(std::forward<Func>(func));
}
/**
* @brief Sets a process as a continuation of the last scheduled process.
* @tparam Proc Type of process to use as a continuation.
* @tparam Args Types of arguments to use to initialize the process.
* @param args Parameters to use to initialize the process.
* @return This process scheduler.
*/
template<typename Proc, typename... Args>
basic_scheduler &then(Args &&...args) {
static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type");
ENTT_ASSERT(!handlers.first().empty(), "Process not available");
auto *curr = handlers.first().back().get();
for(; curr->next; curr = curr->next.get()) {}
curr->next = std::allocate_shared<handler_type<Proc>>(handlers.second(), std::forward<Args>(args)...);
return *this;
}
/**
* @brief Sets a process as a continuation of the last scheduled process.
* @tparam Func Type of process to use as a continuation.
* @param func Either a lambda or a functor to use as a process.
* @return This process scheduler.
*/
template<typename Func>
basic_scheduler &then(Func &&func) {
using Proc = process_adaptor<std::decay_t<Func>, Delta>;
return then<Proc>(std::forward<Func>(func));
type &attach(Func func) {
const auto &allocator = handlers.second();
using process_type = internal::process_adaptor<delta_type, Func, allocator_type>;
return *handlers.first().emplace_back(std::allocate_shared<process_type>(allocator, allocator, std::move(func)));
}
/**
@@ -325,16 +182,18 @@ public:
*/
void update(const delta_type delta, void *data = nullptr) {
for(auto next = handlers.first().size(); next; --next) {
if(const auto pos = next - 1u; handlers.first()[pos]->update(delta, data)) {
// updating might spawn/reallocate, cannot hold refs until here
if(auto &curr = handlers.first()[pos]; curr->next) {
curr = std::move(curr->next);
// forces the process to exit the uninitialized state
curr->update({}, nullptr);
} else {
curr = std::move(handlers.first().back());
handlers.first().pop_back();
}
const auto pos = next - 1u;
handlers.first()[pos]->tick(delta, data);
// updating might spawn/reallocate, cannot hold refs until here
auto &elem = handlers.first()[pos];
if(elem->finished()) {
elem = elem->peek();
}
if(!elem || elem->rejected()) {
elem = std::move(handlers.first().back());
handlers.first().pop_back();
}
}
}
@@ -351,7 +210,11 @@ public:
*/
void abort(const bool immediate = false) {
for(auto &&curr: handlers.first()) {
curr->abort(immediate);
curr->abort();
if(immediate) {
curr->tick({});
}
}
}

View File

@@ -1,6 +1,8 @@
#ifndef ENTT_RESOURCE_RESOURCE_CACHE_HPP
#define ENTT_RESOURCE_RESOURCE_CACHE_HPP
#include <compare>
#include <concepts>
#include <cstddef>
#include <functional>
#include <iterator>
@@ -12,14 +14,14 @@
#include "../core/compressed_pair.hpp"
#include "../core/fwd.hpp"
#include "../core/iterator.hpp"
#include "../core/utility.hpp"
#include "../stl/functional.hpp"
#include "fwd.hpp"
#include "loader.hpp"
#include "resource.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename Type, typename It>
@@ -40,7 +42,8 @@ public:
constexpr resource_cache_iterator(const It iter) noexcept
: it{iter} {}
template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
template<typename Other>
requires (!std::same_as<It, Other> && std::constructible_from<It, Other>)
constexpr resource_cache_iterator(const resource_cache_iterator<std::remove_const_t<Type>, Other> &other) noexcept
: it{other.it} {}
@@ -92,54 +95,25 @@ public:
return operator*();
}
template<typename... Lhs, typename... Rhs>
friend constexpr std::ptrdiff_t operator-(const resource_cache_iterator<Lhs...> &, const resource_cache_iterator<Rhs...> &) noexcept;
template<typename... Args>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const resource_cache_iterator<Args...> &other) const noexcept {
return it - other.it;
}
template<typename... Lhs, typename... Rhs>
friend constexpr bool operator==(const resource_cache_iterator<Lhs...> &, const resource_cache_iterator<Rhs...> &) noexcept;
template<typename... Args>
[[nodiscard]] constexpr bool operator==(const resource_cache_iterator<Args...> &other) const noexcept {
return it == other.it;
}
template<typename... Lhs, typename... Rhs>
friend constexpr bool operator<(const resource_cache_iterator<Lhs...> &, const resource_cache_iterator<Rhs...> &) noexcept;
template<typename... Args>
[[nodiscard]] constexpr auto operator<=>(const resource_cache_iterator<Args...> &other) const noexcept {
return it <=> other.it;
}
private:
It it;
};
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
return lhs.it - rhs.it;
}
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator==(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
return lhs.it == rhs.it;
}
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator!=(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
return !(lhs == rhs);
}
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator<(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
return lhs.it < rhs.it;
}
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator>(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
return rhs < lhs;
}
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator<=(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
return !(lhs > rhs);
}
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator>=(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
return !(lhs < rhs);
}
} // namespace internal
/*! @endcond */
@@ -153,8 +127,8 @@ template<typename Type, typename Loader, typename Allocator>
class resource_cache {
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
using container_allocator = typename alloc_traits::template rebind_alloc<std::pair<const id_type, typename Loader::result_type>>;
using container_type = dense_map<id_type, typename Loader::result_type, identity, std::equal_to<>, container_allocator>;
using container_allocator = 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, stl::identity, std::equal_to<>, container_allocator>;
public:
/*! @brief Allocator type. */
@@ -322,7 +296,7 @@ public:
}
/**
* @brief Force loads a resource, if its identifier does not exist.
* @brief Force loads a resource, even if its identifier already exists.
* @copydetails load
*/
template<typename... Args>

View File

@@ -1,8 +1,9 @@
#ifndef ENTT_RESOURCE_RESOURCE_HPP
#define ENTT_RESOURCE_RESOURCE_HPP
#include <compare>
#include <concepts>
#include <memory>
#include <type_traits>
#include <utility>
#include "fwd.hpp"
@@ -23,9 +24,6 @@ class resource {
template<typename>
friend class resource;
template<typename Other>
static constexpr bool is_acceptable = !std::is_same_v<Type, Other> && std::is_constructible_v<Type &, Other &>;
public:
/*! @brief Resource type. */
using element_type = Type;
@@ -64,7 +62,8 @@ public:
* @tparam Other Type of resource managed by the received handle.
* @param other The handle to copy from.
*/
template<typename Other, typename = std::enable_if_t<is_acceptable<Other>>>
template<typename Other>
requires (!std::same_as<Type, Other> && std::constructible_from<Type &, Other &>)
resource(const resource<Other> &other) noexcept
: value{other.value} {}
@@ -73,7 +72,8 @@ public:
* @tparam Other Type of resource managed by the received handle.
* @param other The handle to move from.
*/
template<typename Other, typename = std::enable_if_t<is_acceptable<Other>>>
template<typename Other>
requires (!std::same_as<Type, Other> && std::constructible_from<Type &, Other &>)
resource(resource<Other> &&other) noexcept
: value{std::move(other.value)} {}
@@ -98,7 +98,8 @@ public:
* @param other The handle to copy from.
* @return This resource handle.
*/
template<typename Other, typename = std::enable_if_t<is_acceptable<Other>>>
template<typename Other>
requires (!std::same_as<Type, Other> && std::constructible_from<Type &, Other &>)
resource &operator=(const resource<Other> &other) noexcept {
value = other.value;
return *this;
@@ -110,7 +111,8 @@ public:
* @param other The handle to move from.
* @return This resource handle.
*/
template<typename Other, typename = std::enable_if_t<is_acceptable<Other>>>
template<typename Other>
requires (!std::same_as<Type, Other> && std::constructible_from<Type &, Other &>)
resource &operator=(resource<Other> &&other) noexcept {
value = std::move(other.value);
return *this;
@@ -158,6 +160,28 @@ public:
return static_cast<bool>(value);
}
/**
* @brief Compares two handles.
* @tparam Other Type of resource managed by the other handle.
* @param other A valid handle.
* @return True if both handles refer to the same resource, false otherwise.
*/
template<typename Other>
[[nodiscard]] bool operator==(const resource<Other> &other) const noexcept {
return (std::addressof(*value) == std::addressof(*other.value));
}
/**
* @brief Lexicographically compares two handles.
* @tparam Other Type of resource managed by the other handle.
* @param other A valid handle.
* @return The relative order between the two handles.
*/
template<typename Other>
[[nodiscard]] auto operator<=>(const resource<Other> &other) const noexcept {
return (std::addressof(*value) <=> std::addressof(*other.value));
}
/*! @brief Releases the ownership of the managed resource. */
void reset() {
value.reset();
@@ -175,7 +199,7 @@ public:
* @brief Returns the underlying resource handle.
* @return The underlying resource handle.
*/
[[nodiscard]] const handle_type &handle() const noexcept {
[[nodiscard]] handle_type handle() const noexcept {
return value;
}
@@ -183,86 +207,6 @@ private:
handle_type value;
};
/**
* @brief Compares two handles.
* @tparam Lhs Type of resource managed by the first handle.
* @tparam Rhs Type of resource managed by the second handle.
* @param lhs A valid handle.
* @param rhs A valid handle.
* @return True if both handles refer to the same resource, false otherwise.
*/
template<typename Lhs, typename Rhs>
[[nodiscard]] bool operator==(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
return (std::addressof(*lhs) == std::addressof(*rhs));
}
/**
* @brief Compares two handles.
* @tparam Lhs Type of resource managed by the first handle.
* @tparam Rhs Type of resource managed by the second handle.
* @param lhs A valid handle.
* @param rhs A valid handle.
* @return False if both handles refer to the same resource, true otherwise.
*/
template<typename Lhs, typename Rhs>
[[nodiscard]] bool operator!=(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
return !(lhs == rhs);
}
/**
* @brief Compares two handles.
* @tparam Lhs Type of resource managed by the first handle.
* @tparam Rhs Type of resource managed by the second handle.
* @param lhs A valid handle.
* @param rhs A valid handle.
* @return True if the first handle is less than the second, false otherwise.
*/
template<typename Lhs, typename Rhs>
[[nodiscard]] bool operator<(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
return (std::addressof(*lhs) < std::addressof(*rhs));
}
/**
* @brief Compares two handles.
* @tparam Lhs Type of resource managed by the first handle.
* @tparam Rhs Type of resource managed by the second handle.
* @param lhs A valid handle.
* @param rhs A valid handle.
* @return True if the first handle is greater than the second, false otherwise.
*/
template<typename Lhs, typename Rhs>
[[nodiscard]] bool operator>(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
return rhs < lhs;
}
/**
* @brief Compares two handles.
* @tparam Lhs Type of resource managed by the first handle.
* @tparam Rhs Type of resource managed by the second handle.
* @param lhs A valid handle.
* @param rhs A valid handle.
* @return True if the first handle is less than or equal to the second, false
* otherwise.
*/
template<typename Lhs, typename Rhs>
[[nodiscard]] bool operator<=(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
return !(lhs > rhs);
}
/**
* @brief Compares two handles.
* @tparam Lhs Type of resource managed by the first handle.
* @tparam Rhs Type of resource managed by the second handle.
* @param lhs A valid handle.
* @param rhs A valid handle.
* @return True if the first handle is greater than or equal to the second,
* false otherwise.
*/
template<typename Lhs, typename Rhs>
[[nodiscard]] bool operator>=(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
return !(lhs < rhs);
}
} // namespace entt
#endif

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