Compare commits

...

384 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
204 changed files with 5035 additions and 5129 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

@@ -40,10 +40,8 @@ jobs:
windows:
strategy:
matrix:
toolset: [default, v142, v143, clang-cl]
toolset: [default, v143, clang-cl]
include:
- toolset: v142
toolset_option: -T"v142"
- toolset: v143
toolset_option: -T"v143"
- toolset: clang-cl
@@ -87,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

@@ -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

View File

@@ -1,6 +1,6 @@
# EnTT
cmake_minimum_required(VERSION 3.15.7)
cmake_minimum_required(VERSION 3.28)
# Read project version
@@ -25,7 +25,7 @@ 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++)
@@ -133,6 +133,7 @@ if(ENTT_INCLUDE_HEADERS)
core/any.hpp
core/bit.hpp
core/compressed_pair.hpp
core/concepts.hpp
core/enum.hpp
core/family.hpp
core/fwd.hpp
@@ -194,6 +195,9 @@ if(ENTT_INCLUDE_HEADERS)
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
@@ -330,7 +334,7 @@ option(ENTT_BUILD_TESTBED "Enable building testbed." 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_17 CACHE STRING "C++ standard revision to use for tests and testbed")
set(ENTT_CXX_STD cxx_std_20 CACHE STRING "C++ standard revision to use for tests and testbed")
# Tests and tesetbed do not work together because SDL gets confused with EnTT tests
if(ENTT_BUILD_TESTING)

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/>
@@ -325,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
@@ -409,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

9
TODO
View File

@@ -18,14 +18,11 @@ TODO:
* auto type info data from types if present
* 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
* 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)
* review build process for testbed (i.e. tests first due to SDL)
* use unique_ptr or any for meta_custom_node
@@ -33,5 +30,9 @@ TODO:
* resource: shared_from_this?
* finish the imgui viewer/editor!
* archetype-like a-là EnTT support (see my own notes)
* meta: conversion_helper machinery has lot of room for improvements
* 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/>
@@ -106,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
@@ -114,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.
@@ -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_const_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_const_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_const_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

@@ -1035,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:
@@ -1350,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
@@ -1378,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
@@ -1877,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>();

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

@@ -145,6 +145,10 @@ I hope the following lists can grow much more in the future.
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:
@@ -247,6 +251,9 @@ I hope the following lists can grow much more in the future.
(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:

View File

@@ -335,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};
@@ -372,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

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

@@ -57,8 +57,8 @@ Here is a minimal example for the sake of curiosity:
```cpp
struct my_process: entt::process {
using allocator_type = typename entt::process::allocator_type;
using delta_type = typename entt::process::delta_type;
using allocator_type = entt::process::allocator_type;
using delta_type = entt::process::delta_type;
my_process(const allocator_type &allocator, delta_type delay)
: entt::process{allocator},

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,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)

View File

@@ -5,8 +5,8 @@
// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*)
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 16
#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,11 +20,12 @@
#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)();
@@ -35,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))} {}
@@ -59,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));
@@ -76,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} {}
@@ -128,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));
@@ -198,13 +174,14 @@ public:
: 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,10 +194,15 @@ 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;
}
@@ -230,16 +212,6 @@ private:
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 */
@@ -268,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, const std::size_t bucket) {
[[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<typename iterator::difference_type>(offset);
return begin() + static_cast<iterator::difference_type>(offset);
}
}
return end();
}
template<typename Other>
[[nodiscard]] auto constrained_find(const Other &key, const std::size_t bucket) const {
[[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<typename const_iterator::difference_type>(offset);
return cbegin() + static_cast<const_iterator::difference_type>(offset);
}
}
@@ -362,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()
@@ -482,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 */
@@ -492,7 +461,7 @@ public:
/*! @copydoc begin */
[[nodiscard]] iterator begin() noexcept {
return packed.first().begin();
return packed.first().data();
}
/**
@@ -501,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 */
@@ -511,7 +480,7 @@ public:
/*! @copydoc end */
[[nodiscard]] iterator end() noexcept {
return packed.first().end();
return packed.first().data() + packed.first().size();
}
/**
@@ -566,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);
}
@@ -728,22 +695,19 @@ public:
/**
* @brief Accesses a given element with bounds checking.
* @tparam Other Type of the key of an element to find.
* @param key A key of an element to find.
* @return A reference to the mapped value of the requested element.
*/
template<typename Other>
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, mapped_type const &>>
at(const Other &key) const {
[[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 */
template<typename Other>
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, mapped_type &>>
at(const Other &key) {
[[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;
@@ -778,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();
}
@@ -806,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));
}
@@ -844,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())};
}
@@ -876,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());
}
@@ -892,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]};
}
/**
@@ -910,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]};
}
/**
@@ -1010,7 +964,7 @@ 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()) {
@@ -1058,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,11 +18,12 @@
#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)();
@@ -29,8 +33,10 @@ 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;
@@ -42,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} {}
@@ -94,61 +101,34 @@ 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;
@@ -160,13 +140,14 @@ public:
: 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,13 +156,18 @@ 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;
}
@@ -191,16 +177,6 @@ private:
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 */
@@ -228,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, const std::size_t bucket) {
[[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<typename iterator::difference_type>(offset);
return begin() + static_cast<iterator::difference_type>(offset);
}
}
return end();
}
template<typename Other>
[[nodiscard]] auto constrained_find(const Other &value, const std::size_t bucket) const {
[[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<typename const_iterator::difference_type>(offset);
return cbegin() + static_cast<const_iterator::difference_type>(offset);
}
}
@@ -303,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()
@@ -427,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 */
@@ -437,7 +410,7 @@ public:
/*! @copydoc begin */
[[nodiscard]] iterator begin() noexcept {
return packed.first().begin();
return packed.first().data();
}
/**
@@ -446,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 */
@@ -456,7 +429,7 @@ public:
/*! @copydoc end */
[[nodiscard]] iterator end() noexcept {
return packed.first().end();
return packed.first().data() + packed.first().size();
}
/**
@@ -548,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);
}
@@ -648,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();
}
@@ -675,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));
}
@@ -713,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())};
}
@@ -745,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());
}
@@ -761,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]};
}
/**
@@ -779,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]};
}
/**
@@ -879,7 +840,7 @@ 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()) {

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,11 +1,13 @@
#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/concepts.hpp"
#include "fwd.hpp"
#include "type_info.hpp"
#include "type_traits.hpp"
@@ -13,7 +15,7 @@
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
enum class any_request : std::uint8_t {
@@ -66,39 +68,38 @@ class basic_any: private internal::basic_any_storage<Len, Align> {
template<typename 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_same_v<std::remove_const_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
switch(const auto *elem = static_cast<const Type *>(value.data()); req) {
case request::info:
using enum internal::any_request;
case info:
return &type_id<Type>();
case request::transfer:
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::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_v<Type>) {
// NOLINTNEXTLINE(bugprone-casting-through-void, bugprone-multi-level-implicit-pointer-conversion)
@@ -109,9 +110,8 @@ class basic_any: private internal::basic_any_storage<Len, Align> {
return nullptr;
}
template<typename Type>
template<cvref_unqualified Type>
static void basic_deleter(const basic_any &value) {
static_assert(std::is_same_v<std::remove_const_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
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());
@@ -127,7 +127,7 @@ class basic_any: private internal::basic_any_storage<Len, Align> {
template<typename Type, typename... Args>
void initialize([[maybe_unused]] Args &&...args) {
using plain_type = std::remove_const_t<std::remove_reference_t<Type>>;
using plain_type = std::remove_cvref_t<Type>;
vtable = basic_vtable<plain_type>;
underlying_type = type_hash<plain_type>::value();
@@ -206,10 +206,9 @@ 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)
: base_type{} {
static_assert(!std::is_const_v<Type> && !std::is_void_v<Type>, "Non-const non-void pointer required");
if(value == nullptr) {
initialize<void>();
} else {
@@ -224,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)} {}
@@ -308,7 +308,8 @@ 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;
@@ -340,9 +341,8 @@ public:
* @return False if the wrapper does not contain the expected type, true
* otherwise.
*/
template<typename Type>
template<cvref_unqualified Type>
[[nodiscard]] bool has_value() const noexcept {
static_assert(std::is_same_v<std::remove_const_t<Type>, Type>, "Invalid type");
return (underlying_type == type_hash<Type>::value());
}
@@ -354,11 +354,6 @@ public:
return *static_cast<const type_info *>(vtable(request::info, *this, nullptr));
}
/*! @copydoc info */
[[deprecated("use ::info instead")]] [[nodiscard]] const type_info &type() const noexcept {
return info();
}
/**
* @brief Returns an opaque pointer to the contained instance.
* @return An opaque pointer the contained instance, if any.
@@ -483,22 +478,24 @@ public:
return (!*this && !other);
}
/**
* @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);
}
/**
* @brief Aliasing constructor.
* @return A wrapper that shares a reference to an unmanaged object.
*/
[[nodiscard]] basic_any as_ref() noexcept {
basic_any other = std::as_const(*this).as_ref();
other.mode = (mode == any_policy::cref ? any_policy::cref : any_policy::ref);
switch(mode) {
using enum any_policy;
case cref:
case empty:
other.mode = mode;
break;
default:
other.mode = any_policy::ref;
break;
}
return other;
}
@@ -563,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_const_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));
}

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_const_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_const_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

@@ -26,7 +26,7 @@ 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
// 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();
};

View File

@@ -7,7 +7,7 @@
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename = id_type>
@@ -61,19 +61,19 @@ class basic_hashed_string: internal::basic_hashed_string<Char> {
struct const_wrapper {
// non-explicit constructor on purpose
constexpr const_wrapper(const typename base_type::value_type *str) noexcept
constexpr const_wrapper(const base_type::value_type *str) noexcept
: repr{str} {}
const typename base_type::value_type *repr;
const base_type::value_type *repr;
};
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.
@@ -194,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();
}
};
/**
@@ -215,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_const_t<Func>>>;
forward_apply(Func) -> forward_apply<std::remove_cvref_t<Func>>;
} // namespace entt

View File

@@ -1,6 +1,7 @@
#ifndef ENTT_CORE_TYPE_INFO_HPP
#define ENTT_CORE_TYPE_INFO_HPP
#include <compare>
#include <string_view>
#include <type_traits>
#include <utility>
@@ -10,7 +11,7 @@
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
struct ENTT_API type_index final {
@@ -42,7 +43,7 @@ template<typename Type>
}
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;
}
@@ -54,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;
@@ -75,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.
@@ -96,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.
@@ -121,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.
@@ -146,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_const_t<std::remove_reference_t<Type>>>::value()},
identifier{type_hash<std::remove_const_t<std::remove_reference_t<Type>>>::value()},
alias{type_name<std::remove_const_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)
/**
@@ -175,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.
*
@@ -257,19 +213,18 @@ private:
*/
template<typename Type>
[[nodiscard]] const type_info &type_id() noexcept {
if constexpr(std::is_same_v<Type, std::remove_const_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_const_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_const_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_const_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,41 +719,43 @@ 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;
@@ -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)
@@ -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;
@@ -309,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);
@@ -355,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)...);
@@ -402,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);
}
@@ -423,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;

View File

@@ -15,7 +15,7 @@
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename>
@@ -181,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. */
@@ -402,9 +402,8 @@ public:
[[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,58 +112,29 @@ 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)
@@ -180,7 +156,7 @@ public:
template<typename Type>
Type &insert_or_assign(const id_type id, Type &&value) {
return any_cast<std::remove_const_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>
@@ -223,7 +199,7 @@ public:
}
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
@@ -240,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;
@@ -259,16 +233,7 @@ class basic_registry {
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);
@@ -276,10 +241,8 @@ 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;
@@ -305,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. */
@@ -324,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()
@@ -522,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));
}
@@ -543,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);
@@ -573,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);
@@ -616,29 +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.
*/
template<typename Type, typename It>
void insert(It first, It last) {
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));
}
/**
* @brief Assigns each entity in a range the given element.
*
* @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);
}
@@ -655,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);
@@ -748,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{};
@@ -801,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>())...};
@@ -980,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();
@@ -1104,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)};
@@ -1128,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
@@ -82,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;
@@ -135,7 +131,7 @@ 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. */

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);
@@ -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. */
@@ -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();
@@ -1086,11 +1054,6 @@ public:
return *descriptor;
}
/*! @copydoc info */
[[deprecated("use ::info instead")]] [[nodiscard]] const type_info &type() const noexcept {
return info();
}
/**
* @brief Forwards variables to derived classes, if any.
* @tparam Type Type of the element to forward.

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. */
@@ -710,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);
}
@@ -731,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);
}
@@ -786,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");
@@ -867,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()};
}
/**
@@ -907,7 +891,7 @@ public:
*
* @param entt A valid identifier.
*/
void emplace(const entity_type entt) {
void emplace(const entity_type entt, const auto &...) {
base_type::try_emplace(entt, false);
}
@@ -925,12 +909,10 @@ public:
/**
* @brief Assigns entities to a storage.
* @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 insert(It first, It last) {
void insert(stl::input_iterator auto first, stl::input_iterator auto last, const auto &...) {
for(; first != last; ++first) {
base_type::try_emplace(*first, true);
}
@@ -979,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;
@@ -1159,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);

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

@@ -12,6 +12,7 @@ namespace entt {}
#include "core/any.hpp"
#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"
@@ -65,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,20 +3,14 @@
#ifndef ENTT_META_CONTAINER_HPP
#define ENTT_META_CONTAINER_HPP
#include <array>
#include <concepts>
#include <cstddef>
#include <deque>
#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"
@@ -24,35 +18,40 @@
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename Type, typename = void>
template<typename Type>
struct sequence_container_extent: integral_constant<meta_dynamic_extent> {};
template<typename Type>
struct sequence_container_extent<Type, std::enable_if_t<is_complete_v<std::tuple_size<Type>>>>: integral_constant<std::tuple_size_v<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 std::size_t sequence_container_extent_v = sequence_container_extent<Type>::value;
template<typename, typename = void>
struct key_only_associative_container: std::true_type {};
template<typename 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>
struct key_only_associative_container<Type, std::void_t<typename Type::mapped_type>>: std::false_type {};
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 */
@@ -61,19 +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_const_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 Number of elements, or `meta_dynamic_extent` if dynamic. */
static constexpr std::size_t extent = internal::sequence_container_extent_v<Type>;
/*! @brief True in case of fixed size containers, false otherwise. */
[[deprecated("use ::extent instead")]] static constexpr bool fixed_size = (extent != meta_dynamic_extent);
/**
* @brief Returns the number of elements in a container.
@@ -90,7 +85,7 @@ struct basic_meta_sequence_container_traits {
* @return True in case of success, false otherwise.
*/
[[nodiscard]] static bool clear([[maybe_unused]] void *container) {
if constexpr(extent == meta_dynamic_extent) {
if constexpr(requires(Type elem) { elem.clear(); }) {
static_cast<Type *>(container)->clear();
return true;
} else {
@@ -105,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 {
@@ -120,7 +115,7 @@ 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((extent == meta_dynamic_extent) && std::is_default_constructible_v<typename Type::value_type>) {
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 {
@@ -156,11 +151,11 @@ 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(extent == meta_dynamic_extent) {
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{};
}
@@ -174,7 +169,7 @@ 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(extent == meta_dynamic_extent) {
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 {
@@ -187,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_const_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.
@@ -225,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 {
@@ -257,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;
}
}
@@ -270,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));
}
/**
@@ -282,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

@@ -4,18 +4,20 @@
#include <memory>
#include "../container/dense_map.hpp"
#include "../core/fwd.hpp"
#include "../core/utility.hpp"
#include "../stl/functional.hpp"
#include "fwd.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
struct meta_type_node;
struct meta_context {
dense_map<id_type, std::unique_ptr<meta_type_node>, identity> value;
using container_type = dense_map<id_type, std::unique_ptr<meta_type_node>, stl::identity>;
container_type bucket;
[[nodiscard]] inline static meta_context &from(meta_ctx &);
[[nodiscard]] inline static const meta_context &from(const meta_ctx &);
@@ -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>
@@ -26,80 +27,74 @@
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)>;
[[nodiscard]] auto &fetch_node() noexcept {
return *meta_context::from(*ctx).value[parent];
}
enum class mode {
type,
data,
func
};
[[nodiscard]] auto *find_member_or_assert() {
auto *member = find_member<&meta_data_node::id>(fetch_node().details->data, bucket);
auto *member = find_member(parent->details->data, bucket);
ENTT_ASSERT(member != nullptr, "Cannot find member");
return member;
}
[[nodiscard]] auto *find_overload_or_assert() {
auto *overload = find_overload(find_member<&meta_func_node::id>(fetch_node().details->func, bucket), invoke);
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, const char *name) noexcept {
reset_bucket(parent);
auto &elem = fetch_node();
ENTT_ASSERT(elem.id == id || !resolve(*ctx, id), "Duplicate identifier");
elem.name = name;
elem.id = id;
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) {
auto &elem = fetch_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>(elem.details->base, node.type);
member ? (*member = node) : elem.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>(elem.details->conv, node.type);
member ? (*member = node) : elem.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>(elem.details->ctor, node.id);
member ? (*member = node) : elem.details->ctor.emplace_back(node);
auto *member = find_member(parent->details->ctor, node.id);
member ? (*member = node) : parent->details->ctor.emplace_back(node);
}
}
void data(meta_data_node node) {
auto &elem = fetch_node();
state = mode::data;
bucket = node.id;
reset_bucket(node.id);
if(auto *member = find_member<&meta_data_node::id>(elem.details->data, node.id); member == nullptr) {
elem.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) {
auto &elem = fetch_node();
state = mode::func;
bucket = node.id;
invoke = node.invoke;
reset_bucket(node.id, node.invoke);
if(auto *member = find_member<&meta_func_node::id>(elem.details->func, node.id); member == nullptr) {
elem.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_unique<meta_func_node>(std::move(node));
@@ -107,44 +102,56 @@ protected:
}
void traits(const meta_traits value, const bool unset) {
auto set_or_unset_on = [=](auto &node) {
const auto set_or_unset_on = [=](auto &node) {
node.traits = (unset ? (node.traits & ~value) : (node.traits | value));
};
if(bucket == parent) {
set_or_unset_on(fetch_node());
} else if(invoke == nullptr) {
switch(state) {
case mode::type:
set_or_unset_on(*parent);
break;
case mode::data:
set_or_unset_on(*find_member_or_assert());
} else {
break;
case mode::func:
set_or_unset_on(*find_overload_or_assert());
break;
}
}
void custom(meta_custom_node node) {
if(bucket == parent) {
fetch_node().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} {
if(auto *curr = meta_context::from(*ctx).value.try_emplace(parent, std::make_unique<meta_type_node>(std::move(node))).first->second.get(); curr->details == nullptr) {
curr->details = std::make_unique<meta_type_descriptor>();
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_node *parent{};
mode state{};
};
} // namespace internal
@@ -202,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;
}
@@ -223,9 +238,14 @@ public:
*/
template<auto Candidate>
auto conv() noexcept {
using conv_type = std::remove_const_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;
}
@@ -240,9 +260,14 @@ public:
*/
template<typename To>
meta_factory conv() noexcept {
using conv_type = std::remove_const_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;
}
@@ -263,8 +288,15 @@ public:
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_const_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;
}
@@ -283,7 +315,13 @@ 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;
@@ -328,8 +366,8 @@ public:
/* 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_const_t<std::remove_reference_t<data_type>>>,
&meta_arg<type_list<std::remove_const_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 {
@@ -347,8 +385,8 @@ public:
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_const_t<std::remove_reference_t<data_type>>>,
&meta_arg<type_list<std::remove_const_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>});
}
@@ -404,12 +442,12 @@ public:
/* this is never static */
internal::meta_traits::is_const,
0u,
&internal::resolve<std::remove_const_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{
@@ -418,7 +456,7 @@ public:
/* this is never static nor const */
internal::meta_traits::is_none,
1u,
&internal::resolve<std::remove_const_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>});
@@ -464,7 +502,7 @@ public:
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_const_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>});
@@ -517,9 +555,9 @@ public:
inline void meta_reset(meta_ctx &ctx, const id_type id) noexcept {
auto &context = internal::meta_context::from(ctx);
for(auto it = context.value.begin(); it != context.value.end();) {
for(auto it = context.bucket.begin(); it != context.bucket.end();) {
if(it->second->id == id) {
it = context.value.erase(it);
it = context.bucket.erase(it);
} else {
++it;
}
@@ -551,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());
}
/**
@@ -574,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

@@ -18,9 +18,11 @@ class meta_handle;
struct meta_custom;
class meta_data;
struct meta_data;
class meta_func;
struct meta_func;
struct meta_base;
class meta_type;

View File

@@ -2,19 +2,23 @@
#define ENTT_META_META_HPP
#include <array>
#include <concepts>
#include <cstddef>
#include <iterator>
#include <memory>
#include <string_view>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/any.hpp"
#include "../core/concepts.hpp"
#include "../core/fwd.hpp"
#include "../core/iterator.hpp"
#include "../core/type_info.hpp"
#include "../core/type_traits.hpp"
#include "../core/utility.hpp"
#include "../locator/locator.hpp"
#include "../stl/iterator.hpp"
#include "adl_pointer.hpp"
#include "context.hpp"
#include "fwd.hpp"
@@ -24,8 +28,22 @@
namespace entt {
class meta_any;
class meta_type;
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename Type>
struct basic_meta_object {
[[nodiscard]] auto &node_or_assert() const noexcept {
ENTT_ASSERT(node != nullptr, "Invalid pointer to node");
return *node;
}
const Type *node{};
const meta_ctx *ctx{&locator<meta_ctx>::value_or()};
};
} // namespace internal
/*! @endcond */
/*! @brief Proxy object for sequence containers. */
class meta_sequence_container {
@@ -51,7 +69,7 @@ public:
: ctx{&area},
data{&instance},
value_type_node{&internal::resolve<typename Type::value_type>},
const_reference_node{&internal::resolve<std::remove_const_t<std::remove_reference_t<typename Type::const_reference>>>},
const_reference_node{&internal::resolve<std::remove_cvref_t<typename Type::const_reference>>},
size_fn{meta_sequence_container_traits<std::remove_const_t<Type>>::size},
clear_fn{meta_sequence_container_traits<std::remove_const_t<Type>>::clear},
reserve_fn{meta_sequence_container_traits<std::remove_const_t<Type>>::reserve},
@@ -157,46 +175,43 @@ private:
/*! @brief Opaque wrapper for values of any type. */
class meta_any {
using vtable_type = void(const internal::meta_traits, const meta_any &, const void *);
template<typename Type>
static void basic_vtable(const internal::meta_traits req, const meta_any &value, [[maybe_unused]] const void *other) {
static_assert(std::is_same_v<std::remove_const_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
using vtable_type = void(const internal::meta_traits, const meta_any &, void *);
template<cvref_unqualified Type>
static void basic_vtable(const internal::meta_traits req, const meta_any &value, [[maybe_unused]] void *other) {
if(req == internal::meta_traits::is_none) {
value.node = &internal::resolve<Type>(internal::meta_context::from(*value.ctx));
}
if constexpr(is_meta_pointer_like_v<Type>) {
if(req == internal::meta_traits::is_pointer_like) {
if constexpr(std::is_function_v<typename std::pointer_traits<Type>::element_type>) {
const_cast<meta_any &>(value).emplace<Type>(*static_cast<const Type *>(other));
} else if constexpr(!std::is_void_v<std::remove_const_t<typename std::pointer_traits<Type>::element_type>>) {
using in_place_type = decltype(adl_meta_pointer_like<Type>::dereference(std::declval<const Type &>()));
if(req == internal::meta_traits::is_pointer) {
if constexpr(!std::is_void_v<std::remove_const_t<typename std::pointer_traits<Type>::element_type>>) {
if constexpr(std::is_constructible_v<bool, Type>) {
if(const auto &pointer_like = *static_cast<const Type *>(other); pointer_like) {
const_cast<meta_any &>(value).emplace<in_place_type>(adl_meta_pointer_like<Type>::dereference(pointer_like));
if(const auto &pointer_like = any_cast<const Type &>(value.storage); pointer_like) {
static_cast<meta_any *>(other)->emplace<decltype(adl_meta_pointer_like<Type>::dereference(std::declval<const Type &>()))>(adl_meta_pointer_like<Type>::dereference(pointer_like));
}
} else {
const_cast<meta_any &>(value).emplace<in_place_type>(adl_meta_pointer_like<Type>::dereference(*static_cast<const Type *>(other)));
static_cast<meta_any *>(other)->emplace<decltype(adl_meta_pointer_like<Type>::dereference(std::declval<const Type &>()))>(adl_meta_pointer_like<Type>::dereference(any_cast<const Type &>(value.storage)));
}
}
}
}
if constexpr(is_complete_v<meta_sequence_container_traits<Type>> || is_complete_v<meta_associative_container_traits<Type>>) {
if(constexpr auto flag = (is_complete_v<meta_sequence_container_traits<Type>> ? internal::meta_traits::is_sequence_container : internal::meta_traits::is_associative_container); !!(req & flag)) {
using container_type = std::conditional_t<is_complete_v<meta_sequence_container_traits<Type>>, meta_sequence_container, meta_associative_container>;
if(!!(req & internal::meta_traits::is_const) || (value.storage.policy() == any_policy::cref)) {
// NOLINTNEXTLINE(bugprone-casting-through-void)
*static_cast<container_type *>(const_cast<void *>(other)) = container_type{*value.ctx, any_cast<const Type &>(value.storage)};
} else {
// NOLINTNEXTLINE(bugprone-casting-through-void)
*static_cast<container_type *>(const_cast<void *>(other)) = container_type{*value.ctx, any_cast<Type &>(const_cast<meta_any &>(value).storage)};
} else if constexpr(requires(Type elem) { *elem; }) {
if(req == internal::meta_traits::is_pointer) {
if constexpr(std::is_class_v<Type>) {
if(const auto &elem = any_cast<const Type &>(value.storage); elem) {
return (value.storage.policy() == any_policy::cref) ? static_cast<meta_any *>(other)->emplace<decltype(*elem)>(*elem) : static_cast<meta_any *>(other)->emplace<decltype(*const_cast<Type &>(elem))>(*const_cast<Type &>(elem));
}
} else if constexpr(!std::is_array_v<Type> && !std::is_void_v<std::remove_const_t<std::remove_pointer_t<Type>>>) {
if(auto *pointer = any_cast<Type>(value.storage); pointer) {
static_cast<meta_any *>(other)->emplace<std::conditional_t<std::is_function_v<std::remove_const_t<std::remove_pointer_t<Type>>>, Type, std::remove_pointer_t<Type> &>>(*pointer);
}
}
}
} else if constexpr(is_complete_v<meta_sequence_container_traits<Type>> || is_complete_v<meta_associative_container_traits<Type>>) {
if(constexpr auto flag = (is_complete_v<meta_sequence_container_traits<Type>> ? internal::meta_traits::is_sequence_container : internal::meta_traits::is_associative_container); req == flag) {
using container_type = std::conditional_t<is_complete_v<meta_sequence_container_traits<Type>>, meta_sequence_container, meta_associative_container>;
*static_cast<container_type *>(other) = (value.storage.policy() == any_policy::cref) ? container_type{*value.ctx, any_cast<const Type &>(value.storage)} : container_type{*value.ctx, any_cast<Type &>(const_cast<meta_any &>(value).storage)};
}
}
}
@@ -248,7 +263,7 @@ public:
explicit meta_any(const meta_ctx &area, std::in_place_type_t<Type>, Args &&...args)
: storage{std::in_place_type<Type>, std::forward<Args>(args)...},
ctx{&area},
vtable{&basic_vtable<std::remove_const_t<std::remove_reference_t<Type>>>} {}
vtable{&basic_vtable<std::remove_cvref_t<Type>>} {}
/**
* @brief Constructs a wrapper taking ownership of the passed object.
@@ -277,7 +292,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>, meta_any>>>
template<typename Type>
requires (!std::same_as<std::remove_cvref_t<Type>, meta_any>)
meta_any(Type &&value)
: meta_any{locator<meta_ctx>::value_or(), std::forward<Type>(value)} {}
@@ -287,7 +303,8 @@ public:
* @param area The context from which to search for meta types.
* @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>, meta_any>>>
template<typename Type>
requires (!std::same_as<std::remove_cvref_t<Type>, meta_any>)
meta_any(const meta_ctx &area, Type &&value)
: meta_any{area, std::in_place_type<std::decay_t<Type>>, std::forward<Type>(value)} {}
@@ -317,7 +334,12 @@ public:
* @brief Copy constructor.
* @param other The instance to copy from.
*/
meta_any(const meta_any &other) = default;
meta_any(const meta_any &other)
: storage{other.storage},
ctx{other.ctx},
node{(other.storage && !storage) ? nullptr : other.node},
vtable{(other.storage && !storage) ? nullptr : other.vtable} {
}
/**
* @brief Move constructor.
@@ -339,10 +361,10 @@ public:
*/
meta_any &operator=(const meta_any &other) {
if(this != &other) {
storage = other.storage;
ctx = other.ctx;
node = other.node;
vtable = other.vtable;
storage = other.storage;
node = (other.storage && !storage) ? nullptr : other.node;
vtable = (other.storage && !storage) ? nullptr : other.vtable;
}
return *this;
@@ -367,13 +389,17 @@ public:
* @param value An instance of an object to use to initialize the wrapper.
* @return This meta any object.
*/
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_any>>>
template<typename Type>
requires (!std::same_as<std::remove_cvref_t<Type>, meta_any>)
meta_any &operator=(Type &&value) {
emplace<std::decay_t<Type>>(std::forward<Type>(value));
return *this;
}
/*! @copydoc any::info */
/**
* @brief Returns the meta type associated with the contained instance.
* @return The meta type associated with the contained instance.
*/
[[nodiscard]] inline meta_type type() const noexcept;
/**
@@ -470,22 +496,22 @@ public:
template<typename Type>
[[nodiscard]] meta_any allow_cast() const {
if constexpr(!std::is_reference_v<Type> || std::is_const_v<std::remove_reference_t<Type>>) {
if(storage.has_value<std::remove_const_t<std::remove_reference_t<Type>>>()) {
if(storage.has_value<std::remove_cvref_t<Type>>()) {
return as_ref();
} else if(*this) {
if constexpr(std::is_arithmetic_v<std::remove_const_t<std::remove_reference_t<Type>>> || std::is_enum_v<std::remove_const_t<std::remove_reference_t<Type>>>) {
if constexpr(std::is_arithmetic_v<std::remove_cvref_t<Type>> || std::is_enum_v<std::remove_cvref_t<Type>>) {
if(const auto &from = fetch_node(); from.conversion_helper) {
return meta_any{*ctx, static_cast<Type>(from.conversion_helper(nullptr, storage.data()))};
}
}
if(const auto &from = fetch_node(); from.details != nullptr) {
if(const auto *elem = internal::find_member<&internal::meta_conv_node::type>(from.details->conv, entt::type_hash<std::remove_const_t<std::remove_reference_t<Type>>>::value()); elem != nullptr) {
if(const auto *elem = internal::find_member(from.details->conv, entt::type_hash<std::remove_cvref_t<Type>>::value()); elem != nullptr) {
return elem->conv(*ctx, storage.data());
}
for(auto &&curr: from.details->base) {
if(auto other = curr.resolve(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, curr.cast(storage.data())); curr.type == entt::type_hash<std::remove_const_t<std::remove_reference_t<Type>>>::value()) {
if(auto other = curr.type(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, curr.cast(storage.data())); curr.id == entt::type_hash<std::remove_cvref_t<Type>>::value()) {
return other;
} else if(auto from_base = std::as_const(other).template allow_cast<Type>(); from_base) {
return from_base;
@@ -508,9 +534,9 @@ public:
if constexpr(std::is_reference_v<Type> && !std::is_const_v<std::remove_reference_t<Type>>) {
return allow_cast<const std::remove_reference_t<Type> &>() && (storage.policy() != any_policy::cref);
} else {
if(storage.has_value<std::remove_const_t<std::remove_reference_t<Type>>>()) {
if(storage.has_value<std::remove_cvref_t<Type>>()) {
return true;
} else if(auto other = std::as_const(*this).allow_cast<std::remove_const_t<std::remove_reference_t<Type>>>(); other) {
} else if(auto other = std::as_const(*this).allow_cast<std::remove_cvref_t<Type>>(); other) {
if(other.storage.owner()) {
std::swap(*this, other);
}
@@ -526,7 +552,7 @@ public:
template<typename Type, typename... Args>
void emplace(Args &&...args) {
storage.emplace<Type>(std::forward<Args>(args)...);
auto *prev = std::exchange(vtable, &basic_vtable<std::remove_const_t<std::remove_reference_t<Type>>>);
auto *prev = std::exchange(vtable, &basic_vtable<std::remove_cvref_t<Type>>);
node = (prev == vtable) ? node : nullptr;
}
@@ -556,7 +582,7 @@ public:
/*! @copydoc as_sequence_container */
[[nodiscard]] meta_sequence_container as_sequence_container() const noexcept {
meta_sequence_container proxy{};
if(*this) { vtable(internal::meta_traits::is_sequence_container | internal::meta_traits::is_const, *this, &proxy); }
if(*this) { vtable(internal::meta_traits::is_sequence_container, as_ref(), &proxy); }
return proxy;
}
@@ -573,7 +599,7 @@ public:
/*! @copydoc as_associative_container */
[[nodiscard]] meta_associative_container as_associative_container() const noexcept {
meta_associative_container proxy{};
if(*this) { vtable(internal::meta_traits::is_associative_container | internal::meta_traits::is_const, *this, &proxy); }
if(*this) { vtable(internal::meta_traits::is_associative_container, as_ref(), &proxy); }
return proxy;
}
@@ -582,9 +608,16 @@ public:
* @return A wrapper that shares a reference to an unmanaged object if the
* wrapped element is dereferenceable, an invalid meta any otherwise.
*/
[[nodiscard]] meta_any operator*() noexcept {
meta_any ret{meta_ctx_arg, *ctx};
if(*this) { vtable(internal::meta_traits::is_pointer, *this, &ret); }
return ret;
}
/*! @copydoc operator* */
[[nodiscard]] meta_any operator*() const noexcept {
meta_any ret{meta_ctx_arg, *ctx};
if(*this) { vtable(internal::meta_traits::is_pointer_like, ret, storage.data()); }
if(*this) { vtable(internal::meta_traits::is_pointer, as_ref(), &ret); }
return ret;
}
@@ -598,11 +631,6 @@ public:
return (ctx == other.ctx) && (!*this == !other) && (storage == other.storage);
}
/*! @copydoc any::operator!= */
[[nodiscard]] bool operator!=(const meta_any &other) const noexcept {
return !(*this == other);
}
/*! @copydoc any::as_ref */
[[nodiscard]] meta_any as_ref() noexcept {
return meta_any{*this, storage.as_ref()};
@@ -661,7 +689,8 @@ template<typename Type>
/*! @brief Opaque pointers to instances of any type. */
class meta_handle {
template<typename Type, typename... Args, typename = std::enable_if_t<std::is_same_v<std::decay_t<Type>, meta_any>>>
template<typename Type, typename... Args>
requires std::same_as<std::remove_cvref_t<Type>, meta_any>
meta_handle(int, Type &value, Args &&...args)
: any{std::forward<Args>(args)..., value.as_ref()} {}
@@ -679,7 +708,8 @@ public:
* @param ctx The context from which to search for meta types.
* @param value An instance of an object to use to initialize the handle.
*/
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_handle>>>
template<typename Type>
requires (!std::same_as<std::remove_cvref_t<Type>, meta_handle>)
meta_handle(const meta_ctx &ctx, Type &value)
: meta_handle{0, value, ctx} {}
@@ -688,7 +718,8 @@ public:
* @tparam Type Type of object to use to initialize the handle.
* @param value An instance of an object to use to initialize the handle.
*/
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_handle>>>
template<typename Type>
requires (!std::same_as<std::remove_cvref_t<Type>, meta_handle>)
meta_handle(Type &value)
: meta_handle{0, value} {}
@@ -737,11 +768,6 @@ public:
return &any;
}
/*! @copydoc operator-> */
[[deprecated("do not use const handles")]] [[nodiscard]] const meta_any *operator->() const {
return &any;
}
private:
meta_any any{};
};
@@ -764,7 +790,7 @@ struct meta_custom {
*/
template<typename Type>
[[nodiscard]] operator Type *() const noexcept {
return ((node != nullptr) && (type_hash<std::remove_const_t<Type>>::value() == node->type)) ? static_cast<Type *>(node->value.get()) : nullptr;
return ((node != nullptr) && (type_hash<std::remove_const_t<Type>>::value() == node->id)) ? static_cast<Type *>(node->value.get()) : nullptr;
}
/**
@@ -781,35 +807,58 @@ private:
const internal::meta_custom_node *node{};
};
/*! @brief Opaque wrapper for data members. */
class meta_data {
[[nodiscard]] auto &node_or_assert() const noexcept {
ENTT_ASSERT(node != nullptr, "Invalid pointer to node");
return *node;
}
public:
/*! @brief Unsigned integer type. */
using size_type = typename internal::meta_data_node::size_type;
/**
* @brief Common opaque wrapper for meta objects.
* @tparam Type Underlying meta node type.
*/
template<typename Type>
struct meta_object: protected internal::basic_meta_object<Type> {
/*! @brief Underlying meta node type. */
using node_type = Type;
/*! @brief Default constructor. */
meta_data() noexcept = default;
meta_object() noexcept = default;
/**
* @brief Context aware constructor for meta objects.
* @param area The context from which to search for meta types.
* @param curr The underlying node with which to construct the instance.
*/
meta_data(const meta_ctx &area, const internal::meta_data_node &curr) noexcept
: node{&curr},
ctx{&area} {}
meta_object(const meta_ctx &area, const node_type &curr) noexcept
: internal::basic_meta_object<Type>{&curr, &area} {
}
/**
* @brief Returns true if an object is valid, false otherwise.
* @return True if the object is valid, false otherwise.
*/
[[nodiscard]] explicit operator bool() const noexcept {
return (this->node != nullptr);
}
/**
* @brief Checks if two objects refer to the same type.
* @param other The object with which to compare.
* @return True if the objects refer to the same type, false otherwise.
*/
[[nodiscard]] bool operator==(const meta_object &other) const noexcept {
return (this->ctx == other.ctx) && (this->node == other.node);
}
};
/*! @brief Opaque wrapper for data members. */
struct meta_data: meta_object<internal::meta_data_node> {
/*! @brief Unsigned integer type. */
using size_type = internal::meta_data_node::size_type;
using meta_object::meta_object;
/**
* @brief Returns the name assigned to a data member, if any.
* @return The name assigned to the data member, if any.
*/
[[nodiscard]] const char *name() const noexcept {
return node_or_assert().name;
[[nodiscard]] std::string_view name() const noexcept {
return (node_or_assert().name == nullptr) ? std::string_view{} : std::string_view{node_or_assert().name};
}
/**
@@ -888,68 +937,21 @@ public:
[[nodiscard]] meta_custom custom() const noexcept {
return {node_or_assert().custom};
}
/**
* @brief Returns true if an object is valid, false otherwise.
* @return True if the object is valid, false otherwise.
*/
[[nodiscard]] explicit operator bool() const noexcept {
return (node != nullptr);
}
/**
* @brief Checks if two objects refer to the same type.
* @param other The object with which to compare.
* @return True if the objects refer to the same type, false otherwise.
*/
[[nodiscard]] bool operator==(const meta_data &other) const noexcept {
return (ctx == other.ctx) && (node == other.node);
}
private:
const internal::meta_data_node *node{};
const meta_ctx *ctx{&locator<meta_ctx>::value_or()};
};
/**
* @brief Checks if two objects refer to the same type.
* @param lhs An object, either valid or not.
* @param rhs An object, either valid or not.
* @return False if the objects refer to the same node, true otherwise.
*/
[[nodiscard]] inline bool operator!=(const meta_data &lhs, const meta_data &rhs) noexcept {
return !(lhs == rhs);
}
/*! @brief Opaque wrapper for member functions. */
class meta_func {
[[nodiscard]] auto &node_or_assert() const noexcept {
ENTT_ASSERT(node != nullptr, "Invalid pointer to node");
return *node;
}
public:
struct meta_func: meta_object<internal::meta_func_node> {
/*! @brief Unsigned integer type. */
using size_type = typename internal::meta_func_node::size_type;
using size_type = internal::meta_func_node::size_type;
/*! @brief Default constructor. */
meta_func() noexcept = default;
/**
* @brief Context aware constructor for meta objects.
* @param area The context from which to search for meta types.
* @param curr The underlying node with which to construct the instance.
*/
meta_func(const meta_ctx &area, const internal::meta_func_node &curr) noexcept
: node{&curr},
ctx{&area} {}
using meta_object::meta_object;
/**
* @brief Returns the name assigned to a member function, if any.
* @return The name assigned to the member function, if any.
*/
[[nodiscard]] const char *name() const noexcept {
return node_or_assert().name;
[[nodiscard]] std::string_view name() const noexcept {
return (node_or_assert().name == nullptr) ? std::string_view{} : std::string_view{node_or_assert().name};
}
/**
@@ -1034,26 +1036,15 @@ public:
[[nodiscard]] meta_func next() const {
return (node_or_assert().next != nullptr) ? meta_func{*ctx, *node_or_assert().next} : meta_func{};
}
/*! @copydoc meta_data::operator bool */
[[nodiscard]] explicit operator bool() const noexcept {
return (node != nullptr);
}
/*! @copydoc meta_data::operator== */
[[nodiscard]] bool operator==(const meta_func &other) const noexcept {
return (ctx == other.ctx) && (node == other.node);
}
private:
const internal::meta_func_node *node{};
const meta_ctx *ctx{&locator<meta_ctx>::value_or()};
};
/*! @copydoc operator!=(const meta_data &, const meta_data &) */
[[nodiscard]] inline bool operator!=(const meta_func &lhs, const meta_func &rhs) noexcept {
return !(lhs == rhs);
}
/*! @brief Opaque wrapper for base types. */
struct meta_base: meta_object<internal::meta_base_node> {
using meta_object::meta_object;
/*! @copydoc meta_any::type */
[[nodiscard]] inline meta_type type() const noexcept;
};
/*! @brief Opaque wrapper for types. */
class meta_type {
@@ -1062,7 +1053,7 @@ class meta_type {
}
template<typename Func>
[[nodiscard]] auto lookup(meta_any *const args, const typename internal::meta_type_node::size_type sz, [[maybe_unused]] bool constness, Func next) const {
[[nodiscard]] auto lookup(meta_any *const args, const internal::meta_type_node::size_type sz, [[maybe_unused]] bool constness, Func next) const {
decltype(next()) candidate = nullptr;
size_type same{};
bool ambiguous{};
@@ -1085,7 +1076,7 @@ class meta_type {
if(const auto &info = other.info(); info == type.info()) {
++match;
} else if(!(type.fetch_node().conversion_helper && other.fetch_node().conversion_helper) && !(type.fetch_node().details && (internal::find_member<&internal::meta_base_node::type>(type.fetch_node().details->base, info.hash()) || internal::find_member<&internal::meta_conv_node::type>(type.fetch_node().details->conv, info.hash())))) {
} else if(!(type.fetch_node().conversion_helper && other.fetch_node().conversion_helper) && !(type.fetch_node().details && (internal::find_member(type.fetch_node().details->base, info.hash()) || internal::find_member(type.fetch_node().details->conv, info.hash())))) {
break;
}
}
@@ -1116,7 +1107,7 @@ class meta_type {
public:
/*! @brief Unsigned integer type. */
using size_type = typename internal::meta_type_node::size_type;
using size_type = internal::meta_type_node::size_type;
/*! @brief Default constructor. */
meta_type() noexcept = default;
@@ -1130,14 +1121,6 @@ public:
: node{&curr},
ctx{&area} {}
/**
* @brief Context aware constructor for meta objects.
* @param area The context from which to search for meta types.
* @param curr The underlying node with which to construct the instance.
*/
meta_type(const meta_ctx &area, const internal::meta_base_node &curr) noexcept
: meta_type{area, curr.resolve(internal::meta_context::from(area))} {}
/**
* @brief Returns the type info object of the underlying type.
* @return The type info object of the underlying type.
@@ -1158,8 +1141,8 @@ public:
* @brief Returns the name assigned to a type, if any.
* @return The name assigned to the type, if any.
*/
[[nodiscard]] const char *name() const noexcept {
return fetch_node().name;
[[nodiscard]] std::string_view name() const noexcept {
return (fetch_node().name == nullptr) ? std::string_view{} : std::string_view{fetch_node().name};
}
/**
@@ -1312,12 +1295,12 @@ public:
if(const auto &to = other.info().hash(); (info().hash() == to) || ((fetch_node().conversion_helper != nullptr) && (other.is_arithmetic() || other.is_enum()))) {
return true;
} else if(const auto &from = fetch_node(); from.details) {
if(const auto *elem = internal::find_member<&internal::meta_conv_node::type>(from.details->conv, to); elem != nullptr) {
if(const auto *elem = internal::find_member(from.details->conv, to); elem != nullptr) {
return true;
}
for(auto &&curr: from.details->base) {
if(curr.type == to || meta_type{*ctx, curr.resolve(internal::meta_context::from(*ctx))}.can_convert(other)) {
if(curr.id == to || meta_type{*ctx, curr.type(internal::meta_context::from(*ctx))}.can_convert(other)) {
return true;
}
}
@@ -1330,8 +1313,8 @@ public:
* @brief Returns a range to visit registered top-level base meta types.
* @return An iterable range to visit registered top-level base meta types.
*/
[[nodiscard]] meta_range<meta_type, typename decltype(internal::meta_type_descriptor::base)::const_iterator> base() const noexcept {
using range_type = meta_range<meta_type, typename decltype(internal::meta_type_descriptor::base)::const_iterator>;
[[nodiscard]] meta_range<meta_base, decltype(internal::meta_type_descriptor::base)::const_iterator> base() const noexcept {
using range_type = meta_range<meta_base, decltype(internal::meta_type_descriptor::base)::const_iterator>;
return fetch_node().details ? range_type{{*ctx, fetch_node().details->base.cbegin()}, {*ctx, fetch_node().details->base.cend()}} : range_type{};
}
@@ -1339,8 +1322,8 @@ public:
* @brief Returns a range to visit registered top-level meta data.
* @return An iterable range to visit registered top-level meta data.
*/
[[nodiscard]] meta_range<meta_data, typename decltype(internal::meta_type_descriptor::data)::const_iterator> data() const noexcept {
using range_type = meta_range<meta_data, typename decltype(internal::meta_type_descriptor::data)::const_iterator>;
[[nodiscard]] meta_range<meta_data, decltype(internal::meta_type_descriptor::data)::const_iterator> data() const noexcept {
using range_type = meta_range<meta_data, decltype(internal::meta_type_descriptor::data)::const_iterator>;
return fetch_node().details ? range_type{{*ctx, fetch_node().details->data.cbegin()}, {*ctx, fetch_node().details->data.cend()}} : range_type{};
}
@@ -1359,8 +1342,8 @@ public:
* @brief Returns a range to visit registered top-level functions.
* @return An iterable range to visit registered top-level functions.
*/
[[nodiscard]] meta_range<meta_func, typename decltype(internal::meta_type_descriptor::func)::const_iterator> func() const noexcept {
using return_type = meta_range<meta_func, typename decltype(internal::meta_type_descriptor::func)::const_iterator>;
[[nodiscard]] meta_range<meta_func, decltype(internal::meta_type_descriptor::func)::const_iterator> func() const noexcept {
using return_type = meta_range<meta_func, decltype(internal::meta_type_descriptor::func)::const_iterator>;
return fetch_node().details ? return_type{{*ctx, fetch_node().details->func.cbegin()}, {*ctx, fetch_node().details->func.cend()}} : return_type{};
}
@@ -1441,7 +1424,7 @@ public:
meta_handle wrapped{*ctx, std::forward<Instance>(instance)};
if(const auto &ref = fetch_node(); ref.details) {
if(auto *elem = internal::find_member<&internal::meta_func_node::id>(ref.details->func, id); elem != nullptr) {
if(auto *elem = internal::find_member(ref.details->func, id); elem != nullptr) {
if(const auto *candidate = lookup(args, sz, (wrapped->base().policy() == any_policy::cref), [curr = elem]() mutable { return (curr != nullptr) ? std::exchange(curr, curr->next.get()) : nullptr; }); candidate) {
return candidate->invoke(std::move(wrapped), args);
}
@@ -1449,7 +1432,7 @@ public:
}
for(auto &&curr: base()) {
if(auto elem = curr.second.invoke(id, *wrapped.operator->(), args, sz); elem) {
if(auto elem = curr.second.type().invoke(id, *wrapped.operator->(), args, sz); elem) {
return elem;
}
}
@@ -1527,11 +1510,6 @@ private:
const meta_ctx *ctx{&locator<meta_ctx>::value_or()};
};
/*! @copydoc operator!=(const meta_data &, const meta_data &) */
[[nodiscard]] inline bool operator!=(const meta_type &lhs, const meta_type &rhs) noexcept {
return !(lhs == rhs);
}
[[nodiscard]] inline meta_type meta_any::type() const noexcept {
return *this ? meta_type{*ctx, fetch_node()} : meta_type{};
}
@@ -1572,12 +1550,12 @@ bool meta_any::set(const id_type id, Type &&value) {
}
if(const auto &from = fetch_node(); from.details) {
if(const auto *elem = internal::find_member<&internal::meta_conv_node::type>(from.details->conv, type.info().hash()); elem != nullptr) {
if(const auto *elem = internal::find_member(from.details->conv, type.info().hash()); elem != nullptr) {
return elem->conv(*ctx, storage.data());
}
for(auto &&curr: from.details->base) {
if(auto other = curr.resolve(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, curr.cast(storage.data())); curr.type == type.info().hash()) {
if(auto other = curr.type(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, curr.cast(storage.data())); curr.id == type.info().hash()) {
return other;
} else if(auto from_base = std::as_const(other).allow_cast(type); from_base) {
return from_base;
@@ -1632,7 +1610,11 @@ inline bool meta_any::assign(meta_any &&other) {
return index < arity() ? node_or_assert().arg(*ctx, index) : meta_type{};
}
/*! @cond TURN_OFF_DOXYGEN */
[[nodiscard]] inline meta_type meta_base::type() const noexcept {
return meta_type{*ctx, node_or_assert().type(internal::meta_context::from(*ctx))};
}
/*! @cond ENTT_INTERNAL */
class meta_sequence_container::meta_iterator final {
using vtable_type = void(const void *, const std::ptrdiff_t, meta_any *);
@@ -1652,7 +1634,7 @@ public:
meta_iterator() = default;
template<typename It>
template<stl::bidirectional_iterator It>
meta_iterator(const meta_ctx &area, It iter) noexcept
: ctx{&area},
vtable{&basic_vtable<It>},
@@ -1706,10 +1688,6 @@ private:
any handle{};
};
[[nodiscard]] inline bool operator!=(const meta_sequence_container::iterator &lhs, const meta_sequence_container::iterator &rhs) noexcept {
return !(lhs == rhs);
}
class meta_associative_container::meta_iterator final {
using vtable_type = void(const void *, std::pair<meta_any, meta_any> *);
@@ -1737,7 +1715,7 @@ public:
meta_iterator() = default;
template<bool KeyOnly, typename It>
template<bool KeyOnly, stl::forward_iterator It>
meta_iterator(const meta_ctx &area, std::bool_constant<KeyOnly>, It iter) noexcept
: ctx{&area},
vtable{&basic_vtable<KeyOnly, It>},
@@ -1777,9 +1755,6 @@ private:
any handle{};
};
[[nodiscard]] inline bool operator!=(const meta_associative_container::iterator &lhs, const meta_associative_container::iterator &rhs) noexcept {
return !(lhs == rhs);
}
/*! @endcond */
/**

View File

@@ -2,6 +2,7 @@
#define ENTT_META_NODE_HPP
#include <array>
#include <bit>
#include <cstddef>
#include <memory>
#include <type_traits>
@@ -9,21 +10,19 @@
#include <vector>
#include "../config/config.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;
class 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{};
id_type id{};
std::shared_ptr<void> value{};
};
struct meta_base_node {
id_type type{};
const 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 *){};
};
@@ -148,15 +147,15 @@ struct meta_type_node {
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) {
@@ -166,16 +165,16 @@ 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, bool recursive) {
using value_type = typename std::remove_reference_t<decltype((node.details.get()->*Member))>::value_type;
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;
}
if(recursive) {
for(auto &&curr: node.details->base) {
if(auto *elem = look_for<Member>(context, curr.resolve(context), id, recursive); elem) {
if(auto *elem = look_for<Member>(context, curr.type(context), id, recursive); elem) {
return elem;
}
}
@@ -185,13 +184,13 @@ template<auto Member>
return static_cast<value_type *>(nullptr);
}
template<typename Type>
template<cvref_unqualified Type>
const meta_type_node &resolve(const meta_context &) noexcept;
template<typename... Args>
[[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_const_t<std::remove_reference_t<Args>>>...};
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);
}
@@ -199,9 +198,9 @@ template<typename... Args>
[[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 *other = curr.cast(instance); curr.type == to) {
if(const void *other = curr.cast(instance); curr.id == to) {
return other;
} else if(const void *elem = try_cast(context, curr.resolve(context), to, other); elem) {
} else if(const void *elem = try_cast(context, curr.type(context), to, other); elem) {
return elem;
}
}
@@ -271,13 +270,12 @@ auto setup_node_for() noexcept {
}
[[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.get() : nullptr;
const auto it = context.bucket.find(info.hash());
return (it != context.bucket.end()) ? it->second.get() : nullptr;
}
template<typename Type>
template<cvref_unqualified Type>
[[nodiscard]] const 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");
static const meta_type_node node = setup_node_for<Type>();
const auto *elem = try_resolve(context, *node.info);
return (elem == nullptr) ? node : *elem;

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,7 +5,7 @@
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
struct meta_policy {};
@@ -15,7 +15,7 @@ struct meta_policy {};
/*! @brief Empty class type used to request the _as-is_ policy. */
struct as_value_t final: private internal::meta_policy {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
template<typename>
static constexpr bool value = true;
/*! @endcond */
@@ -23,7 +23,7 @@ struct as_value_t final: private internal::meta_policy {
/*! @brief Empty class type used to request the _as void_ policy. */
struct as_void_t final: private internal::meta_policy {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
template<typename>
static constexpr bool value = true;
/*! @endcond */
@@ -31,7 +31,7 @@ struct as_void_t final: private internal::meta_policy {
/*! @brief Empty class type used to request the _as ref_ policy. */
struct as_ref_t final: private internal::meta_policy {
/*! @cond TURN_OFF_DOXYGEN */
/*! @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 */
@@ -39,7 +39,7 @@ struct as_ref_t final: private internal::meta_policy {
/*! @brief Empty class type used to request the _as cref_ policy. */
struct as_cref_t final: private internal::meta_policy {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
template<typename Type>
static constexpr bool value = std::is_reference_v<Type>;
/*! @endcond */
@@ -47,7 +47,7 @@ struct as_cref_t final: private internal::meta_policy {
/*! @brief Empty class type used to request the _as auto_ policy. */
struct as_is_t final: private internal::meta_policy {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
template<typename>
static constexpr bool value = true;
/*! @endcond */
@@ -69,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>) {
if constexpr(std::is_same_v<It, typename meta_context::container_type::const_iterator>) {
return {it[value].first, Type{*ctx, *it[value].second}};
} else if constexpr(std::is_same_v<typename std::iterator_traits<It>::value_type, meta_base_node>) {
return {it[value].type, Type{*ctx, it[value]}};
} else {
return {it[value].id, Type{*ctx, it[value]}};
}
@@ -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

@@ -20,7 +20,7 @@ namespace entt {
template<typename Type>
[[nodiscard]] meta_type resolve(const meta_ctx &ctx) noexcept {
const auto &context = internal::meta_context::from(ctx);
return {ctx, internal::resolve<std::remove_const_t<std::remove_reference_t<Type>>>(context)};
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 {
[[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.value.cbegin()}, {ctx, context.value.cend()}};
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());
}

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_const_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_const_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_const_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_const_t<std::remove_reference_t<MaybeType>>, Type>),
std::is_const_v<std::remove_reference_t<MaybeType>> && (std::is_same_v<std::remove_const_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_const_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,8 +165,8 @@ 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_value_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) {
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)};
@@ -185,12 +186,12 @@ template<typename Policy = as_value_t, typename Type>
* @param value Value to wrap.
* @return A meta any containing the returned value, if any.
*/
template<typename Policy = as_value_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>
@@ -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_value_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_const_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));
@@ -352,8 +353,8 @@ template<typename Type, auto Data, typename Policy = as_value_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_value_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) {
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>{});
}
@@ -366,8 +367,8 @@ template<typename Type, typename Policy = as_value_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_value_t>
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_invoke(meta_handle instance, meta_any *const 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>{});
}
@@ -418,7 +419,7 @@ template<typename Type, typename... Args>
*/
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_const_t<std::remove_reference_t<Candidate>>>) {
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 {
@@ -436,8 +437,8 @@ template<typename Type, typename Policy = as_value_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_value_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);
}
@@ -455,8 +456,8 @@ template<typename Type, typename Policy = as_value_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_value_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);
}
@@ -468,8 +469,8 @@ template<typename Type, auto Candidate, typename Policy = as_value_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_value_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,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>
@@ -142,14 +142,14 @@
</Expand>
</Type>
<Type Name="entt::meta_custom">
<DisplayString Condition="node != nullptr">{ node }</DisplayString>
<DisplayString Condition="node != nullptr">{ node,na }</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
</Expand>
</Type>
<Type Name="entt::meta_data">
<DisplayString Condition="node != nullptr">{ node }</DisplayString>
<DisplayString Condition="node != nullptr">{ node,na }</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
@@ -157,7 +157,15 @@
</Expand>
</Type>
<Type Name="entt::meta_func">
<DisplayString Condition="node != nullptr">{ node }</DisplayString>
<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_base">
<DisplayString Condition="node != nullptr">{ node,na }</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
@@ -165,7 +173,7 @@
</Expand>
</Type>
<Type Name="entt::meta_type">
<DisplayString Condition="node != nullptr">{ node }</DisplayString>
<DisplayString Condition="node != nullptr">{ node,na }</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
@@ -173,14 +181,14 @@
</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

@@ -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,16 +215,17 @@ 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_const_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_const_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_const_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 info if any, `type_id<void>()` otherwise.
@@ -230,11 +235,6 @@ public:
return storage.info();
}
/*! @copydoc info */
[[deprecated("use ::info instead")]] [[nodiscard]] const type_info &type() const noexcept {
return info();
}
/**
* @brief Returns an opaque pointer to the contained instance.
* @return An opaque pointer the contained instance, if any.
@@ -257,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_const_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

@@ -11,7 +11,7 @@
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename, typename, typename>
@@ -287,14 +287,14 @@ private:
state current;
};
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
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 = typename base_type::delta_type;
using delta_type = base_type::delta_type;
process_adaptor(const allocator_type &allocator, Func proc)
: base_type{allocator},

View File

@@ -36,7 +36,7 @@ template<typename Delta, typename Allocator>
class basic_scheduler {
using base_type = basic_process<Delta, Allocator>;
using alloc_traits = std::allocator_traits<Allocator>;
using container_allocator = typename alloc_traits::template rebind_alloc<std::shared_ptr<base_type>>;
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:

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();
@@ -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

View File

@@ -12,29 +12,30 @@
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename Ret, typename... Args>
constexpr auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...);
auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...);
template<typename Ret, typename Type, typename... Args, typename Other>
constexpr auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...);
auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...);
template<typename Class, typename Ret, typename... Args, typename... Other>
constexpr auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...);
auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...);
template<typename Class, typename Ret, typename... Args, typename... Other>
constexpr auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...);
auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...);
template<typename Class, typename Type, typename... Other, typename = std::enable_if_t<std::is_member_object_pointer_v<Type Class::*>>>
constexpr auto function_pointer(Type Class::*, Other &&...) -> Type (*)();
template<typename Class, typename Type, typename... Other>
requires std::is_member_object_pointer_v<Type Class::*>
auto function_pointer(Type Class::*, Other &&...) -> Type (*)();
template<typename... Type>
using function_pointer_t = decltype(function_pointer(std::declval<Type>()...));
template<typename... Class, typename Ret, typename... Args>
[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) {
[[nodiscard]] ENTT_CONSTEVAL auto index_sequence_for(Ret (*)(Args...)) {
return std::index_sequence_for<Class..., Args...>{};
}
@@ -285,19 +286,6 @@ private:
delegate_type *fn{};
};
/**
* @brief Compares the contents of two delegates.
* @tparam Ret Return type of a function type.
* @tparam Args Types of arguments of a function type.
* @param lhs A valid delegate object.
* @param rhs A valid delegate object.
* @return True if the two contents differ, false otherwise.
*/
template<typename Ret, typename... Args>
[[nodiscard]] bool operator!=(const delegate<Ret(Args...)> &lhs, const delegate<Ret(Args...)> &rhs) noexcept {
return !(lhs == rhs);
}
/**
* @brief Deduction guide.
* @tparam Candidate Function or member to connect to the delegate.

View File

@@ -9,15 +9,16 @@
#include <vector>
#include "../container/dense_map.hpp"
#include "../core/compressed_pair.hpp"
#include "../core/concepts.hpp"
#include "../core/fwd.hpp"
#include "../core/type_info.hpp"
#include "../core/utility.hpp"
#include "../stl/functional.hpp"
#include "fwd.hpp"
#include "sigh.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
struct basic_dispatcher_handler {
@@ -28,10 +29,8 @@ struct basic_dispatcher_handler {
[[nodiscard]] virtual std::size_t size() const noexcept = 0;
};
template<typename Type, typename Allocator>
template<cvref_unqualified Type, typename Allocator>
class dispatcher_handler final: public basic_dispatcher_handler {
static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Invalid type");
using alloc_traits = std::allocator_traits<Allocator>;
using signal_type = sigh<void(Type &), Allocator>;
using container_type = std::vector<Type, typename alloc_traits::template rebind_alloc<Type>>;
@@ -44,13 +43,12 @@ public:
events{allocator} {}
void publish() override {
const auto length = events.size();
container_type other{};
other.swap(events);
for(std::size_t pos{}; pos < length; ++pos) {
signal.publish(events[pos]);
for(auto &&elem: other) {
signal.publish(elem);
}
events.erase(events.cbegin(), events.cbegin() + static_cast<typename container_type::difference_type>(length));
}
void disconnect(void *instance) override {
@@ -65,7 +63,7 @@ public:
return typename signal_type::sink_type{signal};
}
void trigger(Type event) {
void trigger(Type &event) {
signal.publish(event);
}
@@ -114,12 +112,11 @@ class basic_dispatcher {
using mapped_type = std::shared_ptr<internal::basic_dispatcher_handler>;
using alloc_traits = std::allocator_traits<Allocator>;
using container_allocator = typename alloc_traits::template rebind_alloc<std::pair<const key_type, mapped_type>>;
using container_type = dense_map<key_type, mapped_type, identity, std::equal_to<>, container_allocator>;
using container_allocator = alloc_traits::template rebind_alloc<std::pair<const key_type, mapped_type>>;
using container_type = dense_map<key_type, mapped_type, stl::identity, std::equal_to<>, container_allocator>;
template<typename Type>
template<cvref_unqualified Type>
[[nodiscard]] handler_type<Type> &assure(const id_type id) {
static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Non-decayed types not allowed");
auto &&ptr = pools.first()[id];
if(!ptr) {
@@ -130,10 +127,8 @@ class basic_dispatcher {
return static_cast<handler_type<Type> &>(*ptr);
}
template<typename Type>
template<cvref_unqualified Type>
[[nodiscard]] const handler_type<Type> *assure(const id_type id) const {
static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Non-decayed types not allowed");
if(auto it = pools.first().find(id); it != pools.first().cend()) {
return static_cast<const handler_type<Type> *>(it->second.get());
}
@@ -271,8 +266,8 @@ public:
* @param value An instance of the given type of event.
*/
template<typename Type>
void trigger(Type &&value = {}) {
trigger(type_hash<std::decay_t<Type>>::value(), std::forward<Type>(value));
void trigger(Type value) {
trigger(type_hash<std::decay_t<Type>>::value(), value);
}
/**
@@ -282,8 +277,8 @@ public:
* @param id Name used to map the event queue within the dispatcher.
*/
template<typename Type>
void trigger(const id_type id, Type &&value = {}) {
assure<std::decay_t<Type>>(id).trigger(std::forward<Type>(value));
void trigger(const id_type id, Type value) {
assure<std::decay_t<Type>>(id).trigger(value);
}
/**

View File

@@ -8,7 +8,7 @@
#include "../core/compressed_pair.hpp"
#include "../core/fwd.hpp"
#include "../core/type_info.hpp"
#include "../core/utility.hpp"
#include "../stl/functional.hpp"
#include "fwd.hpp"
namespace entt {
@@ -38,8 +38,8 @@ class emitter {
using mapped_type = std::function<void(void *)>;
using alloc_traits = std::allocator_traits<Allocator>;
using container_allocator = typename alloc_traits::template rebind_alloc<std::pair<const key_type, mapped_type>>;
using container_type = dense_map<key_type, mapped_type, identity, std::equal_to<>, container_allocator>;
using container_allocator = alloc_traits::template rebind_alloc<std::pair<const key_type, mapped_type>>;
using container_type = dense_map<key_type, mapped_type, stl::identity, std::equal_to<>, container_allocator>;
public:
/*! @brief Allocator type. */
@@ -147,7 +147,7 @@ public:
*/
template<typename Type>
void erase() {
handlers.first().erase(type_hash<std::remove_const_t<std::remove_reference_t<Type>>>::value());
handlers.first().erase(type_hash<std::remove_cvref_t<Type>>::value());
}
/*! @brief Disconnects all the listeners. */
@@ -162,7 +162,7 @@ public:
*/
template<typename Type>
[[nodiscard]] bool contains() const {
return handlers.first().contains(type_hash<std::remove_const_t<std::remove_reference_t<Type>>>::value());
return handlers.first().contains(type_hash<std::remove_cvref_t<Type>>::value());
}
/**

View File

@@ -203,14 +203,12 @@ public:
} else {
func();
}
} else {
if constexpr(std::is_invocable_r_v<bool, Func, Ret>) {
if(func(calls[pos - 1u](args...))) {
break;
}
} else {
func(calls[pos - 1u](args...));
} else if constexpr(std::is_invocable_r_v<bool, Func, Ret>) {
if(func(calls[pos - 1u](args...))) {
break;
}
} else {
func(calls[pos - 1u](args...));
}
}
}
@@ -359,8 +357,8 @@ private:
template<typename Ret, typename... Args, typename Allocator>
class sink<sigh<Ret(Args...), Allocator>> {
using signal_type = sigh<Ret(Args...), Allocator>;
using delegate_type = typename signal_type::delegate_type;
using difference_type = typename signal_type::container_type::difference_type;
using delegate_type = signal_type::delegate_type;
using difference_type = signal_type::container_type::difference_type;
template<auto Candidate, typename Type>
static void release(Type value_or_instance, void *signal) {

View File

@@ -0,0 +1,44 @@
#ifndef ENTT_STL_FUNCTIONAL_HPP
#define ENTT_STL_FUNCTIONAL_HPP
#include "../config/config.h"
/*! @cond ENTT_INTERNAL */
#ifndef ENTT_FORCE_STL
# if __has_include(<version>)
# include <version>
#
# if defined(__cpp_lib_ranges)
# define ENTT_HAS_IDENTITY
# include <functional>
namespace entt::stl {
using std::identity;
} // namespace entt::stl
# endif
# endif
#endif
#ifndef ENTT_HAS_IDENTITY
# include <utility>
namespace entt::stl {
struct identity {
using is_transparent = void;
template<typename Type>
[[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept {
return std::forward<Type>(value);
}
};
} // namespace entt::stl
#endif
/*! @endcond */
#endif

102
src/entt/stl/iterator.hpp Normal file
View File

@@ -0,0 +1,102 @@
#ifndef ENTT_STL_ITERATOR_HPP
#define ENTT_STL_ITERATOR_HPP
#include "../config/config.h"
/*! @cond ENTT_INTERNAL */
#ifndef ENTT_FORCE_STL
# if __has_include(<version>)
# include <version>
#
# if defined(__cpp_lib_ranges)
# define ENTT_HAS_ITERATOR_CONCEPTS
# include <iterator>
namespace entt::stl {
using std::bidirectional_iterator;
using std::forward_iterator;
using std::input_iterator;
using std::input_or_output_iterator;
using std::output_iterator;
using std::random_access_iterator;
using std::sentinel_for;
} // namespace entt::stl
# endif
# endif
#endif
#ifndef ENTT_HAS_ITERATOR_CONCEPTS
# include <concepts>
# include <iterator>
# include <utility>
namespace entt::stl {
namespace internal {
template<typename It>
concept has_iterator_category = requires {
typename std::iterator_traits<It>::iterator_category;
};
template<typename It>
concept has_iterator_concept = has_iterator_category<It> && requires {
typename It::iterator_concept;
};
template<has_iterator_category It>
struct iterator_tag {
using type = typename std::iterator_traits<It>::iterator_category;
};
template<has_iterator_concept It>
struct iterator_tag<It> {
using type = typename It::iterator_concept;
};
template<typename It, typename Tag>
concept has_iterator_tag = std::derived_from<typename iterator_tag<It>::type, Tag>;
} // namespace internal
// Bare minimum definitions to support broken platforms like PS4.
// EnTT does not provide full featured definitions for iterator concepts.
template<typename It>
concept input_or_output_iterator = requires(It it) {
*it;
{ ++it } -> std::same_as<It &>;
it++;
};
template<typename It>
concept input_iterator = input_or_output_iterator<It> && internal::has_iterator_tag<It, std::input_iterator_tag>;
template<typename It, typename Type>
concept output_iterator = input_or_output_iterator<It> && requires(It it, Type &&value) {
*it++ = std::forward<Type>(value);
};
template<typename It>
concept forward_iterator = input_iterator<It> && internal::has_iterator_tag<It, std::forward_iterator_tag>;
template<typename It>
concept bidirectional_iterator = forward_iterator<It> && internal::has_iterator_tag<It, std::bidirectional_iterator_tag>;
template<typename It>
concept random_access_iterator = bidirectional_iterator<It> && internal::has_iterator_tag<It, std::random_access_iterator_tag>;
template<class Sentinel, typename It>
concept sentinel_for = input_or_output_iterator<It> && requires(Sentinel sentinel, It it) {
{ it == sentinel } -> std::same_as<bool>;
};
} // namespace entt::stl
#endif
/*! @endcond */
#endif

51
src/entt/stl/memory.hpp Normal file
View File

@@ -0,0 +1,51 @@
#ifndef ENTT_STL_MEMORY_HPP
#define ENTT_STL_MEMORY_HPP
#include "../config/config.h"
/*! @cond ENTT_INTERNAL */
#ifndef ENTT_FORCE_STL
# if __has_include(<version>)
# include <version>
#
# if defined(__cpp_lib_to_address)
# define ENTT_HAS_TO_ADDRESS
# include <memory>
namespace entt::stl {
using std::to_address;
} // namespace entt::stl
# endif
# endif
#endif
#ifndef ENTT_HAS_TO_ADDRESS
# include <memory>
# include <type_traits>
namespace entt::stl {
template<typename Type>
constexpr Type *to_address(Type *ptr) noexcept {
static_assert(!std::is_function_v<Type>, "Invalid type");
return ptr;
}
template<typename Type>
constexpr auto to_address(const Type &ptr) noexcept {
if constexpr(requires { std::pointer_traits<Type>::to_address(ptr); }) {
return std::pointer_traits<Type>::to_address(ptr);
} else {
return to_address(ptr.operator->());
}
}
} // namespace entt::stl
#endif
/*! @endcond */
#endif

View File

@@ -5,7 +5,7 @@
#include <ios>
#include <sstream>
#include <string>
#include <imgui.h>
#include "../config/config.h"
#include "../entity/mixin.hpp"
#include "../entity/registry.hpp"
#include "../entity/sparse_set.hpp"
@@ -17,16 +17,21 @@
#include "../meta/pointer.hpp"
#include "../meta/resolve.hpp"
#if __has_include(<imgui.h>)
# include <imgui.h>
#endif
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename Entity, typename OnEntity>
static void present_element(const meta_any &obj, OnEntity on_entity) {
for([[maybe_unused]] const auto [id, data]: obj.type().data()) {
const auto elem = data.get(obj);
const char *label = data.name() ? data.name() : std::string{data.type().info().name()}.data();
const std::string name = data.name().empty() ? std::string{data.type().info().name()} : std::string{data.name()};
const char *const label = name.c_str();
if(auto type = data.type(); type.info() == type_id<const char *>()) {
ImGui::Text("%s: %s", label, elem.template cast<const char *>());
@@ -43,7 +48,7 @@ static void present_element(const meta_any &obj, OnEntity on_entity) {
for(auto [id, curr]: type.data()) {
if(curr.get({}) == elem) {
as_string = curr.name();
as_string = curr.name().data();
break;
}
}
@@ -130,6 +135,10 @@ static void present_element(const meta_any &obj, OnEntity on_entity) {
ImGui::Text("%s: %s", label, underlying_type.data());
}
}
for([[maybe_unused]] const auto [id, base]: obj.type().base()) {
present_element<Entity>(obj.allow_cast(base.type()), on_entity);
}
}
template<typename Entity, typename Allocator>
@@ -162,7 +171,8 @@ static void present_entity(const meta_ctx &ctx, const Entity entt, const It from
for(auto it = from; it != to; ++it) {
if(const auto &storage = it->second; storage.contains(entt)) {
if(auto type = resolve(ctx, storage.info()); type) {
const char *label = type.name() ? type.name() : std::string{storage.info().name()}.data();
const std::string name = type.name().empty() ? std::string{storage.info().name()} : std::string{type.name()};
const char *const label = name.c_str();
if(ImGui::TreeNode(&storage.info(), "%s", label)) {
if(const auto obj = type.from_void(storage.value(entt)); obj) {
@@ -196,11 +206,12 @@ static void present_view(const meta_ctx &ctx, const basic_view<get_t<Get...>, ex
if(ImGui::TreeNode(&type_id<typename view_type::entity_type>(), "%d [%d/%d]", to_integral(entt), to_entity(entt), to_version(entt))) {
for(const auto *storage: range) {
if(auto type = resolve(ctx, storage->info()); type) {
const char *label = type.name() ? type.name() : std::string{storage->info().name()}.data();
const std::string name = type.name().empty() ? std::string{storage->info().name()} : std::string{type.name()};
const char *const label = name.c_str();
if(ImGui::TreeNode(&storage->info(), "%s", label)) {
if(const auto obj = type.from_void(storage->value(entt)); obj) {
present_element<typename view_type::entity_type>(obj, [](const char *name, const typename view_type::entity_type entt) {
present_element<typename view_type::entity_type>(obj, [](const char *name, const view_type::entity_type entt) {
ImGui::Text("%s: %d [%d/%d]", name, to_integral(entt), to_entity(entt), to_version(entt));
});
}
@@ -301,7 +312,8 @@ void davey(const meta_ctx &ctx, const basic_registry<Entity, Allocator> &registr
if(ImGui::BeginTabItem("Storage")) {
for([[maybe_unused]] auto [id, storage]: registry.storage()) {
const auto type = resolve(ctx, storage.info());
const char *label = type.name() ? type.name() : std::string{storage.info().name()}.data();
const std::string name = type.name().empty() ? std::string{storage.info().name()} : std::string{type.name()};
const char *const label = name.c_str();
if(ImGui::TreeNode(&storage.info(), "%s (%zu)", label, storage.size())) {
internal::present_storage(ctx, storage);

View File

@@ -17,20 +17,21 @@ else()
)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
add_library(GTest::Main ALIAS gtest_main)
target_compile_features(gtest PUBLIC cxx_std_17)
target_compile_features(gtest PUBLIC cxx_std_20)
set_target_properties(gtest PROPERTIES CXX_CLANG_TIDY "")
target_compile_features(gtest_main PUBLIC cxx_std_17)
target_compile_features(gtest_main PUBLIC cxx_std_20)
set_target_properties(gtest_main PROPERTIES CXX_CLANG_TIDY "")
target_compile_features(gmock PUBLIC cxx_std_17)
target_compile_features(gmock PUBLIC cxx_std_20)
set_target_properties(gmock PROPERTIES CXX_CLANG_TIDY "")
target_compile_features(gmock_main PUBLIC cxx_std_17)
target_compile_features(gmock_main PUBLIC cxx_std_20)
set_target_properties(gmock_main PROPERTIES CXX_CLANG_TIDY "")
endif()
@@ -54,7 +55,7 @@ function(SETUP_TARGET TARGET_NAME)
-Wno-exceptions
-Wconversion
>
/EHsc /wd4324 /wd4996
/EHsc /wd4324 /wd4996 /bigobj
# disabling INCREMENTAL is required by SizeBench
$<$<CONFIG:Debug>:/Od /INCREMENTAL:NO>
$<$<CONFIG:Release>:/O2>
@@ -96,19 +97,24 @@ add_library(odr OBJECT odr.cpp)
set_target_properties(odr PROPERTIES POSITION_INDEPENDENT_CODE ON)
SETUP_TARGET(odr)
function(SETUP_BASIC_TEST TEST_NAME TEST_SOURCES)
add_executable(${TEST_NAME} $<TARGET_OBJECTS:odr> ${TEST_SOURCES})
target_link_libraries(${TEST_NAME} PRIVATE GTest::Main Threads::Threads)
SETUP_TARGET(${TEST_NAME} ${ARGN})
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME})
set_tests_properties(${TEST_NAME} PROPERTIES TIMEOUT 60)
function(SETUP_BASIC_TEST)
set(options DEFS)
set(oneValueArgs NAME)
set(multiValueArgs SOURCES INCLUDE)
cmake_parse_arguments(BASIC_TEST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
add_executable(${BASIC_TEST_NAME} $<TARGET_OBJECTS:odr> ${BASIC_TEST_SOURCES})
target_link_libraries(${BASIC_TEST_NAME} PRIVATE GTest::Main Threads::Threads)
target_include_directories(${BASIC_TEST_NAME} PRIVATE ${BASIC_TEST_INCLUDE})
SETUP_TARGET(${BASIC_TEST_NAME} ${BASIC_TEST_UNPARSED_ARGUMENTS})
add_test(NAME ${BASIC_TEST_NAME} COMMAND ${BASIC_TEST_NAME})
set_tests_properties(${BASIC_TEST_NAME} PROPERTIES TIMEOUT 60)
endfunction()
function(SETUP_LIB_SHARED_TEST TEST_NAME SUB_PATH)
set(TARGET_NAME ${TEST_NAME}_${SUB_PATH})
add_library(_${TARGET_NAME} SHARED $<TARGET_OBJECTS:odr> lib/${TEST_NAME}/${SUB_PATH}/lib.cpp)
SETUP_TARGET(_${TARGET_NAME} ENTT_API_EXPORT)
SETUP_BASIC_TEST(lib_${TARGET_NAME} lib/${TEST_NAME}/${SUB_PATH}/main.cpp ENTT_API_IMPORT)
SETUP_BASIC_TEST(NAME lib_${TARGET_NAME} SOURCES lib/${TEST_NAME}/${SUB_PATH}/main.cpp DEFS ENTT_API_IMPORT)
set_target_properties(lib_${TARGET_NAME} PROPERTIES CXX_CLANG_TIDY "")
target_link_libraries(lib_${TARGET_NAME} PRIVATE _${TARGET_NAME})
endfunction()
@@ -117,7 +123,7 @@ function(SETUP_LIB_PLUGIN_TEST TEST_NAME SUB_PATH)
set(TARGET_NAME ${TEST_NAME}_${SUB_PATH})
add_library(_${TARGET_NAME} MODULE $<TARGET_OBJECTS:odr> lib/${TEST_NAME}/${SUB_PATH}/plugin.cpp)
SETUP_TARGET(_${TARGET_NAME} ${ARGVN})
SETUP_BASIC_TEST(lib_${TARGET_NAME} lib/${TEST_NAME}/${SUB_PATH}/main.cpp PLUGIN="$<TARGET_FILE:_${TARGET_NAME}>" ${ARGVN})
SETUP_BASIC_TEST(NAME lib_${TARGET_NAME} SOURCES lib/${TEST_NAME}/${SUB_PATH}/main.cpp DEFS PLUGIN="$<TARGET_FILE:_${TARGET_NAME}>" ${ARGVN})
target_link_libraries(_${TARGET_NAME} PRIVATE cr::cr)
target_link_libraries(lib_${TARGET_NAME} PRIVATE cr::cr ${CMAKE_DL_LIBS})
set_target_properties(_${TARGET_NAME} PROPERTIES CXX_CLANG_TIDY "")
@@ -130,17 +136,25 @@ endfunction()
# Test benchmark
if(ENTT_BUILD_BENCHMARK)
SETUP_BASIC_TEST(benchmark benchmark/benchmark.cpp)
SETUP_BASIC_TEST(
NAME benchmark
SOURCES benchmark/benchmark.cpp
)
set_target_properties(benchmark PROPERTIES CXX_CLANG_TIDY "")
endif()
# Test example
if(ENTT_BUILD_EXAMPLE)
SETUP_BASIC_TEST(custom_identifier example/custom_identifier.cpp)
SETUP_BASIC_TEST(entity_copy example/entity_copy.cpp)
SETUP_BASIC_TEST(reserved_bits example/reserved_bits.cpp)
SETUP_BASIC_TEST(signal_less example/signal_less.cpp)
SETUP_BASIC_TEST(
NAME example
SOURCES
example/custom_identifier.cpp
example/entity_copy.cpp
example/reserved_bits.cpp
example/signal_less.cpp
)
endif()
# Test lib
@@ -186,117 +200,185 @@ if(ENTT_BUILD_SNAPSHOT)
GIT_TAG v1.3.2
GIT_SHALLOW 1
)
set(BUILD_DOC OFF CACHE BOOL "" FORCE)
set(BUILD_SANDBOX OFF CACHE BOOL "" FORCE)
set(SKIP_PORTABILITY_TEST ON CACHE BOOL "" FORCE)
set(SKIP_PERFORMANCE_COMPARISON ON CACHE BOOL "" FORCE)
FetchContent_GetProperties(cereal)
FetchContent_MakeAvailable(cereal)
if(NOT cereal_POPULATED)
FetchContent_Populate(cereal)
set(cereal_INCLUDE_DIR ${cereal_SOURCE_DIR}/include)
endif()
SETUP_BASIC_TEST(
NAME snapshot
SOURCES snapshot/snapshot.cpp
)
SETUP_BASIC_TEST(cereal snapshot/snapshot.cpp)
set_target_properties(cereal PROPERTIES CXX_CLANG_TIDY "")
target_include_directories(cereal PRIVATE ${cereal_INCLUDE_DIR})
target_compile_options(cereal PRIVATE $<$<NOT:$<STREQUAL:"${CMAKE_CXX_COMPILER_ID}","MSVC">>:-Wno-conversion>)
target_link_libraries(snapshot PRIVATE cereal)
target_compile_options(snapshot PRIVATE $<$<NOT:$<STREQUAL:"${CMAKE_CXX_COMPILER_ID}","MSVC">>:-Wno-conversion>)
set_target_properties(snapshot PROPERTIES CXX_CLANG_TIDY "")
endif()
# Test config
SETUP_BASIC_TEST(version entt/config/version.cpp)
SETUP_BASIC_TEST(
NAME config_ext
SOURCES entt/config/config.cpp
INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/include
)
SETUP_BASIC_TEST(
NAME config
SOURCES entt/config/version.cpp
)
# Test container
SETUP_BASIC_TEST(dense_map entt/container/dense_map.cpp)
SETUP_BASIC_TEST(dense_set entt/container/dense_set.cpp)
SETUP_BASIC_TEST(table entt/container/table.cpp)
SETUP_BASIC_TEST(
NAME container
SOURCES
entt/container/dense_map.cpp
entt/container/dense_set.cpp
entt/container/table.cpp
)
# Test core
SETUP_BASIC_TEST(algorithm entt/core/algorithm.cpp)
SETUP_BASIC_TEST(any entt/core/any.cpp)
SETUP_BASIC_TEST(bit entt/core/bit.cpp)
SETUP_BASIC_TEST(compressed_pair entt/core/compressed_pair.cpp)
SETUP_BASIC_TEST(enum entt/core/enum.cpp)
SETUP_BASIC_TEST(family entt/core/family.cpp)
SETUP_BASIC_TEST(hashed_string entt/core/hashed_string.cpp)
SETUP_BASIC_TEST(ident entt/core/ident.cpp)
SETUP_BASIC_TEST(iterator entt/core/iterator.cpp)
SETUP_BASIC_TEST(memory entt/core/memory.cpp)
SETUP_BASIC_TEST(monostate entt/core/monostate.cpp)
SETUP_BASIC_TEST(tuple entt/core/tuple.cpp)
SETUP_BASIC_TEST(type_info entt/core/type_info.cpp)
SETUP_BASIC_TEST(type_traits entt/core/type_traits.cpp)
SETUP_BASIC_TEST(utility entt/core/utility.cpp)
SETUP_BASIC_TEST(
NAME core
SOURCES
entt/core/algorithm.cpp
entt/core/any.cpp
entt/core/bit.cpp
entt/core/compressed_pair.cpp
entt/core/concepts.cpp
entt/core/enum.cpp
entt/core/family.cpp
entt/core/hashed_string.cpp
entt/core/ident.cpp
entt/core/iterator.cpp
entt/core/memory.cpp
entt/core/monostate.cpp
entt/core/tuple.cpp
entt/core/type_info.cpp
entt/core/type_traits.cpp
entt/core/utility.cpp
)
# Test entity
SETUP_BASIC_TEST(component entt/entity/component.cpp)
SETUP_BASIC_TEST(entity entt/entity/entity.cpp)
SETUP_BASIC_TEST(group entt/entity/group.cpp)
SETUP_BASIC_TEST(handle entt/entity/handle.cpp)
SETUP_BASIC_TEST(helper entt/entity/helper.cpp)
SETUP_BASIC_TEST(organizer entt/entity/organizer.cpp)
SETUP_BASIC_TEST(reactive_mixin entt/entity/reactive_mixin.cpp)
SETUP_BASIC_TEST(registry entt/entity/registry.cpp)
SETUP_BASIC_TEST(runtime_view entt/entity/runtime_view.cpp)
SETUP_BASIC_TEST(sigh_mixin entt/entity/sigh_mixin.cpp)
SETUP_BASIC_TEST(snapshot entt/entity/snapshot.cpp)
SETUP_BASIC_TEST(sparse_set entt/entity/sparse_set.cpp)
SETUP_BASIC_TEST(storage entt/entity/storage.cpp)
SETUP_BASIC_TEST(storage_entity entt/entity/storage_entity.cpp)
SETUP_BASIC_TEST(storage_no_instance entt/entity/storage_no_instance.cpp)
SETUP_BASIC_TEST(storage_utility entt/entity/storage_utility.cpp)
SETUP_BASIC_TEST(storage_utility_no_mixin entt/entity/storage_utility.cpp ENTT_NO_MIXIN)
SETUP_BASIC_TEST(view entt/entity/view.cpp)
SETUP_BASIC_TEST(
NAME entity
SOURCES
entt/entity/component.cpp
entt/entity/entity.cpp
entt/entity/group.cpp
entt/entity/handle.cpp
entt/entity/helper.cpp
entt/entity/organizer.cpp
entt/entity/reactive_mixin.cpp
entt/entity/registry.cpp
entt/entity/runtime_view.cpp
entt/entity/sigh_mixin.cpp
entt/entity/snapshot.cpp
entt/entity/sparse_set.cpp
entt/entity/storage.cpp
entt/entity/storage_entity.cpp
entt/entity/storage_no_instance.cpp
entt/entity/storage_utility.cpp
entt/entity/view.cpp
)
SETUP_BASIC_TEST(
NAME entity_no_mixin
SOURCES
entt/entity/storage_utility.cpp
DEFS ENTT_NO_MIXIN
)
# Test graph
SETUP_BASIC_TEST(adjacency_matrix entt/graph/adjacency_matrix.cpp)
SETUP_BASIC_TEST(dot entt/graph/dot.cpp)
SETUP_BASIC_TEST(flow entt/graph/flow.cpp)
SETUP_BASIC_TEST(
NAME graph
SOURCES
entt/graph/adjacency_matrix.cpp
entt/graph/dot.cpp
entt/graph/flow.cpp
)
# Test locator
SETUP_BASIC_TEST(locator entt/locator/locator.cpp)
SETUP_BASIC_TEST(
NAME locator
SOURCES entt/locator/locator.cpp
)
# Test meta
SETUP_BASIC_TEST(meta_any entt/meta/meta_any.cpp)
SETUP_BASIC_TEST(meta_base entt/meta/meta_base.cpp)
SETUP_BASIC_TEST(meta_container entt/meta/meta_container.cpp)
SETUP_BASIC_TEST(meta_context entt/meta/meta_context.cpp)
SETUP_BASIC_TEST(meta_conv entt/meta/meta_conv.cpp)
SETUP_BASIC_TEST(meta_ctor entt/meta/meta_ctor.cpp)
SETUP_BASIC_TEST(meta_custom entt/meta/meta_custom.cpp)
SETUP_BASIC_TEST(meta_data entt/meta/meta_data.cpp)
SETUP_BASIC_TEST(meta_factory entt/meta/meta_factory.cpp)
SETUP_BASIC_TEST(meta_func entt/meta/meta_func.cpp)
SETUP_BASIC_TEST(meta_handle entt/meta/meta_handle.cpp)
SETUP_BASIC_TEST(meta_pointer entt/meta/meta_pointer.cpp)
SETUP_BASIC_TEST(meta_range entt/meta/meta_range.cpp)
SETUP_BASIC_TEST(meta_template entt/meta/meta_template.cpp)
SETUP_BASIC_TEST(meta_type entt/meta/meta_type.cpp)
SETUP_BASIC_TEST(meta_utility entt/meta/meta_utility.cpp)
SETUP_BASIC_TEST(
NAME meta
SOURCES
entt/meta/meta_any.cpp
entt/meta/meta_base.cpp
entt/meta/meta_container.cpp
entt/meta/meta_context.cpp
entt/meta/meta_conv.cpp
entt/meta/meta_ctor.cpp
entt/meta/meta_custom.cpp
entt/meta/meta_data.cpp
entt/meta/meta_dereference.cpp
entt/meta/meta_factory.cpp
entt/meta/meta_func.cpp
entt/meta/meta_handle.cpp
entt/meta/meta_range.cpp
entt/meta/meta_template.cpp
entt/meta/meta_type.cpp
entt/meta/meta_utility.cpp
)
# Test poly
SETUP_BASIC_TEST(poly entt/poly/poly.cpp)
SETUP_BASIC_TEST(
NAME poly
SOURCES entt/poly/poly.cpp
)
# Test process
SETUP_BASIC_TEST(process entt/process/process.cpp)
SETUP_BASIC_TEST(scheduler entt/process/scheduler.cpp)
SETUP_BASIC_TEST(
NAME process
SOURCES
entt/process/process.cpp
entt/process/scheduler.cpp
)
# Test resource
SETUP_BASIC_TEST(resource entt/resource/resource.cpp)
SETUP_BASIC_TEST(resource_cache entt/resource/resource_cache.cpp)
SETUP_BASIC_TEST(resource_loader entt/resource/resource_loader.cpp)
SETUP_BASIC_TEST(
NAME resource
SOURCES
entt/resource/resource.cpp
entt/resource/resource_cache.cpp
entt/resource/resource_loader.cpp
)
# Test signal
SETUP_BASIC_TEST(delegate entt/signal/delegate.cpp)
SETUP_BASIC_TEST(dispatcher entt/signal/dispatcher.cpp)
SETUP_BASIC_TEST(emitter entt/signal/emitter.cpp)
SETUP_BASIC_TEST(sigh entt/signal/sigh.cpp)
SETUP_BASIC_TEST(
NAME signal
SOURCES
entt/signal/delegate.cpp
entt/signal/dispatcher.cpp
entt/signal/emitter.cpp
entt/signal/sigh.cpp
)
# Test stl
SETUP_BASIC_TEST(
NAME stl
SOURCES
entt/stl/functional.cpp
entt/stl/iterator.cpp
entt/stl/memory.cpp
DEFS ENTT_USE_STL
)

View File

@@ -1,25 +0,0 @@
#ifndef ENTT_COMMON_AGGREGATE_H
#define ENTT_COMMON_AGGREGATE_H
#include <type_traits>
namespace test {
struct aggregate {
int value{};
};
[[nodiscard]] inline bool operator==(const aggregate &lhs, const aggregate &rhs) {
return lhs.value == rhs.value;
}
[[nodiscard]] inline bool operator<(const aggregate &lhs, const aggregate &rhs) {
return lhs.value < rhs.value;
}
// ensure aggregate-ness :)
static_assert(std::is_aggregate_v<test::aggregate>, "Not an aggregate type");
} // namespace test
#endif

View File

@@ -27,7 +27,7 @@ struct basic_test_allocator: std::allocator<Type> {
return *this;
}
bool operator==(const basic_test_allocator &other) const {
[[nodiscard]] bool operator==(const basic_test_allocator &other) const noexcept {
return (this == &other);
}
};

View File

@@ -1,25 +0,0 @@
#ifndef ENTT_COMMON_BOXED_TYPE_H
#define ENTT_COMMON_BOXED_TYPE_H
namespace test {
template<typename Type>
struct boxed_type {
Type value{};
operator Type() const noexcept {
return value;
}
};
template<typename Type>
inline bool operator==(const boxed_type<Type> &lhs, const boxed_type<Type> &rhs) {
return lhs.value == rhs.value;
}
using boxed_int = boxed_type<int>;
using boxed_char = boxed_type<char>;
} // namespace test
#endif

View File

@@ -1,11 +0,0 @@
#ifndef ENTT_COMMON_EMPTY_H
#define ENTT_COMMON_EMPTY_H
namespace test {
struct empty {};
struct other_empty {};
} // namespace test
#endif

View File

@@ -1,13 +0,0 @@
#ifndef ENTT_COMMON_ENTITY_H
#define ENTT_COMMON_ENTITY_H
#include <cstdint>
namespace test {
enum entity : std::uint32_t {};
enum other_entity : std::uint32_t {};
} // namespace test
#endif

View File

@@ -1,22 +0,0 @@
#ifndef ENTT_COMMON_NEW_DELETE_H
#define ENTT_COMMON_NEW_DELETE_H
#include <cstddef>
namespace test {
struct new_delete {
static void *operator new(std::size_t count) {
return ::operator new(count);
}
static void operator delete(void *ptr) {
::operator delete(ptr);
}
int value{};
};
} // namespace test
#endif

View File

@@ -1,12 +0,0 @@
#ifndef ENTT_COMMON_NON_COMPARABLE_H
#define ENTT_COMMON_NON_COMPARABLE_H
namespace test {
struct non_comparable {
bool operator==(const non_comparable &) const = delete;
};
} // namespace test
#endif

View File

@@ -1,17 +0,0 @@
#ifndef ENTT_COMMON_NON_DEFAULT_CONSTRUCTIBLE_H
#define ENTT_COMMON_NON_DEFAULT_CONSTRUCTIBLE_H
namespace test {
struct non_default_constructible {
non_default_constructible() = delete;
non_default_constructible(int v)
: value{v} {}
int value;
};
} // namespace test
#endif

View File

@@ -1,20 +0,0 @@
#ifndef ENTT_COMMON_NON_MOVABLE_H
#define ENTT_COMMON_NON_MOVABLE_H
namespace test {
struct non_movable {
non_movable() = default;
non_movable(const non_movable &) = default;
non_movable(non_movable &&) = delete;
non_movable &operator=(const non_movable &) = default;
non_movable &operator=(non_movable &&) = delete;
int value{};
};
} // namespace test
#endif

View File

@@ -1,21 +0,0 @@
#ifndef ENTT_COMMON_POINTER_STABLE_H
#define ENTT_COMMON_POINTER_STABLE_H
namespace test {
struct pointer_stable {
static constexpr auto in_place_delete = true;
int value{};
};
[[nodiscard]] inline bool operator==(const pointer_stable &lhs, const pointer_stable &rhs) {
return lhs.value == rhs.value;
}
[[nodiscard]] inline bool operator<(const pointer_stable &lhs, const pointer_stable &rhs) {
return lhs.value < rhs.value;
}
} // namespace test
#endif

View File

@@ -21,10 +21,10 @@ public:
using base_type::base_type;
using base_type::storage;
using base_type::create;
using base_type::emplace;
using base_type::insert;
using base_type::storage;
};
} // namespace test

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