Compare commits

...

849 Commits

Author SHA1 Message Date
Michele Caini
965c440d86 update single include file 2022-04-28 09:16:13 +02:00
Michele Caini
6b2aff821c ready to cut version 3.10.1 2022-04-28 09:15:38 +02:00
Michele Caini
84d9125ea1 meta: re-added meta_type::remove_pointer (close #878) 2022-04-28 09:14:03 +02:00
Michele Caini
9b969e762c meta (close #872):
* container traits don't really support plain arrays anymore (if they ever did)
* fix an issue with insert/erase of meta sequence containers
* add non-regression tests
2022-04-28 09:13:51 +02:00
Corentin Schreiber
2695d48ba7 registry: overflow of version and max number of entities (#864) 2022-04-28 09:12:25 +02:00
Michele Caini
aba2a6b17d config: decouple ENTT_NOEXCEPT and exceptions handling (close #867) 2022-04-28 09:12:13 +02:00
Michele Caini
7b87d17d22 resource: added more comparison operators for resource handles 2022-04-28 09:11:58 +02:00
Michele Caini
e5172a9240 update single include file 2022-04-15 11:44:22 +02:00
Michele Caini
dcac7942fa type_info: a default constructible type isn't a good idea actually 2022-04-08 12:17:14 +02:00
Michele Caini
4e13529adb registry: ctor that reserves enough memory for count pools 2022-04-08 09:58:05 +02:00
Michele Caini
c553835228 doc: add the kindest feedback ever received to the README 2022-04-07 18:48:38 +02:00
Michele Caini
1b87193fc1 doc: add Ember Sword to the list of games :) 2022-04-07 18:47:40 +02:00
Michele Caini
25fca56319 doc: update AUTHORS file 2022-04-07 18:47:15 +02:00
Alexandr Timofeev
bf4252394c *: protect against minmax (#862) 2022-04-07 14:28:30 +02:00
Michele Caini
22757e064f *: review remove_cv/remove_ref tokens after spotting an error by chance 2022-04-06 15:57:26 +02:00
Michele Caini
fc9fa37e4e doc: minor changes 2022-04-06 15:52:49 +02:00
Michele Caini
2d5e3402fa type_info: allow to construct type_info objects directly 2022-04-06 11:25:44 +02:00
Michele Caini
b7dd26121a type_info: cleanup 2022-04-06 09:55:31 +02:00
Michele Caini
23bdbeef4e doc: updated type_info section 2022-04-06 09:50:42 +02:00
Michele Caini
9ac81c5219 natvis: updated type_info snippet 2022-04-06 09:49:48 +02:00
Michele Caini
6b9d346b8b type_info:
* reserve a seq identifier for empty type_info objects
* require type_index to return non-zero values
* make type_info default constructible
* add type_info::operator bool()
2022-04-06 08:53:55 +02:00
Michele Caini
9e68eb4d2c type_id: type_info from variable 2022-04-05 08:50:08 +02:00
Michele Caini
f1f47ee44a entity: internal changes/cleanup 2022-04-05 08:42:36 +02:00
Michele Caini
436b2b3140 core: use fold expressions rather than std::disjunction if possible 2022-04-05 08:42:20 +02:00
Michele Caini
f6cfa8ae49 enum_as_bitmask: internal changes 2022-04-05 08:41:36 +02:00
Michele Caini
13949a8d1f updated internal TODO list 2022-04-04 17:13:49 +02:00
Michele Caini
88db623dc1 view: avoid unnecessary parameter pack 2022-04-04 12:27:28 +02:00
Michele Caini
976413173a any: invoke type_id with decayed types to avoid unnecessary instantiations 2022-04-04 09:46:52 +02:00
Michele Caini
91d3349585 view: slightly reduce instantiations 2022-04-04 09:46:20 +02:00
Michele Caini
6e1a774921 sigh: slightly reduce instantiations 2022-04-04 09:46:00 +02:00
Michele Caini
6b53b509fa registry/storage: minor changes 2022-04-04 09:18:10 +02:00
Michele Caini
b8b6203ecd doc: minor changes 2022-04-04 09:17:40 +02:00
Michele Caini
81e1500675 dense_set: slightly reduce instantiations 2022-04-04 09:17:13 +02:00
Michele Caini
d2cd18cb1e test: minor changes (try to also please codecov) 2022-04-02 16:10:00 +02:00
Michele Caini
f5ac73f681 resource_cache: use actual template parameters on sfinae expressions 2022-04-01 14:48:52 +02:00
Michele Caini
413630813a resource: updated doc 2022-04-01 14:41:38 +02:00
Michele Caini
73b6ef2293 resource: updated natvis file 2022-04-01 14:41:15 +02:00
Michele Caini
428bfbd810 tests: updated tests for the resource module 2022-03-31 15:43:44 +02:00
Michele Caini
c54409bdfb resource: fully review the resource module 2022-03-31 15:43:07 +02:00
Michele Caini
b63aebfdaf dense_set: strong exception guarantee (emplace/insert) 2022-03-30 15:34:45 +02:00
Michele Caini
335f876a46 dense_map: strong exception guarantee (emplace/insert) 2022-03-30 15:26:39 +02:00
Michele Caini
c5e99342a3 tests: minor changes 2022-03-30 15:14:26 +02:00
Michele Caini
1710eb9249 dense_map: minor changes 2022-03-29 16:56:49 +02:00
Michele Caini
d03770f91f meta: avoid using =default to avoid tricking VS toolset v141 2022-03-29 16:26:01 +02:00
Michele Caini
9fe6f7a6ea entity: avoid using =default to avoid tricking VS toolset v141 2022-03-29 16:25:53 +02:00
Michele Caini
2fc605fc3e container: avoid using =default to avoid tricking VS toolset v141 2022-03-29 16:25:37 +02:00
Michele Caini
edcd5c4713 dense_set: use underlying iterators to exploit their checks 2022-03-28 16:25:09 +02:00
Michele Caini
bd3df1e06a dense_map: use underlying iterators to exploit their checks 2022-03-28 16:24:52 +02:00
Michele Caini
b785d1c82b meta: minor changes (coding style) 2022-03-27 23:25:50 +02:00
Michele Caini
93bf14f84f organizer: cleanup + renaming to avoid conflicts 2022-03-27 23:24:54 +02:00
Michele Caini
5a84646282 dense_map: updated built-in doc 2022-03-25 17:34:44 +01:00
Michele Caini
2e6cfc08f0 registry: refine storage proxy iterator and guarantee iterator conversions 2022-03-25 17:20:23 +01:00
Michele Caini
4b6d3d47ec registry: suppress a false warning from clang 2022-03-25 15:12:25 +01:00
Michele Caini
985abaa12a dense_map/set: minor changes 2022-03-24 17:38:51 +01:00
Michele Caini
a381a7253a resource: updated doc 2022-03-23 15:32:11 +01:00
Michele Caini
b8bb509d5c resource:
* drop the resource_loader class
* made the loader a template parameter of the resource_cache
* updated the API of the resource_cache
2022-03-23 15:31:55 +01:00
Michele Caini
f48dbd0e19 resource_handle: updated doc 2022-03-23 15:29:44 +01:00
Michele Caini
31c1278374 config: removed ENTT_LAUNDER (unused) 2022-03-22 12:44:21 +01:00
Michele Caini
f6aaafd60f config: add ENTT_NOEXCEPT_IF (for future uses) 2022-03-22 12:43:47 +01:00
Michele Caini
ae3dec19fd registry: internal changes to avoid ambiguous calls 2022-03-22 12:43:24 +01:00
Michele Caini
990baa0929 test: typo 2022-03-21 16:14:20 +01:00
Michele Caini
7ecb65a141 doc: updated links 2022-03-21 10:41:53 +01:00
Michele Caini
a1e55103be enum: typo 2022-03-21 10:41:45 +01:00
Michele Caini
1b4fc54405 dense_set:
* emplace create-and-discard mode if strictly required only
* insert uses the insert_or_do_nothing utility internally
* opaque value_to_bucket function
2022-03-21 10:41:28 +01:00
Michele Caini
e5dcea1fa0 dense_map: emplace create-and-discard mode if strictly required only 2022-03-21 10:19:47 +01:00
Michele Caini
9f25195629 dense_map: use insert_or_do_nothing from within insert 2022-03-21 10:17:55 +01:00
Michele Caini
6936fa1c1c dense_map: opaque key_to_bucket function 2022-03-21 08:42:44 +01:00
Michele Caini
7b25eacfc8 dense_map:
* try_emplace should not consume arguments if the element exists
* clean up
2022-03-18 10:51:16 +01:00
Michele Caini
33989a61e3 dense_set: claen up 2022-03-18 10:49:52 +01:00
Michele Caini
08d9e8d076 dense_map/set: minor changes 2022-03-17 14:23:29 +01:00
Michele Caini
da43ef0982 dense_set: avoid potential UBs 2022-03-17 12:36:00 +01:00
Michele Caini
02f12bde81 dense_map: avoid potential UBs 2022-03-17 12:35:46 +01:00
Michele Caini
b55d5375ac *: added missing messages to static asserts 2022-03-17 11:39:45 +01:00
Michele Caini
281f40bd56 dense_set: avoid dispatching based on iterator category if possible 2022-03-17 11:39:45 +01:00
Michele Caini
dcb7c0c27e dense_map: constrain the allocator type 2022-03-17 11:39:45 +01:00
Michele Caini
d3c44a6fa6 dense_map: avoid dispatching based on iterator category if possible 2022-03-17 11:39:45 +01:00
Michele Caini
f432dc6bdc dense_map:
* uses-allocator construction
* iterators model forward/random access iter types but are in the input iter category
* iter value_type is pair<const T &, U &>/pair<const T &, const U &> (zip iter types)
* no risky UBs due to type punning or destroy/construct within a vector
2022-03-17 11:39:45 +01:00
Michele Caini
ce21ee6001 test: minor changes 2022-03-17 11:39:36 +01:00
Michele Caini
a431f5a674 registry: allow creating pools during a destroy (close #853) 2022-03-16 17:18:14 +01:00
Michele Caini
f6153f17ad storage: return a meaningful msg when the type is not movable 2022-03-16 17:17:38 +01:00
Michele Caini
c17ecbc78c resource: add operator==/!= to resource handles 2022-03-16 17:17:35 +01:00
Michele Caini
0feb9f0c56 resource_handle: update member types 2022-03-16 17:17:33 +01:00
Michele Caini
e69cb3d950 memory: suppress warnings due to unreferenced parameters 2022-03-16 17:17:30 +01:00
Michele Caini
e7c03dd512 memory: minor changes 2022-03-16 17:17:26 +01:00
Michele Caini
0c1371ffae test: suppress warning due to unused variables 2022-03-16 17:16:54 +01:00
Michele Caini
8a350acf39 dispatcher: allocator support 2022-03-16 17:16:50 +01:00
Michele Caini
594b3b5531 component: use ignore_as_empty_v to simplify the code here and there 2022-03-16 17:16:36 +01:00
Michele Caini
e13d06fe20 component: reintroduce utility ignore_as_empty_v (it's actually useful sometimes) 2022-03-16 17:16:32 +01:00
Michele Caini
805bb84c8f component_traits:
* removed ::ignore_if_empty member
* use ::page_size 0 for empty types
2022-03-16 17:16:21 +01:00
Michele Caini
1a62338349 storage: split sigh_storage_mixin 2022-03-16 17:15:49 +01:00
Michele Caini
4795ba83ba config: remove module-specific mismatch checks (we cannot protect everyone from themselves) 2022-03-16 17:15:41 +01:00
Michele Caini
ad5b362bca config: use pragma detect_mismatch if possible 2022-03-16 17:15:28 +01:00
Michele Caini
8a735452de config: detect version mismatch 2022-03-16 17:15:25 +01:00
Michele Caini
03d0f3e9ca test: merge no-eto tests with registry tests 2022-03-16 17:15:18 +01:00
Michele Caini
ff0983cc42 storage: more about uses-allocator construction 2022-03-16 17:15:00 +01:00
Michele Caini
159a413c7d meta: avoid creating internal namespace that messes with friend declarations 2022-03-16 17:14:37 +01:00
Michele Caini
6a753d1316 meta: remove unused data member 2022-03-16 17:14:34 +01:00
Michele Caini
c89bf30e05 meta: dereferencing a pointer-like object which converts to bool works in all cases (false implies empty meta_any) 2022-03-16 17:14:32 +01:00
Michele Caini
ba4b5ef9f2 meta: minor changes 2022-03-16 17:14:30 +01:00
Michele Caini
5b39ee221f meta: reduce the number of instantiations due to basic_meta_sequence_container_traits 2022-03-16 17:14:28 +01:00
Michele Caini
65c4432325 meta: use adl lookup within basic_meta_sequence_container_traits 2022-03-16 17:14:26 +01:00
Michele Caini
32bbb27c9f meta: further reduce the number of instantiations due to meta sequence containers 2022-03-16 17:14:24 +01:00
Michele Caini
ef3c030576 meta: further optimize meta sequence containers' iterators 2022-03-16 17:14:22 +01:00
Michele Caini
554a182229 meta: reduce instantiations due to meta containers (drop get_fn) 2022-03-16 17:14:20 +01:00
Michele Caini
9154d78e58 meta: make the meta associative container's iterator an internal class as all other iterators 2022-03-16 17:14:18 +01:00
Michele Caini
82d933aa3d meta: make the meta sequence container's iterator an internal class as all other iterators 2022-03-16 17:14:16 +01:00
Michele Caini
ef0fe9b13c meta:
* relax meta sequence container requirements (::clear is no longer required)
* reduce instantiations due to meta sequence containers
2022-03-16 17:14:14 +01:00
Michele Caini
f6700b7094 meta: drop meta_sequence_container::meta_iterator::base() 2022-03-16 17:14:12 +01:00
Michele Caini
21f00cf251 meta: make meta_sequence_container::meta_iterator model a bidirectional iterator (and keep staying in the input iterator category) 2022-03-16 17:14:10 +01:00
Michele Caini
900d2bf816 meta: prepare meta_sequence_container::meta_iterator to model a bidirectional iterator 2022-03-16 17:14:07 +01:00
Michele Caini
3846fa3f28 meta: begin/end adl lookup 2022-03-16 17:14:04 +01:00
Michele Caini
52f77d61ba sigh:
* fixed doc
* test review/cleanup
2022-03-16 17:13:16 +01:00
Michele Caini
039341de69 poly: fixed forward file 2022-03-16 17:13:09 +01:00
Michele Caini
5adf332a5f registry: ::sortable -> ::owned for component types (close #847) 2022-03-16 17:13:04 +01:00
Michele Caini
efbb32a498 sparse_set: added the ::bump function 2022-03-16 17:12:56 +01:00
Michele Caini
3156dd1321 sigh: constrain the allocator type 2022-03-16 17:12:45 +01:00
Michele Caini
2fbeeafbb8 dense_set: constrain the allocator type 2022-03-16 17:12:42 +01:00
Michele Caini
db680f3bcd snapshot: try to get around an issue of VS, toolset v141 2022-03-16 17:12:01 +01:00
Michele Caini
4a20fa5275 snapshot: internal changes 2022-03-16 17:11:55 +01:00
Michele Caini
3fb2da7174 group: internal changes 2022-03-16 17:11:51 +01:00
Michele Caini
7f8bebbdd5 view: internal changes 2022-03-16 17:11:47 +01:00
Michele Caini
597fc0265f natvis: dense_set 2022-03-16 17:11:31 +01:00
Michele Caini
d5d5770dac natvis: dispatcher 2022-03-16 17:11:26 +01:00
Michele Caini
772ac64e3b config: add ENTT_VERSION 2022-03-16 17:11:03 +01:00
Michele Caini
92d7337f04 config: macro.h (just in case) 2022-03-16 17:10:55 +01:00
Michele Caini
200425114c test: address some feedback by iwyu 2022-03-16 17:10:40 +01:00
Michele Caini
b92a73a2a2 workflow: minor changes 2022-03-16 17:10:34 +01:00
Michele Caini
e83c77b99f build system: updated workflow(s) 2022-03-16 17:10:23 +01:00
Michele Caini
28aa35160d hashed_string: minor changes 2022-02-27 17:46:03 +01:00
Michele Caini
18c9bfa134 compressed_pair: minor changes 2022-02-27 17:45:56 +01:00
Michele Caini
bdb7763237 sigh: rebind allocator internally 2022-02-27 17:45:50 +01:00
Michele Caini
bc9ee46b8f dispatcher: minor changes 2022-02-27 17:45:45 +01:00
Michele Caini
b92b734db2 doc: fixed typo 2022-02-25 16:38:13 +01:00
Michele Caini
2748a6cd3c dense_set: remove the constraint on the allocator type 2022-02-25 15:15:09 +01:00
Michele Caini
888876185d doc: fixed typo 2022-02-25 15:14:14 +01:00
Michele Caini
03fea6358b view/runtime_view: make iterator_type private 2022-02-25 14:10:15 +01:00
Michele Caini
59b0dc6735 test: minor changes 2022-02-25 14:09:22 +01:00
Michele Caini
35a42b2172 dense_set: cleanup 2022-02-24 18:48:28 +01:00
Michele Caini
daa4056f45 registry/storage/sparse_set: minor changes to get around an issue of gcc7.5 2022-02-24 18:48:06 +01:00
Michele Caini
bb5abe633f dense_set: review constructors/assignment operators 2022-02-24 11:46:42 +01:00
Michele Caini
dc238db355 compressed_pair: correctly manage references with piecewise construction 2022-02-24 11:33:12 +01:00
Michele Caini
4c692c23bb test: refine tracked_memory_resource::do_is_equal 2022-02-24 11:33:04 +01:00
Michele Caini
b093e82db3 memory: uninitialized_construct_using_allocator (waiting for C++20) 2022-02-23 09:41:32 +01:00
Michele Caini
7a1c2108a1 dispatcher: enable fetching the number of pending events (#848)
Co-authored-by: WoLfulus <WoLfulus@users.noreply.github.com>
2022-02-23 08:42:33 +01:00
Michele Caini
4aa2b49649 updated TODO file 2022-02-23 08:41:56 +01:00
Michele Caini
cdaced4df6 memory: uses_allocator_construction_args pairs support 2022-02-23 00:19:55 +01:00
Michele Caini
cdd029853b any:
* strong exception guarantee applies
* a test to avoid future regressions
2022-02-22 15:03:07 +01:00
Michele Caini
507bafdd9c any/meta_any: revert changes to introduce ::hash 2022-02-22 12:15:09 +01:00
Michele Caini
07a36123e5 dense_map/set: get around a nasty issue of gcc 7.5 with deduced return types and friend functions 2022-02-22 12:03:41 +01:00
Michele Caini
a205e7ada9 test: it's not a good idea to use discarded values, the sanitizer may complain :) 2022-02-22 09:55:03 +01:00
Michele Caini
25e9330bb4 memory: avoid ambiguous call with C++20 2022-02-22 09:45:03 +01:00
Michele Caini
b2edfa454c test: minor changes (to please all compilers) 2022-02-22 09:30:46 +01:00
Michele Caini
07767a09d9 doc: fixed typo 2022-02-22 08:40:25 +01:00
Michele Caini
e8d85d9269 resource: updated doc/tests 2022-02-22 08:15:09 +01:00
Michele Caini
5cf0ba079f memory: make_obj_using_allocator utility (waiting for C++20) 2022-02-21 16:31:36 +01:00
Michele Caini
e08b1f82ce memory: uses_allocator_construction_args (waiting for C++20) 2022-02-21 08:07:01 +01:00
Michele Caini
c2196149ee test: minor changes 2022-02-20 23:55:04 +01:00
Michele Caini
65b5ce7880 doc: minor changes 2022-02-20 23:44:33 +01:00
Michele Caini
9c254ec3e0 doc: mark C++20 feature (to help the future me find them) 2022-02-20 09:57:24 +01:00
Michele Caini
f4418e0205 test: increase code coverage 2022-02-19 10:46:18 +01:00
Michele Caini
a314f7b42e test: minor changes (code coverage) 2022-02-19 10:41:35 +01:00
Michele Caini
dc5a72cda5 dense set:
* cleaned up implementation
* uses-allocator construction support
* a few more tests
2022-02-19 09:49:47 +01:00
Michele Caini
380cb41ca9 entity: cleanup 2022-02-18 17:08:29 +01:00
Michele Caini
83f7e633a3 tests: allocate_unique with types that don't support uses-allocator construction 2022-02-18 17:08:07 +01:00
Michele Caini
ab1c920435 tests: storage with types that don't support uses-allocator construction 2022-02-18 17:07:44 +01:00
Michele Caini
7094d658f8 test: minor changes 2022-02-18 13:47:59 +01:00
Michele Caini
11617291e3 container: minor changes 2022-02-18 13:47:14 +01:00
Michele Caini
790aa03834 scheduler: allow attaching tasks from already running processes (close #845) 2022-02-18 11:34:42 +01:00
Michele Caini
bcd16ea5ea entity: adjust noexcept policy 2022-02-17 12:17:07 +01:00
Michele Caini
81c044760b core: adjust noexcept policy 2022-02-17 12:16:46 +01:00
Michele Caini
b0e0e80b69 meta: adjust noexcept policy 2022-02-17 09:33:48 +01:00
Michele Caini
280bb1d84f dense_set: suppress shadow warning 2022-02-17 09:22:57 +01:00
Michele Caini
bb42dceaca natvis: updated container/dense_set snippet 2022-02-17 09:22:55 +01:00
Michele Caini
d0873ed551 dense_map: adjust noexcept policy 2022-02-17 09:03:28 +01:00
Michele Caini
0fbddfc47b dense_set: adjust noexcept policy 2022-02-17 09:03:11 +01:00
Michele Caini
3f1dee2650 dense_set: uses-allocator construction 2022-02-17 09:02:35 +01:00
Michele Caini
a4b8b93e7f dense_set: emplace always consumes its arguments 2022-02-17 09:02:21 +01:00
Michele Caini
02d1417ef4 process/scheduler: update noexcept policy 2022-02-16 17:36:49 +01:00
Michele Caini
50812a908c signal: update noexcept policy 2022-02-16 12:40:33 +01:00
Michele Caini
1a1ed55485 resource_handle: minor changes 2022-02-16 12:33:55 +01:00
Michele Caini
09fa322a0e poly: update noexcept policy 2022-02-16 12:33:38 +01:00
Michele Caini
1a9a423c35 locator: adjust noexcept policy 2022-02-16 12:33:22 +01:00
Michele Caini
7b0449213c doc: minor changes 2022-02-16 12:32:33 +01:00
Michele Caini
c0251f5c29 sparse_set/storage: support entity and component types with custom swap functions 2022-02-16 12:29:45 +01:00
Michele Caini
4509d8a60b snapshot: swap properly internally 2022-02-16 12:29:43 +01:00
Michele Caini
b1738b0902 natvis: updated signal/delegate snippet 2022-02-16 12:28:29 +01:00
Michele Caini
45259462aa any: remove call to ENTT_LAUNDER, no longer required due to do p1971r0 2022-02-16 12:27:52 +01:00
Michele Caini
772a7427a2 doc: another update for the EnTT in Action list 2022-02-16 12:26:29 +01:00
Michele Caini
afa58b81f6 doc: added D&D Dark Alliance to the EnTT in Action list 2022-02-16 12:26:25 +01:00
Michele Caini
6822c0f0e3 test: minor changes 2022-02-13 18:40:14 +01:00
Michele Caini
0a155ecb68 test: uses-allocator construction guaranteed for allocate_unique 2022-02-13 18:33:31 +01:00
Michele Caini
15fff94447 test: uses-allocator construction guaranteed for storage classes 2022-02-13 18:32:58 +01:00
Michele Caini
ff06df2acb test: tracked_memory_resource utility 2022-02-13 18:32:10 +01:00
Michele Caini
df9301d591 bjuild system/tests: check for <version> (old compilers support) 2022-02-13 18:31:26 +01:00
Michele Caini
e51b1bbb25 compressed_pair: avoid noexcept specification which cannot be supported 2022-02-13 18:29:34 +01:00
Chris Ohk
aa275f4b1c doc: fix minor typos (#842) 2022-02-12 15:52:43 +01:00
Michele Caini
ed11bda9fd component: automatic traits deduction 2022-02-11 10:35:43 +01:00
Michele Caini
ed34a3f5d4 locator:
* service_locator -> locator
* API review + allocator support
* updated doc and tests
2022-02-10 11:31:55 +01:00
Michele Caini
a99b8b7984 build system: get prepared for the windows-latest switch on GH 2022-02-10 10:32:06 +01:00
Michele Caini
f750254cc2 allocate_unique: static assert that Type isn't an array type 2022-02-09 12:32:36 +01:00
Michele Caini
21ece182f5 any: let the wrapper decide what it means to be an owner 2022-02-09 09:42:49 +01:00
Michele Caini
6439dca43a memory: suppress a (shadow) warning from gcc 2022-02-08 11:56:52 +01:00
Michele Caini
ca424a4577 type_traits: try to also please clang-cl 2022-02-08 11:50:53 +01:00
Michele Caini
87bc2cb1ab any: try to also please gcc7 2022-02-08 11:20:49 +01:00
Michele Caini
58f22cc3b5 meta_any: ::hash function for hashable types (+ bonus doc review) 2022-02-08 11:04:14 +01:00
Michele Caini
fa8362f000 any: ::hash function for hashable types (close #629) 2022-02-08 11:02:30 +01:00
Michele Caini
1816206dfc type_traits: is_std_hashable[_v] 2022-02-08 09:58:26 +01:00
Michele Caini
fcfa994152 doc: compressed_pair, std::tuple_size and std::tuple_element specializations 2022-02-08 09:57:10 +01:00
Michele Caini
e7262660c2 dispatcher: API review to support named queues 2022-02-07 10:52:04 +01:00
Michele Caini
a1e14d5740 doc: memory (power of two, fast modulus, allocate_unique, ...) 2022-02-05 00:18:57 +01:00
Michele Caini
60c11f3f30 dispatcher: minor changes 2022-02-05 00:18:42 +01:00
Michele Caini
3fe584d366 any: rename local variable to make it easier for me to read too :) 2022-02-05 00:18:18 +01:00
Michele Caini
28e38321ee test: put basic test allocator in its own file 2022-02-05 00:18:03 +01:00
Michele Caini
1faeeeeabc memory: allocation_deleter/allocate_unique 2022-02-05 00:14:31 +01:00
Michele Caini
75a0c672b1 iterable_adaptor: decouple iterator types 2022-02-04 08:54:16 +01:00
Michele Caini
db829b21aa doc: minor changes 2022-02-04 08:53:23 +01:00
Michele Caini
24bdc29c27 input_iterator_pointer: minor changes 2022-02-03 08:12:35 +01:00
Michele Caini
692a78984f doc: updated documentation about context variables 2022-02-03 08:09:52 +01:00
Michele Caini
77a399738c registry: support to named context variables (see emplace_hint) 2022-02-03 08:06:52 +01:00
Michele Caini
5d7358899d registry: cleanup the standalone context class 2022-02-03 07:52:27 +01:00
Michele Caini
aca25b9999 build: updated build workflow 2022-02-02 11:55:08 +01:00
Paolo Monteverde
182c6c534b meta: rearrange member data of meta nodes to optimize memory usage (#833) 2022-02-02 08:15:51 +01:00
Michele Caini
0500f155e6 delegate: ::instance() -> ::data() 2022-02-02 08:02:03 +01:00
Michele Caini
125b826f62 meta/signal: added missing includes 2022-02-01 23:11:25 +01:00
Michele Caini
922bfbf724 test: fixed includes (while trying iwyu) 2022-02-01 23:01:04 +01:00
Michele Caini
f1e584fad9 scheduler: add missing header 2022-02-01 14:12:23 +01:00
Michele Caini
0c4dcdea71 config/core: include cstdint also when ENTT_ID_TYPE is defined 2022-02-01 14:11:41 +01:00
Michele Caini
ec4b383198 build: minor changes to workflows 2022-02-01 11:31:44 +01:00
Michele Caini
3328c7e78b doc: fixed funding.yml 2022-02-01 11:30:23 +01:00
Michele Caini
357fcd44d7 poly: make Member type explicit to avoid errors with older compilers 2022-01-31 14:48:20 +01:00
Michele Caini
5a0a682d35 meta: relaxed checks on conversion functions 2022-01-31 09:55:53 +01:00
Michele Caini
1fcb7e5aba dispatcher: minor changes 2022-01-31 08:44:40 +01:00
Michele Caini
eec867bc9e enum:
* remove unused functions
* avoid errors due to narrowing conversions
2022-01-31 08:44:24 +01:00
Michele Caini
5a956d2914 poly: compile-time checks on embedded vtables + code coverage 2022-01-30 16:53:27 +01:00
Michele Caini
e0e29cc363 test: cleanup 2022-01-30 16:52:51 +01:00
Michele Caini
9fc8e03f72 poly: refined fwd file, updated (typed-)tests 2022-01-29 17:32:15 +01:00
Michele Caini
b054e93f0e build system: removed the in-source build check 2022-01-29 17:31:37 +01:00
Michele Caini
653680a356 doc: minor changes 2022-01-29 17:30:20 +01:00
Michele Caini
40035f2e52 gh: updated funding file 2022-01-29 17:29:05 +01:00
Michele Caini
a70b7daec4 build system: workflow cleanup 2022-01-29 17:25:34 +01:00
Michele Caini
0f3e7b5dac doc: minor changes to suppress warnings 2022-01-29 16:15:40 +01:00
Michele Caini
2fbc08b606 test: a handful of minor changes to please all compilers from the CI and remove some lambdas 2022-01-29 11:31:57 +01:00
Michele Caini
e4897f709e test: merge poly tests in a single file, use typed tests to reduce the boilerplate 2022-01-29 10:51:25 +01:00
Michele Caini
84445aa28b test: use gtest typed test suite for enums 2022-01-29 10:50:33 +01:00
Michele Caini
0795f9a9f2 test: separate death tests as suggested in the gtest doc 2022-01-29 10:47:22 +01:00
Michele Caini
44ae0d67f6 sparse_set: set the free list to tombstone rather than null within ::clear 2022-01-28 15:53:36 +01:00
Michele Caini
a74f0ca370 test: code coverage 2022-01-28 14:06:22 +01:00
Michele Caini
266c1fcdcb registry: better/faster remove 2022-01-28 14:06:22 +01:00
Michele Caini
977c3407e7 registry: better/faster erase 2022-01-28 14:06:22 +01:00
Michele Caini
99c2299ed4 test: added missing typename keyword 2022-01-28 14:06:22 +01:00
Michele Caini
39a08f17da sparse_set: clear no longer creates tombstones 2022-01-28 14:06:22 +01:00
Michele Caini
57de1187fc sparse_set/storage:
* reintroduce the swap_and_pop vs in_place_pop split for perf reasons
* rollback to a model with two iterators on virtual functions
* even faster range-erase/clear
2022-01-28 14:06:22 +01:00
Michele Caini
2bba6d4c0d test: code coverage 2022-01-28 14:06:22 +01:00
Michele Caini
2f03b225f7 sparse_set: remove the cursed ::slot function (finally) 2022-01-28 14:06:22 +01:00
Michele Caini
533bc1ce94 sparse_set: review ::clear 2022-01-28 14:06:22 +01:00
Michele Caini
add9df6058 doc: updated todo list 2022-01-28 14:06:21 +01:00
Michele Caini
d3496f3ff5 sparse_set: apparently using an invalid iterator isn't a good idea :) 2022-01-28 14:06:21 +01:00
Michele Caini
e97f7ee725 doc: minor changes 2022-01-28 14:06:21 +01:00
Michele Caini
37497295fb sparse_set/storage:
* refine the try_erase to make it accept an iterator and a count (easier to work with at the call site)
* added a test for the stable clear
* added a note for future improvements
2022-01-28 14:06:21 +01:00
Michele Caini
2e89b3b5b9 sparse_set/storage: make try_erase accept a range of entities 2022-01-28 14:06:21 +01:00
Michele Caini
518bbc651e sparse_set/storage:
* try_emplace returns a basic iterator
* try_erase accepts a basic iterator (prepare for an even faster remove/erase/clear/whatever)
2022-01-28 14:06:21 +01:00
Michele Caini
ffd1d97e03 natvis: updated container.natvis to use the right names 2022-01-28 14:05:41 +01:00
Michele Caini
e503d431f1 doc: minor changes 2022-01-28 14:00:54 +01:00
Michele Caini
49a066714a test: minor changes 2022-01-28 14:00:33 +01:00
Michele Caini
7eec610d21 container:
* dense_hash_map -> dense_map
* dense_hash_set -> dense_set
2022-01-28 13:35:16 +01:00
Michele Caini
530507c36b meta: some compile-time checks to avoid subtle runtime errors 2022-01-27 22:49:35 +01:00
Michele Caini
4d2584d8fc doc: added home hearth to the links page 2022-01-25 18:09:37 +01:00
Michele Caini
0f5057920e doc: added gaia-ecs to the list of similar projects 2022-01-25 18:00:34 +01:00
Michele Caini
a98000cf3b test: I am the winner of the best error message ever but I remain humble :) 2022-01-25 14:56:00 +01:00
Michele Caini
0cec8de164 dense_hash_map/set: suppress warnings due to integral conversions 2022-01-25 09:09:37 +01:00
Michele Caini
1caa8d923d sparse_set/storage: iterator's ::index returns the right offset 2022-01-23 13:05:45 +01:00
Michele Caini
4fa51f8c52 type_traits: removed is_iterator_type[_v], it may be ambiguous and cause errors 2022-01-22 16:59:17 +01:00
Michele Caini
a8be765aa8 registry: disable optimizations based on is_iterator_type, free pools allow to implement them on the user side and more efficiently when needed 2022-01-22 16:52:45 +01:00
Michele Caini
d2d35d490a doc: updated entity.md 2022-01-22 16:45:05 +01:00
Michele Caini
26ba137424 registry: template ::storage support to const qualified types 2022-01-21 19:00:49 +01:00
Michele Caini
e6006663ec registry: remove [[nodiscard]] from the template ::storage method 2022-01-21 11:08:07 +01:00
Michele Caini
74eb2772b6 natvis: support standalone registry context 2022-01-20 15:58:32 +01:00
Michele Caini
3878a696af registry: standalone context type (close #575) 2022-01-20 09:37:31 +01:00
Michele Caini
49ac34c405 doc: avoid confusion with context variables 2022-01-19 16:02:04 +01:00
Michele Caini
a7d4a980ba *: more consistent policy for final classes 2022-01-19 12:41:50 +01:00
Michele Caini
2196db562e registry:: non-template storage(id) returns an iterator rather than a naked pointer 2022-01-19 12:15:22 +01:00
Michele Caini
2ad3e0ed4c hashed_string: updated natvis representation 2022-01-19 09:30:35 +01:00
Michele Caini
6e03fe77ae doc: updated copyright 2022-01-19 07:43:58 +01:00
Michele Caini
af8ec7b5dd registry: remove/erase dispatch based on the iterator type 2022-01-18 08:30:43 +01:00
Michele Caini
7fa29e7341 registry: strict check on storage types to prevent subtle bugs in case of errors 2022-01-18 07:45:03 +01:00
Michele Caini
9c6950d163 hashed_string: I managed to crash clang but I remain humble and try to help it :) 2022-01-17 14:02:02 +01:00
Michele Caini
5d4cbfe8e7 hashed_string: make it work with vs toolset v141 2022-01-17 13:49:11 +01:00
Michele Caini
4747422110 *: added a bunch of final here and there 2022-01-17 13:43:55 +01:00
Michele Caini
7d1416ac74 hashed_string: store size information (close #824) 2022-01-17 08:25:56 +01:00
Michele Caini
2efaa7af7b *: allocator aware classes fail to compile if allocator's value type and container's value type differ 2022-01-15 16:48:33 +01:00
Michele Caini
68e259870f sparse_set/storage:
* support ::erase suppression
* support ::emplace with invalid entities when suppressed
* support ::erase/::remove with invalid entities when suppressed
2022-01-15 00:48:07 +01:00
Michele Caini
29c2c94784 doc: suppress warnings from doc generation 2022-01-15 00:45:35 +01:00
Michele Caini
3f2e1d078f sparse_set/storage: replace the whole try_insert machinery with a force_back parameter to try_emplace 2022-01-15 00:44:47 +01:00
Michele Caini
0a259da05a sparse_set: opaque insert always appends elements 2022-01-14 14:06:41 +01:00
Michele Caini
1b22fe6de2 storage: insert for non-empty types always appends elements 2022-01-13 15:01:06 +01:00
Michele Caini
9b700c3bd2 natvis: removed count filter from sparse set, it makes it harder to debug internal issues 2022-01-13 15:00:34 +01:00
Michele Caini
3b72e30c36 sparse_set: use the right iterator in the catch branch of insert 2022-01-13 14:44:27 +01:00
Michele Caini
9eba103de9 storage: insert for empty types always appends elements 2022-01-13 14:05:33 +01:00
Michele Caini
ecadee3876 sparse_set: added basic virtual try_insert (insert does not use it yet) 2022-01-13 13:47:22 +01:00
Michele Caini
699f9105ae storage: arguments are always ignored for empty types 2022-01-13 13:41:32 +01:00
Michele Caini
8a19e8dafe storage: support chained constructors i.e. parent-to-child propagation 2022-01-13 12:00:15 +01:00
Michele Caini
588c056205 sparse_set/storage: minor changes, more tests 2022-01-11 22:56:22 +01:00
Michele Caini
866a7fb641 natvis: added info about the sparse array of a sparse set 2022-01-11 18:18:41 +01:00
Michele Caini
a5fe42268f test: cleanup 2022-01-11 08:15:09 +01:00
Michele Caini
0bd6816bdd sparse_set: pop_at(pos) -> try_erase(entt) (prepare for inhibited deletions) 2022-01-11 08:06:20 +01:00
Michele Caini
1c6670c7a1 sparse_set: merge ::swap_and_pop and ::in_place_pop 2022-01-10 22:55:46 +01:00
Michele Caini
699a0eb934 sigh_storage_mixin:
* always check the owner in debug
* fixed a test that doesn't invoke bind
2022-01-10 17:22:34 +01:00
Michele Caini
72440ab937 sigh_storage_mixin: always trigger an emplace request notification (in sync with unbalanced destroy) 2022-01-10 00:23:18 +01:00
Michele Caini
9ad807bd58 storage: doc cleanup 2022-01-10 00:16:10 +01:00
Michele Caini
9fb1970ac0 sparse_set/storage: rollback ::policy(pol), incompatible with views 2022-01-09 16:15:47 +01:00
Michele Caini
48dfac2588 storage:
* also support (in theory) fancy pointers
* removed the internal ::construct function
2022-01-09 15:06:41 +01:00
Michele Caini
c6d3714e6f sparse_set: also support (in theory) fancy pointers 2022-01-09 15:06:17 +01:00
Michele Caini
1345c257c3 storage: make final virtual functions private (moving towards private-only hooks) 2022-01-09 14:42:56 +01:00
Michele Caini
19112a8f27 storage: cleanup 2022-01-09 14:25:41 +01:00
Michele Caini
e0979fcf3f sparse_set/storage: make empty virtual functions private (moving towards private-only hooks) 2022-01-08 16:14:54 +01:00
Michele Caini
d514ecbd5e doc: updated documentation about deletion policies 2022-01-08 14:36:04 +01:00
Michele Caini
a53dbb6950 registry: deletion policy is checked at runtime for groups since we can change it dynamically now 2022-01-08 14:34:21 +01:00
Michele Caini
a03b88e0eb dense_hash_[map/set]: suppress super annoying warnings 2022-01-07 14:07:35 +01:00
Michele Caini
4472eb1c40 updated TODO file 2022-01-06 00:23:58 +01:00
Michele Caini
3443987127 meta: meta_type::invoke also searches into the base types for a candidate function 2022-01-04 14:55:30 +01:00
Michele Caini
c73a79fd42 view: cleanup 2022-01-04 13:53:00 +01:00
Michele Caini
dedbe3d431 storage: make shrink_to_size check for tombstones only when needed 2022-01-03 21:30:53 +01:00
Michele Caini
87ef713a17 dense_hash_[map/set]: cleanup 2022-01-03 21:17:37 +01:00
Michele Caini
4ba8ac4fa1 sparse_set: opaque emplace/insert return (eventually invalid) iterators to the inserted elements 2022-01-02 17:21:21 +01:00
Michele Caini
e74e0531bd sparse_set/storage: minor changes 2022-01-02 16:51:08 +01:00
Michele Caini
b67cb4bd1f sparse_set/storage: minor changes to make it easier to read for the future me :) 2022-01-01 17:09:34 +01:00
Michele Caini
22628c9ac4 sparse_set/storage: prepare them for a faster and opaque ::insert 2021-12-31 17:23:50 +01:00
Michele Caini
4dd0862fad runtime_view:
* added ::iterate and ::exclude member functions to attach pools at runtime
* removed vector-based constructor
* removed basic_registry<...>::runtime_view member function
* updated benchmarks accordingly (also reviewed them as a whole just because)
2021-12-30 14:41:05 +01:00
Michele Caini
0ec57fbb8d sparse_set: removed redundant check 2021-12-30 14:38:51 +01:00
Michele Caini
19ab584d32 doc: updated FAQs 2021-12-29 16:34:26 +01:00
Michele Caini
97fe978d14 runtime_view: removed redundant validity check 2021-12-29 00:25:11 +01:00
Michele Caini
2a74c7d897 runtime_view: support to all types of sparse set 2021-12-28 09:28:41 +01:00
Michele Caini
cdf9c2fc6f registry: ctx internal review 2021-12-27 16:18:17 +01:00
Michele Caini
235f84a0d4 registry: minor changes 2021-12-27 15:58:09 +01:00
Michele Caini
ccaa490a2f registry: removed unnecessary dispatching from non-const try_ctx 2021-12-27 15:54:49 +01:00
Michele Caini
8eaf11b510 sparse_set: added the possibility to change the deletion policy at runtime 2021-12-27 15:08:42 +01:00
Michele Caini
57a5faa448 sparse_set: updated doc 2021-12-26 23:31:36 +01:00
Michele Caini
be87191d0a sparse_set:
* opaque emplace returns bool
* opaque insert returnes the number of entities actually emplaced
2021-12-26 17:04:10 +01:00
Michele Caini
40aabe76c7 sparse_set: fixed a throwing case that could leave the set in an inconsistent state 2021-12-26 16:36:30 +01:00
Michele Caini
9d304cb35c doc: fixed typo about in_place_delete 2021-12-23 16:31:07 +01:00
Michele Caini
ee08a4966f doc: updated list of links, added D2R to it too 2021-12-23 14:35:34 +01:00
Michele Caini
aeca69903e doc: updated similar projects 2021-12-22 17:53:45 +01:00
Michele Caini
e1b3f2b95a registry: added weak ::storage for opaque cross registry operations 2021-12-22 16:21:55 +01:00
Michele Caini
25c7436652 doc: minor changes 2021-12-21 14:10:33 +01:00
Michele Caini
771c449621 bump the version number 2021-12-21 13:27:04 +01:00
Michele Caini
61ecaa5756 storage: review to avoid code duplication 2021-12-21 08:33:46 +01:00
Michele Caini
c05dddc56e runtime_view: slightly optimized iterators 2021-12-21 08:33:46 +01:00
Michele Caini
8e0747fd50 updated single include file 2021-12-21 08:32:48 +01:00
Michele Caini
1bcc87c916 runtime_view: removed an useless check, slightly improved performance 2021-12-19 10:25:55 +01:00
Michele Caini
45797907d3 doc: updated toc in the README file 2021-12-18 17:51:02 +01:00
Michele Caini
3498dea486 registry (close #521):
* remove visit
* added storage_proxy_iterator (input iterator category, modeled as a random access iterator)
* added ::storage() (const and non-const) to return an iterable object over all pools
2021-12-17 17:05:14 +01:00
Michele Caini
a4aab8458b group/storage/view: minor changes 2021-12-17 16:57:29 +01:00
Michele Caini
534744d615 iterator: minor changes 2021-12-17 16:57:26 +01:00
Michele Caini
dc5450b95e view: natvis friendly representation 2021-12-17 16:57:24 +01:00
Michele Caini
9e0e276740 dense_hash_set/map: removed unnecessary type members 2021-12-17 16:57:22 +01:00
Michele Caini
1c9c02f3ce view: avoid exposing internal details from iterators 2021-12-17 16:57:20 +01:00
Michele Caini
7774f9f402 storage: review + avoid exposing internal details from iterators 2021-12-17 16:57:18 +01:00
Michele Caini
5607219945 sparse_set: minor changes 2021-12-17 16:57:14 +01:00
Michele Caini
69ef3efd2d dense_hash_map/set: avoid exposing internal details from iterators 2021-12-17 16:57:10 +01:00
Michele Caini
36c118922b group/view:
* added default constructors to input iterators
* make sure that a default constructed iterator compares equal to end()
2021-12-17 08:39:54 +01:00
Michele Caini
1d014953e3 storage: try to also please gcc-9 2021-12-16 14:33:24 +01:00
Michele Caini
c5cb1d9bcc storage/view/group:
* added extended_storage_iterator and storage<T...>::each function to return an extended iterable object
* use extended_storage_iterator and iterable_adaptor everywhere to avoid code duplication
2021-12-16 12:31:44 +01:00
Michele Caini
86d12cf317 doc: brief -> copydoc 2021-12-16 12:30:29 +01:00
Michele Caini
db9e59ede1 core: added iterable_adaptor 2021-12-16 12:29:20 +01:00
Michele Caini
cc9b1b0a05 sparse_set: added const_iterator/const_reverse_iterator and cbegin/cend/crbegin/crend 2021-12-16 08:52:13 +01:00
Michele Caini
5c29de2013 updated TODO 2021-12-15 13:13:16 +01:00
Michele Caini
559db104f8 view: suppress some warnings due to initialization order 2021-12-15 10:24:08 +01:00
Michele Caini
365c57be4c build system: allow compiling the tests with clang-cl (thanks gtest for failing otherwise) 2021-12-15 10:23:34 +01:00
Michele Caini
6f0c84fc68 dense_hash_map/dense_hash_set: remove [[nodiscard]] from friend function declaration 2021-12-15 09:33:23 +01:00
Michele Caini
75782d9e99 registry: review/cleanup 2021-12-14 23:15:57 +01:00
Michele Caini
69ddf4936e registry: remove the visit overload that also takes an entity 2021-12-14 22:57:10 +01:00
Michele Caini
4b3108283f dense_hash_map/dense_hash_set: avoid pointer overflow for end iterators (close #804) 2021-12-14 16:44:34 +01:00
Michele Caini
12d177a6c8 registry: removed ::orphans, use ::each/::orphan instead (no way to further optimize it) 2021-12-13 10:00:32 +01:00
Michele Caini
54717dc7e2 test: minor changes 2021-12-12 17:45:13 +01:00
Michele Caini
631c909abd group: drop conceptually broken reverse iterators 2021-12-12 17:45:06 +01:00
Michele Caini
208746f28f view: get rid of the internal::storage_tuple function, back to the uniform model 2021-12-12 17:05:37 +01:00
Michele Caini
61c5f8ae22 registry/view/group: cleanup 2021-12-12 16:51:48 +01:00
Michele Caini
4e0cca3a08 view: clean up 2021-12-12 01:18:41 +01:00
Michele Caini
b86ffc2370 view: drop conceptually broken reverse iterators 2021-12-12 01:11:20 +01:00
Michele Caini
33574c9885 doc: updated README file 2021-12-10 10:24:08 +01:00
Michele Caini
672c542f99 group: removed ::data, use ::handle<T>().data() and ::storage<T>().data() instead 2021-12-10 09:30:16 +01:00
Michele Caini
f741fe48a1 group: added ::handle to non-owning groups 2021-12-10 08:25:46 +01:00
Michele Caini
4347ebcee2 group: added base_type type member 2021-12-09 17:50:51 +01:00
Michele Caini
4e3e5a5361 doc: updated doc for the ecs module 2021-12-09 17:49:30 +01:00
Michele Caini
8e6439c3d1 group: removed ::raw, use ::storage().raw() instead 2021-12-09 17:49:18 +01:00
Michele Caini
b54d358feb view: removed ::data for single type views, use ::storage().data() instead 2021-12-09 09:37:17 +01:00
Michele Caini
78baa7b12d view: removed ::raw for single type views, use ::storage().raw() instead 2021-12-09 09:21:37 +01:00
Michele Caini
19f1dd22e9 view: minor changes to avoid confusion 2021-12-09 09:12:08 +01:00
Michele Caini
73f583f68a registry: doc cleanup 2021-12-09 09:05:14 +01:00
Michele Caini
52de43e329 registry: removed ::empty<T>(), use ::view<T>().empty() or ::storage<T>().empty() instead 2021-12-09 08:20:34 +01:00
Michele Caini
49ddacaac0 registry: removed ::size<T>(), use ::view<T>().size() or ::storage<T>().size() instead 2021-12-08 15:17:35 +01:00
Michele Caini
4533b3a1c0 registry: minor chages (doc/internals) 2021-12-05 17:01:40 +01:00
Michele Caini
f30ea02794 registry:
* removed registry::reserve<Component> (use registry.storage<Component>.reserve(v) instead)
* removed registry::capacity<Component> (use registry.storage<Component>.capacity() instead)
* removed registry::shrink_to_fit<Component> (use registry.storage<Component>.shrink_to_fit() instead)
2021-12-05 16:20:14 +01:00
Michele Caini
7112dda299 build_system: added all headers to the EnTT target 2021-12-05 16:01:32 +01:00
Michele Caini
02636a7108 view: minor changes 2021-12-05 15:55:40 +01:00
Michele Caini
36a9456e0d test: minor changes to make all major compilers agree at least on something :) 2021-12-04 15:32:38 +01:00
Michele Caini
113893dcea meta: added meta_any::owner 2021-12-04 15:28:52 +01:00
Michele Caini
1abbe9c7b2 test: minor changes (code coverage) 2021-12-04 15:21:49 +01:00
Michele Caini
7205ac791c meta: make policies check function return types 2021-12-03 18:25:11 +01:00
Michele Caini
3335b81ad4 meta:
* dtor support to member functions
* review to introduce strict checks on policies
2021-12-03 17:56:09 +01:00
Michele Caini
d9c9438f3c test: minor changes 2021-12-03 14:39:50 +01:00
Michele Caini
37250843e1 sigh: allocator support 2021-12-02 11:30:54 +01:00
Michele Caini
072ab88721 build system: avoid using windows-2016 (deprecated by GH) 2021-12-01 10:33:43 +01:00
Michele Caini
c208e6b107 natvis: avoid errors due to null/tombstone definition not available in some contexts 2021-11-30 14:44:12 +01:00
Michele Caini
65f3185b39 entity: add page_size to entt_traits to make it more natvis friendly and customizable on a per-type basis 2021-11-30 13:48:12 +01:00
Michele Caini
34f73f8a2b registry: added base_type member type 2021-11-30 11:23:15 +01:00
Michele Caini
cb4142a97d entity: simplified to_version 2021-11-30 09:46:15 +01:00
Michele Caini
80d08d5a34 view:
* fixed init list order to suppress a warning
* try to please gcc that fails to compile valid code
2021-11-30 09:46:14 +01:00
Michele Caini
052aecf533 natvis: basic_view (single and multi type) 2021-11-30 09:46:14 +01:00
Michele Caini
7bde714f18 view: minor changes 2021-11-30 09:46:14 +01:00
Michele Caini
5ff429f656 view:
* added single type view overloads for ::storage to make it compatible with the multi-type view version
* removed the useless tuple from single type views
* updated operator|
2021-11-30 09:46:14 +01:00
Michele Caini
92f27299e1 group: added the ::storage method (with tests) 2021-11-30 09:46:14 +01:00
Michele Caini
f9013375f8 view: added the ::storage method (with tests) 2021-11-30 09:46:14 +01:00
Michele Caini
f18b0045ba registry: get around an issue of the toolset v141 2021-11-30 09:46:14 +01:00
Michele Caini
18173cc4c0 view: minor changes 2021-11-30 09:46:12 +01:00
Michele Caini
6a1ffdd7a5 natvis: bypass some natvis idiosyncrasies to display storage information and content in all cases 2021-11-30 09:45:37 +01:00
Michele Caini
8db78b2190 nativs:
* don't abuse Intrinsic, it also has drawbacks
* refine natvis snippet for dispatcher class
2021-11-30 09:45:37 +01:00
Michele Caini
3fcccae241 component traits:
* added page_size to tune the page size on a per-type basis
* turned in_place_delete into a boolean value
* turned ignore_if_empty into a boolean value
* removed in_place_delete_v (no longer required)
2021-11-30 09:45:37 +01:00
Michele Caini
6ff8add164 entity: added missing includes 2021-11-30 09:45:37 +01:00
Michele Caini
58a8c3c82a natvis: make the ecs stuff work with entity classes 2021-11-30 09:45:37 +01:00
Michele Caini
92c5c656c7 natvis: dispatcher 2021-11-30 09:45:37 +01:00
Michele Caini
61856cd648 natvis: poly 2021-11-30 09:45:37 +01:00
Michele Caini
687f82b5c2 natvis: basic_registry 2021-11-30 09:45:37 +01:00
Michele Caini
920c864bed natvis: tombstone/null 2021-11-30 09:45:37 +01:00
Michele Caini
1360c985b7 natvis: when you discover the intrinsic tag... :) 2021-11-30 09:45:37 +01:00
Michele Caini
b6f953e13b natvis: added simple views + minor changes 2021-11-30 09:45:37 +01:00
Michele Caini
796b96071b registry: minor changes 2021-11-30 09:45:37 +01:00
Michele Caini
ec56ccc690 view: minor changes 2021-11-30 09:45:35 +01:00
Michele Caini
95931b67e1 registry: turn storage_type in a public member type 2021-11-30 09:43:48 +01:00
Michele Caini
706bbb83c9 storage: clean up 2021-11-30 09:43:48 +01:00
Michele Caini
43099f47d0 meta: try to suppress a wrong warning from clang 2021-11-30 09:43:48 +01:00
Michele Caini
eca86999c3 doc: added a section about runtime stuff 2021-11-30 09:43:48 +01:00
Michele Caini
a00b44a5fd example: stamp is back 2021-11-30 09:43:48 +01:00
Michele Caini
5b8dcff2a4 helper: make to_entity also work for stable types (close #768) 2021-11-30 09:43:48 +01:00
Michele Caini
e4d36e4982 registry: decouple ::storage/::assure 2021-11-30 09:43:48 +01:00
Michele Caini
adfdbf138d registry:
* public and runtime pools
* removed basic_registry<...>::prepare
2021-11-30 09:43:48 +01:00
Michele Caini
8d1ba26492 registry: try to also please vs2015 ¯\_(ツ)_/¯ 2021-11-30 09:43:48 +01:00
Michele Caini
792625f763 registry: minor changes/cleanup 2021-11-30 09:43:47 +01:00
Michele Caini
5e8eae4ef8 natvis: delegate, sigh, sink, connection, scoped_connection 2021-11-30 09:43:47 +01:00
Michele Caini
148212da96 natvis: minor changes 2021-11-30 09:43:47 +01:00
Michele Caini
23ddb12529 registry: removed pdata container, pools are standalone objects and thus ready to go public 2021-11-30 09:43:47 +01:00
Michele Caini
186a5e8f7b natvis: updated definition for sparse set and storage 2021-11-30 09:43:47 +01:00
Michele Caini
dfb5fe1e04 sparse_set/storage: opaque get from base 2021-11-30 09:43:47 +01:00
Michele Caini
640f75373e doc: minor changes 2021-11-30 09:43:47 +01:00
Michele Caini
46e5d96ced sparse_set/storage: value type from base, if any 2021-11-30 09:43:47 +01:00
Michele Caini
ad3fbe2052 sparse_set/storage: no virtual swap (the silliest thing I've ever done) 2021-11-30 09:43:47 +01:00
Michele Caini
cd6c60fd46 sparse_set/storage: emplace-from-below with value 2021-11-30 09:43:47 +01:00
Michele Caini
f7be386d5a sparse_set: rename context to bind 2021-11-30 09:43:47 +01:00
Michele Caini
89b2131052 doc:
* const thread safe registry
* detached views
2021-11-30 09:43:47 +01:00
Michele Caini
e5d1ca2e3c registry:
* const thread safe implementation
* views created from const registires can refer placeholder pools (that is, they don't refresh)
2021-11-30 09:43:47 +01:00
Michele Caini
32b8224afd runtime_view: simplify the construction process 2021-11-30 09:43:47 +01:00
Michele Caini
00e7ab1db8 entity: fully remove the poly storage support 2021-11-30 09:43:47 +01:00
Michele Caini
e376a53b09 entity:
* custom move ctor/op for the registry class
* no registry argument from storage emplace, remove, ...
* added sparse set context to forward variables to mixins
* removed sparse set user data
2021-11-30 09:43:47 +01:00
Michele Caini
17a9cd5666 cache: use identity as hashing function 2021-11-30 09:43:47 +01:00
Michele Caini
fc3de662c3 registry: combine dense hash maps and identity when the key is a hashed string already 2021-11-30 09:43:47 +01:00
Michele Caini
8de568c2fc *: combine dense hash map and identity when the key is a hashed string already 2021-11-30 09:43:47 +01:00
Michele Caini
55661883d9 registry: use a dense hash map for context variables 2021-11-30 09:43:47 +01:00
Michele Caini
b59d3ebeaa *: dense_hash_map being a drop-in replacement for unordered_map has its advantages :) 2021-11-30 09:43:47 +01:00
Michele Caini
2866dcdd03 doc: updated documentation about making EnTT work smoothly across boundaries 2021-11-30 09:43:47 +01:00
Michele Caini
b147f9d58c registry: make it run smoothly across boundaries (close #719, close #729) 2021-11-30 09:43:47 +01:00
Michele Caini
8e2a6470ac emitter: make it run smoothly across boundaries 2021-11-30 09:43:47 +01:00
Michele Caini
19230f7672 dispatcher: make it run smoothly across boundaries 2021-11-30 09:43:47 +01:00
Michele Caini
764dfbe46a doc: minor changes 2021-11-30 09:43:47 +01:00
Michele Caini
0ec7631310 view: make all unoptimized single type views check for tombstones 2021-11-30 08:58:51 +01:00
Michele Caini
0b39ece990 meta: fix an issue with meta_factory<T>::prop when using tuple of properties 2021-11-18 15:44:12 +01:00
Michele Caini
97500dff57 meta: fixed an issue with meta_factory::conv when used with member functions + code coverage 2021-11-18 14:53:17 +01:00
Michele Caini
dc6299a029 doc: fixed typos 2021-11-18 09:00:52 +01:00
Michele Caini
58b1e2fb98 hashed string: warnings to clarify that it does not take ownerships of the string or copy it (close #798) 2021-11-16 18:05:17 +01:00
Michele Caini
0302fdaaba build system: update gtest upstream branch name 2021-11-16 09:33:32 +01:00
Michele Caini
09b260e0af view: use explicit types to also please clang 6.x 2021-11-15 14:43:20 +01:00
Michele Caini
24b7e998b2 registry: minor changes 2021-11-13 18:30:35 +01:00
Michele Caini
19068ea5c7 doc: updated links 2021-11-13 12:19:51 +01:00
Michele Caini
b5cd3eb468 natvis: hashed_string + minor changes 2021-11-11 14:40:20 +01:00
Michele Caini
91a2b5a5f7 natvis: expand meta_any as a meta_type_node 2021-11-11 11:16:51 +01:00
Michele Caini
0096f6d962 natvis: meta 2021-11-11 10:04:45 +01:00
Michele Caini
87cfe41b15 natvis: minor changes 2021-11-11 10:04:21 +01:00
Michele Caini
c490086097 license: just make it clear that this is about EnTT :) 2021-11-10 11:02:32 +01:00
Michele Caini
5b3fd7537e meta: avoid a (maybe) UB in meta_any 2021-11-08 15:16:22 +01:00
Michele Caini
2cab2583c0 any: minor changes 2021-11-08 13:41:16 +01:00
Michele Caini
a40a3b594d storage: make try_emplace forward user data to base classes 2021-11-06 23:35:25 +01:00
Michele Caini
0fa586a3d8 type_id: reduce the number of static variables due to type_id 2021-11-06 23:31:57 +01:00
Michele Caini
25977cc569 test: use deduction guides :) 2021-11-05 14:17:33 +01:00
Michele Caini
0be7064493 view:
* support for multiple components of the same type
* removed view.each<T>(F), use view.use<T>().each(F) instead
* removed view.each<T>(), use view.use<T>().each() instead
* added use<integral value>() to set leading pool by index
* added get<integral value...>() to get components by index
2021-11-05 11:02:15 +01:00
Michele Caini
0b13dc254a registry: minor changes 2021-11-05 10:58:30 +01:00
Michele Caini
c1e953ab6e registry: removed redundant compile-time check 2021-11-04 18:45:55 +01:00
Michele Caini
a069764af1 type traits:
* unpack_as_t -> unpack_as_type
* unpack_as_v -> unpack_as_value
2021-11-04 14:17:21 +01:00
Michele Caini
3dbe6c3902 meta: cleanup/review 2021-11-04 12:37:20 +01:00
Michele Caini
9f0b380024 container:
* review dense_hash_map_iterator::operator[]
* review dense_hash_set_iterator::operator[]
* code coverage
2021-11-02 09:29:10 +01:00
Michele Caini
193b102013 entity:
* review sparse_set_iterator::operator[]
* review storage_iterator::operator[]
* code coverage
2021-11-02 09:19:57 +01:00
Michele Caini
511fde91fa natvis: snippet for dense_hash_set 2021-10-30 13:49:11 +02:00
Michele Caini
16e3cfc589 container: added dense_hash_set 2021-10-30 13:45:02 +02:00
Michele Caini
ed2012a36c dense_hash_map: minor changes + test coverage 2021-10-30 13:44:00 +02:00
Michele Caini
46598fde32 dense_hash_map: less risky but still on the edge :) 2021-10-29 12:32:09 +02:00
Michele Caini
94d0c5fa5c compressed_pair: added first_type/second_type member types 2021-10-28 12:18:59 +02:00
Michele Caini
4e72291588 meta: define meta_associative_container_traits for dense_hash_map 2021-10-28 08:22:20 +02:00
Michele Caini
74cdf000b4 doc: fixed a couple of typos 2021-10-26 15:44:05 +02:00
Michele Caini
565dc3327b natvis: snippet for meta_any 2021-10-26 15:01:04 +02:00
Michele Caini
462af21793 entity: avoid shadow warnings 2021-10-26 14:19:55 +02:00
Michele Caini
94af659c86 natvis:
* minor changes to entity.natvis
* snippet for dense_hash_map
2021-10-26 11:04:19 +02:00
Michele Caini
b63c9fc67b dense_hash_map: always guarantee that load factor is less than max load factor 2021-10-26 11:04:19 +02:00
Michele Caini
f519b55a47 natvis: compressed_pair 2021-10-26 11:04:19 +02:00
Michele Caini
2fb039b429 entity: minor changes 2021-10-26 11:04:19 +02:00
Michele Caini
4bce5aed77 natvis: snippets for type_info and basic_any 2021-10-26 11:04:19 +02:00
Michele Caini
eca6032306 entity:
* small changes to the entt_traits class
* natvis snippets for basic_sparse_set and basic_storage (with tombstone detection)
2021-10-26 11:04:19 +02:00
Michele Caini
dd2f515af1 build_system: avoid using INTERNAL for cmake variables since it also implies STRING 2021-10-26 11:04:19 +02:00
Michele Caini
df25482643 build_system: support for natvis files (optionally added to the target) 2021-10-26 11:04:19 +02:00
Michele Caini
df016b3bf9 natvis: prepare support for natvis info 2021-10-26 11:04:19 +02:00
Michele Caini
8699e96609 build_system:
* ENTT_USE_LIBCPP default is OFF
* added ENTT_INCLUDE_HEADERS to make adding headers to the EnTT target optional
* slightly better message handling
2021-10-26 11:04:19 +02:00
Michele Caini
b0dcaaf744 build_system: refine ENTT_USE_SANITIZER usage 2021-10-26 11:04:19 +02:00
Michele Caini
7db001995c build system: make EnTT files show up in IDEs automatically (close #776) 2021-10-26 11:04:19 +02:00
Michele Caini
c88adf9314 meta: avoid unnecessary moves (see #794) 2021-10-26 11:04:19 +02:00
Michele Caini
b13713ce98 meta: support transparently base members when attached to meta types for derived classes 2021-10-26 11:03:29 +02:00
Michele Caini
66b89d170f type_traits: tuple-like type support for is_equality_comparable[_v] 2021-10-22 16:50:00 +02:00
Michele Caini
4311e2e686 type_traits: make is_iterator also support void * 2021-10-22 10:25:02 +02:00
Michele Caini
fd566eff7a code coverage: move to codecov action v2 2021-10-21 11:00:42 +02:00
Michele Caini
efaa3f9e55 *: revert unnecessary changes ¯\_(ツ)_/¯ 2021-10-20 12:18:18 +02:00
Michele Caini
b625ff0902 dense_hash_map: iterator review 2021-10-20 11:44:48 +02:00
Michele Caini
fee80321ef meta_[sequence|associative]_container: iterator review 2021-10-20 10:08:49 +02:00
Michele Caini
4b9331f086 sparse_set: iterator review 2021-10-20 09:41:27 +02:00
Michele Caini
df9316b896 runtime_view: iterator review 2021-10-20 09:32:55 +02:00
Michele Caini
dae5f86d4e storage: iterator review 2021-10-20 09:10:29 +02:00
Michele Caini
75a0cefcb3 meta_range: iterator review 2021-10-20 09:06:54 +02:00
Michele Caini
2271aa5b89 registry: make all_of, any_of and a few other functions remove const qualifiers 2021-10-19 08:45:58 +02:00
Michele Caini
b668e2a967 meta: removed unused reference qualifiers 2021-10-18 16:27:49 +02:00
Michele Caini
ce44c59547 meta: suppress warnings due to unreachable code 2021-10-18 11:27:22 +02:00
Michele Caini
84802d931d clean up todo list 2021-10-12 16:56:12 +02:00
Michele Caini
8183c4383e meta: suppress a shadow warning 2021-10-12 16:55:57 +02:00
Michele Caini
af249098cd container: sparse set based dense hash map 2021-10-12 16:50:28 +02:00
Michele Caini
8fdd063f00 entity: include fwd.hpp from entity.hpp (close #790) 2021-10-12 15:14:36 +02:00
Michele Caini
fe328f6a75 identity: add is_transparent member type 2021-10-10 16:31:45 +02:00
Michele Caini
ef7163acd6 storage: non-const to const iterator conversion guaranteed 2021-10-10 10:58:58 +02:00
Michele Caini
6e45f0d5b8 meta_range: non-const to const iterator conversion guaranteed 2021-10-10 10:46:34 +02:00
Michele Caini
ff41faa3fe memory: all of a sudden I remembered what greater than or equal to means :) 2021-10-10 00:42:25 +02:00
Michele Caini
acb2d332ea type_traits: added is_transparent[_v] utility 2021-10-09 19:19:18 +02:00
Michele Caini
0fb86f21e4 type_traits: add tests for is_ebco_eligible_v 2021-10-09 19:03:28 +02:00
Michele Caini
e4991d367e memory: fast_mod no longer requires a compile-time modulus (but it's still a constexpr function) 2021-10-08 20:24:07 +02:00
Michele Caini
3144cfafd5 memory: added constexpr function next_power_of_two 2021-10-07 16:53:22 +02:00
Michele Caini
c538067544 *: cleanup, minor changes 2021-10-06 11:52:32 +02:00
Michele Caini
f8e137a1dc test: suppress warnings due to missing virtual destructor 2021-10-05 08:40:07 +02:00
Michele Caini
36ea38fd91 resource class vs struct definition 2021-10-05 08:39:54 +02:00
Michele Caini
baae5a7215 test: code coverage 2021-10-05 08:38:06 +02:00
Michele Caini
a4b267227e meta: split meta_invoke_with_args function 2021-10-05 08:37:35 +02:00
Michele Caini
58dd159699 meta: add and typename keywords for fun (thanks to msvc that also accepts invalid code) 2021-10-05 08:14:03 +02:00
Michele Caini
80082f9d51 meta: static functions that require the parent type as first argument are treated as (eventually const) member functions 2021-10-04 18:20:16 +02:00
Michele Caini
7975ffc10b meta: minor changes 2021-10-04 16:36:50 +02:00
Michele Caini
012083a4a1 meta: review utilities + code coverage 2021-10-04 16:21:55 +02:00
Michele Caini
3c67723e83 doc: minor changes 2021-10-04 11:52:22 +02:00
Michele Caini
7f0598b3cb meta: cleanup headers 2021-10-04 09:01:13 +02:00
Michele Caini
c5b27e0ae0 test: minor changes (code coverage) 2021-10-03 16:58:58 +02:00
Michele Caini
4e7ad31f18 resource: dynamic resource handle cast example (close #786) 2021-10-03 00:26:46 +02:00
Michele Caini
c7a3e9d4ac resource: cache const correctness review 2021-10-03 00:01:59 +02:00
Michele Caini
a05e7a84c4 resource: handle as value type, const correctness review 2021-10-02 23:52:22 +02:00
Michele Caini
389cb85410 resource: strict check on resource type for cache 2021-10-02 23:35:40 +02:00
Michele Caini
81ae2bed25 resource: added resource_handle::resource_type 2021-10-02 16:26:44 +02:00
Michele Caini
0ab71bc2e7 view: return a const reference to the leading storage 2021-10-01 16:26:11 +02:00
Michele Caini
81e6bbe643 test: minor changes (thanks msvc for accepting invalid code) 2021-10-01 11:05:21 +02:00
Michele Caini
c4723f3b24 view/group: added missing operator-> here and there 2021-10-01 10:57:32 +02:00
Michele Caini
df4d8e0411 meta: make meta_iterator::operator-> const as it ought to be 2021-10-01 10:57:16 +02:00
Michele Caini
4f1820bcad iterator: review of input iterator pointer type 2021-10-01 10:24:05 +02:00
Michele Caini
e375097e43 doc: minor changes 2021-10-01 09:46:41 +02:00
Michele Caini
c51bec0034 meta: add meta_range::operator-> 2021-09-30 17:34:16 +02:00
Michele Caini
081c3fdb5b iterator: added [[nodiscard]] to input_iterator_proxy::operator-> 2021-09-30 16:57:20 +02:00
Michele Caini
f1e7602775 meta: added operator-> to meta containers' iterators 2021-09-30 16:56:58 +02:00
Michele Caini
acd75ffe1c iterator: added utility input_iterator_proxy 2021-09-30 16:34:09 +02:00
Michele Caini
fc813cb59b doc: minor changes 2021-09-30 14:41:33 +02:00
Michele Caini
044d14f542 *: minor changes 2021-09-29 16:12:39 +02:00
Michele Caini
28b9f07b99 *: code coverage, cleanup 2021-09-29 11:27:13 +02:00
Michele Caini
6f20dd4e45 test: suppress a wrong warning with toolset v141 2021-09-29 11:09:37 +02:00
Michele Caini
2fc05e5b90 meta: add missing template keywords (thanks MSVC for accepting invalid code) 2021-09-29 11:03:08 +02:00
Michele Caini
9afde31f97 meta: make meta_any::assign work with mutating this pointers 2021-09-29 10:57:31 +02:00
Michele Caini
e835ce0697 meta: make meta_any::allow_cast work with mutating this pointers 2021-09-29 09:49:22 +02:00
Michele Caini
6030f56c76 meta: make meta_any::try_cast work with mutating this pointers 2021-09-28 18:50:03 +02:00
Michele Caini
ba89b53c63 doc: cleanup 2021-09-28 15:11:29 +02:00
Michele Caini
1bfb877a3b entity: adds to_entity and to_version to the entt namespace (see #788) 2021-09-28 14:52:52 +02:00
Michele Caini
72776d6fb6 entity: make return type of to_integral explicit 2021-09-28 14:31:09 +02:00
Michele Caini
7bb93420c4 storage: avoid reinventing the wheel :) 2021-09-27 08:55:50 +02:00
Michele Caini
bb1b0e0581 sparse_set: minor changes 2021-09-26 23:28:32 +02:00
Michele Caini
45ddcf0df9 sparse_set: compare allocators before moving in the move operator assignment 2021-09-26 11:08:55 +02:00
Michele Caini
9c4d7a286f memory: avoid warnings due to unused parameters 2021-09-26 11:06:36 +02:00
Michele Caini
7b290b43b5 sparse_set: try not to reinvent the wheel :) 2021-09-26 10:59:00 +02:00
Michele Caini
2e2782b030 sparse_set: partial sorting not supported in case of tombstones 2021-09-25 18:29:45 +02:00
Michele Caini
48f23bd1ba doc: cleanup 2021-09-24 16:22:42 +02:00
Michele Caini
3ece33b26a any: avoid using addressof on the result of an operator assignment without a good reason :) 2021-09-24 13:29:16 +02:00
Michele Caini
efa98379b0 meta: internal changes and cleanup 2021-09-24 12:49:43 +02:00
Michele Caini
324aeba70f meta: suppress a warning due to -Wignored-qualifiers (close #785) 2021-09-24 11:50:51 +02:00
Michele Caini
7b6715c7b5 test: code coverage for the storage class for empty types 2021-09-24 11:18:11 +02:00
Michele Caini
1ea392b6c9 test: minor changes to suppress a bunch of warnings + formatting 2021-09-24 10:19:13 +02:00
Michele Caini
8b1a3849e6 meta_any: support assigning the value wrapped by a meta_any 2021-09-24 10:08:59 +02:00
Michele Caini
e1d7e98c92 any: minor changes 2021-09-23 10:36:30 +02:00
Michele Caini
04b2e1152c any:
* when moving fails, try to copy anyway as a fallback
* code coverage
2021-09-22 17:13:02 +02:00
Michele Caini
d392eba4a0 test: code coverage 2021-09-22 16:49:42 +02:00
Michele Caini
799dec4f1c sparse_set: suppress a warning due to -Wreorder 2021-09-22 16:41:40 +02:00
Michele Caini
11df6ec927 type_id: cannot have a static variable in a constexpr function (right msvc?) 2021-09-22 16:38:41 +02:00
Michele Caini
03e363f5d9 any: added the ::assign function to copy/move assign the wrapped variable 2021-09-22 16:27:56 +02:00
Michele Caini
39211889b0 any (close #782):
* make const type_info * a first citizen
* further reduce vtable size
2021-09-22 12:48:25 +02:00
Michele Caini
db44549276 type_info/type_id:
* type_info is no longer default constructible
* users cannot construct type_info objects anymore
* make type_id refer to an object with static storage duration
2021-09-22 12:41:09 +02:00
Michele Caini
49e702c0c7 hashed_string: minor changes 2021-09-22 08:34:31 +02:00
Michele Caini
c9c5214b3e type_info: operator<, operator<=, operator> and operator>= 2021-09-22 08:34:18 +02:00
Michele Caini
24a537d154 meta: review conversion_helper for meta type nodes 2021-09-21 17:11:28 +02:00
Michele Caini
c32ecf171b sparse_set: added user_data field 2021-09-21 15:54:26 +02:00
Michele Caini
a62ae21628 any: const reference instead of pointer as default argument to ::data 2021-09-21 09:03:39 +02:00
Michele Caini
12f05e8f72 doc: fixed typo (close #784) 2021-09-20 08:07:05 +02:00
Michele Caini
81fb727d20 registry: improved ctx and the like (close #782) 2021-09-19 15:14:43 +02:00
Michele Caini
e7655c761d test: code coverage 2021-09-18 23:22:00 +02:00
Michele Caini
464fcf4300 test: fix a test that I've broken a couple of commits ago :) 2021-09-18 00:06:50 +02:00
Michele Caini
73b67ea9df *: review enums 2021-09-18 00:02:35 +02:00
Michele Caini
04a9c80c14 *: removed pointless continue; 2021-09-17 23:44:15 +02:00
Michele Caini
80b5ed3d22 introduced clang-format (close #733) 2021-09-17 23:42:43 +02:00
Michele Caini
be5ad379b6 *: another handful of changes to please clang-format 2021-09-17 17:21:26 +02:00
Michele Caini
13d901fbfa *: a handful of changes to please clang-format 2021-09-17 16:43:15 +02:00
Michele Caini
f7e7273e91 *: minor changes 2021-09-16 23:09:22 +02:00
Michele Caini
4b3bd265a6 meta: minor changes 2021-09-15 12:43:31 +02:00
Michele Caini
42dcec961b meta: support for multi-setters on meta data members 2021-09-15 09:32:12 +02:00
Michele Caini
6b2a3b2916 meta: prepare for multi-setters on meta data members 2021-09-14 14:57:17 +02:00
Michele Caini
930f9ca436 any: minor changes 2021-09-14 08:52:44 +02:00
Michele Caini
a96a767b9b test: code coverage 2021-09-13 19:32:23 +02:00
Michele Caini
36627dbe78 meta:
* removed meta_ctor, no alternatives provided
* removed meta_type::ctor overloads, no alternatives provided
2021-09-13 19:03:22 +02:00
Michele Caini
f86bc31cbb meta: shared lookup function to avoid unnecessary casts when searching for a meta constructor 2021-09-13 16:37:58 +02:00
Michele Caini
4a08ca4b5e meta: minor changes 2021-09-13 14:58:08 +02:00
Michele Caini
e857aa0640 meta: extrapolate can_cast_or_convert 2021-09-13 12:57:06 +02:00
Michele Caini
d3c98ad334 meta_factory: cleanup 2021-09-13 12:56:39 +02:00
Michele Caini
00dadfe14d meta: reduce the size of instantiated symbols in meta_factory 2021-09-13 12:55:07 +02:00
Michele Caini
f26fa92d47 handle: extended operator==/!= 2021-09-12 18:39:20 +02:00
Michele Caini
7458072df2 hashed_string (close #781):
* operator< and operator<=
* operator> and operator>=
2021-09-12 18:26:08 +02:00
Michele Caini
89aa2a1902 *: operator== is not defined as member if unnecessary 2021-09-12 17:52:29 +02:00
Michele Caini
7ab08779dc type_info: temporarily reintroduce type_info::index for performance reasons 2021-09-10 11:29:05 +02:00
Michele Caini
46b686910b any:
* reintroduced operation::TYPE to avoid fat any types
* added optional req parameter to data
* much faster any_cast
* much faster operator==
* internal review
2021-09-10 11:22:07 +02:00
Michele Caini
940a799207 * : minor changes 2021-09-10 00:04:23 +02:00
Michele Caini
778cee3b27 any:
* operator bool doesn't rely on the vtable
* valid but unspecified state after move converts to true
* compare type info objects to avoid future errors if the logic changes
2021-09-09 17:03:36 +02:00
Michele Caini
fb3252732b type_info: name rollback, hash_code -> hash (why should I introduce a breaking change to match a terrible name from the standard library after all?) 2021-09-09 14:49:39 +02:00
Michele Caini
608f7b75e2 any: avoid creating constexpr variables since constexpr-ness isn't guaranteed 2021-09-09 14:14:58 +02:00
Michele Caini
83853c3dee any:
* ::type() returns the object type if any, type_id<void>() otherwise
* avoid indirection when accessing the type
2021-09-09 14:00:08 +02:00
Michele Caini
8a0343ad6b any: avoid creating type_info objects if unnecessary 2021-09-09 11:44:36 +02:00
Michele Caini
92d43ce620 core: rename type_seq to type_index 2021-09-09 09:53:29 +02:00
Michele Caini
6943822fed type_info:
* make the API closer to that of std::type_info
* remove the ::seq member function
* rename :.hash to ::hash_code
2021-09-09 09:44:58 +02:00
Michele Caini
aa146c4125 type_info: avoid testing constexpr-ness because older compilers don't guarantee it 2021-09-09 09:04:04 +02:00
Michele Caini
b8ec6babb5 any:
* reduced vtable size
* internal rework
2021-09-09 00:40:06 +02:00
Michele Caini
505f4a2b4f type_info:
* constexpr support
* reduce instantiations
* reduce function calls/indirection
2021-09-09 00:20:20 +02:00
Michele Caini
1bb0d017c3 type_info: removed useless friend declaration 2021-09-08 23:43:28 +02:00
Michele Caini
f33ce615d9 meta: suppress warnings due to ignored qualifiers on return types 2021-09-08 11:35:50 +02:00
Michele Caini
0edb49d7c6 core: minor changes 2021-09-08 09:13:54 +02:00
Michele Caini
1fa5a03605 test: code coverage 2021-09-07 17:13:53 +02:00
Michele Caini
fc6bda5cd8 meta: turned an useless function into a constexpr value 2021-09-07 08:54:34 +02:00
Michele Caini
59589b6a71 meta: refine the conversion helper (mostly to please the toolset v141) 2021-09-06 14:51:35 +02:00
Michele Caini
57b0624a0a storage: suppress warnings for unused variables 2021-09-06 12:29:52 +02:00
Michele Caini
2f22395eea meta:
* a more robust meta_any::allow_cast (reviewed all overloads)
* removed internal meta_info dispatcher to reduce useless instantiations
* reviewed meta_conversion_helper
2021-09-06 10:19:23 +02:00
Michele Caini
21bc8d4dfb meta: add meta_conv support to references 2021-09-04 11:43:47 +02:00
Michele Caini
2e74fd8196 meta: const correctness 2021-09-04 11:42:03 +02:00
Michele Caini
fee48ab04c meta: also please toolset v141 :) 2021-09-03 18:55:51 +02:00
Michele Caini
591f885e67 meta: automatic conversion function to underlying type for enums (see #735) 2021-09-03 18:23:49 +02:00
Michele Caini
ef803016c0 meta: suppress a shadow warning 2021-09-03 17:31:15 +02:00
Michele Caini
bac51045e5 meta: automatic arithmetic conversions (see #735) 2021-09-03 14:37:52 +02:00
Michele Caini
cb5e9393a4 meta: internal changes 2021-09-03 11:42:20 +02:00
Michele Caini
097509dd2a view: add operator[] to access (eventually unwrapped) components by entity (close #775) 2021-09-02 16:05:46 +02:00
Michele Caini
6cd44248c7 core: added tuple header and unwrap_tuple utility 2021-09-02 15:32:52 +02:00
Michele Caini
1fd61fbcef meta: added missing [[maybe_unused]] to suppress warnings 2021-09-01 10:06:28 +02:00
Michele Caini
cd2245ef03 doc: fixed typo 2021-09-01 10:03:16 +02:00
Michele Caini
5ddc746915 memory: turn is_power_of_two in a constexpr function 2021-08-31 17:39:47 +02:00
Michele Caini
f7d67067ad test: increase code coverage 2021-08-30 17:06:31 +02:00
Michele Caini
d1a824f76e sparse_set/storage: cleanup 2021-08-30 16:53:20 +02:00
Michele Caini
d7b0fc09d7 storage: refine ::release_memory 2021-08-30 16:36:45 +02:00
Michele Caini
2d07a6ec1f storage:
* decouple iterator from allocator
* test iterator aliases to avoid future regressions
* internal changes/rework
2021-08-30 16:18:28 +02:00
Michele Caini
014ba5a7aa sparse_set:
* test iterator aliases to avoid future regressions
* internal changes/rework
2021-08-30 16:09:59 +02:00
Michele Caini
2f12e524dd config/sparse_set/storage: avoid power-of-two check at config level 2021-08-29 23:27:04 +02:00
Michele Caini
ea6af75e8e memory: added utility function fast_mod 2021-08-29 23:04:40 +02:00
Michele Caini
a595c6ec6f memory:
* added utility class is_power_of_two
* added variable template is_power_of_two_v
2021-08-29 22:15:33 +02:00
Michele Caini
98f785d199 memory: make to_address [[nodiscard]] 2021-08-29 22:00:05 +02:00
Michele Caini
a0ed0c864f entt_traits: removed the (mostly useless) difference_type alias 2021-08-29 19:25:23 +02:00
Michele Caini
d4ac42749e storage: minor changes 2021-08-29 19:10:18 +02:00
Michele Caini
86414671d6 sparse_set: minor changes 2021-08-29 19:09:21 +02:00
Michele Caini
6cb935982d storage: removed a couple of old (and wrong) static asserts 2021-08-29 18:43:35 +02:00
Michele Caini
39ad040d8a sparse_set: decouple iterator from allocator 2021-08-29 18:41:59 +02:00
Michele Caini
4cc3824653 test: basic_registry<...>::get_or_emplace must work for empty types 2021-08-28 09:52:46 +02:00
Michele Caini
6d3e0600a1 storage:
* increase code coverage
* remove a pretty much useless if constexpr
2021-08-28 09:52:19 +02:00
Michele Caini
b52c9c1676 test: use storage (and sigh storage mixin) from base type 2021-08-27 16:47:08 +02:00
Michele Caini
f7506251fe build system: increase ctest timeout because ASSERT_DEATH is... expensive, at least :) 2021-08-27 11:03:35 +02:00
Michele Caini
5b06f298f1 sparse_set/storage: make storage safe to use from the base (to be tested) 2021-08-26 17:52:16 +02:00
Michele Caini
dc5488a198 storage: generalized ::insert for all mixins 2021-08-26 17:14:51 +02:00
Michele Caini
23d162e9e4 sparse_set: ::emplace no longer returns the position 2021-08-26 16:13:26 +02:00
Michele Caini
de6b660b12 sparse_set/storage:
* removed basic_sparse_set<...>::emplace_back
* general cleanup
2021-08-26 15:22:29 +02:00
Michele Caini
60c175a51d storage: ::insert always fills holes before appending 2021-08-26 15:09:30 +02:00
Michele Caini
c0a39c8064 sparse_set: insert always fills holes before appending 2021-08-26 14:56:15 +02:00
Michele Caini
61f4f93cc6 sparse_set: internals review 2021-08-26 14:55:47 +02:00
Michele Caini
764404a472 doc: minor changes, updated todo list 2021-08-26 12:19:43 +02:00
Michele Caini
04da85c88a storage: avoid invoking ::page more than once in a row 2021-08-26 12:11:54 +02:00
Michele Caini
9551b7a597 storage: added empty ::get method to storage for empty types 2021-08-25 17:05:58 +02:00
Michele Caini
f1d537d035 compressed_pair:
* disable structured binding support for clang 6
* avoid using structured binding with compressed pair in the codebase
2021-08-24 13:11:21 +02:00
Michele Caini
b43e723c84 meta: reintroduce meta_type::is_array support 2021-08-24 10:13:56 +02:00
Michele Caini
5466039c1a meta: generic begin/end for meta containers 2021-08-24 09:48:59 +02:00
Michele Caini
74e11117be storage: minor changes 2021-08-24 09:00:55 +02:00
Michele Caini
e0abab2dc4 storage:
* fixed typo in the doc
* get around msvc that also accepts invalid code (again) O.o
2021-08-23 18:18:58 +02:00
Michele Caini
08ff3ff55c sparse_set: erase assertion 2021-08-23 18:15:23 +02:00
Michele Caini
0b79d99cac storage: non-copyable allocator aware container 2021-08-23 18:13:24 +02:00
Michele Caini
38f655e7b8 *: get around the fact that msvc also accepts invalid code O.o 2021-08-21 17:28:30 +02:00
Michele Caini
6f546ffe95 sparse_set: entity ::swap -> ::swap_elements 2021-08-21 17:25:38 +02:00
Michele Caini
ef8211a46e sparse_set: non-copyable allocator-aware container 2021-08-21 17:18:04 +02:00
Michele Caini
52ea5e4074 memory: added pocca/pocma/pocs utility functions 2021-08-21 17:03:07 +02:00
Michele Caini
99621cf08a memory: unfancy -> to_address (waiting for C++20) 2021-08-20 15:28:46 +02:00
Michele Caini
a9ae9ce758 test: code coverage 2021-08-20 00:32:02 +02:00
Michele Caini
fc5a529df8 core:
* added memory.hpp
* added public unfancy function
2021-08-20 00:26:25 +02:00
Michele Caini
bc1081550d storage: minor changes 2021-08-20 00:10:09 +02:00
Michele Caini
e311ab1605 test: try to also please g++-7 2021-08-19 23:32:51 +02:00
Michele Caini
994ade0638 meta_factory: suppress shadow warning 2021-08-19 23:02:28 +02:00
Michele Caini
cf9522bd3b sparse_set: ::clear honors the modality of the set 2021-08-19 17:36:16 +02:00
Michele Caini
7fd1858db0 storage: minor changes 2021-08-19 16:34:03 +02:00
Michele Caini
c41509151b sparse_set:
* (almost) allocator-aware container
* unequal containers not supported
* copying a sparse set isn't supported
2021-08-19 15:50:29 +02:00
Michele Caini
4e12853d4a runtime_view: correctly set the no_tombstone_check member 2021-08-17 11:58:49 +02:00
Michele Caini
e1fadbc9b9 registry: removed ::version/::entity (use entt_traits instead) 2021-08-16 23:30:14 +02:00
Michele Caini
3c4d13d8b9 sparse_set/view:
* added basic_sparse_set<E>::current to return known version of identifiers in set
* sparse arrays also contain updated entity versions, no more unused bits
* basic_sparse_set<E>::contains performs a strict version check
* sparse sets manage correctly foreign entities in all cases now
* no tombstone checks for multi-type views, zero-cost pointer stability model
2021-08-16 17:07:52 +02:00
Michele Caini
732159db07 registry:
* ::current no longer asserts, it returns now tombstone versions for invalid entities
* minor changes to doc and tests
2021-08-16 16:56:11 +02:00
Michele Caini
00894eaccf entity/*: minor changes to doc and tests 2021-08-16 16:52:56 +02:00
Michele Caini
17818178cf entity:
* removed null_t::operator| (use entt_traits<T>::combine instead)
* removed tombstone_t::operator| (use entt_traits<T>::combine instead)
2021-08-14 23:00:02 +02:00
Michele Caini
89c0abfada entity:
* reviewed entt_traits<T>::combine
* added entt_traits::reserved
* use entt_traits<T>::combine where it makes sense to slightly improve perf
2021-08-14 22:26:41 +02:00
Michele Caini
8bbdd92739 entity: added entt_traits<T>::combine 2021-08-14 22:24:43 +02:00
Michele Caini
f724ceb052 entity: cleanup 2021-08-14 16:14:11 +02:00
Michele Caini
b7ac85005f updated TODO 2021-08-13 23:58:57 +02:00
Michele Caini
8ec899a8e7 sigh: make scoped_connection move constructible/assignable (close #760) 2021-08-13 15:35:05 +02:00
Michele Caini
1013c6a50e meta: removed annotation support for meta properties 2021-08-12 19:54:12 +02:00
Michele Caini
2cd5d3bab3 meta: cleanup, minor changes 2021-08-12 19:01:38 +02:00
Michele Caini
d9494960a2 meta:
* removed the possibility of invoking meta_factory::prop multiple times
* further reduced instantiations due to meta properties
2021-08-11 17:11:09 +02:00
Michele Caini
c0eb6590da snapshot: include destroyed list head in the entity count (close #757) 2021-08-11 15:23:46 +02:00
Michele Caini
a9cefcb823 meta: avoid risk of name clashing on meta types and meta data members 2021-08-10 16:07:17 +02:00
Michele Caini
d1ff491010 meta:
* removed meta_type::is_member_object_pointer (no alternative provided)
* removed meta_type::is_member_function_pointer (no alternative provided)
2021-08-10 15:39:26 +02:00
Michele Caini
2aa34f05d3 meta: removed meta_type::is_array (no alternative provided) 2021-08-09 12:47:24 +02:00
Michele Caini
135d6915ee meta, review implicitly generated meta default constructors:
* reduce instantiations and remove their static objects
* these ctors are no longer returned from a direct lookup nor during iterations
2021-08-09 10:07:43 +02:00
Michele Caini
6282195b80 meta:
* removed meta_type::is_integral (no alternative provided)
* removed meta_type::is_floating_point (no alternative provided)
* added meta_type::is_arithmetic, prepare for auto-generated implicit conversions
2021-08-07 20:43:02 +02:00
Michele Caini
adec4f08c6 meta: removed meta_type::is_union (no alternative provided) 2021-08-06 12:07:09 +02:00
Michele Caini
b24fe2f122 test: increase code coverage 2021-08-06 12:06:42 +02:00
Michele Caini
7b35bcb68a meta: use enum-as-bitmask support to get rid of old fashioned enum for meta traits 2021-08-05 10:44:13 +02:00
Michele Caini
4b629045c3 core: added enum-as-bitmask support for enum classes (thanks to @TerensTare for the suggestion) 2021-08-05 10:42:42 +02:00
Michele Caini
c6ba1d05d1 view: avoid polluting global namespace with naked operator| 2021-08-04 16:46:53 +02:00
Michele Caini
3adac90573 meta: removed meta_type::is_function_pointer (no alternative provided) 2021-08-04 16:06:58 +02:00
Michele Caini
0332188b6b meta, get rid of useless info from meta_type to further reduce instantiations:
* removed meta_type::rank (no alternative provided)
* removed meta_type::extent (no alternative provided)
* removed meta_type::rmove_extent (no alternative provided)
* removed meta_type::remove_pointer ((*any).type())
2021-08-03 16:29:19 +02:00
Michele Caini
790a1e2812 view: minor changes to get around an issue of clang 2021-08-02 18:28:48 +02:00
Michele Caini
3bd1e13321 view: removed the foobar alias which was not meant to stay there 2021-08-02 18:24:49 +02:00
Michele Caini
0cd642ca11 view: ::use<T> doesn't modify the view in-place anymore, instead it returns a new view 2021-08-02 18:22:58 +02:00
Michele Caini
d5995998b0 view:
* each<T> doesn't invoke use<T> anymore under the hood
* removed ::traverse function
* minor changes
2021-08-02 18:09:38 +02:00
Michele Caini
62babb9c22 updated TODO 2021-08-01 12:02:43 +02:00
Michele Caini
b98c69521b build_system: add support for c++20 (close #751) 2021-08-01 11:41:31 +02:00
Michele Caini
6cd37b3809 view: get around an issue of msvc when using C++20 2021-08-01 11:21:05 +02:00
Michele Caini
238170d0e2 meta: some changes to make tests compile with C++20 (MSVC) 2021-08-01 11:02:05 +02:00
Michele Caini
6fb5b25530 any: don't compare arrays directly (operator== even deprecated in C++20) 2021-08-01 10:51:48 +02:00
Michele Caini
d63f9e1eff build_system: make ENTT_CXX_STD a cached string variable (not used yet, see #751) 2021-08-01 10:47:14 +02:00
Michele Caini
461865bf3b build_system: make ENTT_ID_TYPE a cached string variable 2021-08-01 10:41:28 +02:00
Michele Caini
e95d9b9cf6 doc: updated links.md 2021-08-01 10:18:21 +02:00
Michele Caini
c52b2a4fa9 entity/*:
* add owned_t utility
* change view def to basic_view<T, get_t<C...>, exclude_t<E...>>
* change group def to basic_group<T, owned_t<O...>, get_t<G...>, exclude_t<E...>>
* remove view dispatcher (reduce instantiations)
* make basic_view::traverse use the local policy
2021-08-01 10:03:45 +02:00
Michele Caini
d0ec5d2da0 dispatcher: mionr changes 2021-08-01 09:58:32 +02:00
Michele Caini
6134058abc type_traits: minor changes (coding style) 2021-08-01 09:58:19 +02:00
Michele Caini
bd4121c260 *: cleanup 2021-07-30 14:27:53 +02:00
Michele Caini
fbab047d7f doc: fixed typo (close #749) 2021-07-30 12:13:13 +02:00
Michele Caini
e216144d07 poly: optimize single function vtables (close #689) 2021-07-29 23:54:10 +02:00
Michele Caini
94d5b11191 any: rvalue any_cast no longer forces by-copy constructor (close #730) 2021-07-29 14:30:16 +02:00
Michele Caini
d3c89da2eb doc: mention vcpkg experimental feature in the readme file (close #743) 2021-07-28 14:52:56 +02:00
Michele Caini
754a8a637e doc: updated list of showcases (close #744) 2021-07-28 14:44:42 +02:00
Michele Caini
ab24a50de7 doc: fixed typo 2021-07-27 18:56:16 +02:00
Michele Caini
8099dde34b view: relax policy constraint if possible 2021-07-27 18:56:14 +02:00
Michele Caini
99198fb07f doc: added a note about the no-policy trick 2021-07-27 18:56:08 +02:00
Michele Caini
18d02a0c95 *: suppress a couple of warnings (shadow/unused) 2021-07-27 18:56:06 +02:00
Michele Caini
e6cac7d5fb group/view: remove references to basic_sparse_set 2021-07-27 18:56:03 +02:00
Michele Caini
d8d6f00fee storage: minor changes + some tests for get/get_as_tuple 2021-07-27 18:56:01 +02:00
Michele Caini
ef5c1a1099 snapshot: minor changes 2021-07-27 18:55:55 +02:00
Michele Caini
2d92d8ee67 storage:
* Removed free function get_as_tuple
* Added get_as_tuple function at storage level
2021-07-27 18:55:52 +02:00
Michele Caini
ae60266477 updated todo list 2021-07-27 18:55:49 +02:00
Michele Caini
88da5261fc component/storage:
* removed ignore_if_empty_v, added ignore_as_empty_v
* removed fake getter requirement for empty storage types
2021-07-27 18:55:46 +02:00
Michele Caini
15efe9f702 sparse_set: make capacity virtual 2021-07-27 18:55:42 +02:00
Michele Caini
badbc5c66e storage: internal review 2021-07-27 18:55:35 +02:00
Michele Caini
d6eea41e13 sparse_set: make reserve virtual 2021-07-27 18:55:31 +02:00
Michele Caini
6a2c54adf5 sparse_set: make shrink_to_fit virtual 2021-07-27 18:55:27 +02:00
Michele Caini
75d4bc7c18 doc: minor changes 2021-07-27 18:55:24 +02:00
Michele Caini
834f7feb27 storage: removed ::sort_n, capture pool and use ::get if needed 2021-07-27 18:55:21 +02:00
Michele Caini
5d2af3f9e1 storage: removed ::sort, capture pool and use ::get if needed 2021-07-27 18:55:19 +02:00
Michele Caini
60e8e4a82a doc: updated list of links (close #723) 2021-07-27 18:54:29 +02:00
Michele Caini
92e23476ab doc: updated list of similar projects (close #731) 2021-07-27 18:54:29 +02:00
Michele Caini
4560fef058 doc: updated list of showcases (close #732) 2021-07-27 18:54:29 +02:00
Michele Caini
c5d6574617 entity: updated fwd decls 2021-07-27 18:54:29 +02:00
Michele Caini
de61a0ca45 view:
* suppress warning for shadow parameter
* adjust deduced type to avoid compile-time errors
2021-07-27 18:54:29 +02:00
Michele Caini
98470caae1 view: suppress warning 2021-07-27 18:54:29 +02:00
Michele Caini
53486c067b view: minor changes 2021-07-27 18:54:29 +02:00
Michele Caini
5b805852fa view: internal iterable_storage class 2021-07-27 18:54:28 +02:00
Michele Caini
94d02eeb4e view: minor changes 2021-07-27 18:54:28 +02:00
Michele Caini
dbfec1b247 view: get rid of filter_to_array, filter is always stored as an array 2021-07-27 18:54:28 +02:00
Michele Caini
a9a1a71145 updated TODO list 2021-07-27 18:54:28 +02:00
Michele Caini
91537bf5f3 sparse_set/storage: try to please the code coverage tool 2021-07-27 18:54:28 +02:00
Michele Caini
1baad08288 storage/sparse_set: ctor review 2021-07-27 18:54:28 +02:00
Michele Caini
1c5b846488 storage: get rid of basic_storage_impl wrapper 2021-07-27 18:54:28 +02:00
Michele Caini
6867760cc4 doc: fixed typo 2021-07-27 18:54:28 +02:00
Michele Caini
516a9209b7 sparse_set/storage: no zero sized allocations on construction 2021-07-27 18:54:28 +02:00
Michele Caini
e352c953dc test: minor changes 2021-07-27 18:54:28 +02:00
Michele Caini
f455846d18 sparse_set/storage: avoid using temporary allocators 2021-07-27 18:54:28 +02:00
Michele Caini
6d45ac942d storage: store aside a compressed allocator to avoid frequent conversions 2021-07-27 18:54:28 +02:00
Michele Caini
48939abafa sparse_set/storage: suppress shadow warnings 2021-07-27 18:54:28 +02:00
Michele Caini
fbc9595f12 sparse_set: used compressed pair to hide the cost of storing allocators 2021-07-27 18:54:28 +02:00
Michele Caini
ed2c714419 compressed_pair: added support for structured binding (with tests) 2021-07-27 18:54:28 +02:00
Michele Caini
ef7731fb8b storage: use underlying allocator 2021-07-27 18:54:28 +02:00
Michele Caini
b5ec03f585 sparse_set:
* added get_allocator
* internal review
2021-07-27 18:54:28 +02:00
Michele Caini
a8b224a364 *: minor explicit names for traits types 2021-07-27 18:54:25 +02:00
Michele Caini
3f3943a233 storage: internal review 2021-07-27 18:53:49 +02:00
Michele Caini
4171b4ae47 resource_handle: add ::use_count (close #727) 2021-07-27 18:53:49 +02:00
Michele Caini
baa4c44632 compressed_pair: minor changes 2021-07-27 18:53:49 +02:00
Hussein Taher
ffed8f37de snapshot: fix warning for discarding a nodiscard (#728) 2021-07-27 18:53:49 +02:00
Michele Caini
8978a0d159 core:
* added trait is_ebco_eligible[_v]
* added class compressed_pair
* tests and doc for compressed_pair
2021-07-27 18:53:49 +02:00
Michele Caini
4d09be0cd3 view: minor changes 2021-07-27 18:53:49 +02:00
Michele Caini
987be01bd6 component: added in_place_delete_v and ignore_if_empty_v 2021-07-27 18:53:49 +02:00
Michele Caini
7f59fc6321 test: minor changes 2021-07-27 18:53:49 +02:00
Michele Caini
feeb122c0d updated todo list 2021-07-27 18:53:49 +02:00
Michele Caini
0754f108c9 sparse_set: fix an issue with assuring pages properly on emplace (close #746) 2021-07-27 18:53:09 +02:00
Michele Caini
ea82f86749 meta: fix precedence issue 2021-07-27 11:39:37 +02:00
Michele Caini
7bd217386a meta: shared simplified visit function 2021-07-27 10:08:20 +02:00
Michele Caini
1f1e02fee1 build system: test id type std::uint64_t on the CI, all platforms 2021-07-26 23:48:26 +02:00
Michele Caini
6d1e4fb3da test: make tests for entity traits work when id_type is std::uint64_t 2021-07-26 23:48:23 +02:00
Michele Caini
9090f84611 test: make tests for hashed string work when id_type is std::uint64_t 2021-07-26 23:48:19 +02:00
Michele Caini
8e5ddba173 sparse set: make vs2017 work (more or less) fine when id_type is std::uint64_t 2021-07-26 23:48:15 +02:00
Michele Caini
86fccb5071 entity: avoid UBs when id type is std::uint64_t (close #745) 2021-07-26 23:48:09 +02:00
Michele Caini
414c3baf15 meta: further reduce size of meta_any's vtable 2021-07-26 17:06:16 +02:00
Michele Caini
bbbddbf617 meta: added internal meta_trait enum to reduce memory usage due to meta node traits 2021-07-26 11:16:00 +02:00
Michele Caini
7638b5a95e meta: suppress warnings, get around visibility issue 2021-07-26 09:55:25 +02:00
Michele Caini
3df6b05c00 doc: fixed typo (close #742) 2021-07-26 09:17:11 +02:00
Michele Caini
8566c58f2b meta: cleanup, prep for hook point 2021-07-23 17:24:41 +02:00
Michele Caini
33ccb71526 meta: add cbegin/cend to meta_range 2021-07-23 15:51:26 +02:00
Michele Caini
2b07b92039 meta (prep for hook func):
* removed meta_type::reset
* added meta_reset overloads
2021-07-23 15:47:40 +02:00
Michele Caini
959bc269e4 test: get rid of inconsistent line (close #741) 2021-07-23 07:53:51 +02:00
Michele Caini
8609068dbf type_traits: try to also please gcc :) 2021-07-22 18:21:22 +02:00
Michele Caini
d6cea80768 core: make is_equality_comparable[_v] work with iterators (close #739) 2021-07-22 18:08:19 +02:00
Michele Caini
0f2d6fb324 meta_any: avoid risky fallthrough in the vtable (close #736) 2021-07-22 17:56:22 +02:00
Michele Caini
f5d303045c meta: make meta_invoke and meta_construct utilities also support lambdas 2021-07-22 17:41:50 +02:00
Michele Caini
ad61b0c84e meta: removed parent link from meta nodes 2021-07-22 17:21:09 +02:00
Michele Caini
18373bb679 meta: meta_any::allow_cast overload for meta types 2021-07-22 16:55:45 +02:00
Michele Caini
c30dfe3bfe meta: add fwd.hpp (see #735) 2021-07-22 16:02:03 +02:00
Michele Caini
4993961c16 *: removed deprecated stuff 2021-07-22 15:20:08 +02:00
Michele Caini
89aece7c28 meta: removed meta_type::is_void 2021-07-21 11:35:35 +02:00
Michele Caini
ec96946513 meta: cleanup/further reduce instantiations 2021-07-21 11:35:35 +02:00
Michele Caini
e49aa0d424 meta: simplified meta_any::try_cast 2021-07-21 11:35:35 +02:00
Michele Caini
b5c929572a meta: possibly reduce number of instantiations due to meta_invoke 2021-07-21 11:35:35 +02:00
Michele Caini
394822aa50 meta: remove internal::meta_visit, further reduce the number of instantiations 2021-07-21 11:35:35 +02:00
Michele Caini
d12ba5e527 meta: reduce lambda instantiations 2021-07-21 11:35:35 +02:00
Michele Caini
ab316bcb2e type_info: use fake vtables 2021-07-21 11:35:35 +02:00
Michele Caini
93fc08df45 meta: template info review 2021-07-21 11:35:35 +02:00
Michele Caini
266dd02ef5 meta: utility review and cleanup 2021-07-21 11:35:35 +02:00
Michele Caini
61c1e359e9 meta: cleanup/review 2021-07-21 11:35:35 +02:00
Michele Caini
32c83986e5 meta: minor changes 2021-07-21 11:35:35 +02:00
Michele Caini
f64af2e69d meta: get rid of useless boolean values in the return types of meta containers 2021-07-21 11:35:35 +02:00
Michele Caini
6e925c7cdd meta: container support review (less instantiations, faster to compile) 2021-07-21 11:35:35 +02:00
Michele Caini
5f50f776c6 meta: remove unnecessary nested alias 2021-07-21 11:35:35 +02:00
Michele Caini
946ccf3db4 meta: reduce instantiations for meta containers 2021-07-21 11:35:35 +02:00
Michele Caini
7a0aea390b meta: minor changes (const correctness) 2021-07-21 11:35:35 +02:00
Michele Caini
ff0e6315f9 doc: fixed typo 2021-07-21 11:35:35 +02:00
Michele Caini
b7fb485349 meta: meta_type::construct should not use base constructors 2021-07-21 11:35:35 +02:00
Michele Caini
45cbe66d5d now working on version 3.9.0 2021-07-21 00:18:12 +02:00
Michele Caini
bc7a6399c1 updated single include file 2021-07-21 00:14:12 +02:00
197 changed files with 63445 additions and 32531 deletions

41
.clang-format Normal file
View File

@@ -0,0 +1,41 @@
BasedOnStyle: llvm
---
AccessModifierOffset: -4
AlignEscapedNewlines: DontAlign
AllowShortBlocksOnASingleLine: Empty
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: WithoutElse
AllowShortLoopsOnASingleLine: true
AlwaysBreakTemplateDeclarations: Yes
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeTernaryOperators: true
ColumnLimit: 0
DerivePointerAlignment: false
IncludeCategories:
- Regex: '<[[:alnum:]_]+>'
Priority: 1
- Regex: '<(gtest|gmock)/'
Priority: 2
- Regex: '<[[:alnum:]_./]+>'
Priority: 3
- Regex: '<entt/'
Priority: 4
- Regex: '.*'
Priority: 5
IndentPPDirectives: AfterHash
IndentWidth: 4
KeepEmptyLinesAtTheStartOfBlocks: false
Language: Cpp
PointerAlignment: Right
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: false
SpaceAroundPointerQualifiers: After
SpaceBeforeCaseColon: false
SpaceBeforeCtorInitializerColon: false
SpaceBeforeInheritanceColon: false
SpaceBeforeParens: Never
SpaceBeforeRangeBasedForLoopColon: false
Standard: Latest
TabWidth: 4
UseTab: Never

8
.github/FUNDING.yml vendored
View File

@@ -1,12 +1,4 @@
# These are supported funding model platforms
github: skypjack
patreon:
open_collective:
ko_fi:
tidelift:
community_bridge:
liberapay:
issuehunt:
otechie:
custom: https://www.paypal.me/skypjack

View File

@@ -5,48 +5,42 @@ on: [push, pull_request]
jobs:
linux:
timeout-minutes: 10
timeout-minutes: 15
strategy:
matrix:
compiler: [
g++-7, g++-8, g++-9, g++,
clang++-8, clang++-9, clang++-10, clang++
]
compiler:
- pkg: g++-7
exe: g++-7
- pkg: g++-8
exe: g++-8
- pkg: g++-9
exe: g++-9
- pkg: g++-10
exe: g++-10
- pkg: clang-8
exe: clang++-8
- pkg: clang-9
exe: clang++-9
- pkg: clang-10
exe: clang++-10
- pkg: clang-11
exe: clang++-11
- pkg: clang-12
exe: clang++-12
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install g++-7
if: ${{ matrix.compiler == 'g++-7' }}
- name: Install compiler
run: |
sudo apt-get update
sudo apt-get install g++-7 -y
- name: Install g++-8
if: ${{ matrix.compiler == 'g++-8' }}
run: |
sudo apt-get update
sudo apt-get install g++-8 -y
- name: Install clang-8
if: ${{ matrix.compiler == 'clang++-8' }}
run: |
sudo apt-get update
sudo apt-get install clang-8 -y
- name: Install clang-9
if: ${{ matrix.compiler == 'clang++-9' }}
run: |
sudo apt-get update
sudo apt-get install clang-9 -y
- name: Install clang-10
if: ${{ matrix.compiler == 'clang++-10' }}
run: |
sudo apt-get update
sudo apt-get install clang-10 -y
sudo apt update
sudo apt install -y ${{ matrix.compiler.pkg }}
- name: Compile tests
working-directory: build
env:
CXX: ${{ matrix.compiler }}
CXX: ${{ matrix.compiler.exe }}
run: |
cmake -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON ..
make -j4
@@ -54,27 +48,49 @@ jobs:
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 10 -C Debug -j4
run: ctest --timeout 30 -C Debug -j4
windows:
timeout-minutes: 10
linux-extra:
timeout-minutes: 15
strategy:
matrix:
os: [windows-latest, windows-2016]
toolset: [clang-cl, default, v141]
compiler: [g++, clang++]
id_type: ["std::uint32_t", "std::uint64_t"]
cxx_std: [cxx_std_17, cxx_std_20]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Compile tests
working-directory: build
env:
CXX: ${{ matrix.compiler }}
run: |
cmake -DENTT_BUILD_TESTING=ON -DENTT_CXX_STD=${{ matrix.cxx_std }} -DENTT_ID_TYPE=${{ matrix.id_type }} ..
make -j4
- name: Run tests
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 30 -C Debug -j4
windows:
timeout-minutes: 15
strategy:
matrix:
toolset: [default, v141, v142, clang-cl]
include:
- toolset: clang-cl
toolset_option: -T"ClangCl"
- toolset: v141
toolset_option: -T"v141"
exclude:
- os: windows-2016
toolset: clang-cl
- os: windows-2016
toolset: v141
- toolset: v142
toolset_option: -T"v142"
- toolset: clang-cl
toolset_option: -T"ClangCl"
runs-on: ${{ matrix.os }}
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
@@ -87,10 +103,33 @@ jobs:
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 10 -C Debug -j4
run: ctest --timeout 30 -C Debug -j4
windows-extra:
timeout-minutes: 15
strategy:
matrix:
id_type: ["std::uint32_t", "std::uint64_t"]
cxx_std: [cxx_std_17, cxx_std_20]
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- name: Compile tests
working-directory: build
run: |
cmake -DENTT_BUILD_TESTING=ON -DENTT_CXX_STD=${{ matrix.cxx_std }} -DENTT_ID_TYPE=${{ matrix.id_type }} ..
cmake --build . -j 4
- name: Run tests
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 30 -C Debug -j4
macos:
timeout-minutes: 10
timeout-minutes: 15
runs-on: macOS-latest
steps:
@@ -104,4 +143,27 @@ jobs:
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 10 -C Debug -j4
run: ctest --timeout 30 -C Debug -j4
macos-extra:
timeout-minutes: 15
strategy:
matrix:
id_type: ["std::uint32_t", "std::uint64_t"]
cxx_std: [cxx_std_17, cxx_std_20]
runs-on: macOS-latest
steps:
- uses: actions/checkout@v2
- name: Compile tests
working-directory: build
run: |
cmake -DENTT_BUILD_TESTING=ON -DENTT_CXX_STD=${{ matrix.cxx_std }} -DENTT_ID_TYPE=${{ matrix.id_type }} ..
make -j4
- name: Run tests
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 30 -C Debug -j4

View File

@@ -5,34 +5,34 @@ on: [push, pull_request]
jobs:
codecov:
timeout-minutes: 10
timeout-minutes: 15
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Compile tests
working-directory: build
env:
CXXFLAGS: "--coverage -fno-inline"
CXX: g++
run: |
cmake -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON ..
make -j4
- name: Run tests
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 10 -C Debug -j4
- name: Collect data
working-directory: build
run: |
sudo apt install lcov
lcov -c -d . -o coverage.info
lcov -l coverage.info
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: build/coverage.info
name: EnTT
fail_ci_if_error: true
- uses: actions/checkout@v2
- name: Compile tests
working-directory: build
env:
CXXFLAGS: "--coverage -fno-inline"
CXX: g++
run: |
cmake -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON ..
make -j4
- name: Run tests
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 30 -C Debug -j4
- name: Collect data
working-directory: build
run: |
sudo apt install lcov
lcov -c -d . -o coverage.info
lcov -l coverage.info
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v2
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: build/coverage.info
name: EnTT
fail_ci_if_error: true

View File

@@ -4,12 +4,14 @@ on: [push, pull_request]
jobs:
linux:
timeout-minutes: 10
clang:
timeout-minutes: 15
strategy:
matrix:
compiler: [clang++]
id_type: ["std::uint32_t", "std::uint64_t"]
cxx_std: [cxx_std_17, cxx_std_20]
runs-on: ubuntu-latest
@@ -20,10 +22,10 @@ jobs:
env:
CXX: ${{ matrix.compiler }}
run: |
cmake -DENTT_USE_SANITIZER=ON -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON ..
cmake -DENTT_USE_SANITIZER=ON -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON -DENTT_CXX_STD=${{ matrix.cxx_std }} -DENTT_ID_TYPE=${{ matrix.id_type }} ..
make -j4
- name: Run tests
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 10 -C Debug -j4
run: ctest --timeout 30 -C Debug -j4

View File

@@ -11,6 +11,7 @@ ceeac
ColinH
corystegel
Croydon
cschreib
cugone
dbacchet
dBagrat
@@ -31,6 +32,7 @@ Lawrencemm
markand
mhammerc
Milerius
Minimonium
morbo84
m-waka
netpoetica
@@ -46,6 +48,7 @@ The5-1
vblanco20-1
willtunnels
WizardIke
WoLfulus
w1th0utnam3
xissburg
zaucy

View File

@@ -4,14 +4,6 @@
cmake_minimum_required(VERSION 3.12.4)
#
# Building in-tree is not allowed (we take care of your craziness).
#
if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
message(FATAL_ERROR "Prevented in-tree built. Please create a build directory outside of the source code and call cmake from there. Thank you.")
endif()
#
# Read project version
#
@@ -30,7 +22,7 @@ project(
VERSION ${ENTT_VERSION}
DESCRIPTION "Gaming meets modern C++ - a fast and reliable entity-component system (ECS) and much more"
HOMEPAGE_URL "https://github.com/skypjack/entt"
LANGUAGES CXX
LANGUAGES C CXX
)
if(NOT CMAKE_BUILD_TYPE)
@@ -39,40 +31,67 @@ endif()
message(VERBOSE "*")
message(VERBOSE "* ${PROJECT_NAME} v${PROJECT_VERSION} (${CMAKE_BUILD_TYPE})")
message(VERBOSE "* Copyright (c) 2017-2021 Michele Caini <michele.caini@gmail.com>")
message(VERBOSE "* Copyright (c) 2017-2022 Michele Caini <michele.caini@gmail.com>")
message(VERBOSE "*")
option(ENTT_USE_LIBCPP "Use libc++ by adding -stdlib=libc++ flag if availbale." ON)
option(ENTT_USE_SANITIZER "Enable sanitizers by adding -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined flags" OFF)
#
# Compiler stuff
#
if(NOT WIN32 AND ENTT_USE_LIBCPP)
include(CheckCXXSourceCompiles)
include(CMakePushCheckState)
option(ENTT_USE_LIBCPP "Use libc++ by adding -stdlib=libc++ flag if available." OFF)
option(ENTT_USE_SANITIZER "Enable sanitizers by adding -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined flags if available." OFF)
cmake_push_check_state()
if(ENTT_USE_LIBCPP)
if(NOT WIN32)
include(CheckCXXSourceCompiles)
include(CMakePushCheckState)
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -stdlib=libc++")
cmake_push_check_state()
check_cxx_source_compiles("
#include<type_traits>
int main() { return std::is_same_v<int, char>; }
" ENTT_HAS_LIBCPP)
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -stdlib=libc++")
if(NOT ENTT_HAS_LIBCPP)
message(VERBOSE "The option ENTT_USE_LIBCPP is set (by default) but libc++ is not available. The flag will not be added to the target.")
check_cxx_source_compiles("
#include<type_traits>
int main() { return std::is_same_v<int, char>; }
" ENTT_HAS_LIBCPP)
cmake_pop_check_state()
endif()
cmake_pop_check_state()
if(NOT ENTT_HAS_LIBCPP)
message(VERBOSE "The option ENTT_USE_LIBCPP is set but libc++ is not available. The flag will not be added to the target.")
endif()
endif()
if(ENTT_USE_SANITIZER)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
set(ENTT_HAS_SANITIZER TRUE CACHE BOOL "" FORCE)
mark_as_advanced(ENTT_HAS_SANITIZER)
endif()
if(NOT ENTT_HAS_SANITIZER)
message(VERBOSE "The option ENTT_USE_SANITIZER is set but sanitizer support is not available. The flags will not be added to the target.")
endif()
endif()
#
# Add EnTT target
#
option(ENTT_INCLUDE_HEADERS "Add all EnTT headers to the EnTT target." OFF)
option(ENTT_INCLUDE_NATVIS "Add EnTT natvis files to the EnTT target." OFF)
if(ENTT_INCLUDE_NATVIS)
if(MSVC)
set(ENTT_HAS_NATVIS TRUE CACHE BOOL "" FORCE)
mark_as_advanced(ENTT_HAS_NATVIS)
endif()
if(NOT ENTT_HAS_NATVIS)
message(VERBOSE "The option ENTT_INCLUDE_NATVIS is set but natvis files are not supported. They will not be added to the target.")
endif()
endif()
include(GNUInstallDirs)
add_library(EnTT INTERFACE)
@@ -85,7 +104,103 @@ target_include_directories(
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
if(ENTT_USE_SANITIZER)
target_compile_features(EnTT INTERFACE cxx_std_17)
if(ENTT_INCLUDE_HEADERS)
target_sources(
EnTT
INTERFACE
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/config/config.h>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/config/macro.h>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/config/version.h>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/container/dense_map.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/container/dense_set.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/container/fwd.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/algorithm.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/any.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/attribute.h>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/compressed_pair.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/enum.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/family.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/fwd.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/hashed_string.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/ident.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/iterator.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/memory.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/monostate.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/tuple.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/type_info.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/type_traits.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/utility.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/component.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/entity.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/fwd.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/group.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/handle.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/helper.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/observer.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/organizer.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/registry.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/runtime_view.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/sigh_storage_mixin.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/snapshot.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/sparse_set.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/storage.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/utility.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/view.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/locator/locator.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/adl_pointer.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/container.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/ctx.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/factory.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/fwd.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/meta.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/node.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/pointer.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/policy.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/range.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/resolve.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/template.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/type_traits.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/utility.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/platform/android-ndk-r17.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/poly/fwd.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/poly/poly.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/process/process.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/process/scheduler.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/resource/cache.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/resource/fwd.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/resource/loader.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/resource/resource.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/signal/delegate.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/signal/dispatcher.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/signal/emitter.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/signal/fwd.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/signal/sigh.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entt.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/fwd.hpp>
)
endif()
if(ENTT_HAS_NATVIS)
target_sources(
EnTT
INTERFACE
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/config.natvis>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/container.natvis>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/core.natvis>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/entity.natvis>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/locator.natvis>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/meta.natvis>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/platform.natvis>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/poly.natvis>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/process.natvis>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/resource.natvis>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/signal.natvis>
)
endif()
if(ENTT_HAS_SANITIZER)
target_compile_options(EnTT INTERFACE $<$<CONFIG:Debug>:-fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined>)
target_link_libraries(EnTT INTERFACE $<$<CONFIG:Debug>:-fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined>)
endif()
@@ -94,8 +209,6 @@ if(ENTT_HAS_LIBCPP)
target_compile_options(EnTT BEFORE INTERFACE -stdlib=libc++)
endif()
target_compile_features(EnTT INTERFACE cxx_std_17)
#
# Install pkg-config file
#
@@ -174,6 +287,9 @@ if(ENTT_BUILD_TESTING)
option(ENTT_BUILD_LIB "Build lib tests." OFF)
option(ENTT_BUILD_SNAPSHOT "Build snapshot test with Cereal." OFF)
set(ENTT_ID_TYPE std::uint32_t CACHE STRING "Type of identifiers to use for the tests")
set(ENTT_CXX_STD cxx_std_17 CACHE STRING "C++ standard revision to use for the tests")
include(CTest)
enable_testing()
add_subdirectory(test)

View File

@@ -1,7 +1,7 @@
# Contributing
First of all, thank you very much for taking the time to contribute to the
`EnTT` framework.<br/>
`EnTT` library.<br/>
How to do it mostly depends on the type of contribution:
* If you have a question, **please** ensure there isn't already an answer for
@@ -28,7 +28,7 @@ How to do it mostly depends on the type of contribution:
* If you found a bug and you wrote a patch to fix it, open a new
[pull request](https://github.com/skypjack/entt/pulls) with your code.
**Please**, add some tests to avoid regressions in future if possible, it
would be really appreciated. Note that the `EnTT` framework has a
would be really appreciated. Note that the `EnTT` library has a
[coverage at 100%](https://coveralls.io/github/skypjack/entt?branch=master)
(at least it was at 100% at the time I wrote this file) and this is the reason
for which you can be confident with using it in a production environment.

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2017-2021 Michele Caini
Copyright (c) 2017-2022 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

@@ -11,6 +11,9 @@
[![Discord channel](https://img.shields.io/discord/707607951396962417?logo=discord)](https://discord.gg/5BjPWBd)
[![Donate](https://img.shields.io/badge/donate-paypal-blue.svg)](https://www.paypal.me/skypjack)
> `EnTT` has been a dream so far, we haven't found a single bug to date and it's
> super easy to work with
`EnTT` is a header-only, tiny and easy to use library for game programming and
much more written in **modern C++**.<br/>
[Among others](https://github.com/skypjack/entt/wiki/EnTT-in-Action), it's used
@@ -49,6 +52,7 @@ Many thanks to [these people](https://skypjack.github.io/sponsorship/) and
* [Integration](#integration)
* [Requirements](#requirements)
* [CMake](#cmake)
* [Natvis support](#natvis-support)
* [Packaging Tools](#packaging-tools)
* [pkg-config](#pkg-config)
* [Documentation](#documentation)
@@ -73,33 +77,32 @@ This project started off as a pure entity-component system. Over time the
codebase has grown as more and more classes and functionalities were added.<br/>
Here is a brief, yet incomplete list of what it offers today:
* Statically generated integer **identifiers** for types (assigned either at
compile-time or at runtime).
* Built-in **RTTI system** mostly similar to the standard one.
* A `constexpr` utility for human readable **resource names**.
* A minimal **configuration system** built using the monostate pattern.
* An incredibly fast **entity-component system** based on sparse sets, with its
own _pay for what you use_ policy to adjust performance and memory usage
according to the users' requirements.
* Minimal **configuration system** built using the monostate pattern.
* Incredibly fast **entity-component system** with its own _pay for what you
use_ policy.
* Views and groups to iterate entities and components and allow different access
patterns, from **perfect SoA** to fully random.
* A lot of **facilities** built on top of the entity-component system to help
the users and avoid reinventing the wheel (dependencies, snapshot, handles,
support for **reactive systems** and so on).
the users and avoid reinventing the wheel.
* The smallest and most basic implementation of a **service locator** ever seen.
* A built-in, non-intrusive and macro-free runtime **reflection system**.
* **Static polymorphism** made simple and within everyone's reach.
* A few homemade containers, like a sparse set based **hash map**.
* A **cooperative scheduler** for processes of any type.
* All that is needed for **resource management** (cache, loaders, handles).
* Delegates, **signal handlers** (with built-in support for collectors) and a
tiny event dispatcher for immediate and delayed events to integrate in loops.
* Delegates, **signal handlers** and a tiny event dispatcher.
* A general purpose **event emitter** as a CRTP idiom based class template.
* And **much more**! Check out the
[**wiki**](https://github.com/skypjack/entt/wiki).
Consider this list a work in progress as well as the project. The whole API is
fully documented in-code for those who are brave enough to read it.
Consider these lists a work in progress as well as the project. The whole API is
fully documented in-code for those who are brave enough to read it.<br/>
Please, do note that all tools are also DLL-friendly now and run smoothly across
boundaries.
It is also known that `EnTT` is used in **Minecraft**.<br/>
One thing known to most is that `EnTT` is also used in **Minecraft**.<br/>
Given that the game is available literally everywhere, I can confidently say
that the library has been sufficiently tested on every platform that can come to
mind.
@@ -156,7 +159,7 @@ int main() {
## Motivation
I started developing `EnTT` for the _wrong_ reason: my goal was to design an
entity-component system to beat another well known open source solution both in
entity-component system to beat another well known open source library both in
terms of performance and possibly memory usage.<br/>
In the end, I did it, but it wasn't very satisfying. Actually it wasn't
satisfying at all. The fastest and nothing more, fairly little indeed. When I
@@ -246,6 +249,14 @@ Covering all possible cases would require a treaty and not a simple README file,
but I'm confident that anyone reading this section also knows what it's about
and can use `EnTT` from a `CMake` project without problems.
## Natvis support
When using `CMake`, just enable the option `ENTT_INCLUDE_NATVIS` and enjoy
it.<br/>
Otherwise, most of the tools are covered via Natvis and all files can be found
in the `natvis` directory, divided by module.<br/>
If you spot errors or have suggestions, any contribution is welcome!
## Packaging Tools
`EnTT` is available for some of the most known packaging tools. In particular:
@@ -265,6 +276,12 @@ and can use `EnTT` from a `CMake` project without problems.
$ vcpkg install entt
```
Or you can use the `experimental` feature to test the latest changes:
```
vcpkg install entt[experimental] --head
```
The `EnTT` port in `vcpkg` is kept up to date by Microsoft team members and
community contributors.<br/>
If the version is out of date, please
@@ -399,7 +416,7 @@ know who has participated so far.
# License
Code and documentation Copyright (c) 2017-2021 Michele Caini.<br/>
Code and documentation Copyright (c) 2017-2022 Michele Caini.<br/>
Colorful logo Copyright (c) 2018-2021 Richard Caseres.
Code released under

38
TODO
View File

@@ -1,36 +1,26 @@
* long term feature: shared_ptr less locator and resource cache
* debugging tools (#60): the issue online already contains interesting tips on this, look at it
* work stealing job system (see #100) + mt scheduler based on const awareness for types
* allow to replace std:: with custom implementations
* add examples (and credits) from @alanjfs :)
* custom pools example (multi instance, tables, enable/disable, and so on...)
EXAMPLES
* filter on runtime values/variables (not only types)
* support to polymorphic types (see #859)
WIP:
* remove view/storage dispatcher, add support to relax policy constraints on user request (eg view.use<T>())
* improve perf for sparse_set/storage::insert/emplace/destroy/remove/...
* custom allocators all over
* view/group: no storage_traits dependency -> use storage instead of components for the definition
* basic_storage::bind for cross-registry setups
* uses-allocator construction: any (with allocator support), poly, ...
* process scheduler: reviews, use free lists internally
* iterator based try_emplace vs try_insert for perf reasons
* dedicated entity storage, in-place O(1) release/destroy for non-orphaned entities, out-of-sync model
* entity-only and exclude-only views
* custom allocators all over (sigh storage mixin, registry, ...)
* consider removing ENTT_NOEXCEPT, use ENTT_NOEXCEPT_IF (or noexcept(...)) as appropriate in any case (ie make compressed_pair conditionally noexcept)
* add test for maximum number of entities reached
WIP:
* make value_type available from meta container types, otherwise we have to default construct a container to get it
* make it possible to register externally managed pools with the registry (allow for system centric mode)
* registry: switch to the udata/mixin model and get rid of poly storage, use pointer to sparse set only for pools, discard pool_data type.
* it's now possible to have 0 as null entity/version, so we can finally switch to it
* make pools available (registry/view/group), review operator| for views
* page size: add per-pool size, allow for 0 sizes (old fully packed array)
* compressed pair to exploit ebo in sparse set and the others
* isolate view iterator, unwrap iterators in registry ::remove/::erase/::destroy to use the faster solution for non-view iterators
* remove view each<T>(F), each<T>(), make view::use return a view and remove the mutable data member
* resource, forward the id to the loader from the cache and if constexpr the call to load, update doc and describe customization points
* make it possible to create views of the type `view<T, T>`, add get by index and such, allow to register custom pools by name with the registry
* add user data to type_info
* any_vector for context variables
* make const registry::view thread safe, switch to a view<T...>{registry} model (long term goal)
* weak reference wrapper example with custom storage
* headless (sparse set only) view
* write documentation for custom storages and views!!
* make runtime views use opaque storage and therefore return also elements.
* add exclude-only views to combine with packs
* entity-aware observer, add observer functions aside observer class
* deprecate non-owning groups in favor of owning views and view packs, introduce lazy owning views
* snapshot: support for range-based archives
* add example: 64 bit ids with 32 bits reserved for users' purposes

View File

@@ -17,6 +17,7 @@ add_custom_target(
SOURCES
dox/extra.dox
md/config.md
md/container.md
md/core.md
md/entity.md
md/faq.md

67
docs/md/container.md Normal file
View File

@@ -0,0 +1,67 @@
# Crash Course: containers
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [Containers](#containers)
* [Dense map](#dense-map)
* [Dense set](#dense-set)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
The standard C++ library offers a wide range of containers and it's really
difficult to do better (although it's very easy to do worse, as many examples
available online demonstrate).<br/>
`EnTT` doesn't try in any way to replace what is offered by the standard. Quite
the opposite, given the widespread use that is made of standard containers.<br/>
However, the library also tries to fill a gap in features and functionality by
making available some containers initially developed for internal use.
This section of the library is likely to grow larger over time. However, for the
moment it's quite small and mainly aimed at satisfying some internal needs.<br/>
For all containers made available, full test coverage and stability over time is
guaranteed as usual.
# Containers
## Dense map
The dense map made available in `EnTT` is a hash map that aims to return a
packed array of elements, so as to reduce the number of jumps in memory during
iterations.<br/>
The implementation is based on _sparse sets_ and each bucket is identified by an
implicit list within the packed array itself.
The interface is very close to its counterpart in the standard library, that is,
`std::unordered_map`.<br/>
However, both local and non-local iterators returned by a dense map belong to
the input iterator category although they respectively model the concepts of a
_forward iterator_ type and a _random access iterator_ type.<br/>
This is because they return a pair of references rather than a reference to a
pair. In other words, dense maps return a so called _proxy iterator_ the value
type of which is:
* `std::pair<const Key &, Type &>` for non-const iterator types.
* `std::pair<const Key &, const Type &>` for const iterator types.
This is quite different from what any standard library map returns and should be
taken into account when looking for a drop-in replacement.
## Dense set
The dense set made available in `EnTT` is a hash set that aims to return a
packed array of elements, so as to reduce the number of jumps in memory during
iterations.<br/>
The implementation is based on _sparse sets_ and each bucket is identified by an
implicit list within the packed array itself.
The interface is in all respects similar to its counterpart in the standard
library, that is, `std::unordered_set`.<br/>
Therefore, there is no need to go into the API description.

View File

@@ -6,18 +6,21 @@
# Table of Contents
* [Introduction](#introduction)
* [Unique sequential identifiers](#unique-sequential-identifiers)
* [Compile-time generator](#compile-time-generator)
* [Runtime generator](#runtime-generator)
* [Hashed strings](#hashed-strings)
* [Wide characters](wide-characters)
* [Conflicts](#conflicts)
* [Monostate](#monostate)
* [Any as in any type](#any-as-in-any-type)
* [Small buffer optimization](#small-buffer-optimization)
* [Alignment requirement](#alignment-requirement)
* [Compressed pair](#compressed-pair)
* [Enum as bitmask](#enum-as-bitmask)
* [Hashed strings](#hashed-strings)
* [Wide characters](wide-characters)
* [Conflicts](#conflicts)
* [Memory](#memory)
* [Power of two and fast modulus](#power-of-two-and-fast-modulus)
* [Allocator aware unique pointers](#allocator-aware-unique-pointers)
* [Monostate](#monostate)
* [Type support](#type-support)
* [Type info](#type-info)
* [Built-in RTTI support](#built-in-rtti-support)
* [Type info](#type-info)
* [Almost unique identifiers](#almost-unique-identifiers)
* [Type traits](#type-traits)
* [Size of](#size-of)
@@ -27,6 +30,9 @@
* [Integral constant](#integral-constant)
* [Tag](#tag)
* [Type list and value list](#type-list-and-value-list)
* [Unique sequential identifiers](#unique-sequential-identifiers)
* [Compile-time generator](#compile-time-generator)
* [Runtime generator](#runtime-generator)
* [Utilities](#utilities)
<!--
@endcond TURN_OFF_DOXYGEN
@@ -39,80 +45,249 @@ of the library itself.<br/>
Hardly users will include these features in their code, but it's worth
describing what `EnTT` offers so as not to reinvent the wheel in case of need.
# Unique sequential identifiers
# Any as in any type
Sometimes it's useful to be able to give unique, sequential numeric identifiers
to types either at compile-time or runtime.<br/>
There are plenty of different solutions for this out there and I could have used
one of them. However, I decided to spend my time to define a couple of tools
that fully embraces what the modern C++ has to offer.
`EnTT` comes with its own `any` type. It may seem redundant considering that
C++17 introduced `std::any`, but it is not (hopefully).<br/>
First of all, the _type_ returned by an `std::any` is a const reference to an
`std::type_info`, an implementation defined class that's not something everyone
wants to see in a software. Furthermore, there is no way to connect it with the
type system of the library and therefore with its integrated RTTI support.<br/>
Note that this class is largely used internally by the library itself.
## Compile-time generator
The API is very similar to that of its most famous counterpart, mainly because
this class serves the same purpose of being an opaque container for any type of
value.<br/>
Instances of `any` also minimize the number of allocations by relying on a well
known technique called _small buffer optimization_ and a fake vtable.
To generate sequential numeric identifiers at compile-time, `EnTT` offers the
`identifier` class template:
Creating an object of the `any` type, whether empty or not, is trivial:
```cpp
// defines the identifiers for the given types
using id = entt::identifier<a_type, another_type>;
// an empty container
entt::any empty{};
// ...
// a container for an int
entt::any any{0};
switch(a_type_identifier) {
case id::type<a_type>:
// ...
break;
case id::type<another_type>:
// ...
break;
default:
// ...
}
// in place construction
entt::any in_place{std::in_place_type<int>, 42};
```
This is all what this class template has to offer: a `type` inline variable that
contains a numeric identifier for the given type. It can be used in any context
where constant expressions are required.
As long as the list remains unchanged, identifiers are also guaranteed to be
stable across different runs. In case they have been used in a production
environment and a type has to be removed, one can just use a placeholder to left
the other identifiers unchanged:
Alternatively, the `make_any` function serves the same purpose but requires to
always be explicit about the type:
```cpp
template<typename> struct ignore_type {};
using id = entt::identifier<
a_type_still_valid,
ignore_type<a_type_no_longer_valid>,
another_type_still_valid
>;
entt::any any = entt::make_any<int>(42);
```
Perhaps a bit ugly to see in a codebase but it gets the job done at least.
In both cases, the `any` class takes the burden of destroying the contained
element when required, regardless of the storage strategy used for the specific
object.<br/>
Furthermore, an instance of `any` isn't tied to an actual type. Therefore, the
wrapper is reconfigured when it's assigned a new object of a type other than
the one it contains.
## Runtime generator
To generate sequential numeric identifiers at runtime, `EnTT` offers the
`family` class template:
There exists also a way to directly assign a value to the variable contained by
an `entt::any`, without necessarily replacing it. This is especially useful when
the object is used in _aliasing mode_, as described below:
```cpp
// defines a custom generator
using id = entt::family<struct my_tag>;
entt::any any{42};
entt::any value{3};
// ...
// assigns by copy
any.assign(value);
const auto a_type_id = id::type<a_type>;
const auto another_type_id = id::type<another_type>;
// assigns by move
any.assign(std::move(value));
```
This is all what a _family_ has to offer: a `type` inline variable that contains
a numeric identifier for the given type.<br/>
The generator is customizable, so as to get different _sequences_ for different
purposes if needed.
The `any` class will also perform a check on the type information and whether or
not the original type was copy or move assignable, as appropriate.<br/>
In all cases, the `assign` function returns a boolean value to indicate the
success or failure of the operation.
Please, note that identifiers aren't guaranteed to be stable across different
runs. Indeed it mostly depends on the flow of execution.
When in doubt about the type of object contained, the `type` member function of
`any` returns a const reference to the `type_info` associated with its element,
or `type_id<void>()` if the container is empty. The type is also used internally
when comparing two `any` objects:
```cpp
if(any == empty) { /* ... */ }
```
In this case, before proceeding with a comparison, it's verified that the _type_
of the two objects is actually the same.<br/>
Refer to the `EnTT` type system documentation for more details about how
`type_info` works and on possible risks of a comparison.
A particularly interesting feature of this class is that it can also be used as
an opaque container for const and non-const references:
```cpp
int value = 42;
entt::any any{std::in_place_type<int &>(value)};
entt::any cany = entt::make_any<const int &>(value);
entt::any fwd = entt::forward_as_any(value);
any.emplace<const int &>(value);
```
In other words, whenever `any` is explicitly told to construct an _alias_, it
acts as a pointer to the original instance rather than making a copy of it or
moving it internally. The contained object is never destroyed and users must
ensure that its lifetime exceeds that of the container.<br/>
Similarly, it's possible to create non-owning copies of `any` from an existing
object:
```cpp
// aliasing constructor
entt::any ref = other.as_ref();
```
In this case, it doesn't matter if the original container actually holds an
object or acts already as a reference for unmanaged elements, the new instance
thus created won't create copies and will only serve as a reference for the
original item.<br/>
This means that, starting from the example above, both `ref` and `other` will
point to the same object, whether it's initially contained in `other` or already
an unmanaged element.
As a side note, it's worth mentioning that, while everything works transparently
when it comes to non-const references, there are some exceptions when it comes
to const references.<br/>
In particular, the `data` member function invoked on a non-const instance of
`any` that wraps a const reference will return a null pointer in all cases.
To cast an instance of `any` to a type, the library offers a set of `any_cast`
functions in all respects similar to their most famous counterparts.<br/>
The only difference is that, in the case of `EnTT`, these won't raise exceptions
but will only trigger an assert in debug mode, otherwise resulting in undefined
behavior in case of misuse in release mode.
## Small buffer optimization
The `any` class uses a technique called _small buffer optimization_ to reduce
the number of allocations where possible.<br/>
The default reserved size for an instance of `any` is `sizeof(double[2])`.
However, this is also configurable if needed. In fact, `any` is defined as an
alias for `basic_any<Len>`, where `Len` is the size above.<br/>
Users can easily set a custom size or define their own aliases:
```cpp
using my_any = entt::basic_any<sizeof(double[4])>;
```
This feature, in addition to allowing the choice of a size that best suits the
needs of an application, also offers the possibility of forcing dynamic creation
of objects during construction.<br/>
In other terms, if the size is 0, `any` avoids the use of any optimization and
always dynamically allocates objects (except for aliasing cases).
Note that the size of the internal storage as well as the alignment requirements
are directly part of the type and therefore contribute to define different types
that won't be able to interoperate with each other.
## Alignment requirement
The alignment requirement is optional and by default the most stringent (the
largest) for any object whose size is at most equal to the one provided.<br/>
The `basic_any` class template inspects the alignment requirements in each case,
even when not provided and may decide not to use the small buffer optimization
in order to meet them.
The alignment requirement is provided as an optional second parameter following
the desired size for the internal storage:
```cpp
using my_any = entt::basic_any<sizeof(double[4]), alignof(double[4])>;
```
Note that the alignment requirements as well as the size of the internal storage
are directly part of the type and therefore contribute to define different types
that won't be able to interoperate with each other.
# Compressed pair
Primarily designed for internal use and far from being feature complete, the
`compressed_pair` class does exactly what it promises: it tries to reduce the
size of a pair by exploiting _Empty Base Class Optimization_ (or _EBCO_).<br/>
This class **is not** a drop-in replacement for `std::pair`. However, it offers
enough functionalities to be a good alternative for when reducing memory usage
is more important than having some cool and probably useless feature.
Although the API is very close to that of `std::pair` (apart from the fact that
the template parameters are inferred from the constructor and therefore there is
no` entt::make_compressed_pair`), the major difference is that `first` and
`second` are functions for implementation needs:
```cpp
entt::compressed_pair pair{0, 3.};
pair.first() = 42;
```
There isn't much to describe then. It's recommended to rely on documentation and
intuition. At the end of the day, it's just a pair and nothing more.
# Enum as bitmask
Sometimes it's useful to be able to use enums as bitmasks. However, enum classes
aren't really suitable for the purpose out of the box. Main problem is that they
don't convert implicitly to their underlying type.<br/>
All that remains is to make a choice between using old-fashioned enums (with all
their problems that I don't want to discuss here) or writing _ugly_ code.
Fortunately, there is also a third way: adding enough operators in the global
scope to treat enum classes as bitmask transparently.<br/>
The ultimate goal is to be able to write code like the following (or maybe
something more meaningful, but this should give a grasp and remain simple at the
same time):
```cpp
enum class my_flag {
unknown = 0x01,
enabled = 0x02,
disabled = 0x04
};
const my_flag flags = my_flag::enabled;
const bool is_enabled = !!(flags & my_flag::enabled);
```
The problem with adding all operators to the global scope is that these will
come into play even when not required, with the risk of introducing errors that
are difficult to deal with.<br/>
However, C++ offers enough tools to get around this problem. In particular, the
library requires users to register all enum classes for which bitmask support
should be enabled:
```cpp
template<>
struct entt::enum_as_bitmask<my_flag>
: std::true_type
{};
```
This is handy when dealing with enum classes defined by third party libraries
and over which the users have no control. However, it's also verbose and can be
avoided by adding a specific value to the enum class itself:
```cpp
enum class my_flag {
unknown = 0x01,
enabled = 0x02,
disabled = 0x04,
_entt_enum_as_bitmask
};
```
In this case, there is no need to specialize the `enum_as_bitmask` traits, since
`EnTT` will automatically detect the flag and enable the bitmask support.<br/>
Once the enum class has been registered (in one way or the other) all the most
common operators will be available, such as `&`, `|` but also `&=` and `|=`.
Refer to the official documentation for the full list of operators.
# Hashed strings
@@ -195,6 +370,54 @@ and over which users have not the control. Choosing a slightly different
identifier is probably the best solution to make the conflict disappear in this
case.
# Memory
There are a handful of tools within EnTT to interact with memory in one way or
another.<br/>
Some are geared towards simplifying the implementation of (internal or external)
allocator aware containers. Others, on the other hand, are designed to help the
developer with everyday problems.
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/>
I won't describe them here in detail. Instead, I recommend reading the inline
documentation to those interested in the subject.
## Power of two and fast modulus
Finding out if a number is a power of two (`is_power_of_two`) or what the next
power of two is given a random value (`next_power_of_two`) is very useful at
times.<br/>
For example, it helps to allocate memory in pages having a size suitable for the
fast modulus:
```cpp
const std::size_t result = entt::fast_mod(value, modulus);
```
Where `modulus` is necessarily a power of two. Perhaps not everyone knows that
this type of operation is far superior in terms of performance to the basic
modulus and for this reason preferred in many areas.
## Allocator aware unique pointers
A nasty thing in C++ (at least up to C++20) is the fact that shared pointers
support allocators while unique pointers don't.<br/>
There is a proposal at the moment that also shows among the other things how
this can be implemented without any compiler support.
The `allocate_unique` function follows this proposal, making a virtue out of
necessity:
```cpp
std::unique_ptr<my_type, entt::allocation_deleter<my_type>> ptr = entt::allocate_unique<my_type>(allocator, arguments);
```
Although the internal implementation is slightly different from what is proposed
for the standard, this function offers an API that is a drop-in replacement for
the same feature.
# Monostate
The monostate pattern is often presented as an alternative to a singleton based
@@ -218,182 +441,28 @@ const bool b = entt::monostate<"mykey"_hs>{};
const int i = entt::monostate<entt::hashed_string{"mykey"}>{};
```
# Any as in any type
`EnTT` comes with its own `any` type. It may seem redundant considering that
C++17 introduced `std::any`, but it is not (hopefully).<br/>
In fact, the _type_ returned by an `std::any` is a const reference to an
`std::type_info`, an implementation defined class that's not something everyone
wants to see in a software. Furthermore, there is no way to connect it with the
type system of the library and therefore with its integrated RTTI support.<br/>
Note that this class is largely used internally by the library itself.
The API is very similar to that of its most famous counterpart, mainly because
this class serves the same purpose of being an opaque container for any type of
value.<br/>
Instances of `any` also minimize the number of allocations by relying on a well
known technique called _small buffer optimization_ and a fake vtable.
Creating an object of the `any` type, whether empty or not, is trivial:
```cpp
// an empty container
entt::any empty{};
// a container for an int
entt::any any{0};
// in place construction
entt::any in_place{std::in_place_type<int>, 42};
```
Alternatively, the `make_any` function serves the same purpose but requires to
always be explicit about the type:
```cpp
entt::any any = entt::make_any<int>(42);
```
In both cases, the `any` class takes the burden of destroying the contained
element when required, regardless of the storage strategy used for the specific
object.<br/>
Furthermore, an instance of `any` is not tied to an actual type. Therefore, the
wrapper will be reconfigured by assigning it an object of a different type than
the one contained, so as to be able to handle the new instance.<br/>
When in doubt about the type of object contained, the `type` member function of
`any` returns an instance of `type_info` associated with its element, or an
invalid `type_info` object if the container is empty. The type is also used
internally when comparing two `any` objects:
```cpp
if(any == empty) { /* ... */ }
```
In this case, before proceeding with a comparison, it's verified that the _type_
of the two objects is actually the same.<br/>
Refer to the `EnTT` type system documentation for more details on how
`type_info` works and on possible risks of a comparison.
A particularly interesting feature of this class is that it can also be used as
an opaque container for const and non-const references:
```cpp
int value = 42;
entt::any any{std::in_place_type<int &>(value)};
entt::any cany = entt::make_any<const int &>(value);
entt::any fwd = entt::forward_as_any(value);
any.emplace<const int &>(value);
```
In other words, whenever `any` is explicitly told to construct an _alias_, it
acts as a pointer to the original instance rather than making a copy of it or
moving it internally. The contained object is never destroyed and users must
ensure that its lifetime exceeds that of the container.<br/>
Similarly, it's possible to create non-owning copies of `any` from an existing
object:
```cpp
// aliasing constructor
entt::any ref = other.as_ref();
```
In this case, it doesn't matter if the original container actually holds an
object or acts already as a reference for unmanaged elements, the new instance
thus created won't create copies and will only serve as a reference for the
original item.<br/>
This means that, starting from the example above, both `ref` and` other` will
point to the same object, whether it's initially contained in `other` or already
an unmanaged element.
As a side note, it's worth mentioning that, while everything works transparently
when it comes to non-const references, there are some exceptions when it comes
to const references.<br/>
In particular, the `data` member function invoked on a non-const instance of
`any` that wraps a const reference will return a null pointer in all cases.
To cast an instance of `any` to a type, the library offers a set of `any_cast`
functions in all respects similar to their most famous counterparts.<br/>
The only difference is that, in the case of `EnTT`, these won't raise exceptions
but will only trigger an assert in debug mode, otherwise resulting in undefined
behavior in case of misuse in release mode.
## Small buffer optimization
The `any` class uses a technique called _small buffer optimization_ to reduce
the number of allocations where possible.<br/>
The default reserved size for an instance of `any` is `sizeof(double[2])`.
However, this is also configurable if needed. In fact, `any` is defined as an
alias for `basic_any<Len>`, where `Len` is the size above.<br/>
Users can easily set a custom size or define their own aliases:
```cpp
using my_any = entt::basic_any<sizeof(double[4])>;
```
This feature, in addition to allowing the choice of a size that best suits the
needs of an application, also offers the possibility of forcing dynamic creation
of objects during construction.<br/>
In other terms, if the size is 0, `any` avoids the use of any optimization and
always dynamically allocates objects (except for aliasing cases).
Note that the size of the internal storage as well as the alignment requirements
are directly part of the type and therefore contribute to define different types
that won't be able to interoperate with each other.
## Alignment requirement
The alignment requirement is optional and by default the most stringent (the
largest) for any object whose size is at most equal to the one provided.<br/>
The `basic_any` class template inspects the alignment requirements in each case,
even when not provided and may decide not to use the small buffer optimization
in order to meet them.
The alignment requirement is provided as an optional second parameter following
the desired size for the internal storage:
```cpp
using my_any = entt::basic_any<sizeof(double[4]), alignof(double[4])>;
```
Note that the alignment requirements as well as the size of the internal storage
are directly part of the type and therefore contribute to define different types
that won't be able to interoperate with each other.
# Type support
`EnTT` provides some basic information about types of all kinds.<br/>
It also offers additional features that are not yet available in the standard
library or that will never be.
## Type info
## Built-in RTTI support
The `type_info` class isn't a drop-in replacement for `std::type_info` but can
provide similar information which are not implementation defined and don't
require to enable RTTI.<br/>
Therefore, they can sometimes be even more reliable than those obtained
otherwise.
Runtime type identification support (or RTTI) is one of the most frequently
disabled features in the C++ world, especially in the gaming sector. Regardless
of the reasons for this, it's often a shame not to be able to rely on opaque
type information at runtime.<br/>
The library tries to fill this gap by offering a built-in system that doesn't
serve as a replacement but comes very close to being one and offers similar
information to that provided by its counterpart.
A type info object is an opaque class that is also copy and move constructible.
This class is returned by the `type_id` function template:
Basically, the whole system relies on a handful of classes. In particular:
```cpp
auto info = entt::type_id<a_type>();
```
These are the information made available by this object:
* The unique, sequential identifier associated with a given type:
* The unique sequential identifier associated with a given type:
```cpp
auto index = entt::type_id<a_type>().seq();
```
This is also an alias for the following:
```cpp
auto index = entt::type_seq<a_type>::value();
auto index = entt::type_index<a_type>::value();
```
The returned value isn't guaranteed to be stable across different runs.
@@ -407,13 +476,13 @@ These are the information made available by this object:
and therefore the generation of custom runtime sequences of indices for their
own purposes, if necessary.
An external generator can also be used if needed. In fact, `type_seq` can be
An external generator can also be used if needed. In fact, `type_index` can be
specialized by type and is also _sfinae-friendly_ in order to allow more
refined specializations such as:
```cpp
template<typename Type>
struct entt::type_seq<Type, std::void_d<decltype(Type::index())>> {
struct entt::type_index<Type, std::void_d<decltype(Type::index())>> {
static entt::id_type value() ENTT_NOEXCEPT {
return Type::index();
}
@@ -426,21 +495,13 @@ These are the information made available by this object:
* The hash value associated with a given type:
```cpp
auto hash = entt::type_id<a_type>().hash();
```
This is also an alias for the following:
```cpp
auto hash = entt::type_hash<a_type>::value();
```
In general, the `value` function exposed by `type_hash` is also `constexpr`
but this isn't guaranteed for all compilers and platforms (although it's valid
with the most well-known and popular ones).<br/>
The `hash` function offered by the type info object isn't `constexpr` in any
case instead.
with the most well-known and popular ones).
This function **can** use non-standard features of the language for its own
purposes. This makes it possible to provide compile-time identifiers that
@@ -450,18 +511,12 @@ These are the information made available by this object:
that identifiers remain stable across executions. Moreover, they are generated
at runtime and are no longer a compile-time thing.
As for `type_seq`, also `type_hash` is a _sfinae-friendly_ class that can be
As for `type_index`, also `type_hash` is a _sfinae-friendly_ class that can be
specialized in order to customize its behavior globally or on a per-type or
per-traits basis.
* The name associated with a given type:
```cpp
auto name = entt::type_id<my_type>().name();
```
This is also an alias for the following:
```cpp
auto name = entt::type_name<a_type>::value();
```
@@ -489,10 +544,84 @@ These are the information made available by this object:
means of the `ENTT_STANDARD_CPP` definition. In this case, the name will be
empty by default.
As for `type_seq`, also `type_name` is a _sfinae-friendly_ class that can be
As for `type_index`, also `type_name` is a _sfinae-friendly_ class that can be
specialized in order to customize its behavior globally or on a per-type or
per-traits basis.
These are then combined into utilities that aim to offer an API that is somewhat
similar to that offered by the language.
### Type info
The `type_info` class isn't a drop-in replacement for `std::type_info` but can
provide similar information which are not implementation defined and don't
require to enable RTTI.<br/>
Therefore, they can sometimes be even more reliable than those obtained
otherwise.
Its type defines an opaque class that is also copyable and movable.<br/>
Objects of this type are generally returned by the `type_id` functions:
```cpp
// by type
auto info = entt::type_id<a_type>();
// by value
auto other = entt::type_id(42);
```
All elements thus received are nothing more than const references to instances
of `type_info` with static storage duration.<br/>
This is convenient for saving the entire object aside for the cost of a pointer.
However, nothing prevents from constructing `type_info` objects directly:
```cpp
entt::type_info info{std::in_place_type<int>};
```
These are the information made available by `type_info`:
* The index associated with a given type:
```cpp
auto idx = entt::type_id<a_type>().index();
```
This is also an alias for the following:
```cpp
auto idx = entt::type_index<std::remove_cv_t<std::remove_reference_t<a_type>>>::value();
```
* The hash value associated with a given type:
```cpp
auto hash = entt::type_id<a_type>().hash();
```
This is also an alias for the following:
```cpp
auto hash = entt::type_hash<std::remove_cv_t<std::remove_reference_t<a_type>>>::value();
```
* The name associated with a given type:
```cpp
auto name = entt::type_id<my_type>().name();
```
This is also an alias for the following:
```cpp
auto name = entt::type_name<std::remove_cv_t<std::remove_reference_t<a_type>>>::value();
```
Where all accessed features are available at compile-time, the `type_info` class
is also fully `constexpr`. However, this cannot be guaranteed in advance and
depends mainly on the compiler in use and any specializations of the classes
described above.
### Almost unique identifiers
Since the default non-standard, compile-time implementation of `type_hash` makes
@@ -633,7 +762,7 @@ Here is a (possibly incomplete) list of the functionalities that come with a
type list:
* `type_list_element[_t]` to get the N-th element of a type list.
* `type_list_cast[_t]` and a handy `operator+` to concatenate type lists.
* `type_list_cat[_t]` and a handy `operator+` to concatenate type lists.
* `type_list_unique[_t]` to remove duplicate types from a type list.
* `type_list_contains[_v]` to know if a type list contains a given type.
* `type_list_diff[_t]` to remove types from type lists.
@@ -644,6 +773,81 @@ Many of these functionalities also exist in their version dedicated to value
lists. We therefore have `value_list_element[_v]` as well as
`value_list_cat[_t]`and so on.
# Unique sequential identifiers
Sometimes it's useful to be able to give unique, sequential numeric identifiers
to types either at compile-time or runtime.<br/>
There are plenty of different solutions for this out there and I could have used
one of them. However, I decided to spend my time to define a couple of tools
that fully embraces what the modern C++ has to offer.
## Compile-time generator
To generate sequential numeric identifiers at compile-time, `EnTT` offers the
`identifier` class template:
```cpp
// defines the identifiers for the given types
using id = entt::identifier<a_type, another_type>;
// ...
switch(a_type_identifier) {
case id::type<a_type>:
// ...
break;
case id::type<another_type>:
// ...
break;
default:
// ...
}
```
This is all what this class template has to offer: a `type` inline variable that
contains a numeric identifier for the given type. It can be used in any context
where constant expressions are required.
As long as the list remains unchanged, identifiers are also guaranteed to be
stable across different runs. In case they have been used in a production
environment and a type has to be removed, one can just use a placeholder to left
the other identifiers unchanged:
```cpp
template<typename> struct ignore_type {};
using id = entt::identifier<
a_type_still_valid,
ignore_type<a_type_no_longer_valid>,
another_type_still_valid
>;
```
Perhaps a bit ugly to see in a codebase but it gets the job done at least.
## Runtime generator
To generate sequential numeric identifiers at runtime, `EnTT` offers the
`family` class template:
```cpp
// defines a custom generator
using id = entt::family<struct my_tag>;
// ...
const auto a_type_id = id::type<a_type>;
const auto another_type_id = id::type<another_type>;
```
This is all what a _family_ has to offer: a `type` inline variable that contains
a numeric identifier for the given type.<br/>
The generator is customizable, so as to get different _sequences_ for different
purposes if needed.
Please, note that identifiers aren't guaranteed to be stable across different
runs. Indeed it mostly depends on the flow of execution.
# Utilities
It's not possible to escape the temptation to add utilities of some kind to a

File diff suppressed because it is too large Load Diff

View File

@@ -149,15 +149,14 @@ more details.
## The standard and the non-copyable types
`EnTT` uses internally the trait `std::is_copy_constructible_v` to check if a
component is actually copyable. This trait doesn't check if an object can
actually be copied but only verifies if there is a copy constructor
available.<br/>
This can lead to surprising results due to some idiosyncrasies of the standard
mainly related to the need to guarantee backward compatibility.
component is actually copyable. However, this trait doesn't really check whether
a type is actually copyable. Instead, it just checks that a suitable copy
constructor and copy operator exist.<br/>
This can lead to surprising results due to some idiosyncrasies of the standard.
For example, `std::vector` defines a copy constructor no matter if its value
type is copyable or not. As a result, `std::is_copy_constructible_v` is true
for the following specialization:
For example, `std::vector` defines a copy constructor that is conditionally
enabled depending on whether the value type is copyable or not. As a result,
`std::is_copy_constructible_v` returns true for the following specialization:
```cpp
struct type {
@@ -165,21 +164,30 @@ struct type {
};
```
When trying to assign an instance of this type to an entity in the ECS part,
this may trigger a compilation error because we cannot really make a copy of
it.<br/>
As a workaround, users can mark the type explicitly as non-copyable:
However, the copy constructor is effectively disabled upon specialization.
Therefore, trying to assign an instance of this type to an entity may trigger a
compilation error.<br/>
As a workaround, users can mark the type explicitly as non-copyable. This also
suppresses the implicit generation of the move constructor and operator, which
will therefore have to be defaulted accordingly:
```cpp
struct type {
type(const type &) = delete;
type(type &&) = default;
type & operator=(const type &) = delete;
type & operator=(type &&) = default;
std::vector<std::unique_ptr<action>> vec;
};
```
Unfortunately, this will also disable aggregate initialization.
Note that aggregate initialization is also disabled as a consequence.<br/>
Fortunately, this type of trick is quite rare. The bad news is that there is no
way to deal with it at the library level, this being due to the design of the
language. On the other hand, the fact that the language itself also offers a way
to mitigate the problem makes it manageable.
## Which functions trigger which signals

View File

@@ -6,7 +6,7 @@
# Table of Contents
* [Working across boundaries](#working-across-boundaries)
* [The EnTT way](#the-entt-way)
* [Smooth until proven otherwise](#smooth-until-proven-otherwise)
* [Meta context](#meta-context)
* [Memory management](#memory-management)
<!--
@@ -19,48 +19,31 @@
general and on GNU/Linux when default visibility was set to hidden. The
limitation was mainly due to a custom utility used to assign unique, sequential
identifiers with different types.<br/>
Fortunately, nowadays using `EnTT` across boundaries is easier. However, use in
standalone applications is favored and user intervention is otherwise required.
Fortunately, nowadays using `EnTT` across boundaries is much easier.
## The EnTT way
## Smooth until proven otherwise
Many classes in `EnTT` make extensive use of type erasure for their purposes.
This isn't a problem in itself (in fact, it's the basis of an API so convenient
This isn't a problem on itself (in fact, it's the basis of an API so convenient
to use). However, a way is needed to recognize the objects whose type has been
erased on the other side of a boundary.<br/>
The `type_hash` class template is how identifiers are generated and thus made
available to the rest of the library. The `type_seq` class template makes all
types _indexable_ instead, so as to speed up the lookup.
available to the rest of the library. In general, this class doesn't arouse much
interest. The only exception is when a conflict between identifiers occurs
(definitely uncommon though) or when the default solution proposed by `EnTT`
isn't suitable for the user's purposes.<br/>
The section dedicated to `type_info` contains all the details to get around the
issue in a concise and elegant way. Please refer to the specific documentation.
In general, these classes don't arouse much interest. The only exceptions are:
* When a conflict between identifiers occurs (definitely uncommon though) or
when the default solution proposed by `EnTT` isn't suitable for the user's
purposes.<br/>
The section dedicated to `type_info` contains all the details to get around
the problem in a concise and elegant way. Please refer to the specific
documentation.
* When working with linked libraries that also export all required symbols.<br/>
Compile definitions `ENTT_API_EXPORT` and `ENTT_API_IMPORT` should be passed
respectively where there is a need to import or export the symbols defined by
`EnTT`, so as to make everything work nicely across boundaries.
* When working with plugins or shared libraries that don't export any symbol. In
this case, `type_seq` confuses the other classes by giving potentially wrong
information to them.<br/>
To avoid problems, it's required to provide a custom generator. Briefly, it's
necessary to specialize the `type_seq` class and make it point to a context
that is also shared between the main application and the dynamically loaded
libraries or plugins.<br/>
This will make the type system available to the whole application, not just to
a particular tool such as the registry or the dispatcher. It means that a call
to `type_seq::value()` will return the same identifier for the same type from
both sides of a boundary and can be used reliably for any purpose.
When working with linked libraries, compile definitions `ENTT_API_EXPORT` and
`ENTT_API_IMPORT` can be used where there is a need to import or export symbols,
so as to make everything work nicely across boundaries.<br/>
On the other hand, everything should run smoothly when working with plugins or
shared libraries that don't export any symbols.
For anyone who needs more details, the test suite contains multiple examples
covering the most common cases (see the `lib` directory for all details).<br/>
It goes without saying that it's impossible to cover all the possible cases.
It goes without saying that it's impossible to cover **all** possible cases.
However, what is offered should hopefully serve as a basis for all of them.
## Meta context

View File

@@ -20,14 +20,24 @@ I hope this list can grow much more in the future:
* [Minecraft Earth](https://www.minecraft.net/en-us/about-earth) by
[Mojang](https://mojang.com/): an augmented reality game for mobile, that
lets users bring Minecraft into the real world.
* [Ember Sword](https://embersword.com/): a modern Free-to-Play MMORPG with a
player-driven economy, a classless combat system, and scarce, tradable
cosmetic collectibles.
* Apparently [Diablo II: Resurrected](https://diablo2.blizzard.com/) by
[Blizzard](https://www.blizzard.com/): monsters, heroes, items, spells, all
resurrected. Thanks unknown insider.
* [Apparently](https://www.youtube.com/watch?v=P8xvOA3ikrQ&t=1105s)
[Call of Duty: Vanguard](https://www.callofduty.com/vanguard) by
[Sledgehammer Games](https://www.sledgehammergames.com/): I can neither
confirm nor deny but there is a license I know in the credits.
* Apparently [D&D Dark Alliance](https://darkalliance.wizards.com) by
[Wizards of the Coast](https://company.wizards.com): your party, their
funeral.
* [TiltedOnline](https://github.com/tiltedphoques/TiltedOnline) by
[Tilted Phoques](https://github.com/tiltedphoques): Skyrim and Fallout 4 mod
to play online.
* [Antkeeper](https://github.com/antkeeper/antkeeper-source): an ant colony
simulation [game](https://antkeeper.com/).
* [War of Rights](https://store.steampowered.com/app/424030/War_of_Rights/): a
multiplayer game set during the perilous days of the American Civil War, by
Campfire Games.
* [Openblack](https://github.com/openblack/openblack): open source
reimplementation of the game _Black & White_ (2001).
* [Land of the Rair](https://github.com/LandOfTheRair/core2): the new backend
@@ -70,8 +80,20 @@ I hope this list can grow much more in the future:
arcade game about shooting dirty rocks in space, inspired by Asteroids.
* [Wanderer](https://github.com/albin-johansson/wanderer): a 2D exploration
based indie game.
* [Spelunky® Classic remake](https://github.com/dbeef/spelunky-psp): A truly
* [Spelunky® Classic remake](https://github.com/dbeef/spelunky-psp): a truly
multiplatform experience with a rewrite from scratch.
* [CubbyTower](https://github.com/utilForever/CubbyTower): a simple tower
defense game using C++ with Entity Component System (ECS).
* [Runeterra](https://github.com/utilForever/Runeterra): Legends of Runeterra
simulator using C++ with some reinforcement learning.
* [Black Sun](https://store.steampowered.com/app/1670930/Black_Sun/): fly your
space ship through a large 2D open world.
* [PokeMaster](https://github.com/utilForever/PokeMaster): Pokemon Battle
simulator using C++ with some reinforcement learning.
* [HomeHearth](https://youtu.be/GrEWl8npL9Y): choose your hero, protect the
town, before it's too late.
* [City Builder Game](https://github.com/PhiGei2000/CityBuilderGame): a simple
city-building game using C++ and OpenGL.
* Engines and the like:
* [Aether Engine](https://hadean.com/spatial-simulation/)
@@ -92,6 +114,9 @@ I hope this list can grow much more in the future:
vrooooommm.
* [Antara Gaming SDK](https://github.com/KomodoPlatform/antara-gaming-sdk):
the Komodo Gaming Software Development Kit.
* [XVP](https://ravingbots.com/xvp-expansive-vehicle-physics-for-unreal-engine/):
[_eXpansive Vehicle Physics_](https://github.com/raving-bots/xvp/wiki/Plugin-integration-guide)
plugin for Unreal Engine.
* [Apparently](https://teamwisp.github.io/credits/)
[Wisp](https://teamwisp.github.io/product/) by
[Team Wisp](https://teamwisp.github.io/): an advanced real-time ray tracing
@@ -116,6 +141,21 @@ I hope this list can grow much more in the future:
framework in C++17 for backend development.
* [Unity/EnTT](https://github.com/TongTungGiang/unity-entt): tech demo of a
native simulation layer using `EnTT` and `Unity` as a rendering engine.
* [OverEngine](https://github.com/OverShifted/OverEngine): an over-engineered
game engine.
* [Electro](https://github.com/Electro-Technologies/Electro): high performance
3D game engine with a high emphasis on rendering.
* [Kawaii](https://github.com/Mathieu-Lala/Kawaii_Engine): a modern data
oriented game engine.
* [Becketron](https://github.com/Doctor-Foxling/Becketron): a game engine
written mostly in C++.
* [Spatial Engine](https://github.com/luizgabriel/Spatial.Engine): a
cross-platform engine created on top of google's filament rendering engine.
* [Kaguya](https://github.com/KaiH0717/Kaguya): D3D12 Rendering Engine.
* [OpenAWE](https://github.com/OpenAWE-Project/OpenAWE): open implementation
of the Alan Wake Engine.
* [Nazara Engine](https://github.com/DigitalPulseSoftware/NazaraEngine): fast,
cross-platform, object-oriented API to help in daily developer life.
* Articles, videos and blog posts:
* [Some posts](https://skypjack.github.io/tags/#entt) on my personal
@@ -180,6 +220,8 @@ I hope this list can grow much more in the future:
controller emulator and renderer.
* [Ragdoll](https://ragdolldynamics.com/): real-time physics for Autodesk Maya
2020.
* [Project Lagrange](https://github.com/adobe/lagrange): a robust geometry
processing library by [Adobe](https://github.com/adobe).
* [AtomicDEX](https://github.com/KomodoPlatform/atomicDEX-Desktop): a secure
wallet and non-custodial decentralized exchange rolled into one application.
* [Apparently](https://www.linkedin.com/in/skypjack/)

View File

@@ -14,62 +14,43 @@
# Introduction
Usually service locators are tightly bound to the services they expose and it's
hard to define a general purpose solution. This template based implementation
tries to fill the gap and to get rid of the burden of defining a different
specific locator for each application.<br/>
This class is tiny, partially unsafe and thus risky to use. Moreover it doesn't
fit probably most of the scenarios in which a service locator is required. Look
at it as a small tool that can sometimes be useful if users know how to handle
it.
hard to define a general purpose solution.<br/>
This tiny class tries to fill the gap and to get rid of the burden of defining a
different specific locator for each application.
# Service locator
The API is straightforward. The basic idea is that services are implemented by
means of interfaces and rely on polymorphism.<br/>
The locator is instantiated with the base type of the service if any and a
concrete implementation is provided along with all the parameters required to
initialize it. As an example:
The service locator API tries to mimic that of `std::optional` and adds some
extra functionalities on top of it such as allocator support.<br/>
There are a couple of functions to set up a service, namely `emplace` and
`allocate_emplace`:
```cpp
// the service has no base type, a locator is used to treat it as a kind of singleton
entt::service_locator<my_service>::set(params...);
// sets up an opaque service
entt::service_locator<audio_interface>::set<audio_implementation>(params...);
// resets (destroys) the service
entt::service_locator<audio_interface>::reset();
entt::locator<interface>::emplace<service>(argument);
entt::locator<interface>::allocate_emplace<service>(allocator, argument);
```
The locator can also be queried to know if an active service is currently set
and to retrieve it if necessary (either as a pointer or as a reference):
The difference is that the latter expects an allocator as the first argument and
uses it to allocate the service itself.<br/>
Once a service has been set up, it's retrieved using the value function:
```cpp
// no service currently set
auto empty = entt::service_locator<audio_interface>::empty();
// gets a (possibly empty) shared pointer to the service ...
std::shared_ptr<audio_interface> ptr = entt::service_locator<audio_interface>::get();
// ... or a reference, but it's undefined behaviour if the service isn't set yet
audio_interface &ref = entt::service_locator<audio_interface>::ref();
interface &service = entt::locator<interface>::value();
```
A common use is to wrap the different locators in a container class, creating
aliases for the various services:
Since the service may not be set (and therefore this function may result in an
undefined behavior), the `has_value` and `value_or` functions are also available
to test a service locator and to get a fallback service in case there is none:
```cpp
struct locator {
using camera = entt::service_locator<camera_interface>;
using audio = entt::service_locator<audio_interface>;
// ...
};
// ...
void init() {
locator::camera::set<camera_null>();
locator::audio::set<audio_implementation>(params...);
if(entt::locator<interface>::has_value()) {
// ...
}
interface &service = entt::locator<interface>::value_or<fallback_impl>(argument);
```
All arguments are used only if necessary, that is, if a service doesn't already
exist and therefore the fallback service is constructed and returned. In all
other cases, they are discarded.<br/>
Finally, to reset a service, use the `reset` function.

View File

@@ -13,6 +13,7 @@
* [Container support](#container-support)
* [Pointer-like types](#pointer-like-types)
* [Template information](#template-information)
* [Automatic conversions](#automatic-conversions)
* [Implicitly generated default constructor](#implicitly-generated-default-constructor)
* [Policies: the more, the less](#policies-the-more-the-less)
* [Named constants and enums](#named-constants-and-enums)
@@ -109,9 +110,10 @@ decorated version of it. This object can be used to add the following:
entt::meta<my_type>().ctor<int, char>().ctor<&factory>();
```
* _Destructors_. Free functions can be set as destructors of reflected types.
The purpose is to give users the ability to free up resources that require
special treatment before an object is actually destroyed.<br/>
* _Destructors_. Free functions and member functions can be used as destructors
of reflected types. The purpose is to give users the ability to free up
resources that require special treatment before an object is actually
destroyed.<br/>
Use the `dtor` member function for this purpose:
```cpp
@@ -146,6 +148,12 @@ decorated version of it. This object can be used to add the following:
entt::meta<my_type>().data<nullptr, &my_type::data_member>("member"_hs);
```
Multiple setters are also supported by means of a `value_list` object:
```cpp
entt::meta<my_type>().data<entt::value_list<&from_int, &from_string>, &my_type::data_member>("member"_hs);
```
Refer to the inline documentation for all the details.
* _Member functions_. Both real member functions of the underlying type and free
@@ -267,19 +275,6 @@ Refer to the inline documentation for all the details.
The meta objects that compose a meta type are accessed in the following ways:
* _Meta constructors_. They are accessed by types of arguments:
```cpp
auto ctor = entt::resolve<my_type>().ctor<int, char>();
```
The returned type is `meta_ctor` and may be invalid if there is no constructor
that accepts the supplied arguments or at least some types from which they are
derived or to which they can be converted.<br/>
A meta constructor offers an API to know the number of its arguments and their
expected meta types. Furthermor, it's possible to invoke it and therefore to
construct new instances of the underlying type.
* _Meta data_. They are accessed by _name_:
```cpp
@@ -328,7 +323,7 @@ Furthermore, all them are also returned by specific overloads that provide the
caller with iterable ranges of top-level elements. As an example:
```cpp
for(auto data = entt::resolve<my_type>().data()) {
for(auto data: entt::resolve<my_type>().data()) {
// ...
}
```
@@ -421,7 +416,7 @@ to case. In particular:
```
* The `resize` member function allows to resize the wrapped container and
returns true in case of succes:
returns true in case of success:
```cpp
const bool ok = view.resize(3u);
@@ -715,6 +710,56 @@ correspondence between real types and meta types.<br/>
Therefore, the specialization will be used as is and the information it contains
will be associated with the appropriate type when required.
## Automatic conversions
In C++, there are a number of conversions allowed between arithmetic types that
make it convenient to work with this kind of data.<br/>
If this were to be translated into explicit registrations with the reflection
system, it would result in a long series of instructions such as the following:
```cpp
entt::meta<int>()
.conv<bool>()
.conv<char>()
// ...
.conv<double>();
```
Repeated for each type eligible to undergo this type of conversions. This is
both error prone and repetitive.<br/>
Similarly, the language allows users to silently convert unscoped enums to their
underlying types and offers what it takes to do the same for scoped enums. It
would result in the following if it were to be done explicitly:
```cpp
entt::meta<my_enum>()
.conv<std::underlying_type_t<my_enum>>();
```
Fortunately, all of this can also be avoided. `EnTT` offers implicit support for
these types of conversions:
```cpp
entt::meta_any any{42};
any.allow_cast<double>();
double value = any.cast<double>();
```
With no need for registration, the conversion takes place automatically under
the hood. The same goes for a call to `allow_cast` involving a meta type:
```cpp
entt::meta_type type = entt::resolve<int>();
entt::meta_any any{my_enum::a_value};
any.allow_cast(type);
int value = any.cast<int>();
```
This should make working with arithmetic types and scoped or unscoped enums as
easy as it is in C++.<br/>
It's also worth noting that it's still possible to set up conversion functions
manually and these will always be preferred over the automatic ones.
## Implicitly generated default constructor
In many cases, it's useful to be able to create objects of default constructible
@@ -726,14 +771,7 @@ them.
For this reason and only for default constructible types, default constructors
are automatically defined and associated with their meta types, whether they are
explicitly or implicitly generated.<br/>
Therefore, it won't be necessary to do this in order to construct an integer
from its meta type:
```cpp
entt::meta<int>().ctor<>();
```
Instead, just do this:
Therefore, this is all is needed to construct an integer from its meta type:
```cpp
entt::resolve<int>().construct();
@@ -742,11 +780,8 @@ entt::resolve<int>().construct();
Where the meta type can be for example the one returned from a meta container,
useful for building keys without knowing or having to register the actual types.
In all cases, when users register custom defaul constructors, they are preferred
both during searches and when the `construct` member function is invoked.<br/>
However, the implicitly generated default constructor will always be returned,
either if one is not explicitly specified or if all constructors are iterated
for some reason (in this case, it will always be the last element).
In all cases, when users register default constructors, they are preferred both
during searches and when the `construct` member function is invoked.
## Policies: the more, the less
@@ -771,9 +806,11 @@ There are a few alternatives available at the moment:
* The _as-void_ policy, associated with the type `entt::as_void_t`.<br/>
Its purpose is to discard the return value of a meta object, whatever it is,
thus making it appear as if its type were `void`:
```cpp
entt::meta<my_type>().func<&my_type::member_function, entt::as_void_t>("member"_hs);
```
If the use with functions is obvious, it must be said that it's also possible
to use this policy with constructors and data members. In the first case, the
constructor will be invoked but the returned wrapper will actually be empty.
@@ -785,9 +822,11 @@ There are a few alternatives available at the moment:
Accessing the object contained in the wrapper for which the _reference_ was
requested will make it possible to directly access the instance used to
initialize the wrapper itself:
```cpp
entt::meta<my_type>().data<&my_type::data_member, entt::as_ref_t>("member"_hs);
```
These policies work with constructors (for example, when objects are taken
from an external container rather than created on demand), data members and
functions in general.<br/>
@@ -881,38 +920,15 @@ Multiple formats are supported when it comes to defining a property:
A tuple contains one or more properties. All of them are treated individually.
* Annotations:
Note that it's not possible to invoke `prop` multiple times for the same meta
object and trying to do that will result in a compilation error.<br/>
However, the `props` function is available to associate several properties at
once. In this case, properties in the key/value form aren't allowed, since they
would be interpreted as two different properties rather than a single one.
```cpp
entt::meta<my_type>().type("reflected_type"_hs).prop(&property_generator);
```
An annotation is an invocable object that returns one or more properties. All
of them are treated individually.
It's possible to invoke the `prop` function several times if needed, one for
each property to associate with the last meta object created:
```cpp
entt::meta<my_type>()
.type("reflected_type"_hs)
.prop(entt::hashed_string{"Name"}, "Reflected Type")
.data<&my_type::data_member>("member"_hs)
.prop(std::make_pair("tooltip"_hs, "Member"))
.prop(my_enum::a_value, 42);
```
Alternatively, the `props` function is available to associate several properties
at a time. However, in this case properties in the key/value form aren't
allowed, since they would be interpreted as two different properties rather than
a single one.
The meta objects for which properties are supported are currently the meta
types, meta constructors, meta data and meta functions. It's not possible to
attach properties to other types of meta objects and the factory returned as a
result of their construction won't allow such an operation.
These types offer a couple of member functions named `prop` to iterate all
The meta objects for which properties are supported are currently meta types,
meta data and meta functions.<br/>
These types also offer a couple of member functions named `prop` to iterate all
properties at once or to search a specific property by key:
```cpp
@@ -942,7 +958,21 @@ objects from it and making its identifier no longer visible. The underlying node
will remain available though, as if it were implicitly generated:
```cpp
entt::resolve<my_type>().reset();
entt::meta_reset<my_type>();
```
The type can be re-registered later with a completely different name and form.
It's also possible to reset types by their unique identifiers if required:
```cpp
entt::meta_reset("my_type"_hs);
```
Finally, there exists a non-template overload of the `meta_reset` function that
doesn't accept argument and resets all searchable types (that is, all types that
were assigned an unique identifier):
```cpp
entt::meta_reset();
```
All types can be re-registered later with a completely different name and form.

View File

@@ -10,7 +10,7 @@
* [Concept and implementation](#concept-and-implementation)
* [Deduced interface](#deduced-interface)
* [Defined interface](#defined-interface)
* [Fullfill a concept](#fullfill-a-concept)
* [Fulfill a concept](#fulfill-a-concept)
* [Inheritance](#inheritance)
* [Static polymorphism in the wild](#static-polymorphism-in-the-wild)
* [Storage size and alignment requirement](#storage-size-and-alignment-requirement)
@@ -24,8 +24,8 @@ Static polymorphism is a very powerful tool in C++, albeit sometimes cumbersome
to obtain.<br/>
This module aims to make it simple and easy to use.
The library allows to define _concepts_ as interfaces to fullfill with concrete
classes withouth having to inherit from a common base.<br/>
The library allows to define _concepts_ as interfaces to fulfill with concrete
classes without having to inherit from a common base.<br/>
This is, among others, one of the advantages of static polymorphism in general
and of a generic wrapper like that offered by the `poly` class template in
particular.<br/>
@@ -74,7 +74,7 @@ limitations and it's therefore useful to be able to get around the deduction by
providing a custom definition for the static virtual table.
Once the interface is defined, it will be sufficient to provide a generic
implementation to fullfill the concept.<br/>
implementation to fulfill the concept.<br/>
Also in this case, the library allows customizations based on types or families
of types, so as to be able to go beyond the generic case where necessary.
@@ -94,7 +94,7 @@ struct Drawable: entt::type_list<> {
```
It's recognizable by the fact that it inherits from an empty type list.<br/>
Functions can also be const, accept any number of paramters and return a type
Functions can also be const, accept any number of parameters and return a type
other than `void`:
```cpp
@@ -120,7 +120,7 @@ external call:
struct Drawable: entt::type_list<> {
template<typename Base>
struct type: Base {
bool draw() const { entt::poly_call<0>(*this); }
void draw() const { entt::poly_call<0>(*this); }
};
// ...
@@ -187,7 +187,7 @@ the interface itself.
Explicitly defining a static virtual table suppresses the deduction step and
allows maximum flexibility when providing the implementation for a concept.
## Fullfill a concept
## Fulfill a concept
The `impl` alias template of a concept is used to define how it's fulfilled:
@@ -202,7 +202,7 @@ struct Drawable: entt::type_list<> {
In this case, it's stated that the `draw` method of a generic type will be
enough to satisfy the requirements of the `Drawable` concept.<br/>
Both member functions and free functions are supported to fullfill concepts:
Both member functions and free functions are supported to fulfill concepts:
```cpp
template<typename Type>

View File

@@ -28,6 +28,8 @@ I hope this list can grow much more in the future:
ECS that uses sparse sets to keep track of entities in systems.
* [EntityX](https://github.com/alecthomas/entityx): a bitset based ECS that
uses a single large matrix of components indexed with entities.
* [Gaia-ECS](https://github.com/richardbiely/gaia-ecs): a chunk based
archetype ECS.
* [Polypropylene](https://github.com/pmbittner/Polypropylene): a hybrid
solution between an ECS and dynamic mixins.
@@ -44,15 +46,25 @@ I hope this list can grow much more in the future:
by `EnTT`.
* Javascript:
* [\@javelin/ecs](https://github.com/3mcd/javelin/tree/master/packages/ecs): an
archetype ECS in TypeScript.
* [\@javelin/ecs](https://github.com/3mcd/javelin/tree/master/packages/ecs):
an archetype ECS in TypeScript.
* [ecsy](https://github.com/MozillaReality/ecsy): I haven't had the time to
investigate the underlying design of `ecsy` but it looks cool anyway.
* Perl:
* [Game::Entities](https://gitlab.com/jjatria/perl-game-entities): a simple
entity registry for ECS designs inspired by `EnTT`.
* Raku:
* [Game::Entities](https://gitlab.com/jjatria/raku-game-entities): a simple
entity registry for ECS designs inspired by `EnTT`.
* Rust:
* [Legion](https://github.com/TomGillen/legion): a chunk based archetype ECS.
* [Shipyard](https://github.com/leudz/shipyard): it borrows some ideas from
`EnTT` and offers a sparse sets based ECS with grouping functionalities.
* [Sparsey](https://github.com/LechintanTudor/sparsey): sparse set based ECS
written in Rust.
* [Specs](https://github.com/amethyst/specs): a parallel ECS based mainly on
hierarchical bitsets that allows different types of storage as needed.

View File

@@ -7,6 +7,9 @@
* [Introduction](#introduction)
* [The resource, the loader and the cache](#the-resource-the-loader-and-the-cache)
* [Resource handle](#resource-handle)
* [Loaders](#loader)
* [The cache class](#the-cache)
<!--
@endcond TURN_OFF_DOXYGEN
-->
@@ -21,211 +24,169 @@ Examples are loading everything on start, loading on request, predictive
loading, and so on.
`EnTT` doesn't pretend to offer a _one-fits-all_ solution for the different
cases. Instead, it offers a minimal and perhaps trivial cache that can be useful
most of the time during prototyping and sometimes even in a production
environments.<br/>
For those interested in the subject, the plan is to improve it considerably over
time in terms of performance, memory usage and functionalities. Hoping to make
it, of course, one step at a time.
cases.<br/>
Instead, the library offers a minimal, general purpose resource cache that might
be useful in many cases.
# The resource, the loader and the cache
There are three main actors in the model: the resource, the loader and the
cache.
The _resource_ is whatever users want it to be. An image, a video, an audio,
whatever. There are no limits.<br/>
As a minimal example:
Resource, loader and cache are the three main actors for the purpose.<br/>
The _resource_ is an image, an audio, a video or any other type:
```cpp
struct my_resource { const int value; };
```
A _loader_ is a class the aim of which is to load a specific resource. It has to
inherit directly from a dedicated base class as in the following example:
The _loader_ is a callable type the aim of which is to load a specific resource:
```cpp
struct my_loader final: entt::resource_loader<my_loader, my_resource> {
// ...
};
```
struct my_loader final {
using result_type = std::shared_ptr<my_resource>;
Where `my_resource` is the type of resources it creates.<br/>
A resource loader must also expose a public const member function named `load`
that accepts a variable number of arguments and returns a shared pointer to a
resource.<br/>
As an example:
```cpp
struct my_loader: entt::resource_loader<my_loader, my_resource> {
std::shared_ptr<my_resource> load(int value) const {
result_type operator()(int value) const {
// ...
return std::shared_ptr<my_resource>(new my_resource{ value });
return std::make_shared<my_resource>(value);
}
};
```
In general, resource loaders should not have a state or retain data of any type.
They should let the cache manage their resources instead.<br/>
As a side note, base class and CRTP idiom aren't strictly required with the
current implementation. One could argue that a cache can easily work with
loaders of any type. However, future changes won't be breaking ones by forcing
the use of a base class today and that's why the model is already in its place.
Its function operator can accept any arguments and should return a value of the
declared result type (`std::shared_ptr<my_resource>` in the example).<br/>
A loader can also overload its function call operator to make it possible to
construct the same or another resource from different lists of arguments.
Finally, a cache is a specialization of a class template tailored to a specific
resource:
resource and (optionally) a loader:
```cpp
using my_cache = entt::resource_cache<my_resource>;
using my_cache = entt::resource_cache<my_resource, my_loader>;
// ...
my_cache cache{};
```
The idea is to create different caches for different types of resources and to
manage each one independently in the most appropriate way.<br/>
The cache is meant to be used to create different caches for different types of
resources and to manage each one independently in the most appropriate way.<br/>
As a (very) trivial example, audio tracks can survive in most of the scenes of
an application while meshes can be associated with a single scene and then
discarded when users leave it.
an application while meshes can be associated with a single scene only, then
discarded when a player leaves it.
A cache offers a set of basic functionalities to query its internal state and to
_organize_ it:
## Resource handle
Resources aren't returned directly to the caller. Instead, they are wrapped in a
_resource handle_ identified by the `entt::resource` class template.<br/>
For those who know the _flyweight design pattern_ already, that's exactly what
it is. To all others, this is the time to brush up on some notions instead.
A shared pointer could have been used as a resource handle. In fact, the default
handle mostly maps the interface of its standard counterpart and only adds a few
things to it.<br/>
However, the handle in `EnTT` is designed as a standalone class template named
`resource`. It boils down to the fact that specializing a class in the standard
is often undefined behavior while having the ability to specialize the handle
for one, more or all resource types could help over time.
## Loaders
A loader is a class that is responsible for _loading_ the resources.<br/>
By default, it's just a callable object which forwards its arguments to the
resource itself. That is, a _passthrough type_. All the work is demanded to the
constructor(s) of the resource itself.<br/>
Loaders also are fully customizable as expected.
A custom loader is a class with at least one function call operator and a member
type named `result_type`.<br/>
The loader isn't required to return a resource handle. As long as `return_type`
is suitable for constructing a handle, that's fine.
When using the default handle, it expects a resource type which is convertible
to or suitable for constructing an `std::shared_ptr<Type>` (where `Type` is the
actual resource type).<br/>
In other terms, the loader should return shared pointers to the given resource
type. However, it isn't mandatory. Users can easily get around this constraint
by specializing both the handle and the loader.
A cache forwards all its arguments to the loader if required. This means that
loaders can also support tag dispatching to offer different loading policies:
```cpp
// gets the number of resources managed by a cache
const auto size = cache.size();
struct my_loader {
using result_type = std::shared_ptr<my_resource>;
// checks if a cache contains at least a valid resource
const auto empty = cache.empty();
struct from_disk_tag{};
struct from_network_tag{};
// clears a cache and discards its content
cache.clear();
template<typename Args>
result_type operator()(from_disk_tag, Args&&... args) {
// ...
return std::make_shared<my_resource>(std::forward<Args>(args)...);
}
template<typename Args>
result_type operator()(from_network_tag, Args&&... args) {
// ...
return std::make_shared<my_resource>(std::forward<Args>(args)...);
}
}
```
Besides these member functions, a cache contains what is needed to load, use and
discard resources of the given type.<br/>
Before exploring this part of the interface, it makes sense to mention how
resources are identified. They have type `id_type` and therefore they can be
created explicitly as in the following example:
This makes the whole loading logic quite flexible and easy to extend over time.
## The cache class
The cache is the class that is asked to _connect the dots_.<br/>
It loads the resources, store them aside and returns handles as needed:
```cpp
constexpr auto identifier = "my/resource/identifier"_hs;
// this is equivalent to the following
constexpr entt::id_type hs = entt::hashed_string{"my/resource/identifier"};
entt::resource_cache<my_resource, my_loader> cache{};
```
The class `hashed_string` is described in a dedicated section, so I won't go in
details here.
Resources are loaded and thus stored in a cache through the `load` member
function. It accepts the loader to use as a template parameter, the resource
identifier and the parameters used to construct the resource as arguments:
Under the hood, a cache is nothing more than a map where the key value has type
`entt::id_type` while the mapped value is whatever type its loader returns.<br/>
For this reason, it offers most of the functionality a user would expect from a
map, such as `empty` or `size` and so on. Similarly, it's an iterable type that
also supports indexing by resource id:
```cpp
// uses the identifier declared above
cache.load<my_loader>(identifier, 0);
for(entt::resource<my_resource> curr: cache) {
// ...
}
// uses a hashed string directly
cache.load<my_loader>("another/identifier"_hs, 42);
```
The function returns a handle to the resource, whether it already exists or is
loaded. In case the loader returns an invalid pointer, the handle is invalid as
well and therefore it can be easily used with an `if` statement:
```cpp
if(entt::resource_handle handle = cache.load<my_loader>("another/identifier"_hs, 42); handle) {
if(entt::resource<my_resource> res = cache["resource/id"_hs]; res) {
// ...
}
```
Before trying to load a resource, the `contains` member function can be used to
know if a cache already contains a specific resource:
Please, refer to the inline documentation for all the details about the other
functions (for example `contains` or `erase`).
Set aside the part of the API that this class shares with a map, it also adds
something on top of it in order to address the most common requirements of a
resource cache.<br/>
In particular, it doesn't have an `emplace` member function which is replaced by
`load` and `force_load` instead (where the former loads a new resource only if
not present while the second triggers a forced loading in any case):
```cpp
auto exists = cache.contains("my/identifier"_hs);
auto ret = cache.load("resource/id"_hs);
// true only if the resource was not already present
const bool loaded = ret.second;
// takes the resource handle pointed to by the returned iterator
entt::resource<my_resource> res = *ret.first;
```
There exists also a member function to use to force a reload of an already
existing resource if needed:
Note that the hashed string is used for convenience in the example above.<br/>
Resource identifiers are nothing more than integral values. Therefore, plain
numbers as well as non-class enum value are accepted.
```cpp
auto handle = cache.reload<my_loader>("another/identifier"_hs, 42);
```
As above, the function returns a handle to the resource that is invalid in case
of errors. The `reload` member function is a kind of alias of the following
snippet:
```cpp
cache.discard(identifier);
cache.load<my_loader>(identifier, 42);
```
Where the `discard` member function is used to get rid of a resource if loaded.
In case the cache doesn't contain a resource for the given identifier, `discard`
does nothing and returns immediately.
So far, so good. Resources are finally loaded and stored within the cache.<br/>
They are returned to users in the form of handles. To get one of them later on:
```cpp
auto handle = cache.handle("my/identifier"_hs);
```
The idea behind a handle is the same of the flyweight pattern. In other terms,
resources aren't copied around. Instead, instances are shared between handles.
Users of a resource own a handle that guarantees that a resource isn't destroyed
until all the handles are destroyed, even if the resource itself is removed from
the cache.<br/>
Handles are tiny objects both movable and copyable. They return the contained
resource as a (possibly const) reference on request:
* By means of the `get` member function:
```cpp
auto &resource = handle.get();
```
* Using the proper cast operator:
```cpp
auto &resource = handle;
```
* Through the dereference operator:
```cpp
auto &resource = *handle;
```
The resource can also be accessed directly using the arrow operator if required:
```cpp
auto value = handle->value;
```
To test if a handle is still valid, the cast operator to `bool` allows users to
use it in a guard:
```cpp
if(handle) {
// ...
}
```
Finally, in case there is the need to load a resource and thus to get a handle
without storing the resource itself in the cache, users can rely on the `temp`
member function template.<br/>
The declaration is similar to that of `load`, a (possibly invalid) handle for
the resource is returned also in this case:
```cpp
if(auto handle = cache.temp<my_loader>(42); handle) {
// ...
}
```
Do not forget to test the handle for validity. Otherwise, getting a reference to
the resource it points may result in undefined behavior.
Moreover, it's worth mentioning that both the iterators of a cache and its
indexing operators return resource handles rather than instances of the mapped
type.<br/>
Since the cache has no control over the loader and a resource isn't required to
also be convertible to bool, these handles can be invalid. This usually means an
error in the user logic but it may also be an _expected_ event.<br/>
It's therefore recommended to verify handles validity with a check in debug (for
example, when loading) or an appropriate logic in retail.

View File

@@ -11,6 +11,7 @@
* [Lambda support](#lambda-support)
* [Signals](#signals)
* [Event dispatcher](#event-dispatcher)
* [Named queues](#named-queues)
* [Event emitter](#event-emitter)
<!--
@endcond TURN_OFF_DOXYGEN
@@ -361,7 +362,7 @@ case:
struct my_collector {
std::vector<int> vec{};
bool operator()(int v) noexcept {
bool operator()(int v) {
vec.push_back(v);
return true;
}
@@ -375,23 +376,20 @@ signal.collect(std::ref(collector));
# Event dispatcher
The event dispatcher class is designed so as to be used in a loop. It allows
users both to trigger immediate events or to queue events to be published all
together once per tick.<br/>
This class shares part of its API with the one of the signal handler, but it
doesn't require that all the types of events are specified when declared:
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's not necessary to
_announce_ the event types in advance:
```cpp
// define a general purpose dispatcher
entt::dispatcher dispatcher{};
```
In order to register an instance of a class to a dispatcher, its type must
expose one or more member functions the arguments of which are such that `E &`
can be converted to them for each type of event `E`, no matter what the return
value is.<br/>
The name of the member function aimed to receive the event must be provided to
the `connect` member function of the sink in charge for the specific event:
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_:
```cpp
struct an_event { int value; };
@@ -409,8 +407,8 @@ dispatcher.sink<an_event>().connect<&listener::receive>(listener);
dispatcher.sink<another_event>().connect<&listener::method>(listener);
```
The `disconnect` member function follows the same pattern and can be used to
remove one listener at a time or all of them at once:
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);
@@ -418,14 +416,10 @@ dispatcher.sink<another_event>().disconnect(listener);
```
The `trigger` member function serves the purpose of sending an immediate event
to all the listeners registered so far. It offers a convenient approach that
relieves users from having to create the event itself. Instead, it's enough to
specify the type of event and provide all the parameters required to construct
it.<br/>
As an example:
to all the listeners registered so far:
```cpp
dispatcher.trigger<an_event>(42);
dispatcher.trigger(an_event{42});
dispatcher.trigger<another_event>();
```
@@ -434,16 +428,14 @@ method can be used to push around urgent messages like an _is terminating_
notification on a mobile app.
On the other hand, the `enqueue` member function queues messages together and
allows to maintain control over the moment they are sent to listeners. The
signature of this method is more or less the same of `trigger`:
helps to maintain control over the moment they are sent to listeners:
```cpp
dispatcher.enqueue<an_event>(42);
dispatcher.enqueue<another_event>();
dispatcher.enqueue(another_event{});
```
Events are stored aside until the `update` member function is invoked, then all
the messages that are still pending are sent to the listeners at once:
Events are stored aside until the `update` member function is invoked:
```cpp
// emits all the events of the given type at once
@@ -456,6 +448,30 @@ dispatcher.update();
This way users can embed the dispatcher in a loop and literally dispatch events
once per tick to their systems.
## Named queues
All queues within a dispatcher are associated by default with an event type and
then retrieved from it.<br/>
However, it's possible to create queues with different _names_ (and therefore
also multiple queues for a single type). In fact, more or less all functions
also take an additional parameter. As an example:
```cpp
dispatcher.sink<an_event>("custom"_hs).connect<&listener::receive>(listener);
```
In this case, the term _name_ is misused as these are actual numeric identifiers
of type `id_type`.<br/>
An exception to this rule is the `enqueue` function. There is no additional
parameter for it but rather a different function:
```cpp
dispatcher.enqueue_hint<an_event>("custom"_hs, 42);
```
This is mainly due to the template argument deduction rules and unfortunately
there is no real (elegant) way to avoid it.
# Event emitter
A general purpose event emitter thought mainly for those cases where it comes to

View File

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

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="entt::dense_map&lt;*&gt;">
<Intrinsic Name="size" Expression="packed.first_base::value.size()"/>
<Intrinsic Name="bucket_count" Expression="sparse.first_base::value.size()"/>
<DisplayString>{{ size={ size() } }}</DisplayString>
<Expand>
<Item Name="[capacity]" ExcludeView="simple">packed.first_base::value.capacity()</Item>
<Item Name="[bucket_count]" ExcludeView="simple">bucket_count()</Item>
<Item Name="[load_factor]" ExcludeView="simple">(float)size() / (float)bucket_count()</Item>
<Item Name="[max_load_factor]" ExcludeView="simple">threshold</Item>
<IndexListItems>
<Size>size()</Size>
<ValueNode>packed.first_base::value[$i].element</ValueNode>
</IndexListItems>
</Expand>
</Type>
<Type Name="entt::dense_set&lt;*&gt;">
<Intrinsic Name="size" Expression="packed.first_base::value.size()"/>
<Intrinsic Name="bucket_count" Expression="sparse.first_base::value.size()"/>
<DisplayString>{{ size={ size() } }}</DisplayString>
<Expand>
<Item Name="[capacity]" ExcludeView="simple">packed.first_base::value.capacity()</Item>
<Item Name="[bucket_count]" ExcludeView="simple">bucket_count()</Item>
<Item Name="[load_factor]" ExcludeView="simple">(float)size() / (float)bucket_count()</Item>
<Item Name="[max_load_factor]" ExcludeView="simple">threshold</Item>
<IndexListItems>
<Size>size()</Size>
<ValueNode>packed.first_base::value[$i].second</ValueNode>
</IndexListItems>
</Expand>
</Type>
</AutoVisualizer>

33
natvis/entt/core.natvis Normal file
View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="entt::basic_any&lt;*&gt;">
<DisplayString>{{ type={ info->alias,na }, policy={ mode,en } }}</DisplayString>
</Type>
<Type Name="entt::compressed_pair&lt;*&gt;">
<Intrinsic Name="first" Optional="true" Expression="((first_base*)this)->value"/>
<Intrinsic Name="first" Optional="true" Expression="*(first_base::base_type*)this"/>
<Intrinsic Name="second" Optional="true" Expression="((second_base*)this)->value"/>
<Intrinsic Name="second" Optional="true" Expression="*(second_base::base_type*)this"/>
<DisplayString >({ first() }, { second() })</DisplayString>
<Expand>
<Item Name="[first]">first()</Item>
<Item Name="[second]">second()</Item>
</Expand>
</Type>
<Type Name="entt::basic_hashed_string&lt;*&gt;">
<DisplayString Condition="base_type::repr != nullptr">{{ hash={ base_type::hash } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[data]">base_type::repr,na</Item>
<Item Name="[length]">base_type::length</Item>
</Expand>
</Type>
<Type Name="entt::type_info">
<DisplayString Condition="seq != 0u">{{ name={ alias,na } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[hash]">identifier</Item>
<Item Name="[index]">seq</Item>
</Expand>
</Type>
</AutoVisualizer>

145
natvis/entt/entity.natvis Normal file
View File

@@ -0,0 +1,145 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="entt::basic_registry&lt;*&gt;">
<Intrinsic Name="pools_size" Expression="pools.packed.first_base::value.size()"/>
<Intrinsic Name="vars_size" Expression="vars.data.packed.first_base::value.size()"/>
<Intrinsic Name="to_entity" Expression="*((entity_traits::entity_type *)&amp;entity) &amp; entity_traits::entity_mask">
<Parameter Name="entity" Type="entity_traits::value_type &amp;"/>
</Intrinsic>
<DisplayString>{{ size={ entities.size() } }}</DisplayString>
<Expand>
<Item IncludeView="simple" Name="[entities]">entities,view(simple)nr</Item>
<Synthetic Name="[entities]" ExcludeView="simple">
<DisplayString>{ entities.size() }</DisplayString>
<Expand>
<CustomListItems>
<Variable Name="pos" InitialValue="0" />
<Variable Name="last" InitialValue="entities.size()"/>
<Loop>
<Break Condition="pos == last"/>
<If Condition="to_entity(entities[pos]) == pos">
<Item Name="[{ pos }]">entities[pos]</Item>
</If>
<Exec>++pos</Exec>
</Loop>
</CustomListItems>
</Expand>
</Synthetic>
<Synthetic Name="[destroyed]" ExcludeView="simple">
<DisplayString>{ to_entity(free_list) != entity_traits::entity_mask }</DisplayString>
<Expand>
<CustomListItems>
<Variable Name="it" InitialValue="to_entity(free_list)" />
<Loop>
<Break Condition="it == entity_traits::entity_mask"/>
<Item Name="[{ it }]">entities[it]</Item>
<Exec>it = to_entity(entities[it])</Exec>
</Loop>
</CustomListItems>
</Expand>
</Synthetic>
<Synthetic Name="[pools]">
<DisplayString>{ pools_size() }</DisplayString>
<Expand>
<IndexListItems ExcludeView="simple">
<Size>pools_size()</Size>
<ValueNode>*pools.packed.first_base::value[$i].element.second</ValueNode>
</IndexListItems>
<IndexListItems IncludeView="simple">
<Size>pools_size()</Size>
<ValueNode>*pools.packed.first_base::value[$i].element.second,view(simple)</ValueNode>
</IndexListItems>
</Expand>
</Synthetic>
<Item Name="[groups]" ExcludeView="simple">groups.size()</Item>
<Synthetic Name="[vars]">
<DisplayString>{ vars_size() }</DisplayString>
<Expand>
<IndexListItems>
<Size>vars_size()</Size>
<ValueNode>vars.data.packed.first_base::value[$i].element.second</ValueNode>
</IndexListItems>
</Expand>
</Synthetic>
</Expand>
</Type>
<Type Name="entt::basic_sparse_set&lt;*&gt;">
<DisplayString>{{ size={ packed.size() }, type={ info->alias,na } }}</DisplayString>
<Expand>
<Item Name="[capacity]" ExcludeView="simple">packed.capacity()</Item>
<Item Name="[policy]">mode,en</Item>
<Synthetic Name="[sparse]">
<DisplayString>{ sparse.size() * entity_traits::page_size }</DisplayString>
<Expand>
<ExpandedItem IncludeView="simple">sparse,view(simple)</ExpandedItem>
<CustomListItems ExcludeView="simple">
<Variable Name="pos" InitialValue="0"/>
<Variable Name="page" InitialValue="0"/>
<Variable Name="offset" InitialValue="0"/>
<Variable Name="last" InitialValue="sparse.size() * entity_traits::page_size"/>
<Loop>
<Break Condition="pos == last"/>
<Exec>page = pos / entity_traits::page_size</Exec>
<Exec>offset = pos &amp; (entity_traits::page_size - 1)</Exec>
<If Condition="sparse[page] &amp;&amp; (*((entity_traits::entity_type *)&amp;sparse[page][offset]) &lt; ~entity_traits::entity_mask)">
<Item Name="[{ pos }]">*((entity_traits::entity_type *)&amp;sparse[page][offset]) &amp; entity_traits::entity_mask</Item>
</If>
<Exec>++pos</Exec>
</Loop>
</CustomListItems>
</Expand>
</Synthetic>
<Synthetic Name="[packed]">
<DisplayString>{ packed.size() }</DisplayString>
<Expand>
<ExpandedItem IncludeView="simple">packed,view(simple)</ExpandedItem>
<CustomListItems ExcludeView="simple">
<Variable Name="pos" InitialValue="0"/>
<Variable Name="last" InitialValue="packed.size()"/>
<Loop>
<Break Condition="pos == last"/>
<If Condition="*((entity_traits::entity_type *)&amp;packed[pos]) &lt; ~entity_traits::entity_mask">
<Item Name="[{ pos }]">packed[pos]</Item>
</If>
<Exec>++pos</Exec>
</Loop>
</CustomListItems>
</Expand>
</Synthetic>
</Expand>
</Type>
<Type Name="entt::basic_storage&lt;*&gt;">
<DisplayString>{{ size={ base_type::packed.size() }, type={ base_type::info->alias,na } }}</DisplayString>
<Expand>
<Item Name="[capacity]" Optional="true" ExcludeView="simple">packed.first_base::value.capacity() * comp_traits::page_size</Item>
<Item Name="[page size]" Optional="true" ExcludeView="simple">comp_traits::page_size</Item>
<Item Name="[base]" ExcludeView="simple">(base_type*)this,nand</Item>
<Item Name="[base]" IncludeView="simple">(base_type*)this,view(simple)nand</Item>
<!-- having SFINAE-like techniques in natvis is priceless :) -->
<CustomListItems Condition="packed.first_base::value.size() != 0" Optional="true">
<Variable Name="pos" InitialValue="0" />
<Variable Name="last" InitialValue="base_type::packed.size()"/>
<Loop>
<Break Condition="pos == last"/>
<If Condition="*((base_type::entity_traits::entity_type *)&amp;base_type::packed[pos]) &lt; ~base_type::entity_traits::entity_mask">
<Item Name="[{ pos }]">packed.first_base::value[pos / comp_traits::page_size][pos &amp; (comp_traits::page_size - 1)]</Item>
</If>
<Exec>++pos</Exec>
</Loop>
</CustomListItems>
</Expand>
</Type>
<Type Name="entt::basic_view&lt;*&gt;">
<DisplayString>{{ size_hint={ view->packed.size() } }}</DisplayString>
<Expand>
<Item Name="[pools]">pools,na</Item>
<Item Name="[filter]">filter,na</Item>
</Expand>
</Type>
<Type Name="entt::null_t">
<DisplayString>&lt;null&gt;</DisplayString>
</Type>
<Type Name="entt::tombstone_t">
<DisplayString>&lt;tombstone&gt;</DisplayString>
</Type>
</AutoVisualizer>

View File

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

197
natvis/entt/meta.natvis Normal file
View File

@@ -0,0 +1,197 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="entt::meta_any">
<DisplayString Condition="node != nullptr">{{ type={ node->info->alias,na }, policy={ storage.mode,en } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<ExpandedItem Condition="node != nullptr">*node</ExpandedItem>
</Expand>
</Type>
<Type Name="entt::meta_associative_container">
<DisplayString Condition="mapped_type_node != nullptr">{{ key_type={ key_type_node->info->alias,na }, mapped_type={ mapped_type_node->info->alias,na } }}</DisplayString>
<DisplayString Condition="key_type_node != nullptr">{{ key_type={ key_type_node->info->alias,na } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
</Type>
<Type Name="entt::internal::meta_base_node">
<DisplayString Condition="type != nullptr">{{ type={ type->info->alias,na } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
</Type>
<Type Name="entt::internal::meta_conv_node">
<DisplayString Condition="type != nullptr">{{ type={ type->info->alias,na } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
</Type>
<Type Name="entt::internal::meta_ctor_node">
<DisplayString Condition="arg != nullptr">{{ arity={ arity } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
</Type>
<Type Name="entt::internal::meta_data_node">
<Intrinsic Name="has_property" Expression="!!(traits &amp; property)">
<Parameter Name="property" Type="int"/>
</Intrinsic>
<DisplayString Condition="type != nullptr">{{ type={ type->info->alias,na } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[id]">id</Item>
<Item Name="[arity]">arity</Item>
<Item Name="[is_const]">has_property(entt::internal::meta_traits::is_const)</Item>
<Item Name="[is_static]">has_property(entt::internal::meta_traits::is_static)</Item>
<Synthetic Name="[prop]" Condition="prop != nullptr">
<Expand>
<LinkedListItems>
<HeadPointer>prop</HeadPointer>
<NextPointer>next</NextPointer>
<ValueNode>*this</ValueNode>
</LinkedListItems>
</Expand>
</Synthetic>
</Expand>
</Type>
<Type Name="entt::meta_data">
<DisplayString Condition="node != nullptr">{ *node }</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
</Expand>
</Type>
<Type Name="entt::internal::meta_func_node" >
<Intrinsic Name="has_property" Expression="!!(traits &amp; property)">
<Parameter Name="property" Type="int"/>
</Intrinsic>
<DisplayString Condition="ret != nullptr">{{ arity={ arity }, ret={ ret->info->alias,na } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[id]">id</Item>
<Item Name="[is_const]">has_property(entt::internal::meta_traits::is_const)</Item>
<Item Name="[is_static]">has_property(entt::internal::meta_traits::is_static)</Item>
<Synthetic Name="[prop]" Condition="prop != nullptr">
<Expand>
<LinkedListItems>
<HeadPointer>prop</HeadPointer>
<NextPointer>next</NextPointer>
<ValueNode>*this</ValueNode>
</LinkedListItems>
</Expand>
</Synthetic>
</Expand>
</Type>
<Type Name="entt::meta_func">
<DisplayString Condition="node != nullptr">{ *node }</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
</Expand>
</Type>
<Type Name="entt::meta_handle">
<DisplayString>{ any }</DisplayString>
</Type>
<Type Name="entt::internal::meta_prop_node">
<DisplayString Condition="value.node != nullptr">{{ key_type={ id.node->info->alias,na }, mapped_type={ value.node->info->alias,na } }}</DisplayString>
<DisplayString Condition="id.node != nullptr">{{ key_type={ id.node->info->alias,na } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[key]">id</Item>
<Item Name="[value]">value</Item>
</Expand>
</Type>
<Type Name="entt::meta_prop">
<DisplayString Condition="node != nullptr">{ *node }</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
</Expand>
</Type>
<Type Name="entt::meta_sequence_container">
<DisplayString Condition="value_type_node != nullptr">{{ value_type={ value_type_node->info->alias,na } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
</Type>
<Type Name="entt::internal::meta_template_node">
<DisplayString Condition="type != nullptr">{{ type={ type->info->alias,na } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[arity]">arity</Item>
</Expand>
</Type>
<Type Name="entt::internal::meta_type_node">
<Intrinsic Name="has_property" Expression="!!(traits &amp; property)">
<Parameter Name="property" Type="int"/>
</Intrinsic>
<DisplayString Condition="info != nullptr">{{ type={ info->alias,na } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[id]">id</Item>
<Item Name="[sizeof]">size_of</Item>
<Item Name="[is_arithmetic]">has_property(entt::internal::meta_traits::is_arithmetic)</Item>
<Item Name="[is_array]">has_property(entt::internal::meta_traits::is_array)</Item>
<Item Name="[is_enum]">has_property(entt::internal::meta_traits::is_enum)</Item>
<Item Name="[is_class]">has_property(entt::internal::meta_traits::is_class)</Item>
<Item Name="[is_pointer]">has_property(entt::internal::meta_traits::is_pointer)</Item>
<Item Name="[is_meta_pointer_like]">has_property(entt::internal::meta_traits::is_meta_pointer_like)</Item>
<Item Name="[is_meta_sequence_container]">has_property(entt::internal::meta_traits::is_meta_sequence_container)</Item>
<Item Name="[is_meta_associative_container]">has_property(entt::internal::meta_traits::is_meta_associative_container)</Item>
<Item Name="[default_constructor]">default_constructor != nullptr</Item>
<Item Name="[conversion_helper]">conversion_helper != nullptr</Item>
<Item Name="[template_info]" Condition="templ != nullptr">*templ</Item>
<Synthetic Name="[ctor]" Condition="ctor != nullptr">
<Expand>
<LinkedListItems>
<HeadPointer>ctor</HeadPointer>
<NextPointer>next</NextPointer>
<ValueNode>*this</ValueNode>
</LinkedListItems>
</Expand>
</Synthetic>
<Synthetic Name="[base]" Condition="base != nullptr">
<Expand>
<LinkedListItems>
<HeadPointer>base</HeadPointer>
<NextPointer>next</NextPointer>
<ValueNode>*this</ValueNode>
</LinkedListItems>
</Expand>
</Synthetic>
<Synthetic Name="[conv]" Condition="conv != nullptr">
<Expand>
<LinkedListItems>
<HeadPointer>conv</HeadPointer>
<NextPointer>next</NextPointer>
<ValueNode>*this</ValueNode>
</LinkedListItems>
</Expand>
</Synthetic>
<Synthetic Name="[data]" Condition="data != nullptr">
<Expand>
<LinkedListItems>
<HeadPointer>data</HeadPointer>
<NextPointer>next</NextPointer>
<ValueNode>*this</ValueNode>
</LinkedListItems>
</Expand>
</Synthetic>
<Synthetic Name="[func]" Condition="func != nullptr">
<Expand>
<LinkedListItems>
<HeadPointer>func</HeadPointer>
<NextPointer>next</NextPointer>
<ValueNode>*this</ValueNode>
</LinkedListItems>
</Expand>
</Synthetic>
<Synthetic Name="[prop]" Condition="prop != nullptr">
<Expand>
<LinkedListItems>
<HeadPointer>prop</HeadPointer>
<NextPointer>next</NextPointer>
<ValueNode>*this</ValueNode>
</LinkedListItems>
</Expand>
</Synthetic>
</Expand>
</Type>
<Type Name="entt::meta_type">
<DisplayString Condition="node != nullptr">{ *node }</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
</Expand>
</Type>
</AutoVisualizer>

View File

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

6
natvis/entt/poly.natvis Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="entt::basic_poly&lt;*&gt;">
<DisplayString>{ storage }</DisplayString>
</Type>
</AutoVisualizer>

View File

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

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="entt::resource&lt;*&gt;">
<DisplayString>{ value }</DisplayString>
<Expand>
<ExpandedItem>value</ExpandedItem>
</Expand>
</Type>
<Type Name="entt::resource_cache&lt;*&gt;">
<DisplayString>{ pool.first_base::value }</DisplayString>
<Expand>
<ExpandedItem>pool.first_base::value</ExpandedItem>
</Expand>
</Type>
</AutoVisualizer>

52
natvis/entt/signal.natvis Normal file
View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="entt::connection">
<DisplayString>{{ bound={ signal != nullptr } }}</DisplayString>
</Type>
<Type Name="entt::delegate&lt;*&gt;">
<DisplayString>{{ type={ "$T1" } }}</DisplayString>
<Expand>
<Item Name="[empty]">fn == nullptr</Item>
<Item Name="[data]">instance</Item>
</Expand>
</Type>
<Type Name="entt::dispatcher">
<DisplayString>{{ size={ pools.size() } }}</DisplayString>
<Expand>
<Synthetic Name="[pools]">
<DisplayString>{ pools.size() }</DisplayString>
<Expand>
<IndexListItems>
<Size>pools.size()</Size>
<ValueNode>*pools.packed.first_base::value[$i].element.second</ValueNode>
</IndexListItems>
</Expand>
</Synthetic>
</Expand>
</Type>
<Type Name="entt::internal::dispatcher_handler&lt;*&gt;">
<DisplayString>{{ size={ events.size() }, event={ "$T1" } }}</DisplayString>
<Expand>
<Item Name="[signal]">signal</Item>
</Expand>
</Type>
<Type Name="entt::scoped_connection">
<DisplayString>{ conn }</DisplayString>
</Type>
<Type Name="entt::sigh&lt;*&gt;">
<DisplayString>{{ size={ calls.size() }, type={ "$T1" } }}</DisplayString>
<Expand>
<IndexListItems>
<Size>calls.size()</Size>
<ValueNode>calls[$i]</ValueNode>
</IndexListItems>
</Expand>
</Type>
<Type Name="entt::sink&lt;*&gt;">
<DisplayString>{{ type={ "$T1" } }}</DisplayString>
<Expand>
<Item Name="[signal]">signal,na</Item>
<Item Name="[offset]">offset</Item>
</Expand>
</Type>
</AutoVisualizer>

File diff suppressed because it is too large Load Diff

View File

@@ -1,85 +1,79 @@
#ifndef ENTT_CONFIG_CONFIG_H
#define ENTT_CONFIG_CONFIG_H
#include "version.h"
#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
# define ENTT_NOEXCEPT noexcept
# define ENTT_THROW throw
# define ENTT_TRY try
# define ENTT_CATCH catch(...)
# define ENTT_THROW throw
# define ENTT_TRY try
# define ENTT_CATCH catch(...)
#else
# define ENTT_NOEXCEPT
# define ENTT_THROW
# define ENTT_TRY if(true)
# define ENTT_CATCH if(false)
# define ENTT_THROW
# define ENTT_TRY if(true)
# define ENTT_CATCH if(false)
#endif
#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L
# include <new>
# define ENTT_LAUNDER(expr) std::launder(expr)
#else
# define ENTT_LAUNDER(expr) expr
#ifndef ENTT_NOEXCEPT
# define ENTT_NOEXCEPT noexcept
# define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
# else
# define ENTT_NOEXCEPT_IF(...)
#endif
#ifndef ENTT_USE_ATOMIC
# define ENTT_MAYBE_ATOMIC(Type) Type
#ifdef ENTT_USE_ATOMIC
# include <atomic>
# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
#else
# include <atomic>
# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
# define ENTT_MAYBE_ATOMIC(Type) Type
#endif
#ifndef ENTT_ID_TYPE
# include <cstdint>
# define ENTT_ID_TYPE std::uint32_t
# include <cstdint>
# define ENTT_ID_TYPE std::uint32_t
#endif
#ifdef ENTT_SPARSE_PAGE
static_assert(ENTT_SPARSE_PAGE && ((ENTT_SPARSE_PAGE & (ENTT_SPARSE_PAGE - 1)) == 0), "ENTT_SPARSE_PAGE must be a power of two");
#else
# define ENTT_SPARSE_PAGE 4096
#ifndef ENTT_SPARSE_PAGE
# define ENTT_SPARSE_PAGE 4096
#endif
#ifdef ENTT_PACKED_PAGE
static_assert(ENTT_PACKED_PAGE && ((ENTT_PACKED_PAGE & (ENTT_PACKED_PAGE - 1)) == 0), "ENTT_PACKED_PAGE must be a power of two");
#else
# define ENTT_PACKED_PAGE 1024
#ifndef ENTT_PACKED_PAGE
# define ENTT_PACKED_PAGE 1024
#endif
#ifdef ENTT_DISABLE_ASSERT
# undef ENTT_ASSERT
# define ENTT_ASSERT(...) (void(0))
# undef ENTT_ASSERT
# define ENTT_ASSERT(...) (void(0))
#elif !defined ENTT_ASSERT
# include <cassert>
# define ENTT_ASSERT(condition, ...) assert(condition)
# include <cassert>
# define ENTT_ASSERT(condition, ...) assert(condition)
#endif
#ifdef ENTT_NO_ETO
# include <type_traits>
# define ENTT_IGNORE_IF_EMPTY std::false_type
# define ENTT_IGNORE_IF_EMPTY false
#else
# include <type_traits>
# define ENTT_IGNORE_IF_EMPTY std::true_type
# define ENTT_IGNORE_IF_EMPTY true
#endif
#ifndef ENTT_STANDARD_CPP
#ifdef ENTT_STANDARD_CPP
# define ENTT_NONSTD false
#else
# define ENTT_NONSTD true
# if defined __clang__ || defined __GNUC__
# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
# define ENTT_PRETTY_FUNCTION_PREFIX '='
# define ENTT_PRETTY_FUNCTION_SUFFIX ']'
# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
# define ENTT_PRETTY_FUNCTION_PREFIX '='
# define ENTT_PRETTY_FUNCTION_SUFFIX ']'
# elif defined _MSC_VER
# define ENTT_PRETTY_FUNCTION __FUNCSIG__
# define ENTT_PRETTY_FUNCTION_PREFIX '<'
# define ENTT_PRETTY_FUNCTION_SUFFIX '>'
# endif
# define ENTT_PRETTY_FUNCTION __FUNCSIG__
# define ENTT_PRETTY_FUNCTION_PREFIX '<'
# define ENTT_PRETTY_FUNCTION_SUFFIX '>'
# endif
#endif
#if defined _MSC_VER
# pragma detect_mismatch("entt.version", ENTT_VERSION)
# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
#endif
#endif

7
src/entt/config/macro.h Normal file
View File

@@ -0,0 +1,7 @@
#ifndef ENTT_CONFIG_MACRO_H
#define ENTT_CONFIG_MACRO_H
#define ENTT_STR(arg) #arg
#define ENTT_XSTR(arg) ENTT_STR(arg)
#endif

View File

@@ -1,10 +1,14 @@
#ifndef ENTT_CONFIG_VERSION_H
#define ENTT_CONFIG_VERSION_H
#include "macro.h"
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 8
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_MINOR 10
#define ENTT_VERSION_PATCH 1
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
"." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
#endif

View File

@@ -0,0 +1,988 @@
#ifndef ENTT_CONTAINER_DENSE_MAP_HPP
#define ENTT_CONTAINER_DENSE_MAP_HPP
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <functional>
#include <iterator>
#include <limits>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../core/compressed_pair.hpp"
#include "../core/iterator.hpp"
#include "../core/memory.hpp"
#include "../core/type_traits.hpp"
#include "fwd.hpp"
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename Key, typename Type>
struct dense_map_node final {
using value_type = std::pair<Key, Type>;
template<typename... Args>
dense_map_node(const std::size_t pos, Args &&...args)
: 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)
: 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)
: 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)
: next{other.next},
element{entt::make_obj_using_allocator<value_type>(allocator, std::move(other.element))} {}
std::size_t next;
value_type element;
};
template<typename It>
class dense_map_iterator final {
template<typename>
friend class dense_map_iterator;
using first_type = decltype(std::as_const(std::declval<It>()->element.first));
using second_type = decltype((std::declval<It>()->element.second));
public:
using value_type = std::pair<first_type, second_type>;
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
dense_map_iterator() ENTT_NOEXCEPT
: it{} {}
dense_map_iterator(const It iter) ENTT_NOEXCEPT
: it{iter} {}
template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
dense_map_iterator(const dense_map_iterator<Other> &other) ENTT_NOEXCEPT
: it{other.it} {}
dense_map_iterator &operator++() ENTT_NOEXCEPT {
return ++it, *this;
}
dense_map_iterator operator++(int) ENTT_NOEXCEPT {
dense_map_iterator orig = *this;
return ++(*this), orig;
}
dense_map_iterator &operator--() ENTT_NOEXCEPT {
return --it, *this;
}
dense_map_iterator operator--(int) ENTT_NOEXCEPT {
dense_map_iterator orig = *this;
return operator--(), orig;
}
dense_map_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
it += value;
return *this;
}
dense_map_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
dense_map_iterator copy = *this;
return (copy += value);
}
dense_map_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
return (*this += -value);
}
dense_map_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
return (*this + -value);
}
[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT {
return {it[value].element.first, it[value].element.second};
}
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
return operator*();
}
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
return {it->element.first, it->element.second};
}
template<typename ILhs, typename IRhs>
friend std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT;
template<typename ILhs, typename IRhs>
friend bool operator==(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT;
template<typename ILhs, typename IRhs>
friend bool operator<(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT;
private:
It it;
};
template<typename ILhs, typename IRhs>
[[nodiscard]] std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return lhs.it - rhs.it;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator==(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return lhs.it == rhs.it;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator!=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return !(lhs == rhs);
}
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator<(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return lhs.it < rhs.it;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator>(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return rhs < lhs;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator<=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return !(lhs > rhs);
}
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator>=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return !(lhs < rhs);
}
template<typename It>
class dense_map_local_iterator final {
template<typename>
friend class dense_map_local_iterator;
using first_type = decltype(std::as_const(std::declval<It>()->element.first));
using second_type = decltype((std::declval<It>()->element.second));
public:
using value_type = std::pair<first_type, second_type>;
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
dense_map_local_iterator() ENTT_NOEXCEPT
: it{},
offset{} {}
dense_map_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT
: it{iter},
offset{pos} {}
template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
dense_map_local_iterator(const dense_map_local_iterator<Other> &other) ENTT_NOEXCEPT
: it{other.it},
offset{other.offset} {}
dense_map_local_iterator &operator++() ENTT_NOEXCEPT {
return offset = it[offset].next, *this;
}
dense_map_local_iterator operator++(int) ENTT_NOEXCEPT {
dense_map_local_iterator orig = *this;
return ++(*this), orig;
}
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
return operator*();
}
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
return {it[offset].element.first, it[offset].element.second};
}
[[nodiscard]] std::size_t index() const ENTT_NOEXCEPT {
return offset;
}
private:
It it;
std::size_t offset;
};
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator==(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return lhs.index() == rhs.index();
}
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator!=(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return !(lhs == rhs);
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Associative container for key-value pairs with unique keys.
*
* Internally, elements are organized into buckets. Which bucket an element is
* placed into depends entirely on the hash of its key. Keys with the same hash
* code appear in the same bucket.
*
* @tparam Key Key type of the associative container.
* @tparam Type Mapped type of the associative container.
* @tparam Hash Type of function to use to hash the keys.
* @tparam KeyEqual Type of function to use to compare the keys for equality.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Key, typename Type, typename Hash, typename KeyEqual, typename Allocator>
class dense_map {
static constexpr float default_threshold = 0.875f;
static constexpr std::size_t minimum_capacity = 8u;
using node_type = internal::dense_map_node<Key, Type>;
using alloc_traits = typename std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, std::pair<const Key, Type>>, "Invalid value type");
using sparse_container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
using packed_container_type = std::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
template<typename Other>
[[nodiscard]] std::size_t key_to_bucket(const Other &key) const ENTT_NOEXCEPT {
return fast_mod(sparse.second()(key), bucket_count());
}
template<typename Other>
[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) {
for(auto it = begin(bucket), last = end(bucket); it != last; ++it) {
if(packed.second()(it->first, key)) {
return begin() + static_cast<typename iterator::difference_type>(it.index());
}
}
return end();
}
template<typename Other>
[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const {
for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) {
if(packed.second()(it->first, key)) {
return cbegin() + static_cast<typename iterator::difference_type>(it.index());
}
}
return cend();
}
template<typename Other, typename... Args>
[[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) {
const auto index = key_to_bucket(key);
if(auto it = constrained_find(key, index); it != end()) {
return std::make_pair(it, false);
}
packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward<Other>(key)), std::forward_as_tuple(std::forward<Args>(args)...));
sparse.first()[index] = packed.first().size() - 1u;
rehash_if_required();
return std::make_pair(--end(), true);
}
template<typename Other, typename Arg>
[[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) {
const auto index = key_to_bucket(key);
if(auto it = constrained_find(key, index); it != end()) {
it->second = std::forward<Arg>(value);
return std::make_pair(it, false);
}
packed.first().emplace_back(sparse.first()[index], std::forward<Other>(key), std::forward<Arg>(value));
sparse.first()[index] = packed.first().size() - 1u;
rehash_if_required();
return std::make_pair(--end(), true);
}
void move_and_pop(const std::size_t pos) {
if(const auto last = size() - 1u; pos != last) {
packed.first()[pos] = std::move(packed.first().back());
size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first);
for(; *curr != last; curr = &packed.first()[*curr].next) {}
*curr = pos;
}
packed.first().pop_back();
}
void rehash_if_required() {
if(size() > (bucket_count() * max_load_factor())) {
rehash(bucket_count() * 2u);
}
}
public:
/*! @brief Key type of the container. */
using key_type = Key;
/*! @brief Mapped type of the container. */
using mapped_type = Type;
/*! @brief Key-value type of the container. */
using value_type = std::pair<const Key, Type>;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Type of function to use to hash the keys. */
using hasher = Hash;
/*! @brief Type of function to use to compare the keys for equality. */
using key_equal = KeyEqual;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Input iterator type. */
using iterator = internal::dense_map_iterator<typename packed_container_type::iterator>;
/*! @brief Constant input iterator type. */
using const_iterator = internal::dense_map_iterator<typename packed_container_type::const_iterator>;
/*! @brief Input iterator type. */
using local_iterator = internal::dense_map_local_iterator<typename packed_container_type::iterator>;
/*! @brief Constant input iterator type. */
using const_local_iterator = internal::dense_map_local_iterator<typename packed_container_type::const_iterator>;
/*! @brief Default constructor. */
dense_map()
: dense_map(minimum_capacity) {}
/**
* @brief Constructs an empty container with a given allocator.
* @param allocator The allocator to use.
*/
explicit dense_map(const allocator_type &allocator)
: dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {}
/**
* @brief Constructs an empty container with a given allocator and user
* supplied minimal number of buckets.
* @param bucket_count Minimal number of buckets.
* @param allocator The allocator to use.
*/
dense_map(const size_type bucket_count, const allocator_type &allocator)
: dense_map{bucket_count, hasher{}, key_equal{}, allocator} {}
/**
* @brief Constructs an empty container with a given allocator, hash
* function and user supplied minimal number of buckets.
* @param bucket_count Minimal number of buckets.
* @param hash Hash function to use.
* @param allocator The allocator to use.
*/
dense_map(const size_type bucket_count, const hasher &hash, const allocator_type &allocator)
: dense_map{bucket_count, hash, key_equal{}, allocator} {}
/**
* @brief Constructs an empty container with a given allocator, hash
* function, compare function and user supplied minimal number of buckets.
* @param bucket_count Minimal number of buckets.
* @param hash Hash function to use.
* @param equal Compare function to use.
* @param allocator The allocator to use.
*/
explicit dense_map(const size_type bucket_count, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{})
: sparse{allocator, hash},
packed{allocator, equal},
threshold{default_threshold} {
rehash(bucket_count);
}
/*! @brief Default copy constructor. */
dense_map(const dense_map &) = default;
/**
* @brief Allocator-extended copy constructor.
* @param other The instance to copy from.
* @param allocator The allocator to use.
*/
dense_map(const dense_map &other, const allocator_type &allocator)
: sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())},
packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())},
threshold{other.threshold} {}
/*! @brief Default move constructor. */
dense_map(dense_map &&) = default;
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
dense_map(dense_map &&other, const allocator_type &allocator)
: sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))},
packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))},
threshold{other.threshold} {}
/**
* @brief Default copy assignment operator.
* @return This container.
*/
dense_map &operator=(const dense_map &) = default;
/**
* @brief Default move assignment operator.
* @return This container.
*/
dense_map &operator=(dense_map &&) = default;
/**
* @brief Returns the associated allocator.
* @return The associated allocator.
*/
[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
return sparse.first().get_allocator();
}
/**
* @brief Returns an iterator to the beginning.
*
* The returned iterator points to the first instance of the internal array.
* If the array is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first instance of the internal array.
*/
[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
return packed.first().begin();
}
/*! @copydoc cbegin */
[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
return cbegin();
}
/*! @copydoc begin */
[[nodiscard]] iterator begin() ENTT_NOEXCEPT {
return packed.first().begin();
}
/**
* @brief Returns an iterator to the end.
*
* The returned iterator points to the element following the last instance
* of the internal array. Attempting to dereference the returned iterator
* results in undefined behavior.
*
* @return An iterator to the element following the last instance of the
* internal array.
*/
[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
return packed.first().end();
}
/*! @copydoc cend */
[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
return cend();
}
/*! @copydoc end */
[[nodiscard]] iterator end() ENTT_NOEXCEPT {
return packed.first().end();
}
/**
* @brief Checks whether a container is empty.
* @return True if the container is empty, false otherwise.
*/
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
return packed.first().empty();
}
/**
* @brief Returns the number of elements in a container.
* @return Number of elements in a container.
*/
[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
return packed.first().size();
}
/*! @brief Clears the container. */
void clear() ENTT_NOEXCEPT {
sparse.first().clear();
packed.first().clear();
rehash(0u);
}
/**
* @brief Inserts an element into the container, if the key does not exist.
* @param value A key-value pair eventually convertible to the value type.
* @return A pair consisting of an iterator to the inserted element (or to
* the element that prevented the insertion) and a bool denoting whether the
* insertion took place.
*/
std::pair<iterator, bool> insert(const value_type &value) {
return insert_or_do_nothing(value.first, value.second);
}
/*! @copydoc insert */
std::pair<iterator, bool> insert(value_type &&value) {
return insert_or_do_nothing(std::move(value.first), std::move(value.second));
}
/**
* @copydoc insert
* @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) {
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) {
for(; first != last; ++first) {
insert(*first);
}
}
/**
* @brief Inserts an element into the container or assigns to the current
* element if the key already exists.
* @tparam Arg Type of the value to insert or assign.
* @param key A key used both to look up and to insert if not found.
* @param value A value to insert or assign.
* @return A pair consisting of an iterator to the element and a bool
* denoting whether the insertion took place.
*/
template<typename Arg>
std::pair<iterator, bool> insert_or_assign(const key_type &key, Arg &&value) {
return insert_or_overwrite(key, std::forward<Arg>(value));
}
/*! @copydoc insert_or_assign */
template<typename Arg>
std::pair<iterator, bool> insert_or_assign(key_type &&key, Arg &&value) {
return insert_or_overwrite(std::move(key), std::forward<Arg>(value));
}
/**
* @brief Constructs an element in-place, if the key does not exist.
*
* The element is also constructed when the container already has the key,
* in which case the newly constructed object is destroyed immediately.
*
* @tparam Args Types of arguments to forward to the constructor of the
* element.
* @param args Arguments to forward to the constructor of the element.
* @return A pair consisting of an iterator to the inserted element (or to
* the element that prevented the insertion) and a bool denoting whether the
* insertion took place.
*/
template<typename... Args>
std::pair<iterator, bool> emplace([[maybe_unused]] Args &&...args) {
if constexpr(sizeof...(Args) == 0u) {
return insert_or_do_nothing(key_type{});
} else if constexpr(sizeof...(Args) == 1u) {
return insert_or_do_nothing(std::forward<Args>(args).first..., std::forward<Args>(args).second...);
} else if constexpr(sizeof...(Args) == 2u) {
return insert_or_do_nothing(std::forward<Args>(args)...);
} else {
auto &node = packed.first().emplace_back(packed.first().size(), std::forward<Args>(args)...);
const auto index = key_to_bucket(node.element.first);
if(auto it = constrained_find(node.element.first, index); it != end()) {
packed.first().pop_back();
return std::make_pair(it, false);
}
std::swap(node.next, sparse.first()[index]);
rehash_if_required();
return std::make_pair(--end(), true);
}
}
/**
* @brief Inserts in-place if the key does not exist, does nothing if the
* key exists.
* @tparam Args Types of arguments to forward to the constructor of the
* element.
* @param key A key used both to look up and to insert if not found.
* @param args Arguments to forward to the constructor of the element.
* @return A pair consisting of an iterator to the inserted element (or to
* the element that prevented the insertion) and a bool denoting whether the
* insertion took place.
*/
template<typename... Args>
std::pair<iterator, bool> try_emplace(const key_type &key, Args &&...args) {
return insert_or_do_nothing(key, std::forward<Args>(args)...);
}
/*! @copydoc try_emplace */
template<typename... Args>
std::pair<iterator, bool> try_emplace(key_type &&key, Args &&...args) {
return insert_or_do_nothing(std::move(key), std::forward<Args>(args)...);
}
/**
* @brief Removes an element from a given position.
* @param pos An iterator to the element to remove.
* @return An iterator following the removed element.
*/
iterator erase(const_iterator pos) {
const auto diff = pos - cbegin();
erase(pos->first);
return begin() + diff;
}
/**
* @brief Removes the given elements from a container.
* @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 An iterator following the last removed element.
*/
iterator erase(const_iterator first, const_iterator last) {
const auto dist = first - cbegin();
for(auto from = last - cbegin(); from != dist; --from) {
erase(packed.first()[from - 1u].element.first);
}
return (begin() + dist);
}
/**
* @brief Removes the element associated with a given key.
* @param key A key value of an element to remove.
* @return Number of elements removed (either 0 or 1).
*/
size_type erase(const key_type &key) {
for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].next) {
if(packed.second()(packed.first()[*curr].element.first, key)) {
const auto index = *curr;
*curr = packed.first()[*curr].next;
move_and_pop(index);
return 1u;
}
}
return 0u;
}
/**
* @brief Exchanges the contents with those of a given container.
* @param other Container to exchange the content with.
*/
void swap(dense_map &other) {
using std::swap;
swap(sparse, other.sparse);
swap(packed, other.packed);
swap(threshold, other.threshold);
}
/**
* @brief Accesses a given element with bounds checking.
* @param key A key of an element to find.
* @return A reference to the mapped value of the requested element.
*/
[[nodiscard]] mapped_type &at(const key_type &key) {
auto it = find(key);
ENTT_ASSERT(it != end(), "Invalid key");
return it->second;
}
/*! @copydoc at */
[[nodiscard]] const mapped_type &at(const key_type &key) const {
auto it = find(key);
ENTT_ASSERT(it != cend(), "Invalid key");
return it->second;
}
/**
* @brief Accesses or inserts a given element.
* @param key A key of an element to find or insert.
* @return A reference to the mapped value of the requested element.
*/
[[nodiscard]] mapped_type &operator[](const key_type &key) {
return insert_or_do_nothing(key).first->second;
}
/**
* @brief Accesses or inserts a given element.
* @param key A key of an element to find or insert.
* @return A reference to the mapped value of the requested element.
*/
[[nodiscard]] mapped_type &operator[](key_type &&key) {
return insert_or_do_nothing(std::move(key)).first->second;
}
/**
* @brief Finds an element with a given key.
* @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.
*/
[[nodiscard]] iterator find(const key_type &key) {
return constrained_find(key, key_to_bucket(key));
}
/*! @copydoc find */
[[nodiscard]] const_iterator find(const key_type &key) const {
return constrained_find(key, key_to_bucket(key));
}
/**
* @brief Finds 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 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) {
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 {
return constrained_find(key, key_to_bucket(key));
}
/**
* @brief Checks if the container contains an element with a given key.
* @param key Key value of an element to search for.
* @return True if there is such an element, false otherwise.
*/
[[nodiscard]] bool contains(const key_type &key) const {
return (find(key) != cend());
}
/**
* @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 {
return (find(key) != cend());
}
/**
* @brief Returns an iterator to the beginning of a given bucket.
* @param index An index of a bucket to access.
* @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]};
}
/**
* @brief Returns an iterator to the beginning of a given bucket.
* @param index An index of a bucket to access.
* @return An iterator to the beginning of the given bucket.
*/
[[nodiscard]] const_local_iterator begin(const size_type index) const {
return cbegin(index);
}
/**
* @brief Returns an iterator to the beginning of a given bucket.
* @param index An index of a bucket to access.
* @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]};
}
/**
* @brief Returns an iterator to the end of a given bucket.
* @param index An index of a bucket to access.
* @return An iterator to the end of the given bucket.
*/
[[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const {
return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
}
/**
* @brief Returns an iterator to the end of a given bucket.
* @param index An index of a bucket to access.
* @return An iterator to the end of the given bucket.
*/
[[nodiscard]] const_local_iterator end([[maybe_unused]] const size_type index) const {
return cend(index);
}
/**
* @brief Returns an iterator to the end of a given bucket.
* @param index An index of a bucket to access.
* @return An iterator to the end of the given bucket.
*/
[[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) {
return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
}
/**
* @brief Returns the number of buckets.
* @return The number of buckets.
*/
[[nodiscard]] size_type bucket_count() const {
return sparse.first().size();
}
/**
* @brief Returns the maximum number of buckets.
* @return The maximum number of buckets.
*/
[[nodiscard]] size_type max_bucket_count() const {
return sparse.first().max_size();
}
/**
* @brief Returns the number of elements in a given bucket.
* @param index The index of the bucket to examine.
* @return The number of elements in the given bucket.
*/
[[nodiscard]] size_type bucket_size(const size_type index) const {
return static_cast<size_type>(std::distance(begin(index), end(index)));
}
/**
* @brief Returns the bucket for a given key.
* @param key The value of the key to examine.
* @return The bucket for the given key.
*/
[[nodiscard]] size_type bucket(const key_type &key) const {
return key_to_bucket(key);
}
/**
* @brief Returns the average number of elements per bucket.
* @return The average number of elements per bucket.
*/
[[nodiscard]] float load_factor() const {
return size() / static_cast<float>(bucket_count());
}
/**
* @brief Returns the maximum average number of elements per bucket.
* @return The maximum average number of elements per bucket.
*/
[[nodiscard]] float max_load_factor() const {
return threshold;
}
/**
* @brief Sets the desired maximum average number of elements per bucket.
* @param value A desired maximum average number of elements per bucket.
*/
void max_load_factor(const float value) {
ENTT_ASSERT(value > 0.f, "Invalid load factor");
threshold = value;
rehash(0u);
}
/**
* @brief Reserves at least the specified number of buckets and regenerates
* the hash table.
* @param count New number of buckets.
*/
void rehash(const size_type count) {
auto value = (std::max)(count, minimum_capacity);
value = (std::max)(value, static_cast<size_type>(size() / max_load_factor()));
if(const auto sz = next_power_of_two(value); sz != bucket_count()) {
sparse.first().resize(sz);
std::fill(sparse.first().begin(), sparse.first().end(), (std::numeric_limits<size_type>::max)());
for(size_type pos{}, last = size(); pos < last; ++pos) {
const auto index = key_to_bucket(packed.first()[pos].element.first);
packed.first()[pos].next = std::exchange(sparse.first()[index], pos);
}
}
}
/**
* @brief Reserves space for at least the specified number of elements and
* regenerates the hash table.
* @param count New number of elements.
*/
void reserve(const size_type count) {
packed.first().reserve(count);
rehash(static_cast<size_type>(std::ceil(count / max_load_factor())));
}
/**
* @brief Returns the function used to hash the keys.
* @return The function used to hash the keys.
*/
[[nodiscard]] hasher hash_function() const {
return sparse.second();
}
/**
* @brief Returns the function used to compare keys for equality.
* @return The function used to compare keys for equality.
*/
[[nodiscard]] key_equal key_eq() const {
return packed.second();
}
private:
compressed_pair<sparse_container_type, hasher> sparse;
compressed_pair<packed_container_type, key_equal> packed;
float threshold;
};
} // namespace entt
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace std {
template<typename Key, typename Value, typename Allocator>
struct uses_allocator<entt::internal::dense_map_node<Key, Value>, Allocator>
: std::true_type {};
} // namespace std
/**
* Internal details not to be documented.
* @endcond
*/
#endif

View File

@@ -0,0 +1,823 @@
#ifndef ENTT_CONTAINER_DENSE_SET_HPP
#define ENTT_CONTAINER_DENSE_SET_HPP
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <functional>
#include <iterator>
#include <limits>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../core/compressed_pair.hpp"
#include "../core/memory.hpp"
#include "../core/type_traits.hpp"
#include "fwd.hpp"
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename It>
class dense_set_iterator final {
template<typename>
friend class dense_set_iterator;
public:
using value_type = typename It::value_type::second_type;
using pointer = const value_type *;
using reference = const value_type &;
using difference_type = std::ptrdiff_t;
using iterator_category = std::random_access_iterator_tag;
dense_set_iterator() ENTT_NOEXCEPT
: it{} {}
dense_set_iterator(const It iter) ENTT_NOEXCEPT
: it{iter} {}
template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
dense_set_iterator(const dense_set_iterator<Other> &other) ENTT_NOEXCEPT
: it{other.it} {}
dense_set_iterator &operator++() ENTT_NOEXCEPT {
return ++it, *this;
}
dense_set_iterator operator++(int) ENTT_NOEXCEPT {
dense_set_iterator orig = *this;
return ++(*this), orig;
}
dense_set_iterator &operator--() ENTT_NOEXCEPT {
return --it, *this;
}
dense_set_iterator operator--(int) ENTT_NOEXCEPT {
dense_set_iterator orig = *this;
return operator--(), orig;
}
dense_set_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
it += value;
return *this;
}
dense_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
dense_set_iterator copy = *this;
return (copy += value);
}
dense_set_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
return (*this += -value);
}
dense_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
return (*this + -value);
}
[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT {
return it[value].second;
}
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
return std::addressof(it->second);
}
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
return *operator->();
}
template<typename ILhs, typename IRhs>
friend std::ptrdiff_t operator-(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) ENTT_NOEXCEPT;
template<typename ILhs, typename IRhs>
friend bool operator==(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) ENTT_NOEXCEPT;
template<typename ILhs, typename IRhs>
friend bool operator<(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) ENTT_NOEXCEPT;
private:
It it;
};
template<typename ILhs, typename IRhs>
[[nodiscard]] std::ptrdiff_t operator-(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return lhs.it - rhs.it;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator==(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return lhs.it == rhs.it;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator!=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return !(lhs == rhs);
}
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator<(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return lhs.it < rhs.it;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator>(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return rhs < lhs;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator<=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return !(lhs > rhs);
}
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator>=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return !(lhs < rhs);
}
template<typename It>
class dense_set_local_iterator final {
template<typename>
friend class dense_set_local_iterator;
public:
using value_type = typename It::value_type::second_type;
using pointer = const value_type *;
using reference = const value_type &;
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
dense_set_local_iterator() ENTT_NOEXCEPT
: it{},
offset{} {}
dense_set_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT
: it{iter},
offset{pos} {}
template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
dense_set_local_iterator(const dense_set_local_iterator<Other> &other) ENTT_NOEXCEPT
: it{other.it},
offset{other.offset} {}
dense_set_local_iterator &operator++() ENTT_NOEXCEPT {
return offset = it[offset].first, *this;
}
dense_set_local_iterator operator++(int) ENTT_NOEXCEPT {
dense_set_local_iterator orig = *this;
return ++(*this), orig;
}
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
return std::addressof(it[offset].second);
}
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
return *operator->();
}
[[nodiscard]] std::size_t index() const ENTT_NOEXCEPT {
return offset;
}
private:
It it;
std::size_t offset;
};
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator==(const dense_set_local_iterator<ILhs> &lhs, const dense_set_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return lhs.index() == rhs.index();
}
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator!=(const dense_set_local_iterator<ILhs> &lhs, const dense_set_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return !(lhs == rhs);
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Associative container for unique objects of a given type.
*
* Internally, elements are organized into buckets. Which bucket an element is
* placed into depends entirely on its hash. Elements with the same hash code
* appear in the same bucket.
*
* @tparam Type Value type of the associative container.
* @tparam Hash Type of function to use to hash the values.
* @tparam KeyEqual Type of function to use to compare the values for equality.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Type, typename Hash, typename KeyEqual, typename Allocator>
class dense_set {
static constexpr float default_threshold = 0.875f;
static constexpr std::size_t minimum_capacity = 8u;
using node_type = std::pair<std::size_t, Type>;
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
using sparse_container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
using packed_container_type = std::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
template<typename Other>
[[nodiscard]] std::size_t value_to_bucket(const Other &value) const ENTT_NOEXCEPT {
return fast_mod(sparse.second()(value), bucket_count());
}
template<typename Other>
[[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) {
for(auto it = begin(bucket), last = end(bucket); it != last; ++it) {
if(packed.second()(*it, value)) {
return begin() + static_cast<typename iterator::difference_type>(it.index());
}
}
return end();
}
template<typename Other>
[[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) const {
for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) {
if(packed.second()(*it, value)) {
return cbegin() + static_cast<typename iterator::difference_type>(it.index());
}
}
return cend();
}
template<typename Other>
[[nodiscard]] auto insert_or_do_nothing(Other &&value) {
const auto index = value_to_bucket(value);
if(auto it = constrained_find(value, index); it != end()) {
return std::make_pair(it, false);
}
packed.first().emplace_back(sparse.first()[index], std::forward<Other>(value));
sparse.first()[index] = packed.first().size() - 1u;
rehash_if_required();
return std::make_pair(--end(), true);
}
void move_and_pop(const std::size_t pos) {
if(const auto last = size() - 1u; pos != last) {
packed.first()[pos] = std::move(packed.first().back());
size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second);
for(; *curr != last; curr = &packed.first()[*curr].first) {}
*curr = pos;
}
packed.first().pop_back();
}
void rehash_if_required() {
if(size() > (bucket_count() * max_load_factor())) {
rehash(bucket_count() * 2u);
}
}
public:
/*! @brief Key type of the container. */
using key_type = Type;
/*! @brief Value type of the container. */
using value_type = Type;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Type of function to use to hash the elements. */
using hasher = Hash;
/*! @brief Type of function to use to compare the elements for equality. */
using key_equal = KeyEqual;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Random access iterator type. */
using iterator = internal::dense_set_iterator<typename packed_container_type::iterator>;
/*! @brief Constant random access iterator type. */
using const_iterator = internal::dense_set_iterator<typename packed_container_type::const_iterator>;
/*! @brief Forward iterator type. */
using local_iterator = internal::dense_set_local_iterator<typename packed_container_type::iterator>;
/*! @brief Constant forward iterator type. */
using const_local_iterator = internal::dense_set_local_iterator<typename packed_container_type::const_iterator>;
/*! @brief Default constructor. */
dense_set()
: dense_set(minimum_capacity) {}
/**
* @brief Constructs an empty container with a given allocator.
* @param allocator The allocator to use.
*/
explicit dense_set(const allocator_type &allocator)
: dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {}
/**
* @brief Constructs an empty container with a given allocator and user
* supplied minimal number of buckets.
* @param bucket_count Minimal number of buckets.
* @param allocator The allocator to use.
*/
dense_set(const size_type bucket_count, const allocator_type &allocator)
: dense_set{bucket_count, hasher{}, key_equal{}, allocator} {}
/**
* @brief Constructs an empty container with a given allocator, hash
* function and user supplied minimal number of buckets.
* @param bucket_count Minimal number of buckets.
* @param hash Hash function to use.
* @param allocator The allocator to use.
*/
dense_set(const size_type bucket_count, const hasher &hash, const allocator_type &allocator)
: dense_set{bucket_count, hash, key_equal{}, allocator} {}
/**
* @brief Constructs an empty container with a given allocator, hash
* function, compare function and user supplied minimal number of buckets.
* @param bucket_count Minimal number of buckets.
* @param hash Hash function to use.
* @param equal Compare function to use.
* @param allocator The allocator to use.
*/
explicit dense_set(const size_type bucket_count, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{})
: sparse{allocator, hash},
packed{allocator, equal},
threshold{default_threshold} {
rehash(bucket_count);
}
/*! @brief Default copy constructor. */
dense_set(const dense_set &) = default;
/**
* @brief Allocator-extended copy constructor.
* @param other The instance to copy from.
* @param allocator The allocator to use.
*/
dense_set(const dense_set &other, const allocator_type &allocator)
: sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())},
packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())},
threshold{other.threshold} {}
/*! @brief Default move constructor. */
dense_set(dense_set &&) = default;
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
dense_set(dense_set &&other, const allocator_type &allocator)
: sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))},
packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))},
threshold{other.threshold} {}
/**
* @brief Default copy assignment operator.
* @return This container.
*/
dense_set &operator=(const dense_set &) = default;
/**
* @brief Default move assignment operator.
* @return This container.
*/
dense_set &operator=(dense_set &&) = default;
/**
* @brief Returns the associated allocator.
* @return The associated allocator.
*/
[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
return sparse.first().get_allocator();
}
/**
* @brief Returns an iterator to the beginning.
*
* The returned iterator points to the first instance of the internal array.
* If the array is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first instance of the internal array.
*/
[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
return packed.first().begin();
}
/*! @copydoc cbegin */
[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
return cbegin();
}
/*! @copydoc begin */
[[nodiscard]] iterator begin() ENTT_NOEXCEPT {
return packed.first().begin();
}
/**
* @brief Returns an iterator to the end.
*
* The returned iterator points to the element following the last instance
* of the internal array. Attempting to dereference the returned iterator
* results in undefined behavior.
*
* @return An iterator to the element following the last instance of the
* internal array.
*/
[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
return packed.first().end();
}
/*! @copydoc cend */
[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
return cend();
}
/*! @copydoc end */
[[nodiscard]] iterator end() ENTT_NOEXCEPT {
return packed.first().end();
}
/**
* @brief Checks whether a container is empty.
* @return True if the container is empty, false otherwise.
*/
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
return packed.first().empty();
}
/**
* @brief Returns the number of elements in a container.
* @return Number of elements in a container.
*/
[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
return packed.first().size();
}
/*! @brief Clears the container. */
void clear() ENTT_NOEXCEPT {
sparse.first().clear();
packed.first().clear();
rehash(0u);
}
/**
* @brief Inserts an element into the container, if it does not exist.
* @param value An element to insert into the container.
* @return A pair consisting of an iterator to the inserted element (or to
* the element that prevented the insertion) and a bool denoting whether the
* insertion took place.
*/
std::pair<iterator, bool> insert(const value_type &value) {
return insert_or_do_nothing(value);
}
/*! @copydoc insert */
std::pair<iterator, bool> insert(value_type &&value) {
return insert_or_do_nothing(std::move(value));
}
/**
* @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) {
for(; first != last; ++first) {
insert(*first);
}
}
/**
* @brief Constructs an element in-place, if it does not exist.
*
* The element is also constructed when the container already has the key,
* in which case the newly constructed object is destroyed immediately.
*
* @tparam Args Types of arguments to forward to the constructor of the
* element.
* @param args Arguments to forward to the constructor of the element.
* @return A pair consisting of an iterator to the inserted element (or to
* the element that prevented the insertion) and a bool denoting whether the
* insertion took place.
*/
template<typename... Args>
std::pair<iterator, bool> emplace(Args &&...args) {
if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, value_type>)) {
return insert_or_do_nothing(std::forward<Args>(args)...);
} else {
auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward<Args>(args)...));
const auto index = value_to_bucket(node.second);
if(auto it = constrained_find(node.second, index); it != end()) {
packed.first().pop_back();
return std::make_pair(it, false);
}
std::swap(node.first, sparse.first()[index]);
rehash_if_required();
return std::make_pair(--end(), true);
}
}
/**
* @brief Removes an element from a given position.
* @param pos An iterator to the element to remove.
* @return An iterator following the removed element.
*/
iterator erase(const_iterator pos) {
const auto diff = pos - cbegin();
erase(*pos);
return begin() + diff;
}
/**
* @brief Removes the given elements from a container.
* @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 An iterator following the last removed element.
*/
iterator erase(const_iterator first, const_iterator last) {
const auto dist = first - cbegin();
for(auto from = last - cbegin(); from != dist; --from) {
erase(packed.first()[from - 1u].second);
}
return (begin() + dist);
}
/**
* @brief Removes the element associated with a given value.
* @param value Value of an element to remove.
* @return Number of elements removed (either 0 or 1).
*/
size_type erase(const value_type &value) {
for(size_type *curr = sparse.first().data() + value_to_bucket(value); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].first) {
if(packed.second()(packed.first()[*curr].second, value)) {
const auto index = *curr;
*curr = packed.first()[*curr].first;
move_and_pop(index);
return 1u;
}
}
return 0u;
}
/**
* @brief Exchanges the contents with those of a given container.
* @param other Container to exchange the content with.
*/
void swap(dense_set &other) {
using std::swap;
swap(sparse, other.sparse);
swap(packed, other.packed);
swap(threshold, other.threshold);
}
/**
* @brief Finds an element with a given value.
* @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.
*/
[[nodiscard]] iterator find(const value_type &value) {
return constrained_find(value, value_to_bucket(value));
}
/*! @copydoc find */
[[nodiscard]] const_iterator find(const value_type &value) const {
return constrained_find(value, value_to_bucket(value));
}
/**
* @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) {
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 {
return constrained_find(value, value_to_bucket(value));
}
/**
* @brief Checks if the container contains an element with a given value.
* @param value Value of an element to search for.
* @return True if there is such an element, false otherwise.
*/
[[nodiscard]] bool contains(const value_type &value) const {
return (find(value) != cend());
}
/**
* @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 {
return (find(value) != cend());
}
/**
* @brief Returns an iterator to the beginning of a given bucket.
* @param index An index of a bucket to access.
* @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]};
}
/**
* @brief Returns an iterator to the beginning of a given bucket.
* @param index An index of a bucket to access.
* @return An iterator to the beginning of the given bucket.
*/
[[nodiscard]] const_local_iterator begin(const size_type index) const {
return cbegin(index);
}
/**
* @brief Returns an iterator to the beginning of a given bucket.
* @param index An index of a bucket to access.
* @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]};
}
/**
* @brief Returns an iterator to the end of a given bucket.
* @param index An index of a bucket to access.
* @return An iterator to the end of the given bucket.
*/
[[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const {
return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
}
/**
* @brief Returns an iterator to the end of a given bucket.
* @param index An index of a bucket to access.
* @return An iterator to the end of the given bucket.
*/
[[nodiscard]] const_local_iterator end([[maybe_unused]] const size_type index) const {
return cend(index);
}
/**
* @brief Returns an iterator to the end of a given bucket.
* @param index An index of a bucket to access.
* @return An iterator to the end of the given bucket.
*/
[[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) {
return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
}
/**
* @brief Returns the number of buckets.
* @return The number of buckets.
*/
[[nodiscard]] size_type bucket_count() const {
return sparse.first().size();
}
/**
* @brief Returns the maximum number of buckets.
* @return The maximum number of buckets.
*/
[[nodiscard]] size_type max_bucket_count() const {
return sparse.first().max_size();
}
/**
* @brief Returns the number of elements in a given bucket.
* @param index The index of the bucket to examine.
* @return The number of elements in the given bucket.
*/
[[nodiscard]] size_type bucket_size(const size_type index) const {
return static_cast<size_type>(std::distance(begin(index), end(index)));
}
/**
* @brief Returns the bucket for a given element.
* @param value The value of the element to examine.
* @return The bucket for the given element.
*/
[[nodiscard]] size_type bucket(const value_type &value) const {
return value_to_bucket(value);
}
/**
* @brief Returns the average number of elements per bucket.
* @return The average number of elements per bucket.
*/
[[nodiscard]] float load_factor() const {
return size() / static_cast<float>(bucket_count());
}
/**
* @brief Returns the maximum average number of elements per bucket.
* @return The maximum average number of elements per bucket.
*/
[[nodiscard]] float max_load_factor() const {
return threshold;
}
/**
* @brief Sets the desired maximum average number of elements per bucket.
* @param value A desired maximum average number of elements per bucket.
*/
void max_load_factor(const float value) {
ENTT_ASSERT(value > 0.f, "Invalid load factor");
threshold = value;
rehash(0u);
}
/**
* @brief Reserves at least the specified number of buckets and regenerates
* the hash table.
* @param count New number of buckets.
*/
void rehash(const size_type count) {
auto value = (std::max)(count, minimum_capacity);
value = (std::max)(value, static_cast<size_type>(size() / max_load_factor()));
if(const auto sz = next_power_of_two(value); sz != bucket_count()) {
sparse.first().resize(sz);
std::fill(sparse.first().begin(), sparse.first().end(), (std::numeric_limits<size_type>::max)());
for(size_type pos{}, last = size(); pos < last; ++pos) {
const auto index = value_to_bucket(packed.first()[pos].second);
packed.first()[pos].first = std::exchange(sparse.first()[index], pos);
}
}
}
/**
* @brief Reserves space for at least the specified number of elements and
* regenerates the hash table.
* @param count New number of elements.
*/
void reserve(const size_type count) {
packed.first().reserve(count);
rehash(static_cast<size_type>(std::ceil(count / max_load_factor())));
}
/**
* @brief Returns the function used to hash the elements.
* @return The function used to hash the elements.
*/
[[nodiscard]] hasher hash_function() const {
return sparse.second();
}
/**
* @brief Returns the function used to compare elements for equality.
* @return The function used to compare elements for equality.
*/
[[nodiscard]] key_equal key_eq() const {
return packed.second();
}
private:
compressed_pair<sparse_container_type, hasher> sparse;
compressed_pair<packed_container_type, key_equal> packed;
float threshold;
};
} // namespace entt
#endif

View File

@@ -0,0 +1,26 @@
#ifndef ENTT_CONTAINER_FWD_HPP
#define ENTT_CONTAINER_FWD_HPP
#include <functional>
#include <memory>
namespace entt {
template<
typename Key,
typename Type,
typename = std::hash<Key>,
typename = std::equal_to<Key>,
typename = std::allocator<std::pair<const Key, Type>>>
class dense_map;
template<
typename Type,
typename = std::hash<Type>,
typename = std::equal_to<Type>,
typename = std::allocator<Type>>
class dense_set;
} // namespace entt
#endif

View File

@@ -1,18 +1,15 @@
#ifndef ENTT_CORE_ALGORITHM_HPP
#define ENTT_CORE_ALGORITHM_HPP
#include <vector>
#include <utility>
#include <iterator>
#include <algorithm>
#include <functional>
#include <iterator>
#include <utility>
#include <vector>
#include "utility.hpp"
namespace entt {
/**
* @brief Function object to wrap `std::sort` in a class type.
*
@@ -36,12 +33,11 @@ struct std_sort {
* @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 {
void operator()(It first, It last, Compare compare = Compare{}, Args &&...args) const {
std::sort(std::forward<Args>(args)..., std::move(first), std::move(last), std::move(compare));
}
};
/*! @brief Function object for performing insertion sort. */
struct insertion_sort {
/**
@@ -62,8 +58,8 @@ struct insertion_sort {
auto value = std::move(*it);
auto pre = it;
for(; pre > first && compare(value, *(pre-1)); --pre) {
*pre = std::move(*(pre-1));
for(; pre > first && compare(value, *(pre - 1)); --pre) {
*pre = std::move(*(pre - 1));
}
*pre = std::move(value);
@@ -72,7 +68,6 @@ struct insertion_sort {
}
};
/**
* @brief Function object for performing LSD radix sort.
* @tparam Bit Number of bits processed per pass.
@@ -137,8 +132,6 @@ struct radix_sort {
}
};
}
} // namespace entt
#endif

View File

@@ -1,9 +1,7 @@
#ifndef ENTT_CORE_ANY_HPP
#define ENTT_CORE_ANY_HPP
#include <cstddef>
#include <functional>
#include <memory>
#include <type_traits>
#include <utility>
@@ -13,10 +11,8 @@
#include "type_info.hpp"
#include "type_traits.hpp"
namespace entt {
/**
* @brief A SBO friendly, type-safe container for single values of any type.
* @tparam Len Size of the storage reserved for the small buffer optimization.
@@ -24,100 +20,102 @@ namespace entt {
*/
template<std::size_t Len, std::size_t Align>
class basic_any {
enum class operation: std::uint8_t { COPY, MOVE, DTOR, COMP, ADDR, CADDR, TYPE };
enum class policy: std::uint8_t { OWNER, REF, CREF };
enum class operation : std::uint8_t {
copy,
move,
transfer,
assign,
destroy,
compare,
get
};
enum class policy : std::uint8_t {
owner,
ref,
cref
};
using storage_type = std::aligned_storage_t<Len + !Len, Align>;
using vtable_type = const void *(const operation, const basic_any &, void *);
using vtable_type = const void *(const operation, const basic_any &, const void *);
template<typename Type>
static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v<Type>;
template<typename Type>
[[nodiscard]] static constexpr policy type_to_policy() {
if constexpr(std::is_lvalue_reference_v<Type>) {
if constexpr(std::is_const_v<std::remove_reference_t<Type>>) {
return policy::CREF;
static const void *basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &value, [[maybe_unused]] const void *other) {
static_assert(!std::is_same_v<Type, void> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
const Type *element = nullptr;
if constexpr(in_situ<Type>) {
element = value.owner() ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance);
} else {
element = static_cast<const Type *>(value.instance);
}
switch(op) {
case operation::copy:
if constexpr(std::is_copy_constructible_v<Type>) {
static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*element);
}
break;
case operation::move:
if constexpr(in_situ<Type>) {
if(value.owner()) {
return new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(element))};
}
}
return (static_cast<basic_any *>(const_cast<void *>(other))->instance = std::exchange(const_cast<basic_any &>(value).instance, nullptr));
case operation::transfer:
if constexpr(std::is_move_assignable_v<Type>) {
*const_cast<Type *>(element) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
return other;
}
[[fallthrough]];
case operation::assign:
if constexpr(std::is_copy_assignable_v<Type>) {
*const_cast<Type *>(element) = *static_cast<const Type *>(other);
return other;
}
break;
case operation::destroy:
if constexpr(in_situ<Type>) {
element->~Type();
} else if constexpr(std::is_array_v<Type>) {
delete[] element;
} else {
return policy::REF;
delete element;
}
} else {
return policy::OWNER;
}
}
template<typename Type>
[[nodiscard]] static bool compare(const void *lhs, const void *rhs) {
if constexpr(!std::is_function_v<Type> && is_equality_comparable_v<Type>) {
return *static_cast<const Type *>(lhs) == *static_cast<const Type *>(rhs);
} else {
return lhs == rhs;
}
}
template<typename Type>
static const void * basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &from, [[maybe_unused]] void *to) {
static_assert(std::is_same_v<std::remove_reference_t<std::remove_const_t<Type>>, Type>, "Invalid type");
if constexpr(!std::is_void_v<Type>) {
const Type *instance = (in_situ<Type> && from.mode == policy::OWNER)
? ENTT_LAUNDER(reinterpret_cast<const Type *>(&from.storage))
: static_cast<const Type *>(from.instance);
switch(op) {
case operation::COPY:
if constexpr(std::is_copy_constructible_v<Type>) {
static_cast<basic_any *>(to)->emplace<Type>(*instance);
}
break;
case operation::MOVE:
if constexpr(in_situ<Type>) {
if(from.mode == policy::OWNER) {
return new (&static_cast<basic_any *>(to)->storage) Type{std::move(*const_cast<Type *>(instance))};
}
}
return (static_cast<basic_any *>(to)->instance = std::exchange(const_cast<basic_any &>(from).instance, nullptr));
case operation::DTOR:
if(from.mode == policy::OWNER) {
if constexpr(in_situ<Type>) {
instance->~Type();
} else if constexpr(std::is_array_v<Type>) {
delete[] instance;
} else {
delete instance;
}
}
break;
case operation::COMP:
return compare<Type>(instance, (*static_cast<const basic_any **>(to))->data()) ? to : nullptr;
case operation::ADDR:
if(from.mode == policy::CREF) {
return nullptr;
}
[[fallthrough]];
case operation::CADDR:
return instance;
case operation::TYPE:
*static_cast<type_info *>(to) = type_id<Type>();
break;
break;
case operation::compare:
if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) {
return *static_cast<const Type *>(element) == *static_cast<const Type *>(other) ? other : nullptr;
} else {
return (element == other) ? other : nullptr;
}
case operation::get:
return element;
}
return nullptr;
}
template<typename Type, typename... Args>
void initialize([[maybe_unused]] Args &&... args) {
void initialize([[maybe_unused]] Args &&...args) {
if constexpr(!std::is_void_v<Type>) {
info = &type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
vtable = basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>;
if constexpr(std::is_lvalue_reference_v<Type>) {
static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v<Args> && ...), "Invalid arguments");
mode = std::is_const_v<std::remove_reference_t<Type>> ? policy::cref : policy::ref;
instance = (std::addressof(args), ...);
} else if constexpr(in_situ<Type>) {
if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) {
new (&storage) Type{std::forward<Args>(args)...};
new(&storage) Type{std::forward<Args>(args)...};
} else {
new (&storage) Type(std::forward<Args>(args)...);
new(&storage) Type(std::forward<Args>(args)...);
}
} else {
if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) {
@@ -131,9 +129,9 @@ class basic_any {
basic_any(const basic_any &other, const policy pol) ENTT_NOEXCEPT
: instance{other.data()},
info{other.info},
vtable{other.vtable},
mode{pol}
{}
mode{pol} {}
public:
/*! @brief Size of the internal storage. */
@@ -142,11 +140,11 @@ public:
static constexpr auto alignment = Align;
/*! @brief Default constructor. */
basic_any() ENTT_NOEXCEPT
constexpr basic_any() ENTT_NOEXCEPT
: instance{},
vtable{&basic_vtable<void>},
mode{policy::OWNER}
{}
info{&type_id<void>()},
vtable{},
mode{policy::owner} {}
/**
* @brief Constructs a wrapper by directly initializing the new object.
@@ -155,27 +153,11 @@ public:
* @param args Parameters to use to construct the instance.
*/
template<typename Type, typename... Args>
explicit basic_any(std::in_place_type_t<Type>, Args &&... args)
: instance{},
vtable{&basic_vtable<std::remove_const_t<std::remove_reference_t<Type>>>},
mode{type_to_policy<Type>()}
{
explicit basic_any(std::in_place_type_t<Type>, Args &&...args)
: basic_any{} {
initialize<Type>(std::forward<Args>(args)...);
}
/**
* @brief Constructs a wrapper that holds an unmanaged object.
* @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>
basic_any(std::reference_wrapper<Type> value) ENTT_NOEXCEPT
: basic_any{}
{
// invokes deprecated assignment operator (and avoids issues with vs2017)
*this = value;
}
/**
* @brief Constructs a wrapper from a given value.
* @tparam Type Type of object to use to initialize the wrapper.
@@ -183,10 +165,7 @@ public:
*/
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
basic_any(Type &&value)
: instance{},
vtable{&basic_vtable<std::decay_t<Type>>},
mode{policy::OWNER}
{
: basic_any{} {
initialize<std::decay_t<Type>>(std::forward<Type>(value));
}
@@ -195,11 +174,10 @@ public:
* @param other The instance to copy from.
*/
basic_any(const basic_any &other)
: instance{},
vtable{&basic_vtable<void>},
mode{policy::OWNER}
{
other.vtable(operation::COPY, other, this);
: basic_any{} {
if(other.vtable) {
other.vtable(operation::copy, other, this);
}
}
/**
@@ -208,15 +186,19 @@ public:
*/
basic_any(basic_any &&other) ENTT_NOEXCEPT
: instance{},
info{other.info},
vtable{other.vtable},
mode{other.mode}
{
vtable(operation::MOVE, other, this);
mode{other.mode} {
if(other.vtable) {
other.vtable(operation::move, other, this);
}
}
/*! @brief Frees the internal storage, whatever it means. */
~basic_any() {
vtable(operation::DTOR, *this, nullptr);
if(vtable && owner()) {
vtable(operation::destroy, *this, nullptr);
}
}
/**
@@ -224,9 +206,13 @@ public:
* @param other The instance to copy from.
* @return This any object.
*/
basic_any & operator=(const basic_any &other) {
basic_any &operator=(const basic_any &other) {
reset();
other.vtable(operation::COPY, other, this);
if(other.vtable) {
other.vtable(operation::copy, other, this);
}
return *this;
}
@@ -235,23 +221,16 @@ public:
* @param other The instance to move from.
* @return This any object.
*/
basic_any & operator=(basic_any &&other) ENTT_NOEXCEPT {
std::exchange(vtable, other.vtable)(operation::DTOR, *this, nullptr);
other.vtable(operation::MOVE, other, this);
mode = other.mode;
return *this;
}
basic_any &operator=(basic_any &&other) ENTT_NOEXCEPT {
reset();
if(other.vtable) {
other.vtable(operation::move, other, this);
info = other.info;
vtable = other.vtable;
mode = other.mode;
}
/**
* @brief Value assignment operator.
* @tparam Type Type of object to use to initialize the wrapper.
* @param value An instance of an object to use to initialize the wrapper.
* @return This any object.
*/
template<typename Type>
[[deprecated("Use std::in_place_type<T &>, entt::make_any<T &>, emplace<Type &> or forward_as_any instead")]]
basic_any & operator=(std::reference_wrapper<Type> value) ENTT_NOEXCEPT {
emplace<Type &>(value.get());
return *this;
}
@@ -269,26 +248,45 @@ public:
}
/**
* @brief Returns the type of the contained object.
* @return The type of the contained object, if any.
* @brief Returns the object type if any, `type_id<void>()` otherwise.
* @return The object type if any, `type_id<void>()` otherwise.
*/
[[nodiscard]] type_info type() const ENTT_NOEXCEPT {
type_info info{};
vtable(operation::TYPE, *this, &info);
return info;
[[nodiscard]] const type_info &type() const ENTT_NOEXCEPT {
return *info;
}
/**
* @brief Returns an opaque pointer to the contained instance.
* @return An opaque pointer the contained instance, if any.
*/
[[nodiscard]] const void * data() const ENTT_NOEXCEPT {
return vtable(operation::CADDR, *this, nullptr);
[[nodiscard]] const void *data() const ENTT_NOEXCEPT {
return vtable ? vtable(operation::get, *this, nullptr) : nullptr;
}
/*! @copydoc data */
[[nodiscard]] void * data() ENTT_NOEXCEPT {
return const_cast<void *>(vtable(operation::ADDR, *this, nullptr));
/**
* @brief Returns an opaque pointer to the contained instance.
* @param req Expected type.
* @return An opaque pointer the contained instance, if any.
*/
[[nodiscard]] const void *data(const type_info &req) const ENTT_NOEXCEPT {
return *info == req ? data() : nullptr;
}
/**
* @brief Returns an opaque pointer to the contained instance.
* @return An opaque pointer the contained instance, if any.
*/
[[nodiscard]] void *data() ENTT_NOEXCEPT {
return (!vtable || mode == policy::cref) ? nullptr : const_cast<void *>(vtable(operation::get, *this, nullptr));
}
/**
* @brief Returns an opaque pointer to the contained instance.
* @param req Expected type.
* @return An opaque pointer the contained instance, if any.
*/
[[nodiscard]] void *data(const type_info &req) ENTT_NOEXCEPT {
return *info == req ? data() : nullptr;
}
/**
@@ -298,16 +296,46 @@ public:
* @param args Parameters to use to construct the instance.
*/
template<typename Type, typename... Args>
void emplace(Args &&... args) {
std::exchange(vtable, &basic_vtable<std::remove_const_t<std::remove_reference_t<Type>>>)(operation::DTOR, *this, nullptr);
mode = type_to_policy<Type>();
void emplace(Args &&...args) {
reset();
initialize<Type>(std::forward<Args>(args)...);
}
/**
* @brief Assigns a value to the contained object without replacing it.
* @param other The value to assign to the contained object.
* @return True in case of success, false otherwise.
*/
bool assign(const any &other) {
if(vtable && mode != policy::cref && *info == *other.info) {
return (vtable(operation::assign, *this, other.data()) != nullptr);
}
return false;
}
/*! @copydoc assign */
bool assign(any &&other) {
if(vtable && mode != policy::cref && *info == *other.info) {
if(auto *val = other.data(); val) {
return (vtable(operation::transfer, *this, val) != nullptr);
} else {
return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr);
}
}
return false;
}
/*! @brief Destroys contained object */
void reset() {
std::exchange(vtable, &basic_vtable<void>)(operation::DTOR, *this, nullptr);
mode = policy::OWNER;
if(vtable && owner()) {
vtable(operation::destroy, *this, nullptr);
}
info = &type_id<void>();
vtable = nullptr;
mode = policy::owner;
}
/**
@@ -315,7 +343,7 @@ public:
* @return False if the wrapper is empty, true otherwise.
*/
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
return !(vtable(operation::CADDR, *this, nullptr) == nullptr);
return vtable != nullptr;
}
/**
@@ -324,8 +352,11 @@ public:
* @return False if the two objects differ in their content, true otherwise.
*/
bool operator==(const basic_any &other) const ENTT_NOEXCEPT {
const basic_any *trampoline = &other;
return type() == other.type() && (vtable(operation::COMP, *this, &trampoline) || !other.data());
if(vtable && *info == *other.info) {
return (vtable(operation::compare, *this, other.data()) != nullptr);
}
return (!vtable && !other.vtable);
}
/**
@@ -333,12 +364,12 @@ public:
* @return A wrapper that shares a reference to an unmanaged object.
*/
[[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT {
return basic_any{*this, (mode == policy::CREF ? policy::CREF : policy::REF)};
return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)};
}
/*! @copydoc as_ref */
[[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT {
return basic_any{*this, policy::CREF};
return basic_any{*this, policy::cref};
}
/**
@@ -346,16 +377,19 @@ public:
* @return True if the wrapper owns its object, false otherwise.
*/
[[nodiscard]] bool owner() const ENTT_NOEXCEPT {
return (mode == policy::OWNER);
return (mode == policy::owner);
}
private:
union { const void *instance; storage_type storage; };
union {
const void *instance;
storage_type storage;
};
const type_info *info;
vtable_type *vtable;
policy mode;
};
/**
* @brief Checks if two wrappers differ in their content.
* @tparam Len Size of the storage reserved for the small buffer optimization.
@@ -369,7 +403,6 @@ template<std::size_t Len, std::size_t Align>
return !(lhs == rhs);
}
/**
* @brief Performs type-safe access to the contained object.
* @tparam Type Type to which conversion is required.
@@ -380,47 +413,51 @@ template<std::size_t Len, std::size_t Align>
*/
template<typename Type, std::size_t Len, std::size_t Align>
Type any_cast(const basic_any<Len, Align> &data) ENTT_NOEXCEPT {
const auto * const instance = any_cast<std::remove_reference_t<Type>>(&data);
const auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
ENTT_ASSERT(instance, "Invalid instance");
return static_cast<Type>(*instance);
}
/*! @copydoc any_cast */
template<typename Type, std::size_t Len, std::size_t Align>
Type any_cast(basic_any<Len, Align> &data) ENTT_NOEXCEPT {
// forces const on non-reference types to make them work also with wrappers for const references
auto * const instance = any_cast<std::remove_reference_t<const Type>>(&data);
auto *const instance = any_cast<std::remove_reference_t<const Type>>(&data);
ENTT_ASSERT(instance, "Invalid instance");
return static_cast<Type>(*instance);
}
/*! @copydoc any_cast */
template<typename Type, std::size_t Len, std::size_t Align>
Type any_cast(basic_any<Len, Align> &&data) ENTT_NOEXCEPT {
// forces const on non-reference types to make them work also with wrappers for const references
auto * const instance = any_cast<std::remove_reference_t<const Type>>(&data);
ENTT_ASSERT(instance, "Invalid instance");
return static_cast<Type>(std::move(*instance));
if constexpr(std::is_copy_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
if(auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); instance) {
return static_cast<Type>(std::move(*instance));
} else {
return any_cast<Type>(data);
}
} else {
auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
ENTT_ASSERT(instance, "Invalid instance");
return static_cast<Type>(std::move(*instance));
}
}
/*! @copydoc any_cast */
template<typename Type, std::size_t Len, std::size_t Align>
const Type * any_cast(const basic_any<Len, Align> *data) ENTT_NOEXCEPT {
return (data->type() == type_id<Type>() ? static_cast<const Type *>(data->data()) : nullptr);
const Type *any_cast(const basic_any<Len, Align> *data) ENTT_NOEXCEPT {
const auto &info = type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
return static_cast<const Type *>(data->data(info));
}
/*! @copydoc any_cast */
template<typename Type, std::size_t Len, std::size_t Align>
Type * any_cast(basic_any<Len, Align> *data) ENTT_NOEXCEPT {
Type *any_cast(basic_any<Len, Align> *data) ENTT_NOEXCEPT {
const auto &info = type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
// last attempt to make wrappers for const references return their values
return (data->type() == type_id<Type>() ? static_cast<Type *>(static_cast<constness_as_t<basic_any<Len, Align>, Type> *>(data)->data()) : nullptr);
return static_cast<Type *>(static_cast<constness_as_t<basic_any<Len, Align>, Type> *>(data)->data(info));
}
/**
* @brief Constructs a wrapper from a given type, passing it all arguments.
* @tparam Type Type of object to use to initialize the wrapper.
@@ -431,11 +468,10 @@ Type * any_cast(basic_any<Len, Align> *data) ENTT_NOEXCEPT {
* @return A properly initialized wrapper for an object of the given type.
*/
template<typename Type, std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename... Args>
basic_any<Len, Align> make_any(Args &&... args) {
basic_any<Len, Align> make_any(Args &&...args) {
return basic_any<Len, Align>{std::in_place_type<Type>, std::forward<Args>(args)...};
}
/**
* @brief Forwards its argument and avoids copies for lvalue references.
* @tparam Len Size of the storage reserved for the small buffer optimization.
@@ -449,8 +485,6 @@ basic_any<Len, Align> forward_as_any(Type &&value) {
return basic_any<Len, Align>{std::in_place_type<std::conditional_t<std::is_rvalue_reference_v<Type>, std::decay_t<Type>, Type>>, std::forward<Type>(value)};
}
}
} // namespace entt
#endif

View File

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

View File

@@ -0,0 +1,280 @@
#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP
#define ENTT_CORE_COMPRESSED_PAIR_HPP
#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "type_traits.hpp"
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename Type, std::size_t, typename = void>
struct compressed_pair_element {
using reference = Type &;
using const_reference = const Type &;
template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>>
compressed_pair_element()
: value{} {}
template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>>
compressed_pair_element(Args &&args)
: value{std::forward<Args>(args)} {}
template<typename... Args, std::size_t... Index>
compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>)
: value{std::forward<Args>(std::get<Index>(args))...} {}
[[nodiscard]] reference get() ENTT_NOEXCEPT {
return value;
}
[[nodiscard]] const_reference get() const ENTT_NOEXCEPT {
return value;
}
private:
Type value;
};
template<typename Type, std::size_t Tag>
struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Type>>>: Type {
using reference = Type &;
using const_reference = const Type &;
using base_type = Type;
template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>>
compressed_pair_element()
: base_type{} {}
template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>>
compressed_pair_element(Args &&args)
: base_type{std::forward<Args>(args)} {}
template<typename... Args, std::size_t... Index>
compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>)
: base_type{std::forward<Args>(std::get<Index>(args))...} {}
[[nodiscard]] reference get() ENTT_NOEXCEPT {
return *this;
}
[[nodiscard]] const_reference get() const ENTT_NOEXCEPT {
return *this;
}
};
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief A compressed pair.
*
* A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to
* reduce its final size to a minimum.
*
* @tparam First The type of the first element that the pair stores.
* @tparam Second The type of the second element that the pair stores.
*/
template<typename First, typename Second>
class compressed_pair final
: internal::compressed_pair_element<First, 0u>,
internal::compressed_pair_element<Second, 1u> {
using first_base = internal::compressed_pair_element<First, 0u>;
using second_base = internal::compressed_pair_element<Second, 1u>;
public:
/*! @brief The type of the first element that the pair stores. */
using first_type = First;
/*! @brief The type of the second element that the pair stores. */
using second_type = Second;
/**
* @brief Default constructor, conditionally enabled.
*
* 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()
: first_base{},
second_base{} {}
/**
* @brief Copy constructor.
* @param other The instance to copy from.
*/
constexpr compressed_pair(const compressed_pair &other) = default;
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
constexpr compressed_pair(compressed_pair &&other) = default;
/**
* @brief Constructs a pair from its values.
* @tparam Arg Type of value to use to initialize the first element.
* @tparam Other Type of value to use to initialize the second element.
* @param arg Value to use to initialize the first element.
* @param other Value to use to initialize the second element.
*/
template<typename Arg, typename Other>
constexpr compressed_pair(Arg &&arg, Other &&other)
: first_base{std::forward<Arg>(arg)},
second_base{std::forward<Other>(other)} {}
/**
* @brief Constructs a pair by forwarding the arguments to its parts.
* @tparam Args Types of arguments to use to initialize the first element.
* @tparam Other Types of arguments to use to initialize the second element.
* @param args Arguments to use to initialize the first element.
* @param other Arguments to use to initialize the second element.
*/
template<typename... Args, typename... Other>
constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other)
: first_base{std::move(args), std::index_sequence_for<Args...>{}},
second_base{std::move(other), std::index_sequence_for<Other...>{}} {}
/**
* @brief Copy assignment operator.
* @param other The instance to copy from.
* @return This compressed pair object.
*/
constexpr compressed_pair &operator=(const compressed_pair &other) = default;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This compressed pair object.
*/
constexpr compressed_pair &operator=(compressed_pair &&other) = default;
/**
* @brief Returns the first element that a pair stores.
* @return The first element that a pair stores.
*/
[[nodiscard]] first_type &first() ENTT_NOEXCEPT {
return static_cast<first_base &>(*this).get();
}
/*! @copydoc first */
[[nodiscard]] const first_type &first() const ENTT_NOEXCEPT {
return static_cast<const first_base &>(*this).get();
}
/**
* @brief Returns the second element that a pair stores.
* @return The second element that a pair stores.
*/
[[nodiscard]] second_type &second() ENTT_NOEXCEPT {
return static_cast<second_base &>(*this).get();
}
/*! @copydoc second */
[[nodiscard]] const second_type &second() const ENTT_NOEXCEPT {
return static_cast<const second_base &>(*this).get();
}
/**
* @brief Swaps two compressed pair objects.
* @param other The compressed pair to swap with.
*/
void swap(compressed_pair &other) {
using std::swap;
swap(first(), other.first());
swap(second(), other.second());
}
/**
* @brief Extracts an element from the compressed pair.
* @tparam Index An integer value that is either 0 or 1.
* @return Returns a reference to the first element if `Index` is 0 and a
* reference to the second element if `Index` is 1.
*/
template<std::size_t Index>
decltype(auto) get() ENTT_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>
decltype(auto) get() const ENTT_NOEXCEPT {
if constexpr(Index == 0u) {
return first();
} else {
static_assert(Index == 1u, "Index out of bounds");
return second();
}
}
};
/**
* @brief Deduction guide.
* @tparam Type Type of value to use to initialize the first element.
* @tparam Other Type of value to use to initialize the second element.
*/
template<typename Type, typename Other>
compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::decay_t<Other>>;
/**
* @brief Swaps two compressed pair objects.
* @tparam First The type of the first element that the pairs store.
* @tparam Second The type of the second element that the pairs store.
* @param lhs A valid compressed pair object.
* @param rhs A valid compressed pair object.
*/
template<typename First, typename Second>
inline void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) {
lhs.swap(rhs);
}
} // namespace entt
// disable structured binding support for clang 6, it messes when specializing tuple_size
#if !defined __clang_major__ || __clang_major__ > 6
namespace std {
/**
* @brief `std::tuple_size` specialization for `compressed_pair`s.
* @tparam First The type of the first element that the pair stores.
* @tparam Second The type of the second element that the pair stores.
*/
template<typename First, typename Second>
struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_t, 2u> {};
/**
* @brief `std::tuple_element` specialization for `compressed_pair`s.
* @tparam Index The index of the type to return.
* @tparam First The type of the first element that the pair stores.
* @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");
};
} // namespace std
#endif
#endif

98
src/entt/core/enum.hpp Normal file
View File

@@ -0,0 +1,98 @@
#ifndef ENTT_CORE_ENUM_HPP
#define ENTT_CORE_ENUM_HPP
#include <type_traits>
#include "../config/config.h"
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>
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> {};
/**
* @brief Helper variable template.
* @tparam Type The enum class type for which to enable bitmask support.
*/
template<typename Type>
inline constexpr bool enum_as_bitmask_v = enum_as_bitmask<Type>::value;
} // namespace entt
/**
* @brief Operator available for enums for which bitmask support is enabled.
* @tparam Type Enum class type.
* @param lhs The first value to use.
* @param rhs The second value to use.
* @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) ENTT_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) ENTT_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) ENTT_NOEXCEPT {
return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) ^ static_cast<std::underlying_type_t<Type>>(rhs));
}
/**
* @brief Operator available for enums for which bitmask support is enabled.
* @tparam Type Enum class type.
* @param value The value to use.
* @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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_NOEXCEPT {
return (lhs = (lhs ^ rhs));
}
#endif

View File

@@ -1,14 +1,11 @@
#ifndef ENTT_CORE_FAMILY_HPP
#define ENTT_CORE_FAMILY_HPP
#include "../config/config.h"
#include "fwd.hpp"
namespace entt {
/**
* @brief Dynamic identifier generator.
*
@@ -30,8 +27,6 @@ public:
inline static const family_type type = identifier++;
};
}
} // namespace entt
#endif

View File

@@ -1,27 +1,21 @@
#ifndef ENTT_CORE_FWD_HPP
#define ENTT_CORE_FWD_HPP
#include <cstdint>
#include <type_traits>
#include "../config/config.h"
namespace entt {
template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)>
class basic_any;
/*! @brief Alias declaration for type identifiers. */
using id_type = ENTT_ID_TYPE;
/*! @brief Alias declaration for the most common use case. */
using any = basic_any<>;
}
} // namespace entt
#endif

View File

@@ -1,29 +1,23 @@
#ifndef ENTT_CORE_HASHED_STRING_HPP
#define ENTT_CORE_HASHED_STRING_HPP
#include <cstddef>
#include <cstdint>
#include "../config/config.h"
#include "fwd.hpp"
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename>
struct fnv1a_traits;
template<>
struct fnv1a_traits<std::uint32_t> {
using type = std::uint32_t;
@@ -31,7 +25,6 @@ struct fnv1a_traits<std::uint32_t> {
static constexpr std::uint32_t prime = 16777619;
};
template<>
struct fnv1a_traits<std::uint64_t> {
using type = std::uint64_t;
@@ -39,84 +32,99 @@ struct fnv1a_traits<std::uint64_t> {
static constexpr std::uint64_t prime = 1099511628211ull;
};
template<typename Char>
struct basic_hashed_string {
using value_type = Char;
using size_type = std::size_t;
using hash_type = id_type;
}
const value_type *repr;
size_type length;
hash_type hash;
};
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Zero overhead unique identifier.
*
* A hashed string is a compile-time tool that allows users to use
* human-readable identifers in the codebase while using their numeric
* human-readable identifiers in the codebase while using their numeric
* counterparts at runtime.<br/>
* Because of that, a hashed string can also be used in constant expressions if
* required.
*
* @warning
* This class doesn't take ownership of user-supplied strings nor does it make a
* copy of them.
*
* @tparam Char Character type.
*/
template<typename Char>
class basic_hashed_string {
using traits_type = internal::fnv1a_traits<id_type>;
class basic_hashed_string: internal::basic_hashed_string<Char> {
using base_type = internal::basic_hashed_string<Char>;
using hs_traits = internal::fnv1a_traits<id_type>;
struct const_wrapper {
// non-explicit constructor on purpose
constexpr const_wrapper(const Char *curr) ENTT_NOEXCEPT: str{curr} {}
const Char *str;
constexpr const_wrapper(const Char *str) ENTT_NOEXCEPT: repr{str} {}
const Char *repr;
};
// FowlerNollVo hash function v. 1a - the good
[[nodiscard]] static constexpr id_type helper(const Char *curr) ENTT_NOEXCEPT {
auto value = traits_type::offset;
[[nodiscard]] static constexpr auto helper(const Char *str) ENTT_NOEXCEPT {
base_type base{str, 0u, hs_traits::offset};
while(*curr != 0) {
value = (value ^ static_cast<traits_type::type>(*(curr++))) * traits_type::prime;
for(; str[base.length]; ++base.length) {
base.hash = (base.hash ^ static_cast<hs_traits::type>(str[base.length])) * hs_traits::prime;
}
return value;
return base;
}
// FowlerNollVo hash function v. 1a - the good
[[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) ENTT_NOEXCEPT {
base_type base{str, len, hs_traits::offset};
for(size_type pos{}; pos < len; ++pos) {
base.hash = (base.hash ^ static_cast<hs_traits::type>(str[pos])) * hs_traits::prime;
}
return base;
}
public:
/*! @brief Character type. */
using value_type = Char;
using value_type = typename base_type::value_type;
/*! @brief Unsigned integer type. */
using hash_type = id_type;
using size_type = typename base_type::size_type;
/*! @brief Unsigned integer type. */
using hash_type = typename base_type::hash_type;
/**
* @brief Returns directly the numeric representation of a string view.
* @param str Human-readable identifer.
* @param size Length of the string to hash.
* @param str Human-readable identifier.
* @param len Length of the string to hash.
* @return The numeric representation of the string.
*/
[[nodiscard]] static constexpr hash_type value(const value_type *str, std::size_t size) ENTT_NOEXCEPT {
id_type partial{traits_type::offset};
while(size--) { partial = (partial^(str++)[0])*traits_type::prime; }
return partial;
[[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) ENTT_NOEXCEPT {
return basic_hashed_string{str, len};
}
/**
* @brief Returns directly the numeric representation of a string.
*
* Forcing template resolution avoids implicit conversions. An
* human-readable identifier can be anything but a plain, old bunch of
* characters.<br/>
* Example of use:
* @code{.cpp}
* const auto value = basic_hashed_string<char>::to_value("my.png");
* @endcode
*
* @tparam N Number of characters of the identifier.
* @param str Human-readable identifer.
* @param str Human-readable identifier.
* @return The numeric representation of the string.
*/
template<std::size_t N>
[[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT {
return helper(str);
return basic_hashed_string{str};
}
/**
@@ -124,97 +132,98 @@ public:
* @param wrapper Helps achieving the purpose by relying on overloading.
* @return The numeric representation of the string.
*/
[[nodiscard]] static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT {
return helper(wrapper.str);
[[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT {
return basic_hashed_string{wrapper};
}
/*! @brief Constructs an empty hashed string. */
constexpr basic_hashed_string() ENTT_NOEXCEPT
: str{nullptr}, hash{}
{}
: base_type{} {}
/**
* @brief Constructs a hashed string from a string view.
* @param str Human-readable identifier.
* @param len Length of the string to hash.
*/
constexpr basic_hashed_string(const value_type *str, const size_type len) ENTT_NOEXCEPT
: base_type{helper(str, len)} {}
/**
* @brief Constructs a hashed string from an array of const characters.
*
* Forcing template resolution avoids implicit conversions. An
* human-readable identifier can be anything but a plain, old bunch of
* characters.<br/>
* Example of use:
* @code{.cpp}
* basic_hashed_string<char> hs{"my.png"};
* @endcode
*
* @tparam N Number of characters of the identifier.
* @param curr Human-readable identifer.
* @param str Human-readable identifier.
*/
template<std::size_t N>
constexpr basic_hashed_string(const value_type (&curr)[N]) ENTT_NOEXCEPT
: str{curr}, hash{helper(curr)}
{}
constexpr basic_hashed_string(const value_type (&str)[N]) ENTT_NOEXCEPT
: base_type{helper(str)} {}
/**
* @brief Explicit constructor on purpose to avoid constructing a hashed
* string directly from a `const value_type *`.
*
* @warning
* The lifetime of the string is not extended nor is it copied.
*
* @param wrapper Helps achieving the purpose by relying on overloading.
*/
explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT
: str{wrapper.str}, hash{helper(wrapper.str)}
{}
: base_type{helper(wrapper.repr)} {}
/**
* @brief Returns the size a hashed string.
* @return The size of the hashed string.
*/
[[nodiscard]] constexpr size_type size() const ENTT_NOEXCEPT {
return base_type::length;
}
/**
* @brief Returns the human-readable representation of a hashed string.
* @return The string used to initialize the instance.
* @return The string used to initialize the hashed string.
*/
[[nodiscard]] constexpr const value_type * data() const ENTT_NOEXCEPT {
return str;
[[nodiscard]] constexpr const value_type *data() const ENTT_NOEXCEPT {
return base_type::repr;
}
/**
* @brief Returns the numeric representation of a hashed string.
* @return The numeric representation of the instance.
* @return The numeric representation of the hashed string.
*/
[[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT {
return hash;
return base_type::hash;
}
/*! @copydoc data */
[[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { return data(); }
[[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT {
return data();
}
/**
* @brief Returns the numeric representation of a hashed string.
* @return The numeric representation of the instance.
* @return The numeric representation of the hashed string.
*/
[[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { return value(); }
/**
* @brief Compares two hashed strings.
* @param other Hashed string with which to compare.
* @return True if the two hashed strings are identical, false otherwise.
*/
[[nodiscard]] constexpr bool operator==(const basic_hashed_string &other) const ENTT_NOEXCEPT {
return hash == other.hash;
[[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT {
return value();
}
private:
const value_type *str;
hash_type hash;
};
/**
* @brief Deduction guide.
*
* It allows to deduce the character type of the hashed string directly from a
* human-readable identifer provided to the constructor.
*
* @tparam Char Character type.
* @param str Human-readable identifier.
* @param len Length of the string to hash.
*/
template<typename Char>
basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string<Char>;
/**
* @brief Deduction guide.
* @tparam Char Character type.
* @tparam N Number of characters of the identifier.
* @param str Human-readable identifer.
* @param str Human-readable identifier.
*/
template<typename Char, std::size_t N>
basic_hashed_string(const Char (&str)[N])
-> basic_hashed_string<Char>;
basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string<Char>;
/**
* @brief Compares two hashed strings.
@@ -224,46 +233,101 @@ basic_hashed_string(const Char (&str)[N])
* @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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_NOEXCEPT {
return !(lhs < rhs);
}
/*! @brief Aliases for common character types. */
using hashed_string = basic_hashed_string<char>;
/*! @brief Aliases for common character types. */
using hashed_wstring = basic_hashed_string<wchar_t>;
inline namespace literals {
/**
* @brief User defined literal for hashed strings.
* @param str The literal without its suffix.
* @return A properly initialized hashed string.
*/
[[nodiscard]] constexpr entt::hashed_string operator"" _hs(const char *str, std::size_t) ENTT_NOEXCEPT {
return entt::hashed_string{str};
[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) ENTT_NOEXCEPT {
return hashed_string{str};
}
/**
* @brief User defined literal for hashed wstrings.
* @param str The literal without its suffix.
* @return A properly initialized hashed wstring.
*/
[[nodiscard]] constexpr entt::hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) ENTT_NOEXCEPT {
return entt::hashed_wstring{str};
[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) ENTT_NOEXCEPT {
return hashed_wstring{str};
}
} // namespace literals
}
}
} // namespace entt
#endif

View File

@@ -1,18 +1,15 @@
#ifndef ENTT_CORE_IDENT_HPP
#define ENTT_CORE_IDENT_HPP
#include <cstddef>
#include <utility>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "fwd.hpp"
#include "type_traits.hpp"
namespace entt {
/**
* @brief Types identifiers.
*
@@ -43,8 +40,8 @@ namespace entt {
template<typename... Types>
class identifier {
template<typename Type, std::size_t... Index>
[[nodiscard]] static constexpr id_type get(std::index_sequence<Index...>) {
static_assert(std::disjunction_v<std::is_same<Type, Types>...>, "Invalid type");
[[nodiscard]] static constexpr id_type get(std::index_sequence<Index...>) ENTT_NOEXCEPT {
static_assert((std::is_same_v<Type, Types> || ...), "Invalid type");
return (0 + ... + (std::is_same_v<Type, type_list_element_t<Index, type_list<std::decay_t<Types>...>>> ? id_type{Index} : id_type{}));
}
@@ -57,8 +54,6 @@ public:
static constexpr identifier_type type = get<std::decay_t<Type>>(std::index_sequence_for<Types...>{});
};
}
} // namespace entt
#endif

117
src/entt/core/iterator.hpp Normal file
View File

@@ -0,0 +1,117 @@
#ifndef ENTT_CORE_ITERATOR_HPP
#define ENTT_CORE_ITERATOR_HPP
#include <iterator>
#include <memory>
#include <utility>
#include "../config/config.h"
namespace entt {
/**
* @brief Helper type to use as pointer with input iterators.
* @tparam Type of wrapped value.
*/
template<typename Type>
struct input_iterator_pointer final {
/*! @brief Pointer type. */
using pointer = Type *;
/*! @brief Default copy constructor, deleted on purpose. */
input_iterator_pointer(const input_iterator_pointer &) = delete;
/*! @brief Default move constructor. */
input_iterator_pointer(input_iterator_pointer &&) = default;
/**
* @brief Constructs a proxy object by move.
* @param val Value to use to initialize the proxy object.
*/
input_iterator_pointer(Type &&val)
: value{std::move(val)} {}
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This proxy object.
*/
input_iterator_pointer &operator=(const input_iterator_pointer &) = delete;
/**
* @brief Default move assignment operator.
* @return This proxy object.
*/
input_iterator_pointer &operator=(input_iterator_pointer &&) = default;
/**
* @brief Access operator for accessing wrapped values.
* @return A pointer to the wrapped value.
*/
[[nodiscard]] pointer operator->() ENTT_NOEXCEPT {
return std::addressof(value);
}
private:
Type value;
};
/**
* @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>
struct iterable_adaptor final {
/*! @brief Value type. */
using value_type = typename std::iterator_traits<It>::value_type;
/*! @brief Iterator type. */
using iterator = It;
/*! @brief Sentinel type. */
using sentinel = Sentinel;
/*! @brief Default constructor. */
iterable_adaptor() = default;
/**
* @brief Creates an iterable object from a pair of iterators.
* @param from Begin iterator.
* @param to End iterator.
*/
iterable_adaptor(iterator from, sentinel to)
: first{from},
last{to} {}
/**
* @brief Returns an iterator to the beginning.
* @return An iterator to the first element of the range.
*/
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
return first;
}
/**
* @brief Returns an iterator to the end.
* @return An iterator to the element following the last element of the
* range.
*/
[[nodiscard]] sentinel end() const ENTT_NOEXCEPT {
return last;
}
/*! @copydoc begin */
[[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT {
return begin();
}
/*! @copydoc end */
[[nodiscard]] sentinel cend() const ENTT_NOEXCEPT {
return end();
}
private:
It first;
Sentinel last;
};
} // namespace entt
#endif

289
src/entt/core/memory.hpp Normal file
View File

@@ -0,0 +1,289 @@
#ifndef ENTT_CORE_MEMORY_HPP
#define ENTT_CORE_MEMORY_HPP
#include <cstddef>
#include <limits>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include "../config/config.h"
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) ENTT_NOEXCEPT {
if constexpr(std::is_pointer_v<std::remove_cv_t<std::remove_reference_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.
* @param lhs A valid allocator.
* @param rhs Another valid allocator.
*/
template<typename Allocator>
constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) {
lhs = rhs;
}
}
/**
* @brief Utility function to design allocation-aware containers.
* @tparam Allocator Type of allocator.
* @param lhs A valid allocator.
* @param rhs Another valid allocator.
*/
template<typename Allocator>
constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) {
lhs = std::move(rhs);
}
}
/**
* @brief Utility function to design allocation-aware containers.
* @tparam Allocator Type of allocator.
* @param lhs A valid allocator.
* @param rhs Another valid allocator.
*/
template<typename Allocator>
constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
ENTT_ASSERT(std::allocator_traits<Allocator>::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers");
if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) {
using std::swap;
swap(lhs, rhs);
}
}
/**
* @brief Checks whether a value is a power of two or not.
* @param value A value that may or may not be a power of two.
* @return True if the value is a power of two, false otherwise.
*/
[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
return value && ((value & (value - 1)) == 0);
}
/**
* @brief Computes the smallest power of two greater than or equal to a value.
* @param value The value to use.
* @return The smallest power of two greater than or equal to the given value.
*/
[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded");
std::size_t curr = value - (value != 0u);
for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) {
curr |= curr >> next;
}
return ++curr;
}
/**
* @brief Fast module utility function (powers of two only).
* @param value A value for which to calculate the modulus.
* @param mod _Modulus_, it must be a power of two.
* @return The common remainder.
*/
[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT {
ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two");
return value & (mod - 1u);
}
/**
* @brief Deleter for allocator-aware unique pointers (waiting for C++20).
* @tparam Args Types of arguments to use to construct the object.
*/
template<typename Allocator>
struct allocation_deleter: private Allocator {
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Pointer type. */
using pointer = typename std::allocator_traits<Allocator>::pointer;
/**
* @brief Inherited constructors.
* @param alloc The allocator to use.
*/
allocation_deleter(const allocator_type &alloc)
: Allocator{alloc} {}
/**
* @brief Destroys the pointed object and deallocates its memory.
* @param ptr A valid pointer to an object of the given type.
*/
void operator()(pointer ptr) {
using alloc_traits = typename std::allocator_traits<Allocator>;
alloc_traits::destroy(*this, to_address(ptr));
alloc_traits::deallocate(*this, ptr, 1u);
}
};
/**
* @brief Allows `std::unique_ptr` to use allocators (waiting for C++20).
* @tparam Type Type of object to allocate for and to construct.
* @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 properly initialized unique pointer with a custom deleter.
*/
template<typename Type, typename Allocator, typename... Args>
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;
allocator_type alloc{allocator};
auto ptr = alloc_traits::allocate(alloc, 1u);
ENTT_TRY {
alloc_traits::construct(alloc, to_address(ptr), std::forward<Args>(args)...);
}
ENTT_CATCH {
alloc_traits::deallocate(alloc, ptr, 1u);
ENTT_THROW;
}
return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc};
}
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename Type>
struct uses_allocator_construction {
template<typename Allocator, typename... Params>
static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) ENTT_NOEXCEPT {
if constexpr(!std::uses_allocator_v<Type, Allocator> && std::is_constructible_v<Type, Params...>) {
return std::forward_as_tuple(std::forward<Params>(params)...);
} else {
static_assert(std::uses_allocator_v<Type, Allocator>, "Ill-formed request");
if constexpr(std::is_constructible_v<Type, std::allocator_arg_t, const Allocator &, Params...>) {
return std::tuple<std::allocator_arg_t, const Allocator &, Params &&...>(std::allocator_arg, allocator, std::forward<Params>(params)...);
} else {
static_assert(std::is_constructible_v<Type, Params..., const Allocator &>, "Ill-formed request");
return std::forward_as_tuple(std::forward<Params>(params)..., allocator);
}
}
}
};
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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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)));
}
};
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Uses-allocator construction utility (waiting for C++20).
*
* Primarily intended for internal use. Prepares the argument list needed to
* 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) ENTT_NOEXCEPT {
return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...);
}
/**
* @brief Uses-allocator construction utility (waiting for C++20).
*
* Primarily intended for internal use. Creates an object of a given type by
* 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) {
return std::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
}
/**
* @brief Uses-allocator construction utility (waiting for C++20).
*
* Primarily intended for internal use. Creates an object of a given type by
* 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) {
return std::apply([&](auto &&...curr) { return new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
}
} // namespace entt
#endif

View File

@@ -1,14 +1,11 @@
#ifndef ENTT_CORE_MONOSTATE_HPP
#define ENTT_CORE_MONOSTATE_HPP
#include "../config/config.h"
#include "fwd.hpp"
namespace entt {
/**
* @brief Minimal implementation of the monostate pattern.
*
@@ -47,7 +44,6 @@ private:
inline static ENTT_MAYBE_ATOMIC(Type) value{};
};
/**
* @brief Helper variable template.
* @tparam Value Value used to differentiate between different variables.
@@ -55,8 +51,6 @@ private:
template<id_type Value>
inline monostate<Value> monostate_v = {};
}
} // namespace entt
#endif

29
src/entt/core/tuple.hpp Normal file
View File

@@ -0,0 +1,29 @@
#ifndef ENTT_CORE_TUPLE_HPP
#define ENTT_CORE_TUPLE_HPP
#include <tuple>
#include <type_traits>
#include <utility>
#include "../config/config.h"
namespace entt {
/**
* @brief Utility function to unwrap tuples of a single element.
* @tparam Type Tuple type of any sizes.
* @param value A tuple object of the given type.
* @return The tuple itself if it contains more than one element, the first
* element otherwise.
*/
template<typename Type>
constexpr decltype(auto) unwrap_tuple(Type &&value) ENTT_NOEXCEPT {
if constexpr(std::tuple_size_v<std::remove_reference_t<Type>> == 1u) {
return std::get<0>(std::forward<Type>(value));
} else {
return std::forward<Type>(value);
}
}
} // namespace entt
#endif

View File

@@ -1,40 +1,35 @@
#ifndef ENTT_CORE_TYPE_INFO_HPP
#define ENTT_CORE_TYPE_INFO_HPP
#include <string_view>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/attribute.h"
#include "hashed_string.hpp"
#include "fwd.hpp"
#include "hashed_string.hpp"
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
struct ENTT_API type_seq final {
struct ENTT_API type_index final {
[[nodiscard]] static id_type next() ENTT_NOEXCEPT {
static ENTT_MAYBE_ATOMIC(id_type) value{};
return value++;
}
};
template<typename Type>
[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT {
#if defined ENTT_PRETTY_FUNCTION
std::string_view pretty_function{ENTT_PRETTY_FUNCTION};
auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX)+1);
auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1);
auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first);
return value;
#else
@@ -42,21 +37,18 @@ template<typename Type>
#endif
}
template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT {
constexpr auto value = stripped_type_name<Type>();
return value;
}
template<typename Type>
[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT {
static const auto value = stripped_type_name<Type>();
return value;
}
template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT {
constexpr auto stripped = stripped_type_name<Type>();
@@ -64,7 +56,6 @@ template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
return value;
}
template<typename Type>
[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT {
static const auto value = [](const auto stripped) {
@@ -73,36 +64,34 @@ template<typename Type>
return value;
}
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Type sequential identifier.
* @tparam Type Type for which to generate a sequential identifier.
*/
template<typename Type, typename = void>
struct ENTT_API type_seq final {
struct ENTT_API type_index final {
/**
* @brief Returns the sequential identifier of a given type.
* @return The sequential identifier of a given type.
*/
[[nodiscard]] static id_type value() ENTT_NOEXCEPT {
static const id_type value = internal::type_seq::next();
static const id_type value = internal::type_index::next();
return value;
}
/*! @copydoc value */
[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); }
[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT {
return value();
}
};
/**
* @brief Type hash.
* @tparam Type Type for which to generate a hash value.
@@ -118,15 +107,16 @@ struct type_hash final {
return internal::type_hash<Type>(0);
#else
[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
return type_seq<Type>::value();
return type_index<Type>::value();
#endif
}
/*! @copydoc value */
[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); }
[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT {
return value();
}
};
/**
* @brief Type name.
* @tparam Type Type for which to generate a name.
@@ -142,119 +132,143 @@ struct type_name final {
}
/*! @copydoc value */
[[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { return value(); }
[[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT {
return value();
}
};
/*! @brief Implementation specific information about a type. */
class type_info final {
template<typename>
friend type_info type_id() ENTT_NOEXCEPT;
type_info(id_type seq_v, id_type hash_v, std::string_view name_v) ENTT_NOEXCEPT
: seq_value{seq_v},
hash_value{hash_v},
name_value{name_v}
{}
public:
/*! @brief Default constructor. */
type_info() ENTT_NOEXCEPT
: type_info({}, {}, {})
{}
/*! @brief Default copy constructor. */
type_info(const type_info &) ENTT_NOEXCEPT = default;
/*! @brief Default move constructor. */
type_info(type_info &&) ENTT_NOEXCEPT = default;
struct type_info final {
/**
* @brief Constructs a type info object for a given type.
* @tparam Type Type for which to construct a type info object.
*/
template<typename Type>
constexpr type_info(std::in_place_type_t<Type>) ENTT_NOEXCEPT
: seq{type_index<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
identifier{type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
alias{type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()} {}
/**
* @brief Default copy assignment operator.
* @return This type info object.
* @brief Type index.
* @return Type index.
*/
type_info & operator=(const type_info &) ENTT_NOEXCEPT = default;
/**
* @brief Default move assignment operator.
* @return This type info object.
*/
type_info & operator=(type_info &&) ENTT_NOEXCEPT = default;
/**
* @brief Checks if a type info object is properly initialized.
* @return True if the object is properly initialized, false otherwise.
*/
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
return name_value.data() != nullptr;
}
/**
* @brief Type sequential identifier.
* @return Type sequential identifier.
*/
[[nodiscard]] id_type seq() const ENTT_NOEXCEPT {
return seq_value;
[[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT {
return seq;
}
/**
* @brief Type hash.
* @return Type hash.
*/
[[nodiscard]] id_type hash() const ENTT_NOEXCEPT {
return hash_value;
[[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT {
return identifier;
}
/**
* @brief Type name.
* @return Type name.
*/
[[nodiscard]] std::string_view name() const ENTT_NOEXCEPT {
return name_value;
}
/**
* @brief Compares the contents of two type info objects.
* @param other Object with which to compare.
* @return False if the two contents differ, true otherwise.
*/
[[nodiscard]] bool operator==(const type_info &other) const ENTT_NOEXCEPT {
return hash_value == other.hash_value;
[[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT {
return alias;
}
private:
id_type seq_value;
id_type hash_value;
std::string_view name_value;
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 contents differ, false otherwise.
* @return True if the two type info objects are identical, false otherwise.
*/
[[nodiscard]] inline bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) ENTT_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]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_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) ENTT_NOEXCEPT {
return lhs.index() < rhs.index();
}
/**
* @brief Returns the type info object for a given type.
* @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) ENTT_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) ENTT_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) ENTT_NOEXCEPT {
return !(lhs < rhs);
}
/**
* @brief Returns the type info object associated to a given type.
*
* The returned element refers to an object with static storage duration.<br/>
* The type doesn't need to be a complete type. If the type is a reference, the
* result refers to the referenced type. In all cases, top-level cv-qualifiers
* are ignored.
*
* @tparam Type Type for which to generate a type info object.
* @return The type info object for the given type.
* @return A reference to a properly initialized type info object.
*/
template<typename Type>
[[nodiscard]] type_info type_id() ENTT_NOEXCEPT {
return type_info{
type_seq<std::remove_cv_t<std::remove_reference_t<Type>>>::value(),
type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value(),
type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()
};
[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT {
if constexpr(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>) {
static type_info instance{std::in_place_type<Type>};
return instance;
} else {
return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
}
}
/*! @copydoc type_id */
template<typename Type>
[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT {
return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
}
} // namespace entt
#endif

View File

@@ -1,7 +1,6 @@
#ifndef ENTT_CORE_TYPE_TRAITS_HPP
#define ENTT_CORE_TYPE_TRAITS_HPP
#include <cstddef>
#include <iterator>
#include <type_traits>
@@ -9,10 +8,8 @@
#include "../config/config.h"
#include "fwd.hpp"
namespace entt {
/**
* @brief Utility class to disambiguate overloaded functions.
* @tparam N Number of choices available.
@@ -20,17 +17,13 @@ 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 TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
{};
/*! @copybrief choice_t */
template<>
struct choice_t<0> {};
/**
* @brief Variable template for the choice trick.
* @tparam N Number of choices available.
@@ -38,7 +31,6 @@ struct choice_t<0> {};
template<std::size_t N>
inline constexpr choice_t<N> choice{};
/**
* @brief Identity type trait.
*
@@ -53,7 +45,6 @@ struct type_identity {
using type = Type;
};
/**
* @brief Helper type.
* @tparam Type A type.
@@ -61,7 +52,6 @@ struct type_identity {
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.
@@ -70,30 +60,25 @@ using type_identity_t = typename type_identity<Type>::type;
template<typename Type, typename = void>
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))>>
: std::integral_constant<std::size_t, sizeof(Type)>
{};
: std::integral_constant<std::size_t, sizeof(Type)> {};
/**
* @brief Helper variable template.
* @tparam Type The type of which to return the size.
*/
template<class Type>
template<typename Type>
inline constexpr std::size_t size_of_v = size_of<Type>::value;
/**
* @brief Using declaration to be used to _repeat_ the same type a number of
* times equal to the size of a given parameter pack.
* @tparam Type A type to repeat.
*/
template<typename Type, typename>
using unpack_as_t = Type;
using unpack_as_type = Type;
/**
* @brief Helper variable template to be used to _repeat_ the same value a
@@ -101,8 +86,7 @@ using unpack_as_t = Type;
* @tparam Value A value to repeat.
*/
template<auto Value, typename>
inline constexpr auto unpack_as_v = Value;
inline constexpr auto unpack_as_value = Value;
/**
* @brief Wraps a static constant.
@@ -111,7 +95,6 @@ inline constexpr auto unpack_as_v = Value;
template<auto Value>
using integral_constant = std::integral_constant<decltype(Value), Value>;
/**
* @brief Alias template to facilitate the creation of named values.
* @tparam Value A constant value at least convertible to `id_type`.
@@ -119,7 +102,6 @@ using integral_constant = std::integral_constant<decltype(Value), Value>;
template<id_type Value>
using tag = integral_constant<Value>;
/**
* @brief A class to use to push around lists of types, nothing more.
* @tparam Type Types provided by the type list.
@@ -132,12 +114,10 @@ struct type_list {
static constexpr auto size = sizeof...(Type);
};
/*! @brief Primary template isn't defined on purpose. */
template<std::size_t, typename>
struct type_list_element;
/**
* @brief Provides compile-time indexed access to the types of a type list.
* @tparam Index Index of the type to return.
@@ -146,9 +126,7 @@ struct type_list_element;
*/
template<std::size_t Index, typename Type, typename... Other>
struct type_list_element<Index, type_list<Type, Other...>>
: type_list_element<Index - 1u, type_list<Other...>>
{};
: type_list_element<Index - 1u, type_list<Other...>> {};
/**
* @brief Provides compile-time indexed access to the types of a type list.
@@ -161,7 +139,6 @@ struct type_list_element<0u, type_list<Type, Other...>> {
using type = Type;
};
/**
* @brief Helper type.
* @tparam Index Index of the type to return.
@@ -170,7 +147,6 @@ struct type_list_element<0u, type_list<Type, Other...>> {
template<std::size_t Index, typename List>
using type_list_element_t = typename type_list_element<Index, List>::type;
/**
* @brief Concatenates multiple type lists.
* @tparam Type Types provided by the first type list.
@@ -178,14 +154,14 @@ using type_list_element_t = typename type_list_element<Index, List>::type;
* @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...>) { return {}; }
constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) {
return {};
}
/*! @brief Primary template isn't defined on purpose. */
template<typename...>
struct type_list_cat;
/*! @brief Concatenates multiple type lists. */
template<>
struct type_list_cat<> {
@@ -193,7 +169,6 @@ struct type_list_cat<> {
using type = type_list<>;
};
/**
* @brief Concatenates multiple type lists.
* @tparam Type Types provided by the first type list.
@@ -206,7 +181,6 @@ struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> {
using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type;
};
/**
* @brief Concatenates multiple type lists.
* @tparam Type Types provided by the type list.
@@ -217,7 +191,6 @@ struct type_list_cat<type_list<Type...>> {
using type = type_list<Type...>;
};
/**
* @brief Helper type.
* @tparam List Type lists to concatenate.
@@ -225,12 +198,10 @@ struct type_list_cat<type_list<Type...>> {
template<typename... List>
using type_list_cat_t = typename type_list_cat<List...>::type;
/*! @brief Primary template isn't defined on purpose. */
template<typename>
struct type_list_unique;
/**
* @brief Removes duplicates types from a type list.
* @tparam Type One of the types provided by the given type list.
@@ -240,13 +211,11 @@ template<typename Type, typename... Other>
struct type_list_unique<type_list<Type, Other...>> {
/*! @brief A type list without duplicate types. */
using type = std::conditional_t<
std::disjunction_v<std::is_same<Type, Other>...>,
(std::is_same_v<Type, Other> || ...),
typename type_list_unique<type_list<Other...>>::type,
type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>
>;
type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>;
};
/*! @brief Removes duplicates types from a type list. */
template<>
struct type_list_unique<type_list<>> {
@@ -254,7 +223,6 @@ struct type_list_unique<type_list<>> {
using type = type_list<>;
};
/**
* @brief Helper type.
* @tparam Type A type list.
@@ -262,7 +230,6 @@ struct type_list_unique<type_list<>> {
template<typename Type>
using type_list_unique_t = typename type_list_unique<Type>::type;
/**
* @brief Provides the member constant `value` to true if a type list contains a
* given type, false otherwise.
@@ -272,7 +239,6 @@ using type_list_unique_t = typename type_list_unique<Type>::type;
template<typename List, typename Type>
struct type_list_contains;
/**
* @copybrief type_list_contains
* @tparam Type Types provided by the type list.
@@ -281,21 +247,18 @@ struct type_list_contains;
template<typename... Type, typename Other>
struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
/**
* @brief Helper variable template.
* @tparam List Type list.
* @tparam Type Type to look for.
*/
template<class List, typename Type>
template<typename List, typename Type>
inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value;
/*! @brief Primary template isn't defined on purpose. */
template<typename...>
struct type_list_diff;
/**
* @brief Computes the difference between two type lists.
* @tparam Type Types provided by the first type list.
@@ -307,7 +270,6 @@ struct type_list_diff<type_list<Type...>, type_list<Other...>> {
using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>;
};
/**
* @brief Helper type.
* @tparam List Type lists between which to compute the difference.
@@ -315,7 +277,6 @@ struct type_list_diff<type_list<Type...>, type_list<Other...>> {
template<typename... List>
using type_list_diff_t = typename type_list_diff<List...>::type;
/**
* @brief A class to use to push around lists of constant values, nothing more.
* @tparam Value Values provided by the value list.
@@ -328,12 +289,10 @@ struct value_list {
static constexpr auto size = sizeof...(Value);
};
/*! @brief Primary template isn't defined on purpose. */
template<std::size_t, typename>
struct value_list_element;
/**
* @brief Provides compile-time indexed access to the values of a value list.
* @tparam Index Index of the value to return.
@@ -342,9 +301,7 @@ struct value_list_element;
*/
template<std::size_t Index, auto Value, auto... Other>
struct value_list_element<Index, value_list<Value, Other...>>
: value_list_element<Index - 1u, value_list<Other...>>
{};
: value_list_element<Index - 1u, value_list<Other...>> {};
/**
* @brief Provides compile-time indexed access to the types of a type list.
@@ -357,7 +314,6 @@ struct value_list_element<0u, value_list<Value, Other...>> {
static constexpr auto value = Value;
};
/**
* @brief Helper type.
* @tparam Index Index of the value to return.
@@ -366,7 +322,6 @@ struct value_list_element<0u, value_list<Value, Other...>> {
template<std::size_t Index, typename List>
inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
/**
* @brief Concatenates multiple value lists.
* @tparam Value Values provided by the first value list.
@@ -374,14 +329,14 @@ inline constexpr auto value_list_element_v = value_list_element<Index, List>::va
* @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...>) { return {}; }
constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) {
return {};
}
/*! @brief Primary template isn't defined on purpose. */
template<typename...>
struct value_list_cat;
/*! @brief Concatenates multiple value lists. */
template<>
struct value_list_cat<> {
@@ -389,7 +344,6 @@ struct value_list_cat<> {
using type = value_list<>;
};
/**
* @brief Concatenates multiple value lists.
* @tparam Value Values provided by the first value list.
@@ -402,7 +356,6 @@ struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> {
using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type;
};
/**
* @brief Concatenates multiple value lists.
* @tparam Value Values provided by the value list.
@@ -413,7 +366,6 @@ struct value_list_cat<value_list<Value...>> {
using type = value_list<Value...>;
};
/**
* @brief Helper type.
* @tparam List Value lists to concatenate.
@@ -421,74 +373,10 @@ struct value_list_cat<value_list<Value...>> {
template<typename... List>
using value_list_cat_t = typename value_list_cat<List...>::type;
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename>
[[nodiscard]] constexpr bool is_equality_comparable(...) { return false; }
template<typename Type>
[[nodiscard]] constexpr auto is_equality_comparable(choice_t<0>)
-> decltype(std::declval<Type>() == std::declval<Type>()) { return true; }
template<typename Type>
[[nodiscard]] constexpr auto is_equality_comparable(choice_t<1>)
-> decltype(std::declval<typename Type::value_type>(), std::declval<Type>() == std::declval<Type>()) {
if constexpr(std::is_same_v<typename Type::value_type, Type>) {
return is_equality_comparable<Type>(choice<0>);
} else {
return is_equality_comparable<typename Type::value_type>(choice<2>);
}
}
template<typename Type>
[[nodiscard]] constexpr auto is_equality_comparable(choice_t<2>)
-> decltype(std::declval<typename Type::mapped_type>(), std::declval<Type>() == std::declval<Type>()) {
return is_equality_comparable<typename Type::key_type>(choice<2>) && is_equality_comparable<typename Type::mapped_type>(choice<2>);
}
}
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Provides the member constant `value` to true if a given type is
* equality comparable, false otherwise.
* @tparam Type The type to test.
*/
template<typename Type, typename = void>
struct is_equality_comparable: std::bool_constant<internal::is_equality_comparable<Type>(choice<2>)> {};
/**
* @brief Helper variable template.
* @tparam Type The type to test.
*/
template<class Type>
inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value;
/*! @brief Same as std::is_invocable, but with tuples. */
template<typename, typename>
struct is_applicable: std::false_type {};
/**
* @copybrief is_applicable
* @tparam Func A valid function type.
@@ -498,7 +386,6 @@ struct is_applicable: std::false_type {};
template<typename Func, template<typename...> class Tuple, typename... Args>
struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
/**
* @copybrief is_applicable
* @tparam Func A valid function type.
@@ -508,7 +395,6 @@ struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
template<typename Func, template<typename...> class Tuple, typename... Args>
struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {};
/**
* @brief Helper variable template.
* @tparam Func A valid function type.
@@ -517,12 +403,10 @@ struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args..
template<typename Func, typename Args>
inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value;
/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */
template<typename, typename, typename>
struct is_applicable_r: std::false_type {};
/**
* @copybrief is_applicable_r
* @tparam Ret The type to which the return type of the function should be
@@ -533,7 +417,6 @@ struct is_applicable_r: std::false_type {};
template<typename Ret, typename Func, typename... Args>
struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {};
/**
* @brief Helper variable template.
* @tparam Ret The type to which the return type of the function should be
@@ -544,7 +427,6 @@ struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret,
template<typename Ret, typename Func, typename Args>
inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value;
/**
* @brief Provides the member constant `value` to true if a given type is
* complete, false otherwise.
@@ -553,12 +435,10 @@ inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::valu
template<typename Type, typename = void>
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 {};
/**
* @brief Helper variable template.
* @tparam Type The type to test.
@@ -566,7 +446,6 @@ struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {}
template<typename Type>
inline constexpr bool is_complete_v = is_complete<Type>::value;
/**
* @brief Provides the member constant `value` to true if a given type is an
* iterator, false otherwise.
@@ -575,13 +454,30 @@ inline constexpr bool is_complete_v = is_complete<Type>::value;
template<typename Type, typename = void>
struct is_iterator: std::false_type {};
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename, typename = void>
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 {};
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @copydoc is_iterator */
template<typename Type>
struct is_iterator<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>
: std::true_type
{};
struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>>
: internal::has_iterator_category<Type> {};
/**
* @brief Helper variable template.
@@ -590,39 +486,110 @@ struct is_iterator<Type, std::void_t<typename std::iterator_traits<Type>::iterat
template<typename Type>
inline constexpr bool is_iterator_v = is_iterator<Type>::value;
/**
* @brief Provides the member constant `value` to true if a given type is of the
* required iterator type, false otherwise.
* @tparam Type The type to test.
* @tparam It Required iterator type.
* @brief Provides the member constant `value` to true if a given type is both
* an empty and non-final class, false otherwise.
* @tparam Type The type to test
*/
template<typename Type, typename It, typename = void>
struct is_iterator_type: std::false_type {};
/*! @copydoc is_iterator_type */
template<typename Type, typename It>
struct is_iterator_type<Type, It, std::enable_if_t<is_iterator_v<Type> && std::is_same_v<Type, It>>>
: std::true_type
{};
/*! @copydoc is_iterator_type */
template<typename Type, typename It>
struct is_iterator_type<Type, It, std::enable_if_t<!std::is_same_v<Type, It>, std::void_t<typename It::iterator_type>>>
: is_iterator_type<Type, typename It::iterator_type>
{};
template<typename Type>
struct is_ebco_eligible
: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
/**
* @brief Helper variable template.
* @tparam Type The type to test.
* @tparam It Required iterator type.
*/
template<typename Type, typename It>
inline constexpr bool is_iterator_type_v = is_iterator_type<Type, It>::value;
template<typename Type>
inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value;
/**
* @brief Provides the member constant `value` to true if `Type::is_transparent`
* is valid and denotes a type, false otherwise.
* @tparam Type The type to test.
*/
template<typename Type, typename = void>
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 {};
/**
* @brief Helper variable template.
* @tparam Type The type to test.
*/
template<typename Type>
inline constexpr bool is_transparent_v = is_transparent<Type>::value;
/**
* @brief Provides the member constant `value` to true if a given type is
* equality comparable, false otherwise.
* @tparam Type The type to test.
*/
template<typename Type, typename = void>
struct is_equality_comparable: std::false_type {};
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename, typename = void>
struct has_tuple_size_value: std::false_type {};
template<typename Type>
struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
template<typename Type, std::size_t... Index>
[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
}
template<typename>
[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
return true;
}
template<typename Type>
[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
if constexpr(is_iterator_v<Type>) {
return true;
} else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
return maybe_equality_comparable<Type>(choice<0>);
} else {
return is_equality_comparable<typename Type::value_type>::value;
}
}
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
if constexpr(has_tuple_size_value<Type>::value) {
return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
} else {
return maybe_equality_comparable<Type>(choice<1>);
}
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @copydoc is_equality_comparable */
template<typename Type>
struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
/**
* @brief Helper variable template.
* @tparam Type The type to test.
*/
template<typename Type>
inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value;
/**
* @brief Transcribes the constness of a type to another type.
@@ -635,7 +602,6 @@ struct constness_as {
using type = std::remove_const_t<To>;
};
/*! @copydoc constness_as */
template<typename To, typename From>
struct constness_as<To, const From> {
@@ -643,7 +609,6 @@ struct constness_as<To, const From> {
using type = std::add_const_t<To>;
};
/**
* @brief Alias template to facilitate the transcription of the constness.
* @tparam To The type to which to transcribe the constness.
@@ -652,7 +617,6 @@ struct constness_as<To, const From> {
template<typename To, typename From>
using constness_as_t = typename constness_as<To, From>::type;
/**
* @brief Extracts the class of a non-static member object or function.
* @tparam Member A pointer to a non-static member object or function.
@@ -662,20 +626,19 @@ class member_class {
static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function");
template<typename Class, typename Ret, typename... Args>
static Class * clazz(Ret(Class:: *)(Args...));
static Class *clazz(Ret (Class::*)(Args...));
template<typename Class, typename Ret, typename... Args>
static Class * clazz(Ret(Class:: *)(Args...) const);
static Class *clazz(Ret (Class::*)(Args...) const);
template<typename Class, typename Type>
static Class * clazz(Type Class:: *);
static Class *clazz(Type Class::*);
public:
/*! @brief The class of the given non-static member object or function. */
using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>;
};
/**
* @brief Helper type.
* @tparam Member A pointer to a non-static member object or function.
@@ -683,8 +646,6 @@ public:
template<typename Member>
using member_class_t = typename member_class<Member>::type;
}
} // namespace entt
#endif

View File

@@ -1,16 +1,16 @@
#ifndef ENTT_CORE_UTILITY_HPP
#define ENTT_CORE_UTILITY_HPP
#include <utility>
#include "../config/config.h"
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.
@@ -18,12 +18,11 @@ struct identity {
* @return The submitted value as-is.
*/
template<class Type>
[[nodiscard]] constexpr Type && operator()(Type &&value) const ENTT_NOEXCEPT {
[[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT {
return std::forward<Type>(value);
}
};
/**
* @brief Constant utility to disambiguate overloaded members of a class.
* @tparam Type Type of the desired overload.
@@ -32,8 +31,9 @@ struct identity {
* @return Pointer to the member.
*/
template<typename Type, typename Class>
[[nodiscard]] constexpr auto overload(Type Class:: *member) ENTT_NOEXCEPT { return member; }
[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT {
return member;
}
/**
* @brief Constant utility to disambiguate overloaded functions.
@@ -42,8 +42,9 @@ template<typename Type, typename Class>
* @return Pointer to the function.
*/
template<typename Func>
[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { return func; }
[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT {
return func;
}
/**
* @brief Helper type for visitors.
@@ -54,15 +55,12 @@ struct overloaded: Func... {
using Func::operator()...;
};
/**
* @brief Deduction guide.
* @tparam Func Types of function objects.
*/
template<class... Func>
overloaded(Func...)
-> overloaded<Func...>;
overloaded(Func...) -> overloaded<Func...>;
/**
* @brief Basic implementation of a y-combinator.
@@ -74,9 +72,8 @@ struct y_combinator {
* @brief Constructs a y-combinator from a given function.
* @param recursive A potentially recursive function.
*/
y_combinator(Func recursive):
func{std::move(recursive)}
{}
y_combinator(Func recursive)
: func{std::move(recursive)} {}
/**
* @brief Invokes a y-combinator and therefore its underlying function.
@@ -84,14 +81,14 @@ struct y_combinator {
* @param args Parameters to use to invoke the underlying function.
* @return Return value of the underlying function, if any.
*/
template <class... Args>
decltype(auto) operator()(Args &&... args) const {
template<class... Args>
decltype(auto) operator()(Args &&...args) const {
return func(*this, std::forward<Args>(args)...);
}
/*! @copydoc operator()() */
template <class... Args>
decltype(auto) operator()(Args &&... args) {
template<class... Args>
decltype(auto) operator()(Args &&...args) {
return func(*this, std::forward<Args>(args)...);
}
@@ -99,8 +96,6 @@ private:
Func func;
};
}
} // namespace entt
#endif

View File

@@ -1,34 +1,61 @@
#ifndef ENTT_ENTITY_COMPONENT_HPP
#define ENTT_ENTITY_COMPONENT_HPP
#include <cstddef>
#include <type_traits>
#include "../config/config.h"
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
/*! @brief Commonly used default traits for all types. */
struct basic_component_traits {
/*! @brief Pointer stability, default is `std::false_type`. */
using in_place_delete = std::false_type;
/*! @brief Empty type optimization, default is `ENTT_IGNORE_IF_EMPTY`. */
using ignore_if_empty = ENTT_IGNORE_IF_EMPTY;
};
namespace internal {
template<typename, typename = void>
struct in_place_delete: std::false_type {};
template<typename Type>
struct in_place_delete<Type, std::enable_if_t<Type::in_place_delete>>
: std::true_type {};
template<typename Type, typename = void>
struct page_size: std::integral_constant<std::size_t, (ENTT_IGNORE_IF_EMPTY && std::is_empty_v<Type>) ? 0u : ENTT_PACKED_PAGE> {};
template<typename Type>
struct page_size<Type, std::enable_if_t<std::is_convertible_v<decltype(Type::page_size), std::size_t>>>
: std::integral_constant<std::size_t, Type::page_size> {};
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Common way to access various properties of components.
* @tparam Type Type of component.
*/
template<typename Type, typename = void>
struct component_traits: basic_component_traits {
struct component_traits {
static_assert(std::is_same_v<std::decay_t<Type>, Type>, "Unsupported type");
/*! @brief Pointer stability, default is `false`. */
static constexpr bool in_place_delete = internal::in_place_delete<Type>::value;
/*! @brief Page size, default is `ENTT_PACKED_PAGE` for non-empty types. */
static constexpr std::size_t page_size = internal::page_size<Type>::value;
};
/**
* @brief Helper variable template.
* @tparam Type Type of component.
*/
template<class Type>
inline constexpr bool ignore_as_empty_v = (component_traits<Type>::page_size == 0u);
}
} // namespace entt
#endif

View File

@@ -1,91 +1,78 @@
#ifndef ENTT_ENTITY_ENTITY_HPP
#define ENTT_ENTITY_ENTITY_HPP
#include <cstddef>
#include <cstdint>
#include <type_traits>
#include "../config/config.h"
#include "fwd.hpp"
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename, typename = void>
struct entt_traits;
template<typename Type>
struct entt_traits<Type, std::enable_if_t<std::is_enum_v<Type>>>
: entt_traits<std::underlying_type_t<Type>>
{};
: entt_traits<std::underlying_type_t<Type>> {};
template<typename Type>
struct entt_traits<Type, std::enable_if_t<std::is_class_v<Type>>>
: entt_traits<typename Type::entity_type>
{};
: entt_traits<typename Type::entity_type> {};
template<>
struct entt_traits<std::uint32_t> {
using entity_type = std::uint32_t;
using version_type = std::uint16_t;
using difference_type = std::int64_t;
static constexpr entity_type entity_mask = 0xFFFFF;
static constexpr entity_type version_mask = 0xFFF;
static constexpr std::size_t entity_shift = 20u;
};
template<>
struct entt_traits<std::uint64_t> {
using entity_type = std::uint64_t;
using version_type = std::uint32_t;
using difference_type = std::int64_t;
static constexpr entity_type entity_mask = 0xFFFFFFFF;
static constexpr entity_type version_mask = 0xFFFFFFFF;
static constexpr std::size_t entity_shift = 32u;
};
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Entity traits.
* @tparam Type Type of identifier.
*/
template<typename Type>
class entt_traits: private internal::entt_traits<Type> {
using traits_type = internal::entt_traits<Type>;
class entt_traits: internal::entt_traits<Type> {
using base_type = internal::entt_traits<Type>;
public:
/*! @brief Value type. */
using value_type = Type;
/*! @brief Underlying entity type. */
using entity_type = typename traits_type::entity_type;
using entity_type = typename base_type::entity_type;
/*! @brief Underlying version type. */
using version_type = typename traits_type::version_type;
/*! @brief Difference type. */
using difference_type = typename traits_type::difference_type;
using version_type = typename base_type::version_type;
/*! @brief Reserved identifier. */
static constexpr entity_type reserved = base_type::entity_mask | (base_type::version_mask << base_type::entity_shift);
/*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */
static constexpr auto page_size = ENTT_SPARSE_PAGE;
/**
* @brief Converts an entity to its underlying type.
@@ -102,7 +89,7 @@ public:
* @return The integral representation of the entity part.
*/
[[nodiscard]] static constexpr entity_type to_entity(const value_type value) ENTT_NOEXCEPT {
return (to_integral(value) & traits_type::entity_mask);
return (to_integral(value) & base_type::entity_mask);
}
/**
@@ -111,8 +98,7 @@ public:
* @return The integral representation of the version part.
*/
[[nodiscard]] static constexpr version_type to_version(const value_type value) ENTT_NOEXCEPT {
constexpr auto mask = (traits_type::version_mask << traits_type::entity_shift);
return ((to_integral(value) & mask) >> traits_type::entity_shift);
return (to_integral(value) >> base_type::entity_shift);
}
/**
@@ -125,34 +111,64 @@ public:
* @param version The version part of the identifier.
* @return A properly constructed identifier.
*/
[[nodiscard]] static constexpr value_type construct(const entity_type entity = traits_type::entity_mask, const version_type version = traits_type::version_mask) ENTT_NOEXCEPT {
return value_type{(entity & traits_type::entity_mask) | (version << traits_type::entity_shift)};
[[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) ENTT_NOEXCEPT {
return value_type{(entity & base_type::entity_mask) | (static_cast<entity_type>(version) << base_type::entity_shift)};
}
/**
* @brief Combines two identifiers in a single one.
*
* The returned identifier is a copy of the first element except for its
* version, which is taken from the second element.
*
* @param lhs The identifier from which to take the entity part.
* @param rhs The identifier from which to take the version part.
* @return A properly constructed identifier.
*/
[[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) ENTT_NOEXCEPT {
constexpr auto mask = (base_type::version_mask << base_type::entity_shift);
return value_type{(lhs & base_type::entity_mask) | (rhs & mask)};
}
};
/**
* @brief Converts an entity to its underlying type.
* @copydoc entt_traits<Entity>::to_integral
* @tparam Entity The value type.
* @param entity The value to convert.
* @return The integral representation of the given value.
*/
template<typename Entity>
[[nodiscard]] constexpr auto to_integral(const Entity entity) ENTT_NOEXCEPT {
return entt_traits<Entity>::to_integral(entity);
[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_integral(const Entity value) ENTT_NOEXCEPT {
return entt_traits<Entity>::to_integral(value);
}
/**
* @copydoc entt_traits<Entity>::to_entity
* @tparam Entity The value type.
*/
template<typename Entity>
[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_entity(const Entity value) ENTT_NOEXCEPT {
return entt_traits<Entity>::to_entity(value);
}
/*! @brief Null object for all entity identifiers. */
/**
* @copydoc entt_traits<Entity>::to_version
* @tparam Entity The value type.
*/
template<typename Entity>
[[nodiscard]] constexpr typename entt_traits<Entity>::version_type to_version(const Entity value) ENTT_NOEXCEPT {
return entt_traits<Entity>::to_version(value);
}
/*! @brief Null object for all identifiers. */
struct null_t {
/**
* @brief Converts the null object to identifiers of any type.
* @tparam Entity Type of entity identifier.
* @tparam Entity Type of identifier.
* @return The null representation for the given type.
*/
template<typename Entity>
[[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT {
return entt_traits<Entity>::construct();
using entity_traits = entt_traits<Entity>;
return entity_traits::combine(entity_traits::reserved, entity_traits::reserved);
}
/**
@@ -174,44 +190,33 @@ struct null_t {
}
/**
* @brief Compares a null object and an entity identifier of any type.
* @tparam Entity Type of entity identifier.
* @param entity Entity identifier with which to compare.
* @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>
[[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT {
return entt_traits<Entity>::to_entity(entity) == entt_traits<Entity>::to_entity(*this);
using entity_traits = entt_traits<Entity>;
return entity_traits::to_entity(entity) == entity_traits::to_entity(*this);
}
/**
* @brief Compares a null object and an entity identifier of any type.
* @tparam Entity Type of entity identifier.
* @param entity Entity identifier with which to compare.
* @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 ENTT_NOEXCEPT {
return !(entity == *this);
}
/**
* @brief Creates a null object from an entity identifier of any type.
* @tparam Entity Type of entity identifier.
* @param entity Entity identifier to turn into a null object.
* @return The null representation for the given identifier.
*/
template<typename Entity>
[[nodiscard]] constexpr Entity operator|(const Entity entity) const ENTT_NOEXCEPT {
return entt_traits<Entity>::construct(entt_traits<Entity>::to_entity(*this), entt_traits<Entity>::to_version(entity));
}
};
/**
* @brief Compares a null object and an entity identifier of any type.
* @tparam Entity Type of entity identifier.
* @param entity Entity identifier with which to compare.
* @brief Compares a null object and an identifier of any type.
* @tparam Entity Type of identifier.
* @param entity Identifier with which to compare.
* @param other A null object yet to be converted.
* @return False if the two elements differ, true otherwise.
*/
@@ -220,11 +225,10 @@ template<typename Entity>
return other.operator==(entity);
}
/**
* @brief Compares a null object and an entity identifier of any type.
* @tparam Entity Type of entity identifier.
* @param entity Entity identifier with which to compare.
* @brief Compares a null object and an identifier of any type.
* @tparam Entity Type of identifier.
* @param entity Identifier with which to compare.
* @param other A null object yet to be converted.
* @return True if the two elements differ, false otherwise.
*/
@@ -233,17 +237,17 @@ template<typename Entity>
return !(other == entity);
}
/*! @brief Tombstone object for all entity identifiers. */
/*! @brief Tombstone object for all identifiers. */
struct tombstone_t {
/**
* @brief Converts the tombstone object to identifiers of any type.
* @tparam Entity Type of entity identifier.
* @tparam Entity Type of identifier.
* @return The tombstone representation for the given type.
*/
template<typename Entity>
[[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT {
return entt_traits<Entity>::construct();
using entity_traits = entt_traits<Entity>;
return entity_traits::combine(entity_traits::reserved, entity_traits::reserved);
}
/**
@@ -265,44 +269,33 @@ struct tombstone_t {
}
/**
* @brief Compares a tombstone object and an entity identifier of any type.
* @tparam Entity Type of entity identifier.
* @param entity Entity identifier with which to compare.
* @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>
[[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT {
return entt_traits<Entity>::to_version(entity) == entt_traits<Entity>::to_version(*this);
using entity_traits = entt_traits<Entity>;
return entity_traits::to_version(entity) == entity_traits::to_version(*this);
}
/**
* @brief Compares a tombstone object and an entity identifier of any type.
* @tparam Entity Type of entity identifier.
* @param entity Entity identifier with which to compare.
* @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 ENTT_NOEXCEPT {
return !(entity == *this);
}
/**
* @brief Creates a tombstone object from an entity identifier of any type.
* @tparam Entity Type of entity identifier.
* @param entity Entity identifier to turn into a tombstone object.
* @return The tombstone representation for the given identifier.
*/
template<typename Entity>
[[nodiscard]] constexpr Entity operator|(const Entity entity) const ENTT_NOEXCEPT {
return entt_traits<Entity>::construct(entt_traits<Entity>::to_entity(entity));
}
};
/**
* @brief Compares a tombstone object and an entity identifier of any type.
* @tparam Entity Type of entity identifier.
* @param entity Entity identifier with which to compare.
* @brief Compares a tombstone object and an identifier of any type.
* @tparam Entity Type of identifier.
* @param entity Identifier with which to compare.
* @param other A tombstone object yet to be converted.
* @return False if the two elements differ, true otherwise.
*/
@@ -311,11 +304,10 @@ template<typename Entity>
return other.operator==(entity);
}
/**
* @brief Compares a tombstone object and an entity identifier of any type.
* @tparam Entity Type of entity identifier.
* @param entity Entity identifier with which to compare.
* @brief Compares a tombstone object and an identifier of any type.
* @tparam Entity Type of identifier.
* @param entity Identifier with which to compare.
* @param other A tombstone object yet to be converted.
* @return True if the two elements differ, false otherwise.
*/
@@ -324,28 +316,24 @@ template<typename Entity>
return !(other == entity);
}
/**
* @brief Compile-time constant for null entities.
*
* There exist implicit conversions from this variable to entity identifiers of
* any allowed type. Similarly, there exist comparision operators between the
* null entity and any other entity identifier.
* There exist implicit conversions from this variable to identifiers of any
* allowed type. Similarly, there exist comparison operators between the null
* entity and any other identifier.
*/
inline constexpr null_t null{};
/**
* @brief Compile-time constant for tombstone entities.
*
* There exist implicit conversions from this variable to entity identifiers of
* any allowed type. Similarly, there exist comparision operators between the
* tombstone entity and any other entity identifier.
* There exist implicit conversions from this variable to identifiers of any
* allowed type. Similarly, there exist comparison operators between the
* tombstone entity and any other identifier.
*/
inline constexpr tombstone_t tombstone{};
}
} // namespace entt
#endif

View File

@@ -1,70 +1,54 @@
#ifndef ENTT_ENTITY_FWD_HPP
#define ENTT_ENTITY_FWD_HPP
#include <memory>
#include "../core/fwd.hpp"
#include "utility.hpp"
namespace entt {
template<typename Entity, typename = std::allocator<Entity>>
class basic_sparse_set;
template<typename, typename Type, typename = std::allocator<Type>>
struct basic_storage;
template<typename, typename Type, typename = std::allocator<Type>, typename = void>
class basic_storage;
template<typename>
class basic_registry;
template<typename...>
struct basic_view;
template<typename, typename, typename, typename = void>
class basic_view;
template<typename>
class basic_runtime_view;
struct basic_runtime_view;
template<typename...>
template<typename, typename, typename, typename>
class basic_group;
template<typename>
class basic_observer;
template<typename>
class basic_organizer;
template<typename, typename...>
struct basic_handle;
template<typename>
class basic_snapshot;
template<typename>
class basic_snapshot_loader;
template<typename>
class basic_continuous_loader;
/*! @brief Default entity identifier. */
enum class entity: id_type {};
enum class entity : id_type {};
/*! @brief Alias declaration for the most common use case. */
using sparse_set = basic_sparse_set<entity>;
/**
* @brief Alias declaration for the most common use case.
* @tparam Args Other template parameters.
@@ -72,27 +56,21 @@ using sparse_set = basic_sparse_set<entity>;
template<typename... Args>
using storage = basic_storage<entity, Args...>;
/*! @brief Alias declaration for the most common use case. */
using registry = basic_registry<entity>;
/*! @brief Alias declaration for the most common use case. */
using observer = basic_observer<entity>;
/*! @brief Alias declaration for the most common use case. */
using organizer = basic_organizer<entity>;
/*! @brief Alias declaration for the most common use case. */
using handle = basic_handle<entity>;
/*! @brief Alias declaration for the most common use case. */
using const_handle = basic_handle<const entity>;
/**
* @brief Alias declaration for the most common use case.
* @tparam Args Other template parameters.
@@ -100,7 +78,6 @@ using const_handle = basic_handle<const entity>;
template<typename... Args>
using handle_view = basic_handle<entity, Args...>;
/**
* @brief Alias declaration for the most common use case.
* @tparam Args Other template parameters.
@@ -108,30 +85,25 @@ using handle_view = basic_handle<entity, Args...>;
template<typename... Args>
using const_handle_view = basic_handle<const entity, Args...>;
/*! @brief Alias declaration for the most common use case. */
using snapshot = basic_snapshot<entity>;
/*! @brief Alias declaration for the most common use case. */
using snapshot_loader = basic_snapshot_loader<entity>;
/*! @brief Alias declaration for the most common use case. */
using continuous_loader = basic_continuous_loader<entity>;
/**
* @brief Alias declaration for the most common use case.
* @tparam Args Other template parameters.
* @tparam Get Types of components iterated by the view.
* @tparam Exclude Types of components used to filter the view.
*/
template<typename... Args>
using view = basic_view<entity, Args...>;
template<typename Get, typename Exclude = exclude_t<>>
using view = basic_view<entity, Get, Exclude>;
/*! @brief Alias declaration for the most common use case. */
using runtime_view = basic_runtime_view<entity>;
using runtime_view = basic_runtime_view<sparse_set>;
/**
* @brief Alias declaration for the most common use case.
@@ -140,8 +112,6 @@ using runtime_view = basic_runtime_view<entity>;
template<typename... Args>
using group = basic_group<entity, Args...>;
}
} // namespace entt
#endif

View File

@@ -1,32 +1,30 @@
#ifndef ENTT_ENTITY_GROUP_HPP
#define ENTT_ENTITY_GROUP_HPP
#include <tuple>
#include <utility>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/iterator.hpp"
#include "../core/type_traits.hpp"
#include "component.hpp"
#include "entity.hpp"
#include "fwd.hpp"
#include "sparse_set.hpp"
#include "storage.hpp"
#include "utility.hpp"
namespace entt {
/**
* @brief Group.
*
* Primary template isn't defined on purpose. All the specializations give a
* compile-time error, but for a few reasonable cases.
*/
template<typename...>
template<typename, typename, typename, typename>
class basic_group;
/**
* @brief Non-owning group.
*
@@ -60,112 +58,112 @@ class basic_group;
* In any other case, attempting to use a group results in undefined behavior.
*
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Exclude Types of components used to filter the group.
* @tparam Get Type of components observed by the group.
* @tparam Exclude Types of components used to filter the group.
*/
template<typename Entity, typename... Exclude, typename... Get>
class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>> final {
template<typename Entity, typename... Get, typename... Exclude>
class basic_group<Entity, owned_t<>, get_t<Get...>, exclude_t<Exclude...>> {
/*! @brief A registry is allowed to create groups. */
friend class basic_registry<Entity>;
using basic_common_type = basic_sparse_set<Entity>;
template<typename Comp>
using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Comp>>::storage_type, Comp>;
template<typename Component>
using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Component>>::storage_type, Component>;
using basic_common_type = std::common_type_t<typename storage_type<Get>::base_type...>;
class iterable final {
template<typename It>
struct iterable_iterator final {
using difference_type = std::ptrdiff_t;
using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_group>().get({})));
using pointer = void;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
struct extended_group_iterator final {
using difference_type = std::ptrdiff_t;
using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_group>().get({})));
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
template<typename... Args>
iterable_iterator(It from, const std::tuple<storage_type<Get> *...> &args) ENTT_NOEXCEPT
: it{from},
pools{args}
{}
extended_group_iterator() = default;
iterable_iterator & operator++() ENTT_NOEXCEPT {
return ++it, *this;
}
extended_group_iterator(typename basic_common_type::iterator from, const std::tuple<storage_type<Get> *...> &args)
: it{from},
pools{args} {}
iterable_iterator operator++(int) ENTT_NOEXCEPT {
iterable_iterator orig = *this;
return ++(*this), orig;
}
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
const auto entt = *it;
return std::tuple_cat(std::make_tuple(entt), get_as_tuple(*std::get<storage_type<Get> *>(pools), entt)...);
}
[[nodiscard]] bool operator==(const iterable_iterator &other) const ENTT_NOEXCEPT {
return other.it == it;
}
[[nodiscard]] bool operator!=(const iterable_iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
private:
It it;
std::tuple<storage_type<Get> *...> pools;
};
public:
using iterator = iterable_iterator<typename basic_common_type::iterator>;
using reverse_iterator = iterable_iterator<typename basic_common_type::reverse_iterator>;
iterable(basic_common_type * const ref, const std::tuple<storage_type<Get> *...> &cpools)
: handler{ref},
pools{cpools}
{}
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
return handler ? iterator{handler->begin(), pools} : iterator{{}, pools};
extended_group_iterator &operator++() ENTT_NOEXCEPT {
return ++it, *this;
}
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
return handler ? iterator{handler->end(), pools} : iterator{{}, pools};
extended_group_iterator operator++(int) ENTT_NOEXCEPT {
extended_group_iterator orig = *this;
return ++(*this), orig;
}
[[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT {
return handler ? reverse_iterator{handler->rbegin(), pools} : reverse_iterator{{}, pools};
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
const auto entt = *it;
return std::tuple_cat(std::make_tuple(entt), std::get<storage_type<Get> *>(pools)->get_as_tuple(entt)...);
}
[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
return handler ? reverse_iterator{handler->rend(), pools} : reverse_iterator{{}, pools};
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
return operator*();
}
[[nodiscard]] bool operator==(const extended_group_iterator &other) const ENTT_NOEXCEPT {
return other.it == it;
}
[[nodiscard]] bool operator!=(const extended_group_iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
private:
basic_common_type * const handler;
const std::tuple<storage_type<Get> *...> pools;
typename basic_common_type::iterator it;
std::tuple<storage_type<Get> *...> pools;
};
basic_group(basic_common_type &ref, storage_type<Get> &... gpool) ENTT_NOEXCEPT
basic_group(basic_common_type &ref, storage_type<Get> &...gpool) ENTT_NOEXCEPT
: handler{&ref},
pools{&gpool...}
{}
pools{&gpool...} {}
public:
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Common type among all storage types. */
using base_type = basic_common_type;
/*! @brief Random access iterator type. */
using iterator = typename basic_common_type::iterator;
using iterator = typename base_type::iterator;
/*! @brief Reversed iterator type. */
using reverse_iterator = typename basic_common_type::reverse_iterator;
using reverse_iterator = typename base_type::reverse_iterator;
/*! @brief Iterable group type. */
using iterable_group = iterable;
using iterable = iterable_adaptor<extended_group_iterator>;
/*! @brief Default constructor to use to create empty, invalid groups. */
basic_group() ENTT_NOEXCEPT
: handler{}
{}
: handler{} {}
/**
* @brief Returns a const reference to the underlying handler.
* @return A const reference to the underlying handler.
*/
const base_type &handle() const ENTT_NOEXCEPT {
return *handler;
}
/**
* @brief Returns the storage for a given component type.
* @tparam Comp Type of component of which to return the storage.
* @return The storage for the given component type.
*/
template<typename Comp>
[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT {
return *std::get<storage_type<Comp> *>(pools);
}
/**
* @brief Returns the storage for a given component type.
* @tparam Comp Index of component of which to return the storage.
* @return The storage for the given component type.
*/
template<std::size_t Comp>
[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT {
return *std::get<Comp>(pools);
}
/**
* @brief Returns the number of entities that have the given components.
@@ -199,18 +197,6 @@ public:
return !*this || handler->empty();
}
/**
* @brief Direct access to the list of entities.
*
* The returned pointer is such that range `[data(), data() + size())` is
* always a valid range, even if the container is empty.
*
* @return A pointer to the array of entities.
*/
[[nodiscard]] auto data() const ENTT_NOEXCEPT {
return *this ? handler->data() : nullptr;
}
/**
* @brief Returns an iterator to the first entity of the group.
*
@@ -269,7 +255,7 @@ public:
* @return The first entity of the group if one exists, the null entity
* otherwise.
*/
[[nodiscard]] entity_type front() const {
[[nodiscard]] entity_type front() const ENTT_NOEXCEPT {
const auto it = begin();
return it != end() ? *it : null;
}
@@ -279,18 +265,18 @@ public:
* @return The last entity of the group if one exists, the null entity
* otherwise.
*/
[[nodiscard]] entity_type back() const {
[[nodiscard]] entity_type back() const ENTT_NOEXCEPT {
const auto it = rbegin();
return it != rend() ? *it : null;
}
/**
* @brief Finds an entity.
* @param entt A valid entity identifier.
* @param entt A valid identifier.
* @return An iterator to the given entity if it's found, past the end
* iterator otherwise.
*/
[[nodiscard]] iterator find(const entity_type entt) const {
[[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT {
const auto it = *this ? handler->find(entt) : iterator{};
return it != end() && *it == entt ? it : end();
}
@@ -314,10 +300,10 @@ public:
/**
* @brief Checks if a group contains an entity.
* @param entt A valid entity identifier.
* @param entt A valid identifier.
* @return True if the group contains the given entity, false otherwise.
*/
[[nodiscard]] bool contains(const entity_type entt) const {
[[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT {
return *this && handler->contains(entt);
}
@@ -332,20 +318,20 @@ public:
* error. Attempting to use an entity that doesn't belong to the group
* results in undefined behavior.
*
* @tparam Component Types of components to get.
* @param entt A valid entity identifier.
* @tparam Comp Types of components to get.
* @param entt A valid identifier.
* @return The components assigned to the entity.
*/
template<typename... Component>
template<typename... Comp>
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
ENTT_ASSERT(contains(entt), "Group does not contain entity");
if constexpr(sizeof...(Component) == 0) {
return std::tuple_cat(get_as_tuple(*std::get<storage_type<Get> *>(pools), entt)...);
} else if constexpr(sizeof...(Component) == 1) {
return (std::get<storage_type<Component> *>(pools)->get(entt), ...);
if constexpr(sizeof...(Comp) == 0) {
return std::tuple_cat(std::get<storage_type<Get> *>(pools)->get_as_tuple(entt)...);
} else if constexpr(sizeof...(Comp) == 1) {
return (std::get<storage_type<Comp> *>(pools)->get(entt), ...);
} else {
return std::tuple_cat(get_as_tuple(*std::get<storage_type<Component> *>(pools), entt)...);
return std::tuple_cat(std::get<storage_type<Comp> *>(pools)->get_as_tuple(entt)...);
}
}
@@ -383,7 +369,7 @@ public:
}
/**
* @brief Returns an iterable object to use to _visit_ the group.
* @brief Returns an iterable object to use to _visit_ a group.
*
* The iterable object returns tuples that contain the current entity and a
* set of references to its non-empty components. The _constness_ of the
@@ -395,8 +381,9 @@ public:
*
* @return An iterable object to use to _visit_ the group.
*/
[[nodiscard]] iterable_group each() const ENTT_NOEXCEPT {
return iterable_group{handler, pools};
[[nodiscard]] iterable each() const ENTT_NOEXCEPT {
return handler ? iterable{extended_group_iterator{handler->begin(), pools}, extended_group_iterator{handler->end(), pools}}
: iterable{extended_group_iterator{{}, pools}, extended_group_iterator{{}, pools}};
}
/**
@@ -420,14 +407,14 @@ public:
* Moreover, the comparison function object shall induce a
* _strict weak ordering_ on the values.
*
* The sort function oject must offer a member function template
* The sort function object must offer a member function template
* `operator()` that accepts three arguments:
*
* * An iterator to the first element of the range to sort.
* * An iterator past the last element of the range to sort.
* * A comparison function to use to compare the elements.
*
* @tparam Component Optional types of components to compare.
* @tparam Comp Optional types of components to compare.
* @tparam Compare Type of comparison function object.
* @tparam Sort Type of sort function object.
* @tparam Args Types of arguments to forward to the sort function object.
@@ -435,20 +422,22 @@ public:
* @param algo A valid sort function object.
* @param args Arguments to forward to the sort function object, if any.
*/
template<typename... Component, typename Compare, typename Sort = std_sort, typename... Args>
void sort(Compare compare, Sort algo = Sort{}, Args &&... args) {
template<typename... Comp, typename Compare, typename Sort = std_sort, typename... Args>
void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
if(*this) {
if constexpr(sizeof...(Component) == 0) {
if constexpr(sizeof...(Comp) == 0) {
static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function");
handler->sort(std::move(compare), std::move(algo), std::forward<Args>(args)...);
} else if constexpr(sizeof...(Component) == 1) {
handler->sort([this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) {
return compare((std::get<storage_type<Component> *>(pools)->get(lhs), ...), (std::get<storage_type<Component> *>(pools)->get(rhs), ...));
}, std::move(algo), std::forward<Args>(args)...);
} else {
handler->sort([this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) {
return compare(std::forward_as_tuple(std::get<storage_type<Component> *>(pools)->get(lhs)...), std::forward_as_tuple(std::get<storage_type<Component> *>(pools)->get(rhs)...));
}, std::move(algo), std::forward<Args>(args)...);
auto comp = [this, &compare](const entity_type lhs, const entity_type rhs) {
if constexpr(sizeof...(Comp) == 1) {
return compare((std::get<storage_type<Comp> *>(pools)->get(lhs), ...), (std::get<storage_type<Comp> *>(pools)->get(rhs), ...));
} else {
return compare(std::forward_as_tuple(std::get<storage_type<Comp> *>(pools)->get(lhs)...), std::forward_as_tuple(std::get<storage_type<Comp> *>(pools)->get(rhs)...));
}
};
handler->sort(std::move(comp), std::move(algo), std::forward<Args>(args)...);
}
}
}
@@ -467,21 +456,20 @@ public:
* can quickly ruin the order imposed to the pool of entities shared between
* the non-owning groups.
*
* @tparam Component Type of component to use to impose the order.
* @tparam Comp Type of component to use to impose the order.
*/
template<typename Component>
template<typename Comp>
void sort() const {
if(*this) {
handler->respect(*std::get<storage_type<Component> *>(pools));
handler->respect(*std::get<storage_type<Comp> *>(pools));
}
}
private:
basic_common_type * const handler;
base_type *const handler;
const std::tuple<storage_type<Get> *...> pools;
};
/**
* @brief Owning group.
*
@@ -524,143 +512,118 @@ private:
* In any other case, attempting to use a group results in undefined behavior.
*
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Exclude Types of components used to filter the group.
* @tparam Get Types of components observed by the group.
* @tparam Owned Types of components owned by the group.
* @tparam Get Types of components observed by the group.
* @tparam Exclude Types of components used to filter the group.
*/
template<typename Entity, typename... Exclude, typename... Get, typename... Owned>
class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>, Owned...> final {
template<typename Entity, typename... Owned, typename... Get, typename... Exclude>
class basic_group<Entity, owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> {
/*! @brief A registry is allowed to create groups. */
friend class basic_registry<Entity>;
using basic_common_type = basic_sparse_set<Entity>;
template<typename Comp>
using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Comp>>::storage_type, Comp>;
template<typename Component>
using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Component>>::storage_type, Component>;
using basic_common_type = std::common_type_t<typename storage_type<Owned>::base_type..., typename storage_type<Get>::base_type...>;
class iterable final {
template<typename, typename>
struct iterable_iterator;
template<typename It, typename... OIt>
struct iterable_iterator<It, type_list<OIt...>> final {
using difference_type = std::ptrdiff_t;
using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_group>().get({})));
using pointer = void;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
template<typename... Other>
iterable_iterator(It from, const std::tuple<Other...> &other, const std::tuple<storage_type<Get> *...> &cpools) ENTT_NOEXCEPT
: it{from},
owned{std::get<OIt>(other)...},
get{cpools}
{}
iterable_iterator & operator++() ENTT_NOEXCEPT {
return ++it, (++std::get<OIt>(owned), ...), *this;
class extended_group_iterator final {
template<typename Type>
auto index_to_element(storage_type<Type> &cpool) const {
if constexpr(ignore_as_empty_v<std::remove_const_t<Type>>) {
return std::make_tuple();
} else {
return std::forward_as_tuple(cpool.rbegin()[it.index()]);
}
iterable_iterator operator++(int) ENTT_NOEXCEPT {
iterable_iterator orig = *this;
return ++(*this), orig;
}
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
return std::tuple_cat(
std::make_tuple(*it),
std::forward_as_tuple(*std::get<OIt>(owned)...),
get_as_tuple(*std::get<storage_type<Get> *>(get), *it)...
);
}
[[nodiscard]] bool operator==(const iterable_iterator &other) const ENTT_NOEXCEPT {
return other.it == it;
}
[[nodiscard]] bool operator!=(const iterable_iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
private:
It it;
std::tuple<OIt...> owned;
std::tuple<storage_type<Get> *...> get;
};
}
public:
using iterator = iterable_iterator<
typename basic_common_type::iterator,
type_list_cat_t<std::conditional_t<std::is_void_v<decltype(std::declval<storage_type<Owned>>().get({}))>, type_list<>, type_list<decltype(std::declval<storage_type<Owned>>().end())>>...>
>;
using reverse_iterator = iterable_iterator<
typename basic_common_type::reverse_iterator,
type_list_cat_t<std::conditional_t<std::is_void_v<decltype(std::declval<storage_type<Owned>>().get({}))>, type_list<>, type_list<decltype(std::declval<storage_type<Owned>>().rbegin())>>...>
>;
using difference_type = std::ptrdiff_t;
using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_group>().get({})));
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
iterable(std::tuple<storage_type<Owned> *..., storage_type<Get> *...> cpools, const std::size_t * const extent)
: pools{cpools},
length{extent}
{}
extended_group_iterator() = default;
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
return length ? iterator{
std::get<0>(pools)->basic_common_type::end() - *length,
std::make_tuple((std::get<storage_type<Owned> *>(pools)->end() - *length)...),
std::make_tuple(std::get<storage_type<Get> *>(pools)...)
} : iterator{{}, std::make_tuple(decltype(std::get<storage_type<Owned> *>(pools)->end()){}...), std::make_tuple(std::get<storage_type<Get> *>(pools)...)};
template<typename... Other>
extended_group_iterator(typename basic_common_type::iterator from, const std::tuple<storage_type<Owned> *..., storage_type<Get> *...> &cpools)
: it{from},
pools{cpools} {}
extended_group_iterator &operator++() ENTT_NOEXCEPT {
return ++it, *this;
}
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
return length ? iterator{
std::get<0>(pools)->basic_common_type::end(),
std::make_tuple((std::get<storage_type<Owned> *>(pools)->end())...),
std::make_tuple(std::get<storage_type<Get> *>(pools)...)
} : iterator{{}, std::make_tuple(decltype(std::get<storage_type<Owned> *>(pools)->end()){}...), std::make_tuple(std::get<storage_type<Get> *>(pools)...)};
extended_group_iterator operator++(int) ENTT_NOEXCEPT {
extended_group_iterator orig = *this;
return ++(*this), orig;
}
[[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT {
return length ? reverse_iterator{
std::get<0>(pools)->basic_common_type::rbegin(),
std::make_tuple((std::get<storage_type<Owned> *>(pools)->rbegin())...),
std::make_tuple(std::get<storage_type<Get> *>(pools)...)
} : reverse_iterator{{}, std::make_tuple(decltype(std::get<storage_type<Owned> *>(pools)->rbegin()){}...), std::make_tuple(std::get<storage_type<Get> *>(pools)...)};
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
return std::tuple_cat(
std::make_tuple(*it),
index_to_element<Owned>(*std::get<storage_type<Owned> *>(pools))...,
std::get<storage_type<Get> *>(pools)->get_as_tuple(*it)...);
}
[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
return length ? reverse_iterator{
std::get<0>(pools)->basic_common_type::rbegin() + *length,
std::make_tuple((std::get<storage_type<Owned> *>(pools)->rbegin() + *length)...),
std::make_tuple(std::get<storage_type<Get> *>(pools)...)
} : reverse_iterator{{}, std::make_tuple(decltype(std::get<storage_type<Owned> *>(pools)->rbegin()){}...), std::make_tuple(std::get<storage_type<Get> *>(pools)...)};
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
return operator*();
}
[[nodiscard]] bool operator==(const extended_group_iterator &other) const ENTT_NOEXCEPT {
return other.it == it;
}
[[nodiscard]] bool operator!=(const extended_group_iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
private:
const std::tuple<storage_type<Owned> *..., storage_type<Get> *...> pools;
const std::size_t * const length;
typename basic_common_type::iterator it;
std::tuple<storage_type<Owned> *..., storage_type<Get> *...> pools;
};
basic_group(const std::size_t &extent, storage_type<Owned> &... opool, storage_type<Get> &... gpool) ENTT_NOEXCEPT
basic_group(const std::size_t &extent, storage_type<Owned> &...opool, storage_type<Get> &...gpool) ENTT_NOEXCEPT
: pools{&opool..., &gpool...},
length{&extent}
{}
length{&extent} {}
public:
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Common type among all storage types. */
using base_type = basic_common_type;
/*! @brief Random access iterator type. */
using iterator = typename basic_common_type::iterator;
using iterator = typename base_type::iterator;
/*! @brief Reversed iterator type. */
using reverse_iterator = typename basic_common_type::reverse_iterator;
using reverse_iterator = typename base_type::reverse_iterator;
/*! @brief Iterable group type. */
using iterable_group = iterable;
using iterable = iterable_adaptor<extended_group_iterator>;
/*! @brief Default constructor to use to create empty, invalid groups. */
basic_group() ENTT_NOEXCEPT
: length{}
{}
: length{} {}
/**
* @brief Returns the storage for a given component type.
* @tparam Comp Type of component of which to return the storage.
* @return The storage for the given component type.
*/
template<typename Comp>
[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT {
return *std::get<storage_type<Comp> *>(pools);
}
/**
* @brief Returns the storage for a given component type.
* @tparam Comp Index of component of which to return the storage.
* @return The storage for the given component type.
*/
template<std::size_t Comp>
[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT {
return *std::get<Comp>(pools);
}
/**
* @brief Returns the number of entities that have the given components.
@@ -678,34 +641,6 @@ public:
return !*this || !*length;
}
/**
* @brief Direct access to the raw representation offered by the storage.
*
* @warning
* This function is only available for owned types.
*
* @tparam Component Type of component in which one is interested.
* @return A pointer to the array of components.
*/
template<typename Component>
[[nodiscard]] auto raw() const ENTT_NOEXCEPT {
static_assert((std::is_same_v<Component, Owned> || ...), "Non-owned type");
auto *cpool = std::get<storage_type<Component> *>(pools);
return cpool ? cpool->raw() : decltype(cpool->raw()){};
}
/**
* @brief Direct access to the list of entities.
*
* The returned pointer is such that range `[data(), data() + size())` is
* always a valid range, even if the container is empty.
*
* @return A pointer to the array of entities.
*/
[[nodiscard]] auto data() const ENTT_NOEXCEPT {
return *this ? std::get<0>(pools)->data() : nullptr;
}
/**
* @brief Returns an iterator to the first entity of the group.
*
@@ -715,7 +650,7 @@ public:
* @return An iterator to the first entity of the group.
*/
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
return *this ? (std::get<0>(pools)->basic_common_type::end() - *length) : iterator{};
return *this ? (std::get<0>(pools)->base_type::end() - *length) : iterator{};
}
/**
@@ -729,7 +664,7 @@ public:
* group.
*/
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
return *this ? std::get<0>(pools)->basic_common_type::end() : iterator{};
return *this ? std::get<0>(pools)->base_type::end() : iterator{};
}
/**
@@ -741,7 +676,7 @@ public:
* @return An iterator to the first entity of the reversed group.
*/
[[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT {
return *this ? std::get<0>(pools)->basic_common_type::rbegin() : reverse_iterator{};
return *this ? std::get<0>(pools)->base_type::rbegin() : reverse_iterator{};
}
/**
@@ -756,7 +691,7 @@ public:
* reversed group.
*/
[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
return *this ? (std::get<0>(pools)->basic_common_type::rbegin() + *length) : reverse_iterator{};
return *this ? (std::get<0>(pools)->base_type::rbegin() + *length) : reverse_iterator{};
}
/**
@@ -764,7 +699,7 @@ public:
* @return The first entity of the group if one exists, the null entity
* otherwise.
*/
[[nodiscard]] entity_type front() const {
[[nodiscard]] entity_type front() const ENTT_NOEXCEPT {
const auto it = begin();
return it != end() ? *it : null;
}
@@ -774,18 +709,18 @@ public:
* @return The last entity of the group if one exists, the null entity
* otherwise.
*/
[[nodiscard]] entity_type back() const {
[[nodiscard]] entity_type back() const ENTT_NOEXCEPT {
const auto it = rbegin();
return it != rend() ? *it : null;
}
/**
* @brief Finds an entity.
* @param entt A valid entity identifier.
* @param entt A valid identifier.
* @return An iterator to the given entity if it's found, past the end
* iterator otherwise.
*/
[[nodiscard]] iterator find(const entity_type entt) const {
[[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT {
const auto it = *this ? std::get<0>(pools)->find(entt) : iterator{};
return it != end() && it >= begin() && *it == entt ? it : end();
}
@@ -809,10 +744,10 @@ public:
/**
* @brief Checks if a group contains an entity.
* @param entt A valid entity identifier.
* @param entt A valid identifier.
* @return True if the group contains the given entity, false otherwise.
*/
[[nodiscard]] bool contains(const entity_type entt) const {
[[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT {
return *this && std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < (*length));
}
@@ -827,20 +762,20 @@ public:
* error. Attempting to use an entity that doesn't belong to the group
* results in undefined behavior.
*
* @tparam Component Types of components to get.
* @param entt A valid entity identifier.
* @tparam Comp Types of components to get.
* @param entt A valid identifier.
* @return The components assigned to the entity.
*/
template<typename... Component>
template<typename... Comp>
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
ENTT_ASSERT(contains(entt), "Group does not contain entity");
if constexpr(sizeof...(Component) == 0) {
return std::tuple_cat(get_as_tuple(*std::get<storage_type<Owned> *>(pools), entt)..., get_as_tuple(*std::get<storage_type<Get> *>(pools), entt)...);
} else if constexpr(sizeof...(Component) == 1) {
return (std::get<storage_type<Component> *>(pools)->get(entt), ...);
if constexpr(sizeof...(Comp) == 0) {
return std::tuple_cat(std::get<storage_type<Owned> *>(pools)->get_as_tuple(entt)..., std::get<storage_type<Get> *>(pools)->get_as_tuple(entt)...);
} else if constexpr(sizeof...(Comp) == 1) {
return (std::get<storage_type<Comp> *>(pools)->get(entt), ...);
} else {
return std::tuple_cat(get_as_tuple(*std::get<storage_type<Component> *>(pools), entt)...);
return std::tuple_cat(std::get<storage_type<Comp> *>(pools)->get_as_tuple(entt)...);
}
}
@@ -872,13 +807,13 @@ public:
if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_group>().get({})))>) {
std::apply(func, args);
} else {
std::apply([&func](auto, auto &&... less) { func(std::forward<decltype(less)>(less)...); }, args);
std::apply([&func](auto, auto &&...less) { func(std::forward<decltype(less)>(less)...); }, args);
}
}
}
/**
* @brief Returns an iterable object to use to _visit_ the group.
* @brief Returns an iterable object to use to _visit_ a group.
*
* The iterable object returns tuples that contain the current entity and a
* set of references to its non-empty components. The _constness_ of the
@@ -890,8 +825,9 @@ public:
*
* @return An iterable object to use to _visit_ the group.
*/
[[nodiscard]] iterable_group each() const ENTT_NOEXCEPT {
return iterable_group{pools, length};
[[nodiscard]] iterable each() const ENTT_NOEXCEPT {
iterator last = length ? std::get<0>(pools)->basic_common_type::end() : iterator{};
return {extended_group_iterator{last - *length, pools}, extended_group_iterator{last, pools}};
}
/**
@@ -916,14 +852,14 @@ public:
* Moreover, the comparison function object shall induce a
* _strict weak ordering_ on the values.
*
* The sort function oject must offer a member function template
* The sort function object must offer a member function template
* `operator()` that accepts three arguments:
*
* * An iterator to the first element of the range to sort.
* * An iterator past the last element of the range to sort.
* * A comparison function to use to compare the elements.
*
* @tparam Component Optional types of components to compare.
* @tparam Comp Optional types of components to compare.
* @tparam Compare Type of comparison function object.
* @tparam Sort Type of sort function object.
* @tparam Args Types of arguments to forward to the sort function object.
@@ -931,39 +867,39 @@ public:
* @param algo A valid sort function object.
* @param args Arguments to forward to the sort function object, if any.
*/
template<typename... Component, typename Compare, typename Sort = std_sort, typename... Args>
void sort(Compare compare, Sort algo = Sort{}, Args &&... args) const {
template<typename... Comp, typename Compare, typename Sort = std_sort, typename... Args>
void sort(Compare compare, Sort algo = Sort{}, Args &&...args) const {
auto *cpool = std::get<0>(pools);
if constexpr(sizeof...(Component) == 0) {
if constexpr(sizeof...(Comp) == 0) {
static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function");
cpool->sort_n(*length, std::move(compare), std::move(algo), std::forward<Args>(args)...);
} else if constexpr(sizeof...(Component) == 1) {
cpool->sort_n(*length, [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) {
return compare((std::get<storage_type<Component> *>(pools)->get(lhs), ...), (std::get<storage_type<Component> *>(pools)->get(rhs), ...));
}, std::move(algo), std::forward<Args>(args)...);
} else {
cpool->sort_n(*length, [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) {
return compare(std::forward_as_tuple(std::get<storage_type<Component> *>(pools)->get(lhs)...), std::forward_as_tuple(std::get<storage_type<Component> *>(pools)->get(rhs)...));
}, std::move(algo), std::forward<Args>(args)...);
auto comp = [this, &compare](const entity_type lhs, const entity_type rhs) {
if constexpr(sizeof...(Comp) == 1) {
return compare((std::get<storage_type<Comp> *>(pools)->get(lhs), ...), (std::get<storage_type<Comp> *>(pools)->get(rhs), ...));
} else {
return compare(std::forward_as_tuple(std::get<storage_type<Comp> *>(pools)->get(lhs)...), std::forward_as_tuple(std::get<storage_type<Comp> *>(pools)->get(rhs)...));
}
};
cpool->sort_n(*length, std::move(comp), std::move(algo), std::forward<Args>(args)...);
}
[this](auto *head, auto *... other) {
[this](auto *head, auto *...other) {
for(auto next = *length; next; --next) {
const auto pos = next - 1;
[[maybe_unused]] const auto entt = head->data()[pos];
(other->swap(other->data()[pos], entt), ...);
(other->swap_elements(other->data()[pos], entt), ...);
}
}(std::get<storage_type<Owned> *>(pools)...);
}
private:
const std::tuple<storage_type<Owned> *..., storage_type<Get> *...> pools;
const size_type * const length;
const size_type *const length;
};
}
} // namespace entt
#endif

View File

@@ -1,7 +1,6 @@
#ifndef ENTT_ENTITY_HANDLE_HPP
#define ENTT_ENTITY_HANDLE_HPP
#include <tuple>
#include <type_traits>
#include <utility>
@@ -10,10 +9,8 @@
#include "fwd.hpp"
#include "registry.hpp"
namespace entt {
/**
* @brief Non-owning handle to an entity.
*
@@ -35,29 +32,17 @@ struct basic_handle {
/*! @brief Constructs an invalid handle. */
basic_handle() ENTT_NOEXCEPT
: reg{}, entt{null}
{}
: reg{},
entt{null} {}
/**
* @brief Constructs a handle from a given registry and entity.
* @param ref An instance of the registry class.
* @param value An entity identifier.
* @param value A valid identifier.
*/
basic_handle(registry_type &ref, entity_type value) ENTT_NOEXCEPT
: reg{&ref}, entt{value}
{}
/**
* @brief Compares two handles.
* @tparam Args Template parameters of the handle with which to compare.
* @param other Handle with which to compare.
* @return True if both handles refer to the same registry and the same
* entity, false otherwise.
*/
template<typename... Args>
[[nodiscard]] bool operator==(const basic_handle<Args...> &other) const ENTT_NOEXCEPT {
return reg == other.registry() && entt == other.entity();
}
: reg{&ref},
entt{value} {}
/**
* @brief Constructs a const handle from a non-const one.
@@ -68,18 +53,15 @@ struct basic_handle {
*/
template<typename Other, typename... Args>
operator basic_handle<Other, Args...>() const ENTT_NOEXCEPT {
static_assert(
(std::is_same_v<Other, Entity> || std::is_same_v<std::remove_const_t<Other>, Entity>)
&& (sizeof...(Type) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Type)) && ... && (type_list_contains_v<type_list<Type...>, Args>))),
"Invalid conversion between different handles"
);
static_assert(std::is_same_v<Other, Entity> || std::is_same_v<std::remove_const_t<Other>, Entity>, "Invalid conversion between different handles");
static_assert((sizeof...(Type) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Type)) && ... && (type_list_contains_v<type_list<Type...>, Args>))), "Invalid conversion between different handles");
return reg ? basic_handle<Other, Args...>{*reg, entt} : basic_handle<Other, Args...>{};
}
/**
* @brief Converts a handle to its underlying entity.
* @return An entity identifier.
* @return The contained identifier.
*/
[[nodiscard]] operator entity_type() const ENTT_NOEXCEPT {
return entity();
@@ -105,7 +87,7 @@ struct basic_handle {
* @brief Returns a pointer to the underlying registry, if any.
* @return A pointer to the underlying registry, if any.
*/
[[nodiscard]] registry_type * registry() const ENTT_NOEXCEPT {
[[nodiscard]] registry_type *registry() const ENTT_NOEXCEPT {
return reg;
}
@@ -143,7 +125,7 @@ struct basic_handle {
* @return A reference to the newly created component.
*/
template<typename Component, typename... Args>
decltype(auto) emplace(Args &&... args) const {
decltype(auto) emplace(Args &&...args) const {
static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
return reg->template emplace<Component>(entt, std::forward<Args>(args)...);
}
@@ -157,7 +139,7 @@ struct basic_handle {
* @return A reference to the newly created component.
*/
template<typename Component, typename... Args>
decltype(auto) emplace_or_replace(Args &&... args) const {
decltype(auto) emplace_or_replace(Args &&...args) const {
static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
return reg->template emplace_or_replace<Component>(entt, std::forward<Args>(args)...);
}
@@ -171,7 +153,7 @@ struct basic_handle {
* @return A reference to the patched component.
*/
template<typename Component, typename... Func>
decltype(auto) patch(Func &&... func) const {
decltype(auto) patch(Func &&...func) const {
static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
return reg->template patch<Component>(entt, std::forward<Func>(func)...);
}
@@ -185,7 +167,7 @@ struct basic_handle {
* @return A reference to the component being replaced.
*/
template<typename Component, typename... Args>
decltype(auto) replace(Args &&... args) const {
decltype(auto) replace(Args &&...args) const {
static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
return reg->template replace<Component>(entt, std::forward<Args>(args)...);
}
@@ -213,23 +195,6 @@ struct basic_handle {
reg->template erase<Component...>(entt);
}
/*! @copydoc remove */
template<typename... Component>
[[deprecated("Use ::remove instead")]]
size_type remove_if_exists() const {
return remove<Component...>();
}
/**
* @brief Removes all the components from a handle and makes it orphaned.
* @sa basic_registry::remove_all
*/
[[deprecated("No longer supported")]]
void remove_all() const {
static_assert(sizeof...(Type) == 0, "Invalid operation");
reg->remove_all(entt);
}
/**
* @brief Checks if a handle has all the given components.
* @sa basic_registry::all_of
@@ -274,7 +239,7 @@ struct basic_handle {
* @return Reference to the component owned by the handle.
*/
template<typename Component, typename... Args>
[[nodiscard]] decltype(auto) get_or_emplace(Args &&... args) const {
[[nodiscard]] decltype(auto) get_or_emplace(Args &&...args) const {
static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
return reg->template get_or_emplace<Component>(entt, std::forward<Args>(args)...);
}
@@ -300,14 +265,27 @@ struct basic_handle {
}
/**
* @brief Visits a handle and returns the types for its components.
* @sa basic_registry::visit
* @brief Visits a handle and returns the pools for its components.
*
* The signature of the function should be equivalent to the following:
*
* @code{.cpp}
* void(id_type, const basic_sparse_set<entity_type> &);
* @endcode
*
* Returned pools are those that contain the entity associated with the
* handle.
*
* @tparam Func Type of the function object to invoke.
* @param func A valid function object.
*/
template<typename Func>
void visit(Func &&func) const {
reg->visit(entt, std::forward<Func>(func));
for(auto [id, storage]: reg->storage()) {
if(storage.contains(entt)) {
func(id, storage);
}
}
}
private:
@@ -315,41 +293,48 @@ 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) ENTT_NOEXCEPT {
return lhs.registry() == rhs.registry() && lhs.entity() == rhs.entity();
}
/**
* @brief Compares two handles.
* @tparam Type A valid entity type (see entt_traits for more details).
* @tparam Other A valid entity type (see entt_traits for more details).
* @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 Type, typename Other>
bool operator!=(const basic_handle<Type> &lhs, const basic_handle<Other> &rhs) ENTT_NOEXCEPT {
template<typename... Args, typename... Other>
[[nodiscard]] bool operator!=(const basic_handle<Args...> &lhs, const basic_handle<Other...> &rhs) ENTT_NOEXCEPT {
return !(lhs == rhs);
}
/**
* @brief Deduction guide.
* @tparam Entity A valid entity type (see entt_traits for more details).
*/
template<typename Entity>
basic_handle(basic_registry<Entity> &, Entity) -> basic_handle<Entity>;
/**
* @brief Deduction guide.
* @tparam Entity A valid entity type (see entt_traits for more details).
*/
template<typename Entity>
basic_handle(basic_registry<Entity> &, Entity)
-> basic_handle<Entity>;
/**
* @brief Deduction guide.
* @tparam Entity A valid entity type (see entt_traits for more details).
*/
template<typename Entity>
basic_handle(const basic_registry<Entity> &, Entity)
-> basic_handle<const Entity>;
}
basic_handle(const basic_registry<Entity> &, Entity) -> basic_handle<const Entity>;
} // namespace entt
#endif

View File

@@ -1,19 +1,16 @@
#ifndef ENTT_ENTITY_HELPER_HPP
#define ENTT_ENTITY_HELPER_HPP
#include <type_traits>
#include "../config/config.h"
#include "../core/fwd.hpp"
#include "../core/type_traits.hpp"
#include "../signal/delegate.hpp"
#include "registry.hpp"
#include "fwd.hpp"
#include "registry.hpp"
namespace entt {
/**
* @brief Converts a registry to a view.
* @tparam Entity A valid entity type (see entt_traits for more details).
@@ -38,7 +35,7 @@ struct as_view {
* @return A newly created view.
*/
template<typename Exclude, typename... Component>
operator basic_view<entity_type, Exclude, Component...>() const {
operator basic_view<entity_type, get_t<Component...>, Exclude>() const {
return reg.template view<Component...>(Exclude{});
}
@@ -46,7 +43,6 @@ private:
registry_type &reg;
};
/**
* @brief Deduction guide.
* @tparam Entity A valid entity type (see entt_traits for more details).
@@ -54,7 +50,6 @@ private:
template<typename Entity>
as_view(basic_registry<Entity> &) -> as_view<Entity>;
/**
* @brief Deduction guide.
* @tparam Entity A valid entity type (see entt_traits for more details).
@@ -62,7 +57,6 @@ as_view(basic_registry<Entity> &) -> as_view<Entity>;
template<typename Entity>
as_view(const basic_registry<Entity> &) -> as_view<const Entity>;
/**
* @brief Converts a registry to a group.
* @tparam Entity A valid entity type (see entt_traits for more details).
@@ -82,13 +76,13 @@ struct as_group {
/**
* @brief Conversion function from a registry to a group.
* @tparam Exclude Types of components used to filter the group.
* @tparam Get Types of components observed by the group.
* @tparam Exclude Types of components used to filter the group.
* @tparam Owned Types of components owned by the group.
* @return A newly created group.
*/
template<typename Exclude, typename Get, typename... Owned>
operator basic_group<entity_type, Exclude, Get, Owned...>() const {
template<typename Get, typename Exclude, typename... Owned>
operator basic_group<entity_type, owned_t<Owned...>, Get, Exclude>() const {
if constexpr(std::is_const_v<registry_type>) {
return reg.template group_if_exists<Owned...>(Get{}, Exclude{});
} else {
@@ -100,7 +94,6 @@ private:
registry_type &reg;
};
/**
* @brief Deduction guide.
* @tparam Entity A valid entity type (see entt_traits for more details).
@@ -108,7 +101,6 @@ private:
template<typename Entity>
as_group(basic_registry<Entity> &) -> as_group<Entity>;
/**
* @brief Deduction guide.
* @tparam Entity A valid entity type (see entt_traits for more details).
@@ -116,8 +108,6 @@ as_group(basic_registry<Entity> &) -> as_group<Entity>;
template<typename Entity>
as_group(const basic_registry<Entity> &) -> as_group<const Entity>;
/**
* @brief Helper to create a listener that directly invokes a member function.
* @tparam Member Member function to invoke on a component of the given type.
@@ -133,7 +123,6 @@ void invoke(basic_registry<Entity> &reg, const Entity entt) {
func(reg, entt);
}
/**
* @brief Returns the entity associated with a given component.
*
@@ -149,20 +138,19 @@ void invoke(basic_registry<Entity> &reg, const Entity entt) {
*/
template<typename Entity, typename Component>
Entity to_entity(const basic_registry<Entity> &reg, const Component &instance) {
const auto view = reg.template view<const Component>();
const auto &storage = reg.template storage<Component>();
const typename basic_registry<Entity>::base_type &base = storage;
const auto *addr = std::addressof(instance);
for(auto it = view.rbegin(), last = view.rend(); it < last; it += ENTT_PACKED_PAGE) {
if(const auto dist = (addr - std::addressof(view.template get<const Component>(*it))); dist >= 0 && dist < ENTT_PACKED_PAGE) {
for(auto it = base.rbegin(), last = base.rend(); it < last; it += ENTT_PACKED_PAGE) {
if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < ENTT_PACKED_PAGE) {
return *(it + dist);
}
}
return entt::null;
}
return null;
}
} // namespace entt
#endif

View File

@@ -1,30 +1,26 @@
#ifndef ENTT_ENTITY_OBSERVER_HPP
#define ENTT_ENTITY_OBSERVER_HPP
#include <limits>
#include <cstddef>
#include <cstdint>
#include <utility>
#include <limits>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/type_traits.hpp"
#include "../signal/delegate.hpp"
#include "entity.hpp"
#include "fwd.hpp"
#include "registry.hpp"
#include "storage.hpp"
#include "utility.hpp"
#include "entity.hpp"
#include "fwd.hpp"
namespace entt {
/*! @brief Grouping matcher. */
template<typename...>
struct matcher {};
/**
* @brief Collector.
*
@@ -34,7 +30,6 @@ struct matcher {};
template<typename...>
struct basic_collector;
/**
* @brief Collector.
*
@@ -114,11 +109,9 @@ struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule
}
};
/*! @brief Variable template used to ease the definition of collectors. */
inline constexpr basic_collector<> collector{};
/**
* @brief Observer.
*
@@ -215,14 +208,15 @@ class basic_observer {
struct matcher_handler<matcher<type_list<Reject...>, type_list<Require...>, type_list<NoneOf...>, AllOf...>> {
template<std::size_t Index, typename... Ignore>
static void maybe_valid_if(basic_observer &obs, basic_registry<Entity> &reg, const Entity entt) {
if([&reg, entt]() {
auto condition = [&reg, entt]() {
if constexpr(sizeof...(Ignore) == 0) {
return reg.template all_of<AllOf..., Require...>(entt) && !reg.template any_of<NoneOf..., Reject...>(entt);
} else {
return reg.template all_of<AllOf..., Require...>(entt) && ((std::is_same_v<Ignore..., NoneOf> || !reg.template any_of<NoneOf>(entt)) && ...) && !reg.template any_of<Reject...>(entt);
}
}())
{
};
if(condition()) {
if(!obs.storage.contains(entt)) {
obs.storage.emplace(entt);
}
@@ -281,8 +275,7 @@ public:
/*! @brief Default constructor. */
basic_observer()
: release{},
storage{}
{}
storage{} {}
/*! @brief Default copy constructor, deleted on purpose. */
basic_observer(const basic_observer &) = delete;
@@ -296,8 +289,7 @@ public:
*/
template<typename... Matcher>
basic_observer(basic_registry<entity_type> &reg, basic_collector<Matcher...>)
: basic_observer{}
{
: basic_observer{} {
connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{});
}
@@ -308,13 +300,13 @@ public:
* @brief Default copy assignment operator, deleted on purpose.
* @return This observer.
*/
basic_observer & operator=(const basic_observer &) = delete;
basic_observer &operator=(const basic_observer &) = delete;
/**
* @brief Default move assignment operator, deleted on purpose.
* @return This observer.
*/
basic_observer & operator=(basic_observer &&) = delete;
basic_observer &operator=(basic_observer &&) = delete;
/**
* @brief Connects an observer to a given registry.
@@ -364,7 +356,7 @@ public:
*
* @return A pointer to the array of entities.
*/
[[nodiscard]] const entity_type * data() const ENTT_NOEXCEPT {
[[nodiscard]] const entity_type *data() const ENTT_NOEXCEPT {
return storage.data();
}
@@ -439,8 +431,6 @@ private:
basic_storage<entity_type, payload_type> storage;
};
}
} // namespace entt
#endif

View File

@@ -1,54 +1,47 @@
#ifndef ENTT_ENTITY_ORGANIZER_HPP
#define ENTT_ENTITY_ORGANIZER_HPP
#include <cstddef>
#include <algorithm>
#include <cstddef>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>
#include "../container/dense_map.hpp"
#include "../core/type_info.hpp"
#include "../core/type_traits.hpp"
#include "../core/utility.hpp"
#include "fwd.hpp"
#include "helper.hpp"
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename>
struct is_view: std::false_type {};
template<typename Entity, typename... Exclude, typename... Component>
struct is_view<basic_view<Entity, exclude_t<Exclude...>, Component...>>: std::true_type {};
template<typename Entity, typename... Component, typename... Exclude>
struct is_view<basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>>>: std::true_type {};
template<typename Type>
inline constexpr bool is_view_v = is_view<Type>::value;
template<typename Type, typename Override>
struct unpack_type {
using ro = std::conditional_t<
type_list_contains_v<Override, std::add_const_t<Type>> || (std::is_const_v<Type> && !type_list_contains_v<Override, std::remove_const_t<Type>>),
type_list<std::remove_const_t<Type>>,
type_list<>
>;
type_list<>>;
using rw = std::conditional_t<
type_list_contains_v<Override, std::remove_const_t<Type>> || (!std::is_const_v<Type> && !type_list_contains_v<Override, std::add_const_t<Type>>),
type_list<Type>,
type_list<>
>;
type_list<>>;
};
template<typename Entity, typename... Override>
@@ -59,57 +52,47 @@ struct unpack_type<basic_registry<Entity>, type_list<Override...>> {
template<typename Entity, typename... Override>
struct unpack_type<const basic_registry<Entity>, type_list<Override...>>
: unpack_type<basic_registry<Entity>, type_list<Override...>>
{};
: unpack_type<basic_registry<Entity>, type_list<Override...>> {};
template<typename Entity, typename... Exclude, typename... Component, typename... Override>
struct unpack_type<basic_view<Entity, exclude_t<Exclude...>, Component...>, type_list<Override...>> {
template<typename Entity, typename... Component, typename... Exclude, typename... Override>
struct unpack_type<basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>>, type_list<Override...>> {
using ro = type_list_cat_t<type_list<Exclude...>, typename unpack_type<Component, type_list<Override...>>::ro...>;
using rw = type_list_cat_t<typename unpack_type<Component, type_list<Override...>>::rw...>;
};
template<typename Entity, typename... Exclude, typename... Component, typename... Override>
struct unpack_type<const basic_view<Entity, exclude_t<Exclude...>, Component...>, type_list<Override...>>
: unpack_type<basic_view<Entity, exclude_t<Exclude...>, Component...>, type_list<Override...>>
{};
template<typename Entity, typename... Component, typename... Exclude, typename... Override>
struct unpack_type<const basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>>, type_list<Override...>>
: unpack_type<basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>>, type_list<Override...>> {};
template<typename, typename>
struct resource;
struct resource_traits;
template<typename... Args, typename... Req>
struct resource<type_list<Args...>, type_list<Req...>> {
struct resource_traits<type_list<Args...>, type_list<Req...>> {
using args = type_list<std::remove_const_t<Args>...>;
using ro = type_list_cat_t<typename unpack_type<Args, type_list<Req...>>::ro..., typename unpack_type<Req, type_list<>>::ro...>;
using rw = type_list_cat_t<typename unpack_type<Args, type_list<Req...>>::rw..., typename unpack_type<Req, type_list<>>::rw...>;
};
template<typename... Req, typename Ret, typename... Args>
resource<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> free_function_to_resource(Ret(*)(Args...));
resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> free_function_to_resource_traits(Ret (*)(Args...));
template<typename... Req, typename Ret, typename Type, typename... Args>
resource<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource(Ret(*)(Type &, Args...));
resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (*)(Type &, Args...));
template<typename... Req, typename Ret, typename Class, typename... Args>
resource<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource(Ret(Class:: *)(Args...));
resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...));
template<typename... Req, typename Ret, typename Class, typename... Args>
resource<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource(Ret(Class:: *)(Args...) const);
template<typename... Req>
resource<type_list<>, type_list<Req...>> to_resource();
}
resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...) const);
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Utility class for creating a static task graph.
*
@@ -123,9 +106,9 @@ resource<type_list<>, type_list<Req...>> to_resource();
*/
template<typename Entity>
class basic_organizer final {
using callback_type = void(const void *, entt::basic_registry<Entity> &);
using prepare_type = void(entt::basic_registry<Entity> &);
using dependency_type = std::size_t(const bool, type_info *, const std::size_t);
using callback_type = void(const void *, basic_registry<Entity> &);
using prepare_type = void(basic_registry<Entity> &);
using dependency_type = std::size_t(const bool, const type_info **, const std::size_t);
struct vertex_data final {
std::size_t ro_count{};
@@ -135,7 +118,7 @@ class basic_organizer final {
callback_type *callback{};
dependency_type *dependency;
prepare_type *prepare{};
type_info info{};
const type_info *info{};
};
template<typename Type>
@@ -145,7 +128,7 @@ class basic_organizer final {
} else if constexpr(internal::is_view_v<Type>) {
return as_view{reg};
} else {
return reg.template ctx_or_set<std::remove_reference_t<Type>>();
return reg.ctx().template emplace<std::remove_reference_t<Type>>();
}
}
@@ -155,11 +138,11 @@ class basic_organizer final {
}
template<typename... Type>
static std::size_t fill_dependencies(type_list<Type...>, [[maybe_unused]] type_info *buffer, [[maybe_unused]] const std::size_t count) {
static std::size_t fill_dependencies(type_list<Type...>, [[maybe_unused]] const type_info **buffer, [[maybe_unused]] const std::size_t count) {
if constexpr(sizeof...(Type) == 0u) {
return {};
} else {
type_info info[sizeof...(Type)]{type_id<Type>()...};
const type_info *info[sizeof...(Type)]{&type_id<Type>()...};
const auto length = (std::min)(count, sizeof...(Type));
std::copy_n(info, length, buffer);
return length;
@@ -177,7 +160,7 @@ class basic_organizer final {
const auto length = vertices.size();
std::vector<bool> edges(length * length, false);
// creates the ajacency matrix
// creates the adjacency matrix
for(const auto &deps: dependencies) {
const auto last = deps.second.cend();
auto it = deps.second.cbegin();
@@ -262,8 +245,7 @@ public:
vertex(const bool vtype, vertex_data data, std::vector<std::size_t> edges)
: is_top_level{vtype},
node{std::move(data)},
reachable{std::move(edges)}
{}
reachable{std::move(edges)} {}
/**
* @brief Fills a buffer with the type info objects for the writable
@@ -272,7 +254,7 @@ public:
* @param length The length of the user-supplied buffer.
* @return The number of type info objects written to the buffer.
*/
size_type ro_dependency(type_info *buffer, const std::size_t length) const ENTT_NOEXCEPT {
size_type ro_dependency(const type_info **buffer, const std::size_t length) const ENTT_NOEXCEPT {
return node.dependency(false, buffer, length);
}
@@ -283,7 +265,7 @@ public:
* @param length The length of the user-supplied buffer.
* @return The number of type info objects written to the buffer.
*/
size_type rw_dependency(type_info *buffer, const std::size_t length) const ENTT_NOEXCEPT {
size_type rw_dependency(const type_info **buffer, const std::size_t length) const ENTT_NOEXCEPT {
return node.dependency(true, buffer, length);
}
@@ -315,15 +297,15 @@ public:
* @brief Returns a type info object associated with a vertex.
* @return A properly initialized type info object.
*/
type_info info() const ENTT_NOEXCEPT {
return node.info;
const type_info &info() const ENTT_NOEXCEPT {
return *node.info;
}
/**
* @brief Returns a user defined name associated with a vertex, if any.
* @return The user defined name associated with the vertex, if any.
*/
const char * name() const ENTT_NOEXCEPT {
const char *name() const ENTT_NOEXCEPT {
return node.name;
}
@@ -331,7 +313,7 @@ public:
* @brief Returns the function associated with a vertex.
* @return The function associated with the vertex.
*/
function_type * callback() const ENTT_NOEXCEPT {
function_type *callback() const ENTT_NOEXCEPT {
return node.callback;
}
@@ -339,7 +321,7 @@ public:
* @brief Returns the payload associated with a vertex, if any.
* @return The payload associated with the vertex, if any.
*/
const void * data() const ENTT_NOEXCEPT {
const void *data() const ENTT_NOEXCEPT {
return node.payload;
}
@@ -347,7 +329,7 @@ public:
* @brief Returns the list of nodes reachable from a given vertex.
* @return The list of nodes reachable from the vertex.
*/
const std::vector<std::size_t> & children() const ENTT_NOEXCEPT {
const std::vector<std::size_t> &children() const ENTT_NOEXCEPT {
return reachable;
}
@@ -374,25 +356,25 @@ public:
*/
template<auto Candidate, typename... Req>
void emplace(const char *name = nullptr) {
using resource_type = decltype(internal::free_function_to_resource<Req...>(Candidate));
using resource_type = decltype(internal::free_function_to_resource_traits<Req...>(Candidate));
constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, basic_registry<entity_type>>;
callback_type *callback = +[](const void *, basic_registry<entity_type> &reg) {
std::apply(Candidate, to_args(reg, typename resource_type::args{}));
};
track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{});
vertices.push_back({
vertex_data vdata{
resource_type::ro::size,
resource_type::rw::size,
name,
nullptr,
callback,
+[](const bool rw, type_info *buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); },
+[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); },
+[](basic_registry<entity_type> &reg) { void(to_args(reg, typename resource_type::args{})); },
type_id<std::integral_constant<decltype(Candidate), Candidate>>()
});
&type_id<std::integral_constant<decltype(Candidate), Candidate>>()};
track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{});
vertices.push_back(std::move(vdata));
}
/**
@@ -406,7 +388,7 @@ public:
*/
template<auto Candidate, typename... Req, typename Type>
void emplace(Type &value_or_instance, const char *name = nullptr) {
using resource_type = decltype(internal::constrained_function_to_resource<Req...>(Candidate));
using resource_type = decltype(internal::constrained_function_to_resource_traits<Req...>(Candidate));
constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, basic_registry<entity_type>>;
callback_type *callback = +[](const void *payload, basic_registry<entity_type> &reg) {
@@ -414,22 +396,18 @@ public:
std::apply(Candidate, std::tuple_cat(std::forward_as_tuple(*curr), to_args(reg, typename resource_type::args{})));
};
track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{});
vertices.push_back({
vertex_data vdata{
resource_type::ro::size,
resource_type::rw::size,
name,
&value_or_instance,
callback,
+[](const bool rw, type_info *buffer, const std::size_t length) {
return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length);
},
+[](basic_registry<entity_type> &reg) {
void(to_args(reg, typename resource_type::args{}));
},
type_id<std::integral_constant<decltype(Candidate), Candidate>>()
});
+[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); },
+[](basic_registry<entity_type> &reg) { void(to_args(reg, typename resource_type::args{})); },
&type_id<std::integral_constant<decltype(Candidate), Candidate>>()};
track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{});
vertices.push_back(std::move(vdata));
}
/**
@@ -442,21 +420,20 @@ public:
*/
template<typename... Req>
void emplace(function_type *func, const void *payload = nullptr, const char *name = nullptr) {
using resource_type = internal::resource<type_list<>, type_list<Req...>>;
using resource_type = internal::resource_traits<type_list<>, type_list<Req...>>;
track_dependencies(vertices.size(), true, typename resource_type::ro{}, typename resource_type::rw{});
vertices.push_back({
vertex_data vdata{
resource_type::ro::size,
resource_type::rw::size,
name,
payload,
func,
+[](const bool rw, type_info *buffer, const std::size_t length) {
return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length);
},
+[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); },
nullptr,
type_info{}
});
&type_id<void>()};
vertices.push_back(std::move(vdata));
}
/**
@@ -498,12 +475,10 @@ public:
}
private:
std::unordered_map<entt::id_type, std::vector<std::pair<std::size_t, bool>>> dependencies;
dense_map<id_type, std::vector<std::pair<std::size_t, bool>>, identity> dependencies;
std::vector<vertex_data> vertices;
};
}
} // namespace entt
#endif

View File

@@ -1,60 +0,0 @@
#ifndef ENTT_ENTITY_POLY_STORAGE_HPP
#define ENTT_ENTITY_POLY_STORAGE_HPP
#include <cstddef>
#include <tuple>
#include "../core/type_info.hpp"
#include "../core/type_traits.hpp"
#include "../poly/poly.hpp"
#include "fwd.hpp"
namespace entt {
/**
* @brief Basic poly storage implementation.
* @tparam Entity A valid entity type (see entt_traits for more details).
*/
template<typename Entity>
struct Storage: type_list<type_info() const ENTT_NOEXCEPT> {
/**
* @brief Concept definition.
* @tparam Base Opaque base class from which to inherit.
*/
template<typename Base>
struct type: Base {
/**
* @brief Returns a type info for the contained objects.
* @return The type info for the contained objects.
*/
type_info value_type() const ENTT_NOEXCEPT {
return poly_call<0>(*this);
}
};
/**
* @brief Concept implementation.
* @tparam Type Type for which to generate an implementation.
*/
template<typename Type>
using impl = value_list<&type_id<typename Type::value_type>>;
};
/**
* @brief Defines the poly storage type associate with a given entity type.
* @tparam Entity A valid entity type (see entt_traits for more details).
*/
template<typename Entity, typename = void>
struct poly_storage_traits {
/*! @brief Poly storage type for the given entity type. */
using storage_type = poly<Storage<Entity>>;
};
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,23 +1,119 @@
#ifndef ENTT_ENTITY_RUNTIME_VIEW_HPP
#define ENTT_ENTITY_RUNTIME_VIEW_HPP
#include <iterator>
#include <vector>
#include <utility>
#include <algorithm>
#include <iterator>
#include <type_traits>
#include <utility>
#include <vector>
#include "../config/config.h"
#include "entity.hpp"
#include "sparse_set.hpp"
#include "fwd.hpp"
#include "sparse_set.hpp"
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename Set>
class runtime_view_iterator final {
using iterator_type = typename Set::iterator;
[[nodiscard]] bool valid() const {
return (!tombstone_check || *it != tombstone)
&& std::all_of(++pools->begin(), pools->end(), [entt = *it](const auto *curr) { return curr->contains(entt); })
&& std::none_of(filter->cbegin(), filter->cend(), [entt = *it](const auto *curr) { return curr && curr->contains(entt); });
}
public:
using difference_type = typename iterator_type::difference_type;
using value_type = typename iterator_type::value_type;
using pointer = typename iterator_type::pointer;
using reference = typename iterator_type::reference;
using iterator_category = std::bidirectional_iterator_tag;
runtime_view_iterator() ENTT_NOEXCEPT
: pools{},
filter{},
it{},
tombstone_check{} {}
runtime_view_iterator(const std::vector<const Set *> &cpools, const std::vector<const Set *> &ignore, iterator_type curr) ENTT_NOEXCEPT
: pools{&cpools},
filter{&ignore},
it{curr},
tombstone_check{pools->size() == 1u && (*pools)[0u]->policy() == deletion_policy::in_place} {
if(it != (*pools)[0]->end() && !valid()) {
++(*this);
}
}
runtime_view_iterator &operator++() {
while(++it != (*pools)[0]->end() && !valid()) {}
return *this;
}
runtime_view_iterator operator++(int) {
runtime_view_iterator orig = *this;
return ++(*this), orig;
}
runtime_view_iterator &operator--() {
while(--it != (*pools)[0]->begin() && !valid()) {}
return *this;
}
runtime_view_iterator operator--(int) {
runtime_view_iterator orig = *this;
return operator--(), orig;
}
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
return it.operator->();
}
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
return *operator->();
}
[[nodiscard]] bool operator==(const runtime_view_iterator &other) const ENTT_NOEXCEPT {
return it == other.it;
}
[[nodiscard]] bool operator!=(const runtime_view_iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
private:
const std::vector<const Set *> *pools;
const std::vector<const Set *> *filter;
iterator_type it;
bool tombstone_check;
};
} // namespace internal
/**
* @brief Runtime view.
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Runtime view implementation.
*
* Primary template isn't defined on purpose. All the specializations give a
* compile-time error, but for a few reasonable cases.
*/
template<typename>
struct basic_runtime_view;
/**
* @brief Generic runtime view.
*
* Runtime views iterate over those entities that have at least all the given
* components in their bags. During initialization, a runtime view looks at the
@@ -53,115 +149,47 @@ namespace entt {
* In any other case, attempting to use a view results in undefined behavior.
*
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Entity>
class basic_runtime_view final {
using basic_common_type = basic_sparse_set<Entity>;
using underlying_iterator = typename basic_common_type::iterator;
class view_iterator final {
[[nodiscard]] bool valid() const {
const auto entt = *it;
return (!stable_storage || (entt != tombstone))
&& std::all_of(pools->begin()++, pools->end(), [entt](const auto *curr) { return curr->contains(entt); })
&& std::none_of(filter->cbegin(), filter->cend(), [entt](const auto *curr) { return curr && curr->contains(entt); });
}
public:
using difference_type = typename underlying_iterator::difference_type;
using value_type = typename underlying_iterator::value_type;
using pointer = typename underlying_iterator::pointer;
using reference = typename underlying_iterator::reference;
using iterator_category = std::bidirectional_iterator_tag;
view_iterator() ENTT_NOEXCEPT = default;
view_iterator(const std::vector<const basic_common_type *> &cpools, const std::vector<const basic_common_type *> &ignore, underlying_iterator curr) ENTT_NOEXCEPT
: pools{&cpools},
filter{&ignore},
it{curr},
stable_storage{std::any_of(pools->cbegin(), pools->cend(), [](const basic_common_type *cpool) { return (cpool->policy() == deletion_policy::in_place); })}
{
if(it != (*pools)[0]->end() && !valid()) {
++(*this);
}
}
view_iterator & operator++() {
while(++it != (*pools)[0]->end() && !valid());
return *this;
}
view_iterator operator++(int) {
view_iterator orig = *this;
return ++(*this), orig;
}
view_iterator & operator--() ENTT_NOEXCEPT {
while(--it != (*pools)[0]->begin() && !valid());
return *this;
}
view_iterator operator--(int) ENTT_NOEXCEPT {
view_iterator orig = *this;
return operator--(), orig;
}
[[nodiscard]] bool operator==(const view_iterator &other) const ENTT_NOEXCEPT {
return other.it == it;
}
[[nodiscard]] bool operator!=(const view_iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
[[nodiscard]] pointer operator->() const {
return it.operator->();
}
[[nodiscard]] reference operator*() const {
return *operator->();
}
private:
const std::vector<const basic_common_type *> *pools;
const std::vector<const basic_common_type *> *filter;
underlying_iterator it;
bool stable_storage;
};
[[nodiscard]] bool valid() const {
return !pools.empty() && pools.front();
}
public:
template<typename Entity, typename Allocator>
struct basic_runtime_view<basic_sparse_set<Entity, Allocator>> {
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Common type among all storage types. */
using base_type = basic_sparse_set<Entity, Allocator>;
/*! @brief Bidirectional iterator type. */
using iterator = view_iterator;
using iterator = internal::runtime_view_iterator<base_type>;
/*! @brief Default constructor to use to create empty, invalid views. */
basic_runtime_view() ENTT_NOEXCEPT
: pools{},
filter{}
{}
filter{} {}
/**
* @brief Constructs a runtime view from a set of storage classes.
* @param cpools The storage for the types to iterate.
* @param epools The storage for the types used to filter the view.
* @brief Appends an opaque storage object to a runtime view.
* @param base An opaque reference to a storage object.
* @return This runtime view.
*/
basic_runtime_view(std::vector<const basic_common_type *> cpools, std::vector<const basic_common_type *> epools) ENTT_NOEXCEPT
: pools{std::move(cpools)},
filter{std::move(epools)}
{
// brings the best candidate (if any) on front of the vector
std::rotate(pools.begin(), std::min_element(pools.begin(), pools.end(), [](const auto *lhs, const auto *rhs) {
return (!lhs && rhs) || (lhs && rhs && lhs->size() < rhs->size());
}), pools.end());
basic_runtime_view &iterate(const base_type &base) {
if(pools.empty() || !(base.size() < pools[0u]->size())) {
pools.push_back(&base);
} else {
pools.push_back(std::exchange(pools[0u], &base));
}
return *this;
}
/**
* @brief Adds an opaque storage object as a filter of a runtime view.
* @param base An opaque reference to a storage object.
* @return This runtime view.
*/
basic_runtime_view &exclude(const base_type &base) {
filter.push_back(&base);
return *this;
}
/**
@@ -169,7 +197,7 @@ public:
* @return Estimated number of entities iterated by the view.
*/
[[nodiscard]] size_type size_hint() const {
return valid() ? pools.front()->size() : size_type{};
return pools.empty() ? size_type{} : pools.front()->size();
}
/**
@@ -183,7 +211,7 @@ public:
* @return An iterator to the first entity that has the given components.
*/
[[nodiscard]] iterator begin() const {
return valid() ? iterator{pools, filter, pools[0]->begin()} : iterator{};
return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->begin()};
}
/**
@@ -198,17 +226,18 @@ public:
* given components.
*/
[[nodiscard]] iterator end() const {
return valid() ? iterator{pools, filter, pools[0]->end()} : iterator{};
return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->end()};
}
/**
* @brief Checks if a view contains an entity.
* @param entt A valid entity identifier.
* @param entt A valid identifier.
* @return True if the view contains the given entity, false otherwise.
*/
[[nodiscard]] bool contains(const entity_type entt) const {
return valid() && std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *curr) { return curr->contains(entt); })
&& std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); });
return !pools.empty()
&& std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *curr) { return curr->contains(entt); })
&& std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); });
}
/**
@@ -234,12 +263,10 @@ public:
}
private:
std::vector<const basic_common_type *> pools;
std::vector<const basic_common_type *> filter;
std::vector<const base_type *> pools;
std::vector<const base_type *> filter;
};
}
} // namespace entt
#endif

View File

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

View File

@@ -1,25 +1,23 @@
#ifndef ENTT_ENTITY_SNAPSHOT_HPP
#define ENTT_ENTITY_SNAPSHOT_HPP
#include <array>
#include <cstddef>
#include <iterator>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../container/dense_map.hpp"
#include "../core/type_traits.hpp"
#include "component.hpp"
#include "entity.hpp"
#include "fwd.hpp"
#include "registry.hpp"
namespace entt {
/**
* @brief Utility class to create snapshots from a registry.
*
@@ -32,12 +30,12 @@ namespace entt {
*/
template<typename Entity>
class basic_snapshot {
using traits_type = entt_traits<Entity>;
using entity_traits = entt_traits<Entity>;
template<typename Component, typename Archive, typename It>
void get(Archive &archive, std::size_t sz, It first, It last) const {
const auto view = reg->template view<std::add_const_t<Component>>();
archive(typename traits_type::entity_type(sz));
archive(typename entity_traits::entity_type(sz));
while(first != last) {
const auto entt = *(first++);
@@ -55,7 +53,7 @@ class basic_snapshot {
while(begin != last) {
const auto entt = *(begin++);
((reg->template all_of<Component>(entt) ? ++size[Index] : size[Index]), ...);
((reg->template all_of<Component>(entt) ? ++size[Index] : 0u), ...);
}
(get<Component>(archive, size[Index], first, last), ...);
@@ -70,14 +68,13 @@ public:
* @param source A valid reference to a registry.
*/
basic_snapshot(const basic_registry<entity_type> &source) ENTT_NOEXCEPT
: reg{&source}
{}
: reg{&source} {}
/*! @brief Default move constructor. */
basic_snapshot(basic_snapshot &&) = default;
basic_snapshot(basic_snapshot &&) ENTT_NOEXCEPT = default;
/*! @brief Default move assignment operator. @return This snapshot. */
basic_snapshot & operator=(basic_snapshot &&) = default;
basic_snapshot &operator=(basic_snapshot &&) ENTT_NOEXCEPT = default;
/**
* @brief Puts aside all the entities from the underlying registry.
@@ -90,17 +87,16 @@ public:
* @return An object of this type to continue creating the snapshot.
*/
template<typename Archive>
const basic_snapshot & entities(Archive &archive) const {
const basic_snapshot &entities(Archive &archive) const {
const auto sz = reg->size();
archive(typename traits_type::entity_type(sz));
archive(typename entity_traits::entity_type(sz + 1u));
archive(reg->released());
for(auto first = reg->data(), last = first + sz; first != last; ++first) {
archive(*first);
}
archive(reg->released());
return *this;
}
@@ -116,10 +112,10 @@ public:
* @return An object of this type to continue creating the snapshot.
*/
template<typename... Component, typename Archive>
const basic_snapshot & component(Archive &archive) const {
const basic_snapshot &component(Archive &archive) const {
if constexpr(sizeof...(Component) == 1u) {
const auto view = reg->template view<const Component...>();
(component<Component>(archive, view.data(), view.data() + view.size()), ...);
(component<Component>(archive, view.rbegin(), view.rend()), ...);
return *this;
} else {
(component<Component>(archive), ...);
@@ -142,7 +138,7 @@ public:
* @return An object of this type to continue creating the snapshot.
*/
template<typename... Component, typename Archive, typename It>
const basic_snapshot & component(Archive &archive, It first, It last) const {
const basic_snapshot &component(Archive &archive, It first, It last) const {
component<Component...>(archive, first, last, std::index_sequence_for<Component...>{});
return *this;
}
@@ -151,7 +147,6 @@ private:
const basic_registry<entity_type> *reg;
};
/**
* @brief Utility class to restore a snapshot as a whole.
*
@@ -164,30 +159,30 @@ private:
*/
template<typename Entity>
class basic_snapshot_loader {
using traits_type = entt_traits<Entity>;
using entity_traits = entt_traits<Entity>;
template<typename Type, typename Archive>
void assign(Archive &archive) const {
typename traits_type::entity_type length{};
typename entity_traits::entity_type length{};
entity_type entt;
archive(length);
entity_type entt{};
if constexpr(std::tuple_size_v<decltype(reg->template view<Type>().get({}))> == 0) {
if constexpr(ignore_as_empty_v<Type>) {
while(length--) {
archive(entt);
const auto entity = reg->valid(entt) ? entt : reg->create(entt);
ENTT_ASSERT(entity == entt, "Entity not available for use");
reg->template emplace<Type>(entity);
reg->template emplace<Type>(entt);
}
} else {
Type instance{};
Type instance;
while(length--) {
archive(entt, instance);
const auto entity = reg->valid(entt) ? entt : reg->create(entt);
ENTT_ASSERT(entity == entt, "Entity not available for use");
reg->template emplace<Type>(entity, std::move(instance));
reg->template emplace<Type>(entt, std::move(instance));
}
}
}
@@ -201,17 +196,16 @@ public:
* @param source A valid reference to a registry.
*/
basic_snapshot_loader(basic_registry<entity_type> &source) ENTT_NOEXCEPT
: reg{&source}
{
: reg{&source} {
// restoring a snapshot as a whole requires a clean registry
ENTT_ASSERT(reg->empty(), "Registry must be empty");
}
/*! @brief Default move constructor. */
basic_snapshot_loader(basic_snapshot_loader &&) = default;
basic_snapshot_loader(basic_snapshot_loader &&) ENTT_NOEXCEPT = default;
/*! @brief Default move assignment operator. @return This loader. */
basic_snapshot_loader & operator=(basic_snapshot_loader &&) = default;
basic_snapshot_loader &operator=(basic_snapshot_loader &&) ENTT_NOEXCEPT = default;
/**
* @brief Restores entities that were in use during serialization.
@@ -224,20 +218,17 @@ public:
* @return A valid loader to continue restoring data.
*/
template<typename Archive>
const basic_snapshot_loader & entities(Archive &archive) const {
typename traits_type::entity_type length{};
const basic_snapshot_loader &entities(Archive &archive) const {
typename entity_traits::entity_type length{};
archive(length);
std::vector<entity_type> all(length);
for(decltype(length) pos{}; pos < length; ++pos) {
for(std::size_t pos{}; pos < length; ++pos) {
archive(all[pos]);
}
entity_type destroyed;
archive(destroyed);
reg->assign(all.cbegin(), all.cend(), destroyed);
reg->assign(++all.cbegin(), all.cend(), all[0u]);
return *this;
}
@@ -256,7 +247,7 @@ public:
* @return A valid loader to continue restoring data.
*/
template<typename... Component, typename Archive>
const basic_snapshot_loader & component(Archive &archive) const {
const basic_snapshot_loader &component(Archive &archive) const {
(assign<Component>(archive), ...);
return *this;
}
@@ -271,9 +262,11 @@ public:
*
* @return A valid loader to continue restoring data.
*/
const basic_snapshot_loader & orphans() const {
reg->orphans([this](const auto entt) {
reg->release(entt);
const basic_snapshot_loader &orphans() const {
reg->each([this](const auto entt) {
if(reg->orphan(entt)) {
reg->release(entt);
}
});
return *this;
@@ -283,7 +276,6 @@ private:
basic_registry<entity_type> *reg;
};
/**
* @brief Utility class for _continuous loading_.
*
@@ -302,7 +294,7 @@ private:
*/
template<typename Entity>
class basic_continuous_loader {
using traits_type = entt_traits<Entity>;
using entity_traits = entt_traits<Entity>;
void destroy(Entity entt) {
if(const auto it = remloc.find(entt); it == remloc.cend()) {
@@ -329,8 +321,7 @@ class basic_continuous_loader {
}
template<typename Container>
auto update(int, Container &container)
-> decltype(typename Container::mapped_type{}, void()) {
auto update(int, Container &container) -> decltype(typename Container::mapped_type{}, void()) {
// map like container
Container other;
@@ -348,12 +339,12 @@ class basic_continuous_loader {
}
}
std::swap(container, other);
using std::swap;
swap(container, other);
}
template<typename Container>
auto update(char, Container &container)
-> decltype(typename Container::value_type{}, void()) {
auto update(char, Container &container) -> decltype(typename Container::value_type{}, void()) {
// vector like container
static_assert(std::is_same_v<typename Container::value_type, entity_type>, "Invalid value type");
@@ -363,7 +354,7 @@ class basic_continuous_loader {
}
template<typename Other, typename Type, typename Member>
void update([[maybe_unused]] Other &instance, [[maybe_unused]] Member Type:: *member) {
void update([[maybe_unused]] Other &instance, [[maybe_unused]] Member Type::*member) {
if constexpr(!std::is_same_v<Other, Type>) {
return;
} else if constexpr(std::is_same_v<Member, entity_type>) {
@@ -386,20 +377,20 @@ class basic_continuous_loader {
}
template<typename Other, typename Archive, typename... Type, typename... Member>
void assign(Archive &archive, [[maybe_unused]] Member Type:: *... member) {
typename traits_type::entity_type length{};
void assign(Archive &archive, [[maybe_unused]] Member Type::*...member) {
typename entity_traits::entity_type length{};
entity_type entt;
archive(length);
entity_type entt{};
if constexpr(std::tuple_size_v<decltype(reg->template view<Other>().get({}))> == 0) {
if constexpr(ignore_as_empty_v<Other>) {
while(length--) {
archive(entt);
restore(entt);
reg->template emplace_or_replace<Other>(map(entt));
}
} else {
Other instance{};
Other instance;
while(length--) {
archive(entt, instance);
@@ -419,14 +410,13 @@ public:
* @param source A valid reference to a registry.
*/
basic_continuous_loader(basic_registry<entity_type> &source) ENTT_NOEXCEPT
: reg{&source}
{}
: reg{&source} {}
/*! @brief Default move constructor. */
basic_continuous_loader(basic_continuous_loader &&) = default;
/*! @brief Default move assignment operator. @return This loader. */
basic_continuous_loader & operator=(basic_continuous_loader &&) = default;
basic_continuous_loader &operator=(basic_continuous_loader &&) = default;
/**
* @brief Restores entities that were in use during serialization.
@@ -439,25 +429,24 @@ public:
* @return A non-const reference to this loader.
*/
template<typename Archive>
basic_continuous_loader & entities(Archive &archive) {
typename traits_type::entity_type length{};
basic_continuous_loader &entities(Archive &archive) {
typename entity_traits::entity_type length{};
entity_type entt{};
archive(length);
// discards the head of the list of destroyed entities
archive(entt);
for(decltype(length) pos{}; pos < length; ++pos) {
for(std::size_t pos{}, last = length - 1u; pos < last; ++pos) {
archive(entt);
if(const auto entity = traits_type::to_entity(entt); entity == pos) {
if(const auto entity = entity_traits::to_entity(entt); entity == pos) {
restore(entt);
} else {
destroy(entt);
}
}
// discards the head of the list of destroyed entities
archive(entt);
return *this;
}
@@ -481,7 +470,7 @@ public:
* @return A non-const reference to this loader.
*/
template<typename... Component, typename Archive, typename... Type, typename... Member>
basic_continuous_loader & component(Archive &archive, Member Type:: *... member) {
basic_continuous_loader &component(Archive &archive, Member Type::*...member) {
(remove_if_exists<Component>(), ...);
(assign<Component>(archive, member...), ...);
return *this;
@@ -495,7 +484,7 @@ public:
*
* @return A non-const reference to this loader.
*/
basic_continuous_loader & shrink() {
basic_continuous_loader &shrink() {
auto it = remloc.begin();
while(it != remloc.cend()) {
@@ -527,9 +516,11 @@ public:
*
* @return A non-const reference to this loader.
*/
basic_continuous_loader & orphans() {
reg->orphans([this](const auto entt) {
reg->release(entt);
basic_continuous_loader &orphans() {
reg->each([this](const auto entt) {
if(reg->orphan(entt)) {
reg->release(entt);
}
});
return *this;
@@ -537,7 +528,7 @@ public:
/**
* @brief Tests if a loader knows about a given entity.
* @param entt An entity identifier.
* @param entt A valid identifier.
* @return True if `entity` is managed by the loader, false otherwise.
*/
[[nodiscard]] bool contains(entity_type entt) const ENTT_NOEXCEPT {
@@ -546,7 +537,7 @@ public:
/**
* @brief Returns the identifier to which an entity refers.
* @param entt An entity identifier.
* @param entt A valid identifier.
* @return The local identifier if any, the null entity otherwise.
*/
[[nodiscard]] entity_type map(entity_type entt) const ENTT_NOEXCEPT {
@@ -561,12 +552,10 @@ public:
}
private:
std::unordered_map<entity_type, std::pair<entity_type, bool>> remloc;
dense_map<entity_type, std::pair<entity_type, bool>> remloc;
basic_registry<entity_type> *reg;
};
}
} // namespace entt
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,10 @@
#ifndef ENTT_ENTITY_UTILITY_HPP
#define ENTT_ENTITY_UTILITY_HPP
#include "../core/type_traits.hpp"
namespace entt {
/**
* @brief Alias for exclusion lists.
* @tparam Type List of types.
@@ -15,7 +12,6 @@ namespace entt {
template<typename... Type>
struct exclude_t: type_list<Type...> {};
/**
* @brief Variable template for exclusion lists.
* @tparam Type List of types.
@@ -23,14 +19,12 @@ struct exclude_t: type_list<Type...> {};
template<typename... Type>
inline constexpr exclude_t<Type...> exclude{};
/**
* @brief Alias for lists of observed components.
* @tparam Type List of types.
*/
template<typename... Type>
struct get_t: type_list<Type...>{};
struct get_t: type_list<Type...> {};
/**
* @brief Variable template for lists of observed components.
@@ -39,8 +33,20 @@ struct get_t: type_list<Type...>{};
template<typename... Type>
inline constexpr get_t<Type...> get{};
/**
* @brief Alias for lists of owned components.
* @tparam Type List of types.
*/
template<typename... Type>
struct owned_t: type_list<Type...> {};
}
/**
* @brief Variable template for lists of owned components.
* @tparam Type List of types.
*/
template<typename... Type>
inline constexpr owned_t<Type...> owned{};
} // namespace entt
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,20 @@
#include "config/config.h"
#include "config/macro.h"
#include "config/version.h"
#include "container/dense_map.hpp"
#include "container/dense_set.hpp"
#include "core/algorithm.hpp"
#include "core/any.hpp"
#include "core/attribute.h"
#include "core/compressed_pair.hpp"
#include "core/enum.hpp"
#include "core/family.hpp"
#include "core/hashed_string.hpp"
#include "core/ident.hpp"
#include "core/iterator.hpp"
#include "core/memory.hpp"
#include "core/monostate.hpp"
#include "core/tuple.hpp"
#include "core/type_info.hpp"
#include "core/type_traits.hpp"
#include "core/utility.hpp"
@@ -16,9 +25,9 @@
#include "entity/helper.hpp"
#include "entity/observer.hpp"
#include "entity/organizer.hpp"
#include "entity/poly_storage.hpp"
#include "entity/registry.hpp"
#include "entity/runtime_view.hpp"
#include "entity/sigh_storage_mixin.hpp"
#include "entity/snapshot.hpp"
#include "entity/sparse_set.hpp"
#include "entity/storage.hpp"
@@ -43,8 +52,8 @@
#include "process/process.hpp"
#include "process/scheduler.hpp"
#include "resource/cache.hpp"
#include "resource/handle.hpp"
#include "resource/loader.hpp"
#include "resource/resource.hpp"
#include "signal/delegate.hpp"
#include "signal/dispatcher.hpp"
#include "signal/emitter.hpp"

View File

@@ -1,5 +1,7 @@
#include "container/fwd.hpp"
#include "core/fwd.hpp"
#include "entity/fwd.hpp"
#include "meta/fwd.hpp"
#include "poly/fwd.hpp"
#include "resource/fwd.hpp"
#include "signal/fwd.hpp"

View File

@@ -1,111 +1,114 @@
#ifndef ENTT_LOCATOR_LOCATOR_HPP
#define ENTT_LOCATOR_LOCATOR_HPP
#include <memory>
#include <utility>
#include "../config/config.h"
namespace entt {
/**
* @brief Service locator, nothing more.
*
* A service locator can be used to do what it promises: locate services.<br/>
* A service locator is used to do what it promises: locate services.<br/>
* Usually service locators are tightly bound to the services they expose and
* thus it's hard to define a general purpose class to do that. This template
* based implementation tries to fill the gap and to get rid of the burden of
* defining a different specific locator for each application.
* thus it's hard to define a general purpose class to do that. This tiny class
* tries to fill the gap and to get rid of the burden of defining a different
* specific locator for each application.
*
* @tparam Service Type of service managed by the locator.
* @note
* Users shouldn't retain references to a service. The recommended way is to
* retrieve the service implementation currently set each and every time the
* need for it arises. The risk is to incur in unexpected behaviors otherwise.
*
* @tparam Service Service type.
*/
template<typename Service>
struct service_locator {
/*! @brief Type of service offered. */
using service_type = Service;
struct locator final {
/*! @brief Service type. */
using type = Service;
/*! @brief Default constructor, deleted on purpose. */
service_locator() = delete;
locator() = delete;
/*! @brief Default destructor, deleted on purpose. */
~service_locator() = delete;
~locator() = delete;
/**
* @brief Tests if a valid service implementation is set.
* @return True if the service is set, false otherwise.
* @brief Checks whether a service locator contains a value.
* @return True if the service locator contains a value, false otherwise.
*/
[[nodiscard]] static bool empty() ENTT_NOEXCEPT {
return !static_cast<bool>(service);
[[nodiscard]] static bool has_value() ENTT_NOEXCEPT {
return (service != nullptr);
}
/**
* @brief Returns a weak pointer to a service implementation, if any.
*
* Clients of a service shouldn't retain references to it. The recommended
* way is to retrieve the service implementation currently set each and
* every time the need of using it arises. Otherwise users can incur in
* unexpected behaviors.
*
* @return A reference to the service implementation currently set, if any.
*/
[[nodiscard]] static std::weak_ptr<Service> get() ENTT_NOEXCEPT {
return service;
}
/**
* @brief Returns a weak reference to a service implementation, if any.
*
* Clients of a service shouldn't retain references to it. The recommended
* way is to retrieve the service implementation currently set each and
* every time the need of using it arises. Otherwise users can incur in
* unexpected behaviors.
* @brief Returns a reference to a valid service, if any.
*
* @warning
* In case no service implementation has been set, a call to this function
* results in undefined behavior.
* Invoking this function can result in undefined behavior if the service
* hasn't been set yet.
*
* @return A reference to the service implementation currently set, if any.
* @return A reference to the service currently set, if any.
*/
[[nodiscard]] static Service & ref() ENTT_NOEXCEPT {
[[nodiscard]] static Service &value() ENTT_NOEXCEPT {
ENTT_ASSERT(has_value(), "Service not available");
return *service;
}
/**
* @brief Sets or replaces a service.
* @tparam Impl Type of the new service to use.
* @tparam Args Types of arguments to use to construct the service.
* @param args Parameters to use to construct the service.
* @brief Returns a service if available or sets it from a fallback type.
*
* Arguments are used only if a service doesn't already exist. In all other
* cases, they are discarded.
*
* @tparam Args Types of arguments to use to construct the fallback service.
* @tparam Impl Fallback service type.
* @param args Parameters to use to construct the fallback service.
* @return A reference to a valid service.
*/
template<typename Impl = Service, typename... Args>
static void set(Args &&... args) {
service = std::make_shared<Impl>(std::forward<Args>(args)...);
[[nodiscard]] static Service &value_or(Args &&...args) {
return service ? *service : emplace<Impl>(std::forward<Args>(args)...);
}
/**
* @brief Sets or replaces a service.
* @param ptr Service to use to replace the current one.
* @tparam Impl Service type.
* @tparam Args Types of arguments to use to construct the service.
* @param args Parameters to use to construct the service.
* @return A reference to a valid service.
*/
static void set(std::shared_ptr<Service> ptr) {
ENTT_ASSERT(static_cast<bool>(ptr), "Null service not allowed");
service = std::move(ptr);
template<typename Impl = Service, typename... Args>
static Service &emplace(Args &&...args) {
service = std::make_shared<Impl>(std::forward<Args>(args)...);
return *service;
}
/**
* @brief Resets a service.
*
* The service is no longer valid after a reset.
* @brief Sets or replaces a service using a given allocator.
* @tparam Impl 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.
*/
static void reset() {
template<typename Impl = Service, typename Allocator, typename... Args>
static Service &allocate_emplace(Allocator alloc, Args &&...args) {
service = std::allocate_shared<Impl>(alloc, std::forward<Args>(args)...);
return *service;
}
/*! @brief Resets a service. */
static void reset() ENTT_NOEXCEPT {
service.reset();
}
private:
// std::shared_ptr because of its type erased allocator which is pretty useful here
inline static std::shared_ptr<Service> service = nullptr;
};
}
} // namespace entt
#endif

View File

@@ -1,10 +1,8 @@
#ifndef ENTT_META_ADL_POINTER_HPP
#define ENTT_META_ADL_POINTER_HPP
namespace entt {
/**
* @brief ADL based lookup function for dereferencing meta pointer-like types.
* @tparam Type Element type.
@@ -16,7 +14,6 @@ decltype(auto) dereference_meta_pointer_like(const Type &value) {
return *value;
}
/**
* @brief Fake ADL based lookup function for meta pointer-like types.
* @tparam Type Element type.
@@ -33,8 +30,6 @@ struct adl_meta_pointer_like {
}
};
}
} // namespace entt
#endif

View File

@@ -1,295 +1,161 @@
#ifndef ENTT_META_CONTAINER_HPP
#define ENTT_META_CONTAINER_HPP
#include <array>
#include <map>
#include <set>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../core/type_traits.hpp"
#include "../container/dense_map.hpp"
#include "../container/dense_set.hpp"
#include "meta.hpp"
#include "type_traits.hpp"
namespace entt {
/**
* @brief Container traits.
* @tparam Container Type of the underlying container.
* @tparam Trait Traits associated with the underlying container.
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
template<typename Container, template<typename> class... Trait>
struct meta_container_traits: public Trait<Container>... {
/*! @brief Type of container. */
using type = Container;
};
namespace internal {
/**
* @brief Basic STL-compatible container traits
* @tparam Container The type of the container.
*/
template<typename Container>
struct basic_container {
/**
* @brief Returns the size of the given container.
* @param cont The container for which to return the size.
* @return The size of the given container.
*/
[[nodiscard]] static typename Container::size_type size(const Container &cont) ENTT_NOEXCEPT {
return cont.size();
template<typename, typename = void>
struct is_dynamic_sequence_container: std::false_type {};
template<typename Type>
struct is_dynamic_sequence_container<Type, std::void_t<decltype(&Type::reserve)>>: std::true_type {};
template<typename, typename = void>
struct is_key_only_meta_associative_container: std::true_type {};
template<typename Type>
struct is_key_only_meta_associative_container<Type, std::void_t<typename Type::mapped_type>>: std::false_type {};
template<typename Type>
struct basic_meta_sequence_container_traits {
using iterator = meta_sequence_container::iterator;
using size_type = std::size_t;
[[nodiscard]] static size_type size(const any &container) ENTT_NOEXCEPT {
return any_cast<const Type &>(container).size();
}
/**
* @brief Returns an iterator to the first element of the given container.
* @param cont The container for which to return the iterator.
* @return An iterator to the first element of the given container.
*/
[[nodiscard]] static typename Container::iterator begin(Container &cont) {
return cont.begin();
}
[[nodiscard]] static bool resize([[maybe_unused]] any &container, [[maybe_unused]] size_type sz) {
if constexpr(is_dynamic_sequence_container<Type>::value) {
if(auto *const cont = any_cast<Type>(&container); cont) {
cont->resize(sz);
return true;
}
}
/**
* @brief Returns an iterator to the first element of the given container.
* @param cont The container for which to return the iterator.
* @return An iterator to the first element of the given container.
*/
[[nodiscard]] static typename Container::const_iterator cbegin(const Container &cont) {
return cont.begin();
}
/**
* @brief Returns an iterator past the last element of the given container.
* @param cont The container for which to return the iterator.
* @return An iterator past the last element of the given container.
*/
[[nodiscard]] static typename Container::iterator end(Container &cont) {
return cont.end();
}
/**
* @brief Returns an iterator past the last element of the given container.
* @param cont The container for which to return the iterator.
* @return An iterator past the last element of the given container.
*/
[[nodiscard]] static typename Container::const_iterator cend(const Container &cont) {
return cont.end();
}
};
/**
* @brief Basic STL-compatible associative container traits
* @tparam Container The type of the container.
*/
template<typename Container>
struct basic_associative_container {
/**
* @brief Returns an iterator to the element with key equivalent to the
* given one, if any.
* @param cont The container in which to search for the element.
* @param key The key of the element to search.
* @return An iterator to the element with the given key, if any.
*/
[[nodiscard]] static typename Container::iterator find(Container &cont, const typename Container::key_type &key) {
return cont.find(key);
}
/*! @copydoc find */
[[nodiscard]] static typename Container::const_iterator cfind(const Container &cont, const typename Container::key_type &key) {
return cont.find(key);
}
};
/**
* @brief Basic STL-compatible dynamic container traits
* @tparam Container The type of the container.
*/
template<typename Container>
struct basic_dynamic_container {
/**
* @brief Clears the content of the given container.
* @param cont The container for which to clear the content.
* @return True in case of success, false otherwise.
*/
[[nodiscard]] static bool clear([[maybe_unused]] Container &cont) {
return cont.clear(), true;
}
};
/**
* @brief Basic STL-compatible dynamic associative container traits
* @tparam Container The type of the container.
*/
template<typename Container>
struct basic_dynamic_associative_container {
/**
* @brief Removes the specified element from the given container.
* @param cont The container from which to remove the element.
* @param key The element to remove.
* @return A bool denoting whether the removal took place.
*/
[[nodiscard]] static bool erase([[maybe_unused]] Container &cont, [[maybe_unused]] const typename Container::key_type &key) {
const auto sz = cont.size();
return cont.erase(key) != sz;
}
};
/**
* @brief Basic STL-compatible sequence container traits
* @tparam Container The type of the container.
*/
template<typename Container>
struct basic_sequence_container {
/**
* @brief Returns a reference to the element at the specified location of
* the given container (no bounds checking is performed).
* @param cont The container from which to get the element.
* @param pos The position of the element to return.
* @return A reference to the requested element.
*/
[[nodiscard]] static typename Container::reference get(Container &cont, typename Container::size_type pos) {
return cont[pos];
}
/*! @copydoc get */
[[nodiscard]] static typename Container::const_reference cget(const Container &cont, typename Container::size_type pos) {
return cont[pos];
}
};
/**
* @brief STL-compatible dynamic associative key-only container traits
* @tparam Container The type of the container.
*/
template<typename Container>
struct dynamic_associative_key_only_container {
/**
* @brief Inserts an element into the given container.
* @param cont The container in which to insert the element.
* @param key The element to insert.
* @return A bool denoting whether the insertion took place.
*/
[[nodiscard]] static bool insert([[maybe_unused]] Container &cont, [[maybe_unused]] const typename Container::key_type &key) {
return cont.insert(key).second;
}
};
/**
* @brief STL-compatible dynamic key-value associative container traits
* @tparam Container The type of the container.
*/
template<typename Container>
struct dynamic_associative_key_value_container {
/**
* @brief Inserts an element (a key/value pair) into the given container.
* @param cont The container in which to insert the element.
* @param key The key of the element to insert.
* @param value The value of the element to insert.
* @return A bool denoting whether the insertion took place.
*/
[[nodiscard]] static bool insert([[maybe_unused]] Container &cont, [[maybe_unused]] const typename Container::key_type &key, [[maybe_unused]] const typename Container::mapped_type &value) {
return cont.insert(std::make_pair(key, value)).second;
}
};
/**
* @brief STL-compatible dynamic sequence container traits
* @tparam Container The type of the container.
*/
template<typename Container>
struct dynamic_sequence_container {
/**
* @brief Resizes the given container to contain the given number of
* elements.
* @param cont The container to resize.
* @param sz The new size of the container.
* @return True in case of success, false otherwise.
*/
[[nodiscard]] static bool resize([[maybe_unused]] Container &cont, [[maybe_unused]] typename Container::size_type sz) {
return cont.resize(sz), true;
}
/**
* @brief Inserts an element at the specified location of the given
* container.
* @param cont The container into which to insert the element.
* @param it Iterator before which the element will be inserted.
* @param value Element value to insert.
* @return A pair consisting of an iterator to the inserted element (in case
* of success) and a bool denoting whether the insertion took place.
*/
[[nodiscard]] static std::pair<typename Container::iterator, bool> insert([[maybe_unused]] Container &cont, [[maybe_unused]] typename Container::const_iterator it, [[maybe_unused]] const typename Container::value_type &value) {
return { cont.insert(it, value), true };
}
/**
* @brief Removes the element at the specified location from the given
* container.
* @param cont The container from which to remove the element.
* @param it Iterator to the element to remove.
* @return A pair consisting of an iterator following the last removed
* element (in case of success) and a bool denoting whether the insertion
* took place.
*/
[[nodiscard]] static std::pair<typename Container::iterator, bool> erase([[maybe_unused]] Container &cont, [[maybe_unused]] typename Container::const_iterator it) {
return { cont.erase(it), true };
}
};
/**
* @brief STL-compatible fixed sequence container traits
* @tparam Container The type of the container.
*/
template<typename Container>
struct fixed_sequence_container {
/**
* @brief Does nothing.
* @return False to indicate failure in all cases.
*/
[[nodiscard]] static bool resize(const Container &, typename Container::size_type) {
return false;
}
/**
* @brief Does nothing.
* @return False to indicate failure in all cases.
*/
[[nodiscard]] static bool clear(const Container &) {
return false;
[[nodiscard]] static iterator iter(any &container, const bool as_end) {
if(auto *const cont = any_cast<Type>(&container); cont) {
return iterator{*cont, static_cast<typename iterator::difference_type>(as_end * cont->size())};
}
const Type &as_const = any_cast<const Type &>(container);
return iterator{as_const, static_cast<typename iterator::difference_type>(as_end * as_const.size())};
}
/**
* @brief Does nothing.
* @return A pair consisting of an invalid iterator and a false value to
* indicate failure in all cases.
*/
[[nodiscard]] static std::pair<typename Container::iterator, bool> insert(const Container &, typename Container::const_iterator, const typename Container::value_type &) {
return { {}, false };
[[nodiscard]] static iterator insert([[maybe_unused]] any &container, [[maybe_unused]] const std::ptrdiff_t offset, [[maybe_unused]] meta_any &value) {
if constexpr(is_dynamic_sequence_container<Type>::value) {
if(auto *const cont = any_cast<Type>(&container); cont) {
// this abomination is necessary because only on macos value_type and const_reference are different types for std::vector<bool>
if(value.allow_cast<typename Type::const_reference>() || value.allow_cast<typename Type::value_type>()) {
const auto *element = value.try_cast<std::remove_reference_t<typename Type::const_reference>>();
const auto curr = cont->insert(cont->begin() + offset, element ? *element : value.cast<typename Type::value_type>());
return iterator{*cont, curr - cont->begin()};
}
}
}
return {};
}
/**
* @brief Does nothing.
* @return A pair consisting of an invalid iterator and a false value to
* indicate failure in all cases.
*/
[[nodiscard]] static std::pair<typename Container::iterator, bool> erase(const Container &, typename Container::const_iterator) {
return { {}, false };
[[nodiscard]] static iterator erase([[maybe_unused]] any &container, [[maybe_unused]] const std::ptrdiff_t offset) {
if constexpr(is_dynamic_sequence_container<Type>::value) {
if(auto *const cont = any_cast<Type>(&container); cont) {
const auto curr = cont->erase(cont->begin() + offset);
return iterator{*cont, curr - cont->begin()};
}
}
return {};
}
};
template<typename Type>
struct basic_meta_associative_container_traits {
using iterator = meta_associative_container::iterator;
using size_type = std::size_t;
static constexpr auto key_only = is_key_only_meta_associative_container<Type>::value;
[[nodiscard]] static size_type size(const any &container) ENTT_NOEXCEPT {
return any_cast<const Type &>(container).size();
}
[[nodiscard]] static bool clear(any &container) {
if(auto *const cont = any_cast<Type>(&container); cont) {
cont->clear();
return true;
}
return false;
}
[[nodiscard]] static iterator iter(any &container, const bool as_end) {
if(auto *const cont = any_cast<Type>(&container); cont) {
return iterator{std::integral_constant<bool, key_only>{}, as_end ? cont->end() : cont->begin()};
}
const auto &as_const = any_cast<const Type &>(container);
return iterator{std::integral_constant<bool, key_only>{}, as_end ? as_const.end() : as_const.begin()};
}
[[nodiscard]] static bool insert(any &container, meta_any &key, [[maybe_unused]] meta_any &value) {
auto *const cont = any_cast<Type>(&container);
if constexpr(is_key_only_meta_associative_container<Type>::value) {
return cont && key.allow_cast<const typename Type::key_type &>()
&& cont->insert(key.cast<const typename Type::key_type &>()).second;
} else {
return cont && key.allow_cast<const typename Type::key_type &>() && value.allow_cast<const typename Type::mapped_type &>()
&& cont->emplace(key.cast<const typename Type::key_type &>(), value.cast<const typename Type::mapped_type &>()).second;
}
}
[[nodiscard]] static bool erase(any &container, meta_any &key) {
auto *const cont = any_cast<Type>(&container);
return cont && key.allow_cast<const typename Type::key_type &>()
&& (cont->erase(key.cast<const typename Type::key_type &>()) != cont->size());
}
[[nodiscard]] static iterator find(any &container, meta_any &key) {
if(key.allow_cast<const typename Type::key_type &>()) {
if(auto *const cont = any_cast<Type>(&container); cont) {
return iterator{std::integral_constant<bool, key_only>{}, cont->find(key.cast<const typename Type::key_type &>())};
}
return iterator{std::integral_constant<bool, key_only>{}, any_cast<const Type &>(container).find(key.cast<const typename Type::key_type &>())};
}
return {};
}
};
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Meta sequence container traits for `std::vector`s of any type.
@@ -298,15 +164,7 @@ struct fixed_sequence_container {
*/
template<typename Type, typename... Args>
struct meta_sequence_container_traits<std::vector<Type, Args...>>
: meta_container_traits<
std::vector<Type, Args...>,
basic_container,
basic_dynamic_container,
basic_sequence_container,
dynamic_sequence_container
>
{};
: internal::basic_meta_sequence_container_traits<std::vector<Type, Args...>> {};
/**
* @brief Meta sequence container traits for `std::array`s of any type.
@@ -315,14 +173,7 @@ struct meta_sequence_container_traits<std::vector<Type, Args...>>
*/
template<typename Type, auto N>
struct meta_sequence_container_traits<std::array<Type, N>>
: meta_container_traits<
std::array<Type, N>,
basic_container,
basic_sequence_container,
fixed_sequence_container
>
{};
: internal::basic_meta_sequence_container_traits<std::array<Type, N>> {};
/**
* @brief Meta associative container traits for `std::map`s of any type.
@@ -332,16 +183,7 @@ struct meta_sequence_container_traits<std::array<Type, N>>
*/
template<typename Key, typename Value, typename... Args>
struct meta_associative_container_traits<std::map<Key, Value, Args...>>
: meta_container_traits<
std::map<Key, Value, Args...>,
basic_container,
basic_associative_container,
basic_dynamic_container,
basic_dynamic_associative_container,
dynamic_associative_key_value_container
>
{};
: internal::basic_meta_associative_container_traits<std::map<Key, Value, Args...>> {};
/**
* @brief Meta associative container traits for `std::unordered_map`s of any
@@ -352,16 +194,7 @@ struct meta_associative_container_traits<std::map<Key, Value, Args...>>
*/
template<typename Key, typename Value, typename... Args>
struct meta_associative_container_traits<std::unordered_map<Key, Value, Args...>>
: meta_container_traits<
std::unordered_map<Key, Value, Args...>,
basic_container,
basic_associative_container,
basic_dynamic_container,
basic_dynamic_associative_container,
dynamic_associative_key_value_container
>
{};
: internal::basic_meta_associative_container_traits<std::unordered_map<Key, Value, Args...>> {};
/**
* @brief Meta associative container traits for `std::set`s of any type.
@@ -370,16 +203,7 @@ struct meta_associative_container_traits<std::unordered_map<Key, Value, Args...>
*/
template<typename Key, typename... Args>
struct meta_associative_container_traits<std::set<Key, Args...>>
: meta_container_traits<
std::set<Key, Args...>,
basic_container,
basic_associative_container,
basic_dynamic_container,
basic_dynamic_associative_container,
dynamic_associative_key_only_container
>
{};
: internal::basic_meta_associative_container_traits<std::set<Key, Args...>> {};
/**
* @brief Meta associative container traits for `std::unordered_set`s of any
@@ -389,18 +213,27 @@ struct meta_associative_container_traits<std::set<Key, Args...>>
*/
template<typename Key, typename... Args>
struct meta_associative_container_traits<std::unordered_set<Key, Args...>>
: meta_container_traits<
std::unordered_set<Key, Args...>,
basic_container,
basic_associative_container,
basic_dynamic_container,
basic_dynamic_associative_container,
dynamic_associative_key_only_container
>
{};
: internal::basic_meta_associative_container_traits<std::unordered_set<Key, Args...>> {};
/**
* @brief Meta associative container traits for `dense_map`s of any type.
* @tparam Key The key type of the elements.
* @tparam Type The value type of the elements.
* @tparam Args Other arguments.
*/
template<typename Key, typename Type, typename... Args>
struct meta_associative_container_traits<dense_map<Key, Type, Args...>>
: internal::basic_meta_associative_container_traits<dense_map<Key, Type, Args...>> {};
}
/**
* @brief Meta associative container traits for `dense_set`s of any type.
* @tparam Type The value type of the elements.
* @tparam Args Other arguments.
*/
template<typename Type, typename... Args>
struct meta_associative_container_traits<dense_set<Type, Args...>>
: internal::basic_meta_associative_container_traits<dense_set<Type, Args...>> {};
} // namespace entt
#endif

View File

@@ -1,52 +1,43 @@
#ifndef ENTT_META_CTX_HPP
#define ENTT_META_CTX_HPP
#include "../core/attribute.h"
#include "../config/config.h"
#include "../core/attribute.h"
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
struct meta_type_node;
struct ENTT_API meta_context {
// we could use the lines below but VS2017 returns with an ICE if combined with ENTT_API despite the code being valid C++
// inline static meta_type_node *local = nullptr;
// inline static meta_type_node **global = &local;
[[nodiscard]] static meta_type_node * & local() ENTT_NOEXCEPT {
[[nodiscard]] static meta_type_node *&local() ENTT_NOEXCEPT {
static meta_type_node *chain = nullptr;
return chain;
}
[[nodiscard]] static meta_type_node ** & global() ENTT_NOEXCEPT {
[[nodiscard]] static meta_type_node **&global() ENTT_NOEXCEPT {
static meta_type_node **chain = &local();
return chain;
}
};
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @brief Opaque container for a meta context. */
struct meta_ctx {
/**
@@ -61,8 +52,6 @@ private:
internal::meta_type_node **ctx{&internal::meta_context::local()};
};
}
} // namespace entt
#endif

View File

@@ -1,8 +1,9 @@
#ifndef ENTT_META_FACTORY_HPP
#define ENTT_META_FACTORY_HPP
#include <algorithm>
#include <cstddef>
#include <functional>
#include <tuple>
#include <type_traits>
#include <utility>
@@ -13,46 +14,11 @@
#include "meta.hpp"
#include "node.hpp"
#include "policy.hpp"
#include "range.hpp"
#include "utility.hpp"
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename Node>
[[nodiscard]] bool find_if(const Node *candidate, const Node *node) ENTT_NOEXCEPT {
return node && (node == candidate || find_if(candidate, node->next));
}
template<typename Id, typename Node>
[[nodiscard]] bool find_if_not(const Id id, Node *node, const Node *owner) ENTT_NOEXCEPT {
if constexpr(std::is_pointer_v<Id>) {
return node && ((*node->id == *id && node != owner) || find_if_not(id, node->next, owner));
} else {
return node && ((node->id == id && node != owner) || find_if_not(id, node->next, owner));
}
}
}
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Meta factory to be used for reflection purposes.
*
@@ -62,8 +28,7 @@ template<typename Id, typename Node>
* there are no subtle errors at runtime.
*/
template<typename...>
struct meta_factory;
class meta_factory;
/**
* @brief Extended meta factory to be used for reflection purposes.
@@ -71,58 +36,51 @@ struct meta_factory;
* @tparam Spec Property specialization pack used to disambiguate overloads.
*/
template<typename Type, typename... Spec>
struct meta_factory<Type, Spec...>: public meta_factory<Type> {
private:
template<std::size_t Step = 0, std::size_t... Index, typename... Property, typename... Other>
void unpack(std::index_sequence<Index...>, std::tuple<Property...> property, Other &&... other) {
unroll<Step>(choice<3>, std::move(std::get<Index>(property))..., std::forward<Other>(other)...);
class meta_factory<Type, Spec...>: public meta_factory<Type> {
void link_prop_if_required(internal::meta_prop_node &node) ENTT_NOEXCEPT {
if(meta_range<internal::meta_prop_node *, internal::meta_prop_node> range{*ref}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) {
ENTT_ASSERT(std::find_if(range.cbegin(), range.cend(), [&node](const auto *curr) { return curr->id == node.id; }) == range.cend(), "Duplicate identifier");
node.next = *ref;
*ref = &node;
}
}
template<std::size_t Step = 0, typename... Property, typename... Other>
void unroll(choice_t<3>, std::tuple<Property...> property, Other &&... other) {
unpack<Step>(std::index_sequence_for<Property...>{}, std::move(property), std::forward<Other>(other)...);
void unroll(choice_t<2>, std::tuple<Property...> property, Other &&...other) ENTT_NOEXCEPT {
std::apply([this](auto &&...curr) { (this->unroll<Step>(choice<2>, std::forward<Property>(curr)...)); }, property);
unroll<Step + sizeof...(Property)>(choice<2>, std::forward<Other>(other)...);
}
template<std::size_t Step = 0, typename... Property, typename... Other>
void unroll(choice_t<2>, std::pair<Property...> property, Other &&... other) {
void unroll(choice_t<1>, std::pair<Property...> property, Other &&...other) ENTT_NOEXCEPT {
assign<Step>(std::move(property.first), std::move(property.second));
unroll<Step+1>(choice<3>, std::forward<Other>(other)...);
unroll<Step + 1>(choice<2>, std::forward<Other>(other)...);
}
template<std::size_t Step = 0, typename Property, typename... Other>
std::enable_if_t<!std::is_invocable_v<Property>>
unroll(choice_t<1>, Property &&property, Other &&... other) {
void unroll(choice_t<0>, Property &&property, Other &&...other) ENTT_NOEXCEPT {
assign<Step>(std::forward<Property>(property));
unroll<Step+1>(choice<3>, std::forward<Other>(other)...);
}
template<std::size_t Step = 0, typename Func, typename... Other>
void unroll(choice_t<0>, Func &&invocable, Other &&... other) {
unroll<Step>(choice<3>, std::forward<Func>(invocable)(), std::forward<Other>(other)...);
unroll<Step + 1>(choice<2>, std::forward<Other>(other)...);
}
template<std::size_t>
void unroll(choice_t<0>) {}
void unroll(choice_t<0>) ENTT_NOEXCEPT {}
template<std::size_t = 0, typename Key>
void assign(Key &&key, meta_any value = {}) {
template<std::size_t = 0>
void assign(meta_any key, meta_any value = {}) {
static meta_any property[2u]{};
static internal::meta_prop_node node{
nullptr,
property[0u],
property[1u]
// tricks clang-format
};
entt::meta_any instance{std::forward<Key>(key)};
ENTT_ASSERT(!internal::find_if_not(&instance, *curr, &node), "Duplicate key");
property[0u] = std::move(instance);
property[0u] = std::move(key);
property[1u] = std::move(value);
if(!internal::find_if(&node, *curr)) {
node.next = *curr;
*curr = &node;
}
link_prop_if_required(node);
}
public:
@@ -131,8 +89,7 @@ public:
* @param target The underlying node to which to assign the properties.
*/
meta_factory(internal::meta_prop_node **target) ENTT_NOEXCEPT
: curr{target}
{}
: ref{target} {}
/**
* @brief Assigns a property to the last meta object created.
@@ -146,60 +103,127 @@ public:
* @return A meta factory for the parent type.
*/
template<typename PropertyOrKey, typename... Value>
auto prop(PropertyOrKey &&property_or_key, Value &&... value) && {
meta_factory<Type> prop(PropertyOrKey &&property_or_key, Value &&...value) {
if constexpr(sizeof...(Value) == 0) {
unroll(choice<3>, std::forward<PropertyOrKey>(property_or_key));
unroll(choice<2>, std::forward<PropertyOrKey>(property_or_key));
} else {
assign(std::forward<PropertyOrKey>(property_or_key), std::forward<Value>(value)...);
}
return meta_factory<Type, Spec..., PropertyOrKey, Value...>{curr};
return {};
}
/**
* @brief Assigns properties to the last meta object created.
*
* Both the keys and the values (if any) must be at least copy
* constructible.
* Both key and value (if any) must be at least copy constructible.
*
* @tparam Property Types of the properties.
* @param property Properties to assign to the last meta object created.
* @return A meta factory for the parent type.
*/
template <typename... Property>
auto props(Property... property) && {
unroll(choice<3>, std::forward<Property>(property)...);
return meta_factory<Type, Spec..., Property...>{curr};
template<typename... Property>
meta_factory<Type> props(Property... property) {
unroll(choice<2>, std::forward<Property>(property)...);
return {};
}
private:
internal::meta_prop_node **curr;
internal::meta_prop_node **ref;
};
/**
* @brief Basic meta factory to be used for reflection purposes.
* @tparam Type Reflected type for which the factory was created.
*/
template<typename Type>
struct meta_factory<Type> {
class meta_factory<Type> {
void link_base_if_required(internal::meta_base_node &node) ENTT_NOEXCEPT {
if(meta_range<internal::meta_base_node *, internal::meta_base_node> range{owner->base}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) {
node.next = owner->base;
owner->base = &node;
}
}
void link_conv_if_required(internal::meta_conv_node &node) ENTT_NOEXCEPT {
if(meta_range<internal::meta_conv_node *, internal::meta_conv_node> range{owner->conv}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) {
node.next = owner->conv;
owner->conv = &node;
}
}
void link_ctor_if_required(internal::meta_ctor_node &node) ENTT_NOEXCEPT {
if(meta_range<internal::meta_ctor_node *, internal::meta_ctor_node> range{owner->ctor}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) {
node.next = owner->ctor;
owner->ctor = &node;
}
}
void link_data_if_required(const id_type id, internal::meta_data_node &node) ENTT_NOEXCEPT {
meta_range<internal::meta_data_node *, internal::meta_data_node> range{owner->data};
ENTT_ASSERT(std::find_if(range.cbegin(), range.cend(), [id, &node](const auto *curr) { return curr != &node && curr->id == id; }) == range.cend(), "Duplicate identifier");
node.id = id;
if(std::find(range.cbegin(), range.cend(), &node) == range.cend()) {
node.next = owner->data;
owner->data = &node;
}
}
void link_func_if_required(const id_type id, internal::meta_func_node &node) ENTT_NOEXCEPT {
node.id = id;
if(meta_range<internal::meta_func_node *, internal::meta_func_node> range{owner->func}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) {
node.next = owner->func;
owner->func = &node;
}
}
template<typename Setter, auto Getter, typename Policy, std::size_t... Index>
auto data(const id_type id, std::index_sequence<Index...>) ENTT_NOEXCEPT {
using data_type = std::invoke_result_t<decltype(Getter), Type &>;
using args_type = type_list<typename meta_function_helper_t<Type, decltype(value_list_element_v<Index, Setter>)>::args_type...>;
static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
static internal::meta_data_node node{
{},
/* this is never static */
(std::is_member_object_pointer_v<decltype(value_list_element_v<Index, Setter>)> && ... && std::is_const_v<std::remove_reference_t<data_type>>) ? internal::meta_traits::is_const : internal::meta_traits::is_none,
nullptr,
nullptr,
Setter::size,
internal::meta_node<std::remove_cv_t<std::remove_reference_t<data_type>>>::resolve(),
&meta_arg<type_list<type_list_element_t<type_list_element_t<Index, args_type>::size != 1u, type_list_element_t<Index, args_type>>...>>,
[](meta_handle instance, meta_any value) -> bool { return (meta_setter<Type, value_list_element_v<Index, Setter>>(*instance.operator->(), value.as_ref()) || ...); },
&meta_getter<Type, Getter, Policy>
// tricks clang-format
};
link_data_if_required(id, node);
return meta_factory<Type, Setter, std::integral_constant<decltype(Getter), Getter>>{&node.prop};
}
public:
/*! @brief Default constructor. */
meta_factory() ENTT_NOEXCEPT
: owner{internal::meta_node<Type>::resolve()} {}
/**
* @brief Makes a meta type _searchable_.
* @param id Optional unique identifier.
* @return An extended meta factory for the given type.
*/
auto type(const id_type id = type_hash<Type>::value()) {
auto * const node = internal::meta_info<Type>::resolve();
auto type(const id_type id = type_hash<Type>::value()) ENTT_NOEXCEPT {
meta_range<internal::meta_type_node *, internal::meta_type_node> range{*internal::meta_context::global()};
ENTT_ASSERT(std::find_if(range.cbegin(), range.cend(), [id, this](const auto *curr) { return curr != owner && curr->id == id; }) == range.cend(), "Duplicate identifier");
owner->id = id;
ENTT_ASSERT(!internal::find_if_not(id, *internal::meta_context::global(), node), "Duplicate identifier");
node->id = id;
if(!internal::find_if(node, *internal::meta_context::global())) {
node->next = *internal::meta_context::global();
*internal::meta_context::global() = node;
if(std::find(range.cbegin(), range.cend(), owner) == range.cend()) {
owner->next = *internal::meta_context::global();
*internal::meta_context::global() = owner;
}
return meta_factory<Type, Type>{&node->prop};
return meta_factory<Type, Type>{&owner->prop};
}
/**
@@ -212,23 +236,22 @@ struct meta_factory<Type> {
*/
template<typename Base>
auto base() ENTT_NOEXCEPT {
static_assert(std::is_base_of_v<Base, Type>, "Invalid base type");
auto * const type = internal::meta_info<Type>::resolve();
static_assert(!std::is_same_v<Type, Base> && std::is_base_of_v<Base, Type>, "Invalid base type");
static internal::meta_base_node node{
type,
nullptr,
&internal::meta_info<Base>::resolve,
[](const void *instance) ENTT_NOEXCEPT -> const void * {
return static_cast<const Base *>(static_cast<const Type *>(instance));
internal::meta_node<Base>::resolve(),
[](meta_any other) ENTT_NOEXCEPT -> meta_any {
if(auto *ptr = other.data(); ptr) {
return forward_as_meta(*static_cast<Base *>(static_cast<Type *>(ptr)));
}
return forward_as_meta(*static_cast<const Base *>(static_cast<const Type *>(std::as_const(other).data())));
}
// tricks clang-format
};
if(!internal::find_if(&node, type->base)) {
node.next = type->base;
type->base = &node;
}
link_base_if_required(node);
return meta_factory<Type>{};
}
@@ -245,47 +268,17 @@ struct meta_factory<Type> {
* @return A meta factory for the parent type.
*/
template<auto Candidate>
std::enable_if_t<std::is_member_function_pointer_v<decltype(Candidate)>, meta_factory<Type>> conv() ENTT_NOEXCEPT {
using conv_type = std::invoke_result_t<decltype(Candidate), Type &>;
auto * const type = internal::meta_info<Type>::resolve();
auto conv() ENTT_NOEXCEPT {
static internal::meta_conv_node node{
type,
nullptr,
&internal::meta_info<conv_type>::resolve,
[](const void *instance) -> meta_any {
return (static_cast<const Type *>(instance)->*Candidate)();
internal::meta_node<std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(Candidate), Type &>>>>::resolve(),
[](const meta_any &instance) -> meta_any {
return forward_as_meta(std::invoke(Candidate, *static_cast<const Type *>(instance.data())));
}
// tricks clang-format
};
if(!internal::find_if(&node, type->conv)) {
node.next = type->conv;
type->conv = &node;
}
return meta_factory<Type>{};
}
/*! @copydoc conv */
template<auto Candidate>
std::enable_if_t<!std::is_member_function_pointer_v<decltype(Candidate)>, meta_factory<Type>> conv() ENTT_NOEXCEPT {
using conv_type = std::invoke_result_t<decltype(Candidate), Type &>;
auto * const type = internal::meta_info<Type>::resolve();
static internal::meta_conv_node node{
type,
nullptr,
&internal::meta_info<conv_type>::resolve,
[](const void *instance) -> meta_any {
return Candidate(*static_cast<const Type *>(instance));
}
};
if(!internal::find_if(&node, type->conv)) {
node.next = type->conv;
type->conv = &node;
}
link_conv_if_required(node);
return meta_factory<Type>{};
}
@@ -300,23 +293,14 @@ struct meta_factory<Type> {
*/
template<typename To>
auto conv() ENTT_NOEXCEPT {
static_assert(std::is_convertible_v<Type, To>, "Could not convert to the required type");
auto * const type = internal::meta_info<Type>::resolve();
static internal::meta_conv_node node{
type,
nullptr,
&internal::meta_info<To>::resolve,
[](const void *instance) -> meta_any {
return static_cast<To>(*static_cast<const Type *>(instance));
}
internal::meta_node<std::remove_cv_t<std::remove_reference_t<To>>>::resolve(),
[](const meta_any &instance) -> meta_any { return forward_as_meta(static_cast<To>(*static_cast<const Type *>(instance.data()))); }
// tricks clang-format
};
if(!internal::find_if(&node, type->conv)) {
node.next = type->conv;
type->conv = &node;
}
link_conv_if_required(node);
return meta_factory<Type>{};
}
@@ -336,28 +320,19 @@ struct meta_factory<Type> {
template<auto Candidate, typename Policy = as_is_t>
auto ctor() ENTT_NOEXCEPT {
using descriptor = meta_function_helper_t<Type, decltype(Candidate)>;
static_assert(std::is_same_v<std::decay_t<typename descriptor::return_type>, Type>, "The function doesn't return an object of the required type");
auto * const type = internal::meta_info<Type>::resolve();
static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy");
static_assert(std::is_same_v<std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>, Type>, "The function doesn't return an object of the required type");
static internal::meta_ctor_node node{
type,
nullptr,
nullptr,
descriptor::args_type::size,
[](const typename internal::meta_ctor_node::size_type index) ENTT_NOEXCEPT {
return meta_arg(typename descriptor::args_type{}, index);
},
[](meta_any * const args) {
return meta_invoke<Type, Candidate, Policy>({}, args, std::make_index_sequence<descriptor::args_type::size>{});
}
&meta_arg<typename descriptor::args_type>,
&meta_construct<Type, Candidate, Policy>
// tricks clang-format
};
if(!internal::find_if(&node, type->ctor)) {
node.next = type->ctor;
type->ctor = &node;
}
return meta_factory<Type, std::integral_constant<decltype(Candidate), Candidate>>{&node.prop};
link_ctor_if_required(node);
return meta_factory<Type>{};
}
/**
@@ -372,40 +347,32 @@ struct meta_factory<Type> {
*/
template<typename... Args>
auto ctor() ENTT_NOEXCEPT {
using descriptor = meta_function_helper_t<Type, Type(*)(Args...)>;
auto * const type = internal::meta_info<Type>::resolve();
using descriptor = meta_function_helper_t<Type, Type (*)(Args...)>;
static internal::meta_ctor_node node{
type,
nullptr,
nullptr,
descriptor::args_type::size,
[](const typename internal::meta_ctor_node::size_type index) ENTT_NOEXCEPT {
return meta_arg(typename descriptor::args_type{}, index);
},
[](meta_any * const args) {
return meta_construct<Type, Args...>(args, std::make_index_sequence<descriptor::args_type::size>{});
}
&meta_arg<typename descriptor::args_type>,
&meta_construct<Type, Args...>
// tricks clang-format
};
if(!internal::find_if(&node, type->ctor)) {
node.next = type->ctor;
type->ctor = &node;
}
return meta_factory<Type, Type(Args...)>{&node.prop};
link_ctor_if_required(node);
return meta_factory<Type>{};
}
/**
* @brief Assigns a meta destructor to a meta type.
*
* Free functions can be assigned to meta types in the role of destructors.
* The signature of the function should identical to the following:
* Both free functions and member functions can be assigned to meta types in
* the role of destructors.<br/>
* The signature of a free function should be identical to the following:
*
* @code{.cpp}
* void(Type &);
* @endcode
*
* Member functions should not take arguments instead.<br/>
* The purpose is to give users the ability to free up resources that
* require special treatment before an object is actually destroyed.
*
@@ -415,12 +382,7 @@ struct meta_factory<Type> {
template<auto Func>
auto dtor() ENTT_NOEXCEPT {
static_assert(std::is_invocable_v<decltype(Func), Type &>, "The function doesn't accept an object of the type provided");
auto * const type = internal::meta_info<Type>::resolve();
type->dtor = [](void *instance) {
Func(*static_cast<Type *>(instance));
};
owner->dtor = [](void *instance) { std::invoke(Func, *static_cast<Type *>(instance)); };
return meta_factory<Type>{};
}
@@ -440,31 +402,41 @@ struct meta_factory<Type> {
template<auto Data, typename Policy = as_is_t>
auto data(const id_type id) ENTT_NOEXCEPT {
if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
return data<Data, Data, Policy>(id);
} else {
using data_type = std::remove_pointer_t<decltype(Data)>;
auto * const type = internal::meta_info<Type>::resolve();
using data_type = std::remove_reference_t<std::invoke_result_t<decltype(Data), Type &>>;
static internal::meta_data_node node{
{},
type,
/* this is never static */
std::is_const_v<data_type> ? internal::meta_traits::is_const : internal::meta_traits::is_none,
nullptr,
nullptr,
std::is_same_v<Type, data_type> || std::is_const_v<data_type>,
true,
&internal::meta_info<data_type>::resolve,
1u,
internal::meta_node<std::remove_const_t<data_type>>::resolve(),
&meta_arg<type_list<std::remove_const_t<data_type>>>,
&meta_setter<Type, Data>,
&meta_getter<Type, Data, Policy>
// tricks clang-format
};
ENTT_ASSERT(!internal::find_if_not(id, type->data, &node), "Duplicate identifier");
node.id = id;
link_data_if_required(id, node);
return meta_factory<Type, std::integral_constant<decltype(Data), Data>, std::integral_constant<decltype(Data), Data>>{&node.prop};
} else {
using data_type = std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>;
if(!internal::find_if(&node, type->data)) {
node.next = type->data;
type->data = &node;
}
static internal::meta_data_node node{
{},
((std::is_same_v<Type, std::remove_const_t<data_type>> || std::is_const_v<data_type>) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static,
nullptr,
nullptr,
1u,
internal::meta_node<std::remove_const_t<data_type>>::resolve(),
&meta_arg<type_list<std::remove_const_t<data_type>>>,
&meta_setter<Type, Data>,
&meta_getter<Type, Data, Policy>
// tricks clang-format
};
link_data_if_required(id, node);
return meta_factory<Type, std::integral_constant<decltype(Data), Data>>{&node.prop};
}
}
@@ -491,34 +463,72 @@ struct meta_factory<Type> {
*/
template<auto Setter, auto Getter, typename Policy = as_is_t>
auto data(const id_type id) ENTT_NOEXCEPT {
using underlying_type = std::remove_reference_t<std::invoke_result_t<decltype(Getter), Type &>>;
auto * const type = internal::meta_info<Type>::resolve();
using data_type = std::invoke_result_t<decltype(Getter), Type &>;
static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
static internal::meta_data_node node{
{},
type,
nullptr,
nullptr,
std::is_same_v<decltype(Setter), std::nullptr_t> || (std::is_member_object_pointer_v<decltype(Setter)> && std::is_const_v<underlying_type>),
false,
&internal::meta_info<underlying_type>::resolve,
&meta_setter<Type, Setter>,
&meta_getter<Type, Getter, Policy>
};
if constexpr(std::is_same_v<decltype(Setter), std::nullptr_t>) {
static internal::meta_data_node node{
{},
/* this is never static */
internal::meta_traits::is_const,
nullptr,
nullptr,
0u,
internal::meta_node<std::remove_cv_t<std::remove_reference_t<data_type>>>::resolve(),
&meta_arg<type_list<>>,
&meta_setter<Type, Setter>,
&meta_getter<Type, Getter, Policy>
// tricks clang-format
};
ENTT_ASSERT(!internal::find_if_not(id, type->data, &node), "Duplicate identifier");
node.id = id;
link_data_if_required(id, node);
return meta_factory<Type, std::integral_constant<decltype(Setter), Setter>, std::integral_constant<decltype(Getter), Getter>>{&node.prop};
} else {
using args_type = typename meta_function_helper_t<Type, decltype(Setter)>::args_type;
if(!internal::find_if(&node, type->data)) {
node.next = type->data;
type->data = &node;
static internal::meta_data_node node{
{},
/* this is never static nor const */
internal::meta_traits::is_none,
nullptr,
nullptr,
1u,
internal::meta_node<std::remove_cv_t<std::remove_reference_t<data_type>>>::resolve(),
&meta_arg<type_list<type_list_element_t<args_type::size != 1u, args_type>>>,
&meta_setter<Type, Setter>,
&meta_getter<Type, Getter, Policy>
// tricks clang-format
};
link_data_if_required(id, node);
return meta_factory<Type, std::integral_constant<decltype(Setter), Setter>, std::integral_constant<decltype(Getter), Getter>>{&node.prop};
}
return meta_factory<Type, std::integral_constant<decltype(Setter), Setter>, std::integral_constant<decltype(Getter), Getter>>{&node.prop};
}
/**
* @brief Assigns a meta funcion to a meta type.
* @brief Assigns a meta data to a meta type by means of its setters and
* getter.
*
* Multi-setter support for meta data members. All setters are tried in the
* order of definition before returning to the caller.<br/>
* Setters can be either free functions, member functions or a mix of them
* and are provided via a `value_list` type.
*
* @sa data
*
* @tparam Setter The actual functions to use as setters.
* @tparam Getter The actual getter function.
* @tparam Policy Optional policy (no policy set by default).
* @param id Unique identifier.
* @return An extended meta factory for the parent type.
*/
template<typename Setter, auto Getter, typename Policy = as_is_t>
auto data(const id_type id) ENTT_NOEXCEPT {
return data<Setter, Getter, Policy>(id, std::make_index_sequence<Setter::size>{});
}
/**
* @brief Assigns a meta function to a meta type.
*
* Both member functions and free functions can be assigned to a meta
* type.<br/>
@@ -533,44 +543,27 @@ struct meta_factory<Type> {
template<auto Candidate, typename Policy = as_is_t>
auto func(const id_type id) ENTT_NOEXCEPT {
using descriptor = meta_function_helper_t<Type, decltype(Candidate)>;
auto * const type = internal::meta_info<Type>::resolve();
static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy");
static internal::meta_func_node node{
{},
type,
(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),
nullptr,
nullptr,
descriptor::args_type::size,
descriptor::is_const,
descriptor::is_static,
&internal::meta_info<std::conditional_t<std::is_same_v<Policy, as_void_t>, void, typename descriptor::return_type>>::resolve,
[](const typename internal::meta_func_node::size_type index) ENTT_NOEXCEPT {
return meta_arg(typename descriptor::args_type{}, index);
},
[](meta_handle instance, meta_any *args) {
return meta_invoke<Type, Candidate, Policy>(std::move(instance), args, std::make_index_sequence<descriptor::args_type::size>{});
}
internal::meta_node<std::conditional_t<std::is_same_v<Policy, as_void_t>, void, std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>>>::resolve(),
&meta_arg<typename descriptor::args_type>,
&meta_invoke<Type, Candidate, Policy>
// tricks clang-format
};
for(auto *it = &type->func; *it; it = &(*it)->next) {
if(*it == &node) {
*it = node.next;
break;
}
}
internal::meta_func_node **it = &type->func;
for(; *it && (*it)->id != id; it = &(*it)->next);
for(; *it && (*it)->id == id && (*it)->arity < node.arity; it = &(*it)->next);
node.id = id;
node.next = *it;
*it = &node;
link_func_if_required(id, node);
return meta_factory<Type, std::integral_constant<decltype(Candidate), Candidate>>{&node.prop};
}
};
private:
internal::meta_type_node *owner;
};
/**
* @brief Utility function to use for reflection.
@@ -585,13 +578,73 @@ struct meta_factory<Type> {
*/
template<typename Type>
[[nodiscard]] auto meta() ENTT_NOEXCEPT {
auto * const node = internal::meta_info<Type>::resolve();
auto *const node = internal::meta_node<Type>::resolve();
// extended meta factory to allow assigning properties to opaque meta types
return meta_factory<Type, Type>{&node->prop};
}
/**
* @brief Resets a type and all its parts.
*
* Resets a type and all its data members, member functions and properties, as
* well as its constructors, destructors and conversion functions if any.<br/>
* Base classes aren't reset but the link between the two types is removed.
*
* The type is also removed from the list of searchable types.
*
* @param id Unique identifier.
*/
inline void meta_reset(const id_type id) ENTT_NOEXCEPT {
auto clear_chain = [](auto **curr, auto... member) {
for(; *curr; *curr = std::exchange((*curr)->next, nullptr)) {
if constexpr(sizeof...(member) != 0u) {
static_assert(sizeof...(member) == 1u, "Assert in defense of the future me");
for(auto **sub = (&((*curr)->*member), ...); *sub; *sub = std::exchange((*sub)->next, nullptr)) {}
}
}
};
for(auto **it = internal::meta_context::global(); *it; it = &(*it)->next) {
if(auto *node = *it; node->id == id) {
clear_chain(&node->prop);
clear_chain(&node->base);
clear_chain(&node->conv);
clear_chain(&node->ctor);
clear_chain(&node->data, &internal::meta_data_node::prop);
clear_chain(&node->func, &internal::meta_func_node::prop);
node->id = {};
node->dtor = nullptr;
*it = std::exchange(node->next, nullptr);
break;
}
}
}
/**
* @brief Resets a type and all its parts.
*
* @sa meta_reset
*
* @tparam Type Type to reset.
*/
template<typename Type>
void meta_reset() ENTT_NOEXCEPT {
meta_reset(internal::meta_node<Type>::resolve()->id);
}
/**
* @brief Resets all searchable types.
*
* @sa meta_reset
*/
inline void meta_reset() ENTT_NOEXCEPT {
while(*internal::meta_context::global()) {
meta_reset((*internal::meta_context::global())->id);
}
}
} // namespace entt
#endif

24
src/entt/meta/fwd.hpp Normal file
View File

@@ -0,0 +1,24 @@
#ifndef ENTT_META_FWD_HPP
#define ENTT_META_FWD_HPP
namespace entt {
class meta_sequence_container;
class meta_associative_container;
class meta_any;
struct meta_handle;
struct meta_prop;
struct meta_data;
struct meta_func;
class meta_type;
} // namespace entt
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,190 +1,161 @@
#ifndef ENTT_META_NODE_HPP
#define ENTT_META_NODE_HPP
#include <cstddef>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/attribute.h"
#include "../core/enum.hpp"
#include "../core/fwd.hpp"
#include "../core/type_info.hpp"
#include "../core/type_traits.hpp"
#include "type_traits.hpp"
namespace entt {
class meta_any;
class meta_type;
struct meta_handle;
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
enum class meta_traits : std::uint32_t {
is_none = 0x0000,
is_const = 0x0001,
is_static = 0x0002,
is_arithmetic = 0x0004,
is_array = 0x0008,
is_enum = 0x0010,
is_class = 0x0020,
is_pointer = 0x0040,
is_meta_pointer_like = 0x0080,
is_meta_sequence_container = 0x0100,
is_meta_associative_container = 0x0200,
_entt_enum_as_bitmask
};
struct meta_type_node;
struct meta_prop_node {
meta_prop_node * next;
meta_prop_node *next;
const meta_any &id;
meta_any &value;
};
struct meta_base_node {
meta_type_node * const parent;
meta_base_node * next;
meta_type_node *(* const type)() ENTT_NOEXCEPT;
const void *(* const cast)(const void *) ENTT_NOEXCEPT;
meta_base_node *next;
meta_type_node *const type;
meta_any (*const cast)(meta_any) ENTT_NOEXCEPT;
};
struct meta_conv_node {
meta_type_node * const parent;
meta_conv_node * next;
meta_type_node *(* const type)() ENTT_NOEXCEPT;
meta_any(* const conv)(const void *);
meta_conv_node *next;
meta_type_node *const type;
meta_any (*const conv)(const meta_any &);
};
struct meta_ctor_node {
using size_type = std::size_t;
meta_type_node * const parent;
meta_ctor_node * next;
meta_prop_node * prop;
meta_ctor_node *next;
const size_type arity;
meta_type(* const arg)(const size_type) ENTT_NOEXCEPT;
meta_any(* const invoke)(meta_any * const);
meta_type (*const arg)(const size_type) ENTT_NOEXCEPT;
meta_any (*const invoke)(meta_any *const);
};
struct meta_data_node {
using size_type = std::size_t;
id_type id;
meta_type_node * const parent;
meta_data_node * next;
meta_prop_node * prop;
const bool is_const;
const bool is_static;
meta_type_node *(* const type)() ENTT_NOEXCEPT;
bool(* const set)(meta_handle, meta_any);
meta_any(* const get)(meta_handle);
const meta_traits traits;
meta_data_node *next;
meta_prop_node *prop;
const size_type arity;
meta_type_node *const type;
meta_type (*const arg)(const size_type) ENTT_NOEXCEPT;
bool (*const set)(meta_handle, meta_any);
meta_any (*const get)(meta_handle);
};
struct meta_func_node {
using size_type = std::size_t;
id_type id;
meta_type_node * const parent;
meta_func_node * next;
meta_prop_node * prop;
const meta_traits traits;
meta_func_node *next;
meta_prop_node *prop;
const size_type arity;
const bool is_const;
const bool is_static;
meta_type_node *(* const ret)() ENTT_NOEXCEPT;
meta_type(* const arg)(const size_type) ENTT_NOEXCEPT;
meta_any(* const invoke)(meta_handle, meta_any *);
meta_type_node *const ret;
meta_type (*const arg)(const size_type) ENTT_NOEXCEPT;
meta_any (*const invoke)(meta_handle, meta_any *const);
};
struct meta_template_info {
struct meta_template_node {
using size_type = std::size_t;
const bool is_template_specialization;
const size_type arity;
meta_type_node *(* const type)() ENTT_NOEXCEPT;
meta_type_node *(* const arg)(const size_type) ENTT_NOEXCEPT;
meta_type_node *const type;
meta_type_node *(*const arg)(const size_type)ENTT_NOEXCEPT;
};
struct meta_type_node {
using size_type = std::size_t;
const type_info info;
const type_info *info;
id_type id;
meta_type_node * next;
meta_prop_node * prop;
const meta_traits traits;
meta_type_node *next;
meta_prop_node *prop;
const size_type size_of;
const bool is_void;
const bool is_integral;
const bool is_floating_point;
const bool is_array;
const bool is_enum;
const bool is_union;
const bool is_class;
const bool is_pointer;
const bool is_function_pointer;
const bool is_member_object_pointer;
const bool is_member_function_pointer;
const bool is_pointer_like;
const bool is_sequence_container;
const bool is_associative_container;
const meta_template_info template_info;
const size_type rank;
size_type(* const extent)(const size_type) ENTT_NOEXCEPT ;
meta_type_node *(* const remove_pointer)() ENTT_NOEXCEPT;
meta_type_node *(* const remove_extent)() ENTT_NOEXCEPT;
meta_ctor_node * const def_ctor;
meta_type_node *(*const remove_pointer)() ENTT_NOEXCEPT;
meta_any (*const default_constructor)();
double (*const conversion_helper)(void *, const void *);
const meta_template_node *const templ;
meta_ctor_node *ctor{nullptr};
meta_base_node *base{nullptr};
meta_conv_node *conv{nullptr};
meta_data_node *data{nullptr};
meta_func_node *func{nullptr};
void(* dtor)(void *){nullptr};
void (*dtor)(void *){nullptr};
};
template<auto Member, typename Op, typename Node>
auto meta_visit(const Op &op, const Node *node)
-> std::decay_t<decltype(node->*Member)> {
for(auto *curr = node->*Member; curr; curr = curr->next) {
if(op(curr)) {
return curr;
}
}
if constexpr(std::is_same_v<Node, meta_type_node>) {
for(auto *curr = node->base; curr; curr = curr->next) {
if(auto *ret = meta_visit<Member>(op, curr->type()); ret) {
return ret;
}
}
}
return nullptr;
}
template<typename... Args>
meta_type_node * meta_arg_node(type_list<Args...>, const std::size_t index) ENTT_NOEXCEPT;
meta_type_node *meta_arg_node(type_list<Args...>, const std::size_t index) ENTT_NOEXCEPT;
template<typename Type>
class ENTT_API meta_node {
static_assert(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>, "Invalid type");
template<std::size_t... Index>
[[nodiscard]] static auto extent(const meta_type_node::size_type dim, std::index_sequence<Index...>) ENTT_NOEXCEPT {
meta_type_node::size_type ext{};
((ext = (dim == Index ? std::extent_v<Type, Index> : ext)), ...);
return ext;
[[nodiscard]] static auto *meta_default_constructor() ENTT_NOEXCEPT {
if constexpr(std::is_default_constructible_v<Type>) {
return +[]() { return meta_any{std::in_place_type<Type>}; };
} else {
return static_cast<std::decay_t<decltype(meta_type_node::default_constructor)>>(nullptr);
}
}
[[nodiscard]] static meta_ctor_node * meta_default_constructor([[maybe_unused]] meta_type_node *type) ENTT_NOEXCEPT {
if constexpr(std::is_default_constructible_v<Type>) {
static meta_ctor_node node{
type,
nullptr,
nullptr,
0u,
nullptr,
[](meta_any * const) { return meta_any{std::in_place_type<Type>}; }
[[nodiscard]] static auto *meta_conversion_helper() ENTT_NOEXCEPT {
if constexpr(std::is_arithmetic_v<Type>) {
return +[](void *bin, const void *value) {
return bin ? static_cast<double>(*static_cast<Type *>(bin) = static_cast<Type>(*static_cast<const double *>(value))) : static_cast<double>(*static_cast<const Type *>(value));
};
} else if constexpr(std::is_enum_v<Type>) {
return +[](void *bin, const void *value) {
return bin ? static_cast<double>(*static_cast<Type *>(bin) = static_cast<Type>(static_cast<std::underlying_type_t<Type>>(*static_cast<const double *>(value)))) : static_cast<double>(*static_cast<const Type *>(value));
};
} else {
return static_cast<std::decay_t<decltype(meta_type_node::conversion_helper)>>(nullptr);
}
}
[[nodiscard]] static meta_template_node *meta_template_info() ENTT_NOEXCEPT {
if constexpr(is_complete_v<meta_template_traits<Type>>) {
static meta_template_node node{
meta_template_traits<Type>::args_type::size,
meta_node<typename meta_template_traits<Type>::class_type>::resolve(),
[](const std::size_t index) ENTT_NOEXCEPT { return meta_arg_node(typename meta_template_traits<Type>::args_type{}, index); }
// tricks clang-format
};
return &node;
@@ -193,78 +164,74 @@ class ENTT_API meta_node {
}
}
[[nodiscard]] static meta_template_info meta_template_descriptor() ENTT_NOEXCEPT {
if constexpr(is_complete_v<meta_template_traits<Type>>) {
return {
true,
meta_template_traits<Type>::args_type::size,
&meta_node<typename meta_template_traits<Type>::class_type>::resolve,
[](const std::size_t index) ENTT_NOEXCEPT {
return meta_arg_node(typename meta_template_traits<Type>::args_type{}, index);
}
};
} else {
return { false, 0u, nullptr, nullptr };
}
}
public:
[[nodiscard]] static meta_type_node * resolve() ENTT_NOEXCEPT {
[[nodiscard]] static meta_type_node *resolve() ENTT_NOEXCEPT {
static meta_type_node node{
type_id<Type>(),
&type_id<Type>(),
{},
internal::meta_traits::is_none
| (std::is_arithmetic_v<Type> ? internal::meta_traits::is_arithmetic : internal::meta_traits::is_none)
| (std::is_array_v<Type> ? internal::meta_traits::is_array : internal::meta_traits::is_none)
| (std::is_enum_v<Type> ? internal::meta_traits::is_enum : internal::meta_traits::is_none)
| (std::is_class_v<Type> ? internal::meta_traits::is_class : internal::meta_traits::is_none)
| (std::is_pointer_v<Type> ? internal::meta_traits::is_pointer : internal::meta_traits::is_none)
| (is_meta_pointer_like_v<Type> ? internal::meta_traits::is_meta_pointer_like : internal::meta_traits::is_none)
| (is_complete_v<meta_sequence_container_traits<Type>> ? internal::meta_traits::is_meta_sequence_container : internal::meta_traits::is_none)
| (is_complete_v<meta_associative_container_traits<Type>> ? internal::meta_traits::is_meta_associative_container : internal::meta_traits::is_none),
nullptr,
nullptr,
size_of_v<Type>,
std::is_void_v<Type>,
std::is_integral_v<Type>,
std::is_floating_point_v<Type>,
std::is_array_v<Type>,
std::is_enum_v<Type>,
std::is_union_v<Type>,
std::is_class_v<Type>,
std::is_pointer_v<Type>,
std::is_pointer_v<Type> && std::is_function_v<std::remove_pointer_t<Type>>,
std::is_member_object_pointer_v<Type>,
std::is_member_function_pointer_v<Type>,
is_meta_pointer_like_v<Type>,
is_complete_v<meta_sequence_container_traits<Type>>,
is_complete_v<meta_associative_container_traits<Type>>,
meta_template_descriptor(),
std::rank_v<Type>,
[](meta_type_node::size_type dim) ENTT_NOEXCEPT { return extent(dim, std::make_index_sequence<std::rank_v<Type>>{}); },
&meta_node<std::remove_cv_t<std::remove_reference_t<std::remove_pointer_t<Type>>>>::resolve,
&meta_node<std::remove_cv_t<std::remove_reference_t<std::remove_extent_t<Type>>>>::resolve,
meta_default_constructor(&node),
meta_default_constructor(&node)
meta_default_constructor(),
meta_conversion_helper(),
meta_template_info()
// tricks clang-format
};
return &node;
}
};
template<typename Type>
struct meta_info: meta_node<std::remove_cv_t<std::remove_reference_t<Type>>> {};
template<typename... Args>
meta_type_node * meta_arg_node(type_list<Args...>, const std::size_t index) ENTT_NOEXCEPT {
meta_type_node *args[sizeof...(Args) + 1u]{nullptr, internal::meta_info<Args>::resolve()...};
[[nodiscard]] meta_type_node *meta_arg_node(type_list<Args...>, const std::size_t index) ENTT_NOEXCEPT {
meta_type_node *args[sizeof...(Args) + 1u]{nullptr, internal::meta_node<std::remove_cv_t<std::remove_reference_t<Args>>>::resolve()...};
return args[index + 1u];
}
template<auto Member, typename Type>
[[nodiscard]] static std::decay_t<decltype(std::declval<internal::meta_type_node>().*Member)> find_by(const Type &info_or_id, const internal::meta_type_node *node) ENTT_NOEXCEPT {
for(auto *curr = node->*Member; curr; curr = curr->next) {
if constexpr(std::is_same_v<Type, type_info>) {
if(*curr->type->info == info_or_id) {
return curr;
}
} else if constexpr(std::is_same_v<decltype(curr), meta_base_node *>) {
if(curr->type->id == info_or_id) {
return curr;
}
} else {
if(curr->id == info_or_id) {
return curr;
}
}
}
for(auto *curr = node->base; curr; curr = curr->next) {
if(auto *ret = find_by<Member>(info_or_id, curr->type); ret) {
return ret;
}
}
return nullptr;
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
}
} // namespace entt
#endif

View File

@@ -1,24 +1,19 @@
#ifndef ENTT_META_POINTER_HPP
#define ENTT_META_POINTER_HPP
#include <memory>
#include <type_traits>
#include "type_traits.hpp"
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
{};
: std::true_type {};
/**
* @brief Partial specialization used to reject pointers to arrays.
@@ -26,10 +21,8 @@ struct is_meta_pointer_like<Type *>
* @tparam N Number of elements of the array.
*/
template<typename Type, std::size_t N>
struct is_meta_pointer_like<Type(*)[N]>
: std::false_type
{};
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
@@ -38,9 +31,7 @@ struct is_meta_pointer_like<Type(*)[N]>
*/
template<typename Type>
struct is_meta_pointer_like<std::shared_ptr<Type>>
: std::true_type
{};
: std::true_type {};
/**
* @brief Makes `std::unique_ptr`s of any type pointer-like types for the meta
@@ -50,11 +41,8 @@ struct is_meta_pointer_like<std::shared_ptr<Type>>
*/
template<typename Type, typename... Args>
struct is_meta_pointer_like<std::unique_ptr<Type, Args...>>
: std::true_type
{};
}
: std::true_type {};
} // namespace entt
#endif

View File

@@ -1,27 +1,66 @@
#ifndef ENTT_META_POLICY_HPP
#define ENTT_META_POLICY_HPP
#include <type_traits>
namespace entt {
/*! @brief Empty class type used to request the _as ref_ policy. */
struct as_ref_t {};
struct as_ref_t {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
template<typename Type>
static constexpr bool value = std::is_reference_v<Type> && !std::is_const_v<std::remove_reference_t<Type>>;
/**
* Internal details not to be documented.
* @endcond
*/
};
/*! @brief Empty class type used to request the _as cref_ policy. */
struct as_cref_t {};
struct as_cref_t {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
template<typename Type>
static constexpr bool value = std::is_reference_v<Type>;
/**
* Internal details not to be documented.
* @endcond
*/
};
/*! @brief Empty class type used to request the _as-is_ policy. */
struct as_is_t {};
struct as_is_t {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
template<typename>
static constexpr bool value = true;
/**
* Internal details not to be documented.
* @endcond
*/
};
/*! @brief Empty class type used to request the _as void_ policy. */
struct as_void_t {};
}
struct as_void_t {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
template<typename>
static constexpr bool value = true;
/**
* Internal details not to be documented.
* @endcond
*/
};
} // namespace entt
#endif

View File

@@ -1,13 +1,69 @@
#ifndef ENTT_META_RANGE_HPP
#define ENTT_META_RANGE_HPP
#include <cstddef>
#include <iterator>
#include "../core/iterator.hpp"
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename Type, typename Node>
struct meta_range_iterator final {
using difference_type = std::ptrdiff_t;
using value_type = Type;
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
using node_type = Node;
meta_range_iterator() ENTT_NOEXCEPT
: it{} {}
meta_range_iterator(node_type *head) ENTT_NOEXCEPT
: it{head} {}
meta_range_iterator &operator++() ENTT_NOEXCEPT {
return (it = it->next), *this;
}
meta_range_iterator operator++(int) ENTT_NOEXCEPT {
meta_range_iterator orig = *this;
return ++(*this), orig;
}
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
return it;
}
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
return operator*();
}
[[nodiscard]] bool operator==(const meta_range_iterator &other) const ENTT_NOEXCEPT {
return it == other.it;
}
[[nodiscard]] bool operator!=(const meta_range_iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
private:
node_type *it;
};
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Iterable range to use to iterate all types of meta objects.
@@ -15,51 +71,13 @@ namespace entt {
* @tparam Node Type of meta nodes iterated.
*/
template<typename Type, typename Node = typename Type::node_type>
class meta_range {
struct range_iterator {
using difference_type = std::ptrdiff_t;
using value_type = Type;
using pointer = void;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
using node_type = Node;
range_iterator() ENTT_NOEXCEPT = default;
range_iterator(node_type *head) ENTT_NOEXCEPT
: it{head}
{}
range_iterator & operator++() ENTT_NOEXCEPT {
return (it = it->next), *this;
}
range_iterator operator++(int) ENTT_NOEXCEPT {
range_iterator orig = *this;
return ++(*this), orig;
}
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
return it;
}
[[nodiscard]] bool operator==(const range_iterator &other) const ENTT_NOEXCEPT {
return other.it == it;
}
[[nodiscard]] bool operator!=(const range_iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
private:
node_type *it{};
};
public:
struct meta_range final {
/*! @brief Node type. */
using node_type = Node;
/*! @brief Input iterator type. */
using iterator = range_iterator;
using iterator = internal::meta_range_iterator<Type, Node>;
/*! @brief Constant input iterator type. */
using const_iterator = iterator;
/*! @brief Default constructor. */
meta_range() ENTT_NOEXCEPT = default;
@@ -68,33 +86,40 @@ public:
* @brief Constructs a meta range from a given node.
* @param head The underlying node with which to construct the range.
*/
meta_range(node_type *head)
: node{head}
{}
meta_range(node_type *head) ENTT_NOEXCEPT
: node{head} {}
/**
* @brief Returns an iterator to the beginning.
* @return An iterator to the first meta object of the range.
*/
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
return iterator{node};
}
/*! @copydoc cbegin */
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
return cbegin();
}
/**
* @brief Returns an iterator to the end.
* @return An iterator to the element following the last meta object of the
* range.
*/
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
return iterator{};
}
/*! @copydoc cend */
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
return cend();
}
private:
node_type *node{nullptr};
};
}
} // namespace entt
#endif

View File

@@ -1,7 +1,6 @@
#ifndef ENTT_META_RESOLVE_HPP
#define ENTT_META_RESOLVE_HPP
#include <algorithm>
#include "../core/type_info.hpp"
#include "ctx.hpp"
@@ -9,10 +8,8 @@
#include "node.hpp"
#include "range.hpp"
namespace entt {
/**
* @brief Returns the meta type associated with a given type.
* @tparam Type Type to use to search for a meta type.
@@ -20,27 +17,25 @@ namespace entt {
*/
template<typename Type>
[[nodiscard]] meta_type resolve() ENTT_NOEXCEPT {
return internal::meta_info<Type>::resolve();
return internal::meta_node<std::remove_cv_t<std::remove_reference_t<Type>>>::resolve();
}
/**
* @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> resolve() {
[[nodiscard]] inline meta_range<meta_type> resolve() ENTT_NOEXCEPT {
return *internal::meta_context::global();
}
/**
* @brief Returns the meta type associated with a given identifier, if any.
* @param id Unique identifier.
* @return The meta type associated with the given identifier, if any.
*/
[[nodiscard]] inline meta_type resolve(const id_type id) ENTT_NOEXCEPT {
for(auto *curr = *internal::meta_context::global(); curr; curr = curr->next) {
if(curr->id == id) {
for(auto &&curr: resolve()) {
if(curr.id() == id) {
return curr;
}
}
@@ -48,16 +43,14 @@ template<typename Type>
return {};
}
/**
* @brief Returns the meta type associated with a given type info object, if
* any.
* @brief Returns the meta type associated with a given type info object.
* @param info The type info object of the requested type.
* @return The meta type associated with the given type info object, if any.
*/
[[nodiscard]] inline meta_type resolve(const type_info info) ENTT_NOEXCEPT {
for(auto *curr = *internal::meta_context::global(); curr; curr = curr->next) {
if(curr->info == info) {
[[nodiscard]] inline meta_type resolve(const type_info &info) ENTT_NOEXCEPT {
for(auto &&curr: resolve()) {
if(curr.info() == info) {
return curr;
}
}
@@ -65,8 +58,6 @@ template<typename Type>
return {};
}
}
} // namespace entt
#endif

View File

@@ -1,24 +1,20 @@
#ifndef ENTT_META_TEMPLATE_HPP
#define ENTT_META_TEMPLATE_HPP
#include "../core/type_traits.hpp"
namespace entt {
/*! @brief Utility class to disambiguate class templates. */
template<template<typename...> typename>
template<template<typename...> class>
struct meta_class_template_tag {};
/**
* @brief General purpose traits class for generating meta template information.
* @tparam Clazz Type of class template.
* @tparam Args Types of template arguments.
*/
template<template<typename...> typename Clazz, typename... Args>
template<template<typename...> class Clazz, typename... Args>
struct meta_template_traits<Clazz<Args...>> {
/*! @brief Wrapped class template. */
using class_type = meta_class_template_tag<Clazz>;
@@ -26,8 +22,6 @@ struct meta_template_traits<Clazz<Args...>> {
using args_type = type_list<Args...>;
};
}
} // namespace entt
#endif

View File

@@ -1,13 +1,11 @@
#ifndef ENTT_META_TYPE_TRAITS_HPP
#define ENTT_META_TYPE_TRAITS_HPP
#include <type_traits>
#include <utility>
namespace entt {
/**
* @brief Traits class template to be specialized to enable support for meta
* template information.
@@ -15,7 +13,6 @@ namespace entt {
template<typename>
struct meta_template_traits;
/**
* @brief Traits class template to be specialized to enable support for meta
* sequence containers.
@@ -23,7 +20,6 @@ struct meta_template_traits;
template<typename>
struct meta_sequence_container_traits;
/**
* @brief Traits class template to be specialized to enable support for meta
* associative containers.
@@ -31,31 +27,6 @@ struct meta_sequence_container_traits;
template<typename>
struct meta_associative_container_traits;
/**
* @brief Provides the member constant `value` to true if a meta associative
* container claims to wrap a key-only type, false otherwise.
* @tparam Type Potentially key-only meta associative container type.
*/
template<typename, typename = void>
struct is_key_only_meta_associative_container: std::true_type {};
/*! @copydoc is_key_only_meta_associative_container */
template<typename Type>
struct is_key_only_meta_associative_container<Type, std::void_t<typename meta_associative_container_traits<Type>::type::mapped_type>>
: std::false_type
{};
/**
* @brief Helper variable template.
* @tparam Type Potentially key-only meta associative container type.
*/
template<typename Type>
inline constexpr auto is_key_only_meta_associative_container_v = is_key_only_meta_associative_container<Type>::value;
/**
* @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.
@@ -64,7 +35,6 @@ inline constexpr auto is_key_only_meta_associative_container_v = is_key_only_met
template<typename>
struct is_meta_pointer_like: std::false_type {};
/**
* @brief Partial specialization to ensure that const pointer-like types are
* also accepted.
@@ -73,7 +43,6 @@ struct is_meta_pointer_like: std::false_type {};
template<typename Type>
struct is_meta_pointer_like<const Type>: is_meta_pointer_like<Type> {};
/**
* @brief Helper variable template.
* @tparam Type Potentially pointer-like type.
@@ -81,8 +50,6 @@ struct is_meta_pointer_like<const Type>: is_meta_pointer_like<Type> {};
template<typename Type>
inline constexpr auto is_meta_pointer_like_v = is_meta_pointer_like<Type>::value;
}
} // namespace entt
#endif

View File

@@ -1,7 +1,6 @@
#ifndef ENTT_META_UTILITY_HPP
#define ENTT_META_UTILITY_HPP
#include <cstddef>
#include <functional>
#include <type_traits>
@@ -12,15 +11,12 @@
#include "node.hpp"
#include "policy.hpp"
namespace entt {
/*! @brief Primary template isn't defined on purpose. */
template<typename, typename>
struct meta_function_descriptor;
/**
* @brief Meta function descriptor.
* @tparam Type Reflected type to which the meta function is associated.
@@ -29,19 +25,18 @@ struct meta_function_descriptor;
* @tparam Args Function arguments.
*/
template<typename Type, typename Ret, typename Class, typename... Args>
struct meta_function_descriptor<Type, Ret(Class:: *)(Args...) const> {
struct meta_function_descriptor<Type, Ret (Class::*)(Args...) const> {
/*! @brief Meta function return type. */
using return_type = Ret;
/*! @brief Meta function arguments. */
using args_type = std::conditional_t<std::is_same_v<Type, Class>, type_list<Args...>, type_list<const Class &, Args...>>;
using args_type = std::conditional_t<std::is_base_of_v<Class, Type>, type_list<Args...>, type_list<const Class &, Args...>>;
/*! @brief True if the meta function is const, false otherwise. */
static constexpr auto is_const = true;
/*! @brief True if the meta function is static, false otherwise. */
static constexpr auto is_static = !std::is_same_v<Type, Class>;
static constexpr auto is_static = !std::is_base_of_v<Class, Type>;
};
/**
* @brief Meta function descriptor.
* @tparam Type Reflected type to which the meta function is associated.
@@ -50,31 +45,68 @@ struct meta_function_descriptor<Type, Ret(Class:: *)(Args...) const> {
* @tparam Args Function arguments.
*/
template<typename Type, typename Ret, typename Class, typename... Args>
struct meta_function_descriptor<Type, Ret(Class:: *)(Args...)> {
struct meta_function_descriptor<Type, Ret (Class::*)(Args...)> {
/*! @brief Meta function return type. */
using return_type = Ret;
/*! @brief Meta function arguments. */
using args_type = std::conditional_t<std::is_same_v<Type, Class>, type_list<Args...>, type_list<Class &, Args...>>;
using args_type = std::conditional_t<std::is_base_of_v<Class, Type>, type_list<Args...>, type_list<Class &, Args...>>;
/*! @brief True if the meta function is const, false otherwise. */
static constexpr auto is_const = false;
/*! @brief True if the meta function is static, false otherwise. */
static constexpr auto is_static = !std::is_same_v<Type, Class>;
static constexpr auto is_static = !std::is_base_of_v<Class, Type>;
};
/**
* @brief Meta function descriptor.
* @tparam Type Reflected type to which the meta data is associated.
* @tparam Class Actual owner of the data member.
* @tparam Ret Data member type.
*/
template<typename Type, typename Ret, typename Class>
struct meta_function_descriptor<Type, Ret Class::*> {
/*! @brief Meta data return type. */
using return_type = Ret &;
/*! @brief Meta data arguments. */
using args_type = std::conditional_t<std::is_base_of_v<Class, Type>, type_list<>, type_list<Class &>>;
/*! @brief True if the meta data is const, false otherwise. */
static constexpr auto is_const = false;
/*! @brief True if the meta data is static, false otherwise. */
static constexpr auto is_static = !std::is_base_of_v<Class, Type>;
};
/**
* @brief Meta function descriptor.
* @tparam Type Reflected type to which the meta function is associated.
* @tparam Ret Function return type.
* @tparam Args Function arguments.
* @tparam MaybeType First function argument.
* @tparam Args Other function arguments.
*/
template<typename Type, typename Ret, typename... Args>
struct meta_function_descriptor<Type, Ret(*)(Args...)> {
template<typename Type, typename Ret, typename MaybeType, typename... Args>
struct meta_function_descriptor<Type, Ret (*)(MaybeType, Args...)> {
/*! @brief Meta function return type. */
using return_type = Ret;
/*! @brief Meta function arguments. */
using args_type = type_list<Args...>;
using args_type = std::conditional_t<std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>, type_list<Args...>, type_list<MaybeType, Args...>>;
/*! @brief True if the meta function is const, false otherwise. */
static constexpr auto is_const = std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> && std::is_const_v<std::remove_reference_t<MaybeType>>;
/*! @brief True if the meta function is static, false otherwise. */
static constexpr auto is_static = !std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>;
};
/**
* @brief Meta function descriptor.
* @tparam Type Reflected type to which the meta function is associated.
* @tparam Ret Function return type.
*/
template<typename Type, typename Ret>
struct meta_function_descriptor<Type, Ret (*)()> {
/*! @brief Meta function return type. */
using return_type = Ret;
/*! @brief Meta function arguments. */
using args_type = type_list<>;
/*! @brief True if the meta function is const, false otherwise. */
static constexpr auto is_const = false;
@@ -82,7 +114,6 @@ struct meta_function_descriptor<Type, Ret(*)(Args...)> {
static constexpr auto is_static = true;
};
/**
* @brief Meta function helper.
*
@@ -95,20 +126,25 @@ struct meta_function_descriptor<Type, Ret(*)(Args...)> {
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 constexpr 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 constexpr meta_function_descriptor<Type, Ret (Class::*)(Args...)> get_rid_of_noexcept(Ret (Class::*)(Args...));
template<typename Ret, typename Class>
static constexpr 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 constexpr 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);
public:
/*! @brief The meta function descriptor of the given function. */
using type = decltype(get_rid_of_noexcept(std::declval<Candidate>()));
};
/**
* @brief Helper type.
* @tparam Type Reflected type to which the meta function is associated.
@@ -117,36 +153,38 @@ public:
template<typename Type, typename Candidate>
using meta_function_helper_t = typename meta_function_helper<Type, Candidate>::type;
/**
* @brief Wraps a value depending on the given policy.
* @tparam Policy Optional policy (no policy set by default).
* @tparam Type Type of value to wrap.
* @param value Value to wrap.
* @return A meta any containing the returned value, if any.
*/
template<typename Policy = as_is_t, typename Type>
meta_any meta_dispatch([[maybe_unused]] Type &&value) {
if constexpr(std::is_same_v<Policy, as_void_t>) {
return meta_any{std::in_place_type<void>};
} else if constexpr(std::is_same_v<Policy, as_ref_t>) {
return meta_any{std::in_place_type<Type>, std::forward<Type>(value)};
} else if constexpr(std::is_same_v<Policy, as_cref_t>) {
static_assert(std::is_lvalue_reference_v<Type>, "Invalid type");
return meta_any{std::in_place_type<const std::remove_reference_t<Type> &>, std::as_const(value)};
} else {
static_assert(std::is_same_v<Policy, as_is_t>, "Policy not supported");
return meta_any{std::forward<Type>(value)};
}
}
/**
* @brief Returns the meta type of the i-th element of a list of arguments.
* @tparam Args Actual types of arguments.
* @tparam Type Type list of the actual types of arguments.
* @return The meta type of the i-th element of the list of arguments.
*/
template<typename... Args>
[[nodiscard]] static meta_type meta_arg(type_list<Args...>, const std::size_t index) ENTT_NOEXCEPT {
return internal::meta_arg_node(type_list<Args...>{}, index);
template<typename Type>
[[nodiscard]] static meta_type meta_arg(const std::size_t index) ENTT_NOEXCEPT {
return internal::meta_arg_node(Type{}, index);
}
/**
* @brief Constructs an instance given a list of erased parameters, if possible.
* @tparam Type Actual type of the instance to construct.
* @tparam Args Types of arguments expected.
* @tparam Index Indexes to use to extract erased arguments from their list.
* @param args Parameters to use to construct the instance.
* @return A meta any containing the new instance, if any.
*/
template<typename Type, typename... Args, std::size_t... Index>
[[nodiscard]] meta_any meta_construct(meta_any * const args, std::index_sequence<Index...>) {
if(((args+Index)->allow_cast<Args>() && ...)) {
return Type{(args+Index)->cast<Args>()...};
}
return {};
}
/**
* @brief Sets the value of a given variable.
* @tparam Type Reflected type to which the variable is associated.
@@ -158,26 +196,20 @@ template<typename Type, typename... Args, std::size_t... Index>
template<typename Type, auto Data>
[[nodiscard]] bool meta_setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) {
if constexpr(!std::is_same_v<decltype(Data), Type> && !std::is_same_v<decltype(Data), std::nullptr_t>) {
if constexpr(std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>>) {
using data_type = type_list_element_t<1u, typename meta_function_helper_t<Type, decltype(Data)>::args_type>;
if constexpr(std::is_member_function_pointer_v<decltype(Data)> || std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>>) {
using descriptor = meta_function_helper_t<Type, decltype(Data)>;
using data_type = type_list_element_t<descriptor::is_static, typename descriptor::args_type>;
if(auto * const clazz = instance->try_cast<Type>(); clazz && value.allow_cast<data_type>()) {
Data(*clazz, value.cast<data_type>());
return true;
}
} else if constexpr(std::is_member_function_pointer_v<decltype(Data)>) {
using data_type = type_list_element_t<0u, typename meta_function_helper_t<Type, decltype(Data)>::args_type>;
if(auto * const clazz = instance->try_cast<Type>(); clazz && value.allow_cast<data_type>()) {
(clazz->*Data)(value.cast<data_type>());
if(auto *const clazz = instance->try_cast<Type>(); clazz && value.allow_cast<data_type>()) {
std::invoke(Data, *clazz, value.cast<data_type>());
return true;
}
} else if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
using data_type = std::remove_reference_t<decltype(std::declval<Type>().*Data)>;
using data_type = std::remove_reference_t<typename meta_function_helper_t<Type, decltype(Data)>::return_type>;
if constexpr(!std::is_array_v<data_type> && !std::is_const_v<data_type>) {
if(auto * const clazz = instance->try_cast<Type>(); clazz && value.allow_cast<data_type>()) {
clazz->*Data = value.cast<data_type>();
if(auto *const clazz = instance->try_cast<Type>(); clazz && value.allow_cast<data_type>()) {
std::invoke(Data, *clazz) = value.cast<data_type>();
return true;
}
}
@@ -196,30 +228,6 @@ template<typename Type, auto Data>
return false;
}
/**
* @brief Wraps a value depending on the given policy.
* @tparam Policy Optional policy (no policy set by default).
* @tparam Type Type of value to wrap.
* @param value Value to wrap.
* @return A meta any containing the returned value.
*/
template<typename Policy = as_is_t, typename Type>
meta_any meta_dispatch(Type &&value) {
if constexpr(std::is_same_v<Policy, as_void_t>) {
return meta_any{std::in_place_type<void>, std::forward<Type>(value)};
} else if constexpr(std::is_same_v<Policy, as_ref_t>) {
return meta_any{std::in_place_type<Type>, std::forward<Type>(value)};
} else if constexpr(std::is_same_v<Policy, as_cref_t>) {
static_assert(std::is_lvalue_reference_v<Type>, "Invalid type");
return meta_any{std::in_place_type<const std::remove_reference_t<Type> &>, std::as_const(value)};
} else {
static_assert(std::is_same_v<Policy, as_is_t>, "Policy not supported");
return meta_any{std::forward<Type>(value)};
}
}
/**
* @brief Gets the value of a given variable.
* @tparam Type Reflected type to which the variable is associated.
@@ -230,18 +238,18 @@ meta_any meta_dispatch(Type &&value) {
*/
template<typename Type, auto Data, typename Policy = as_is_t>
[[nodiscard]] meta_any meta_getter([[maybe_unused]] meta_handle instance) {
if constexpr(std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>>) {
auto * const clazz = instance->try_cast<std::conditional_t<std::is_invocable_v<decltype(Data), const Type &>, const Type, Type>>();
return clazz ? meta_dispatch<Policy>(Data(*clazz)) : meta_any{};
} else if constexpr(std::is_member_function_pointer_v<decltype(Data)>) {
auto * const clazz = instance->try_cast<std::conditional_t<std::is_invocable_v<decltype(Data), const Type &>, const Type, Type>>();
return clazz ? meta_dispatch<Policy>((clazz->*Data)()) : meta_any{};
} else if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
if constexpr(!std::is_array_v<std::remove_cv_t<std::remove_reference_t<decltype(std::declval<Type>().*Data)>>>) {
if(auto * clazz = instance->try_cast<Type>(); clazz) {
return meta_dispatch<Policy>(clazz->*Data);
} else if(auto * fallback = instance->try_cast<const Type>(); fallback) {
return meta_dispatch<Policy>(fallback->*Data);
if constexpr(std::is_member_pointer_v<decltype(Data)> || std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>>) {
if constexpr(!std::is_array_v<std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(Data), Type &>>>>) {
if constexpr(std::is_invocable_v<decltype(Data), Type &>) {
if(auto *clazz = instance->try_cast<Type>(); clazz) {
return meta_dispatch<Policy>(std::invoke(Data, *clazz));
}
}
if constexpr(std::is_invocable_v<decltype(Data), const Type &>) {
if(auto *fallback = instance->try_cast<const Type>(); fallback) {
return meta_dispatch<Policy>(std::invoke(Data, *fallback));
}
}
}
@@ -257,77 +265,132 @@ template<typename Type, auto Data, typename Policy = as_is_t>
}
}
/**
* @brief Invokes a function given a list of erased parameters, if possible.
* @tparam Type Reflected type to which the function is associated.
* @tparam Candidate The actual function to invoke.
* @tparam Policy Optional policy (no policy set by default).
* @tparam Index Indexes to use to extract erased arguments from their list.
* @param instance An opaque instance of the underlying type, if required.
* @param args Parameters to use to invoke the function.
* @return A meta any containing the returned value, if any.
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
template<typename Type, auto Candidate, typename Policy = as_is_t, std::size_t... Index>
[[nodiscard]] std::enable_if_t<!std::is_invocable_v<decltype(Candidate)>, meta_any> meta_invoke([[maybe_unused]] meta_handle instance, meta_any *args, std::index_sequence<Index...>) {
using descriptor = meta_function_helper_t<Type, decltype(Candidate)>;
const auto invoke = [](auto &&maybe_clazz, auto &&... other) {
if constexpr(std::is_member_function_pointer_v<decltype(Candidate)>) {
if constexpr(std::is_void_v<typename descriptor::return_type>) {
(std::forward<decltype(maybe_clazz)>(maybe_clazz).*Candidate)(std::forward<decltype(other)>(other)...);
return meta_any{std::in_place_type<void>};
} else {
return meta_dispatch<Policy>((std::forward<decltype(maybe_clazz)>(maybe_clazz).*Candidate)(std::forward<decltype(other)>(other)...));
}
} else {
if constexpr(std::is_void_v<typename descriptor::return_type>) {
Candidate(std::forward<decltype(maybe_clazz)>(maybe_clazz), std::forward<decltype(other)>(other)...);
return meta_any{std::in_place_type<void>};
} else {
return meta_dispatch<Policy>(Candidate(std::forward<decltype(maybe_clazz)>(maybe_clazz), std::forward<decltype(other)>(other)...));
}
}
};
namespace internal {
if constexpr(std::is_invocable_v<decltype(Candidate), const Type &, type_list_element_t<Index, typename descriptor::args_type>...>) {
if(const auto * const clazz = instance->try_cast<const Type>(); clazz && ((args+Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
return invoke(*clazz, (args+Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
template<typename Type, typename Policy, typename Candidate, typename... Args>
[[nodiscard]] meta_any meta_invoke_with_args(Candidate &&candidate, Args &&...args) {
if constexpr(std::is_same_v<std::invoke_result_t<decltype(candidate), Args...>, void>) {
std::invoke(candidate, args...);
return meta_any{std::in_place_type<void>};
} else {
return meta_dispatch<Policy>(std::invoke(candidate, args...));
}
}
template<typename Type, typename Policy, typename Candidate, std::size_t... Index>
[[nodiscard]] meta_any meta_invoke([[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *args, std::index_sequence<Index...>) {
using descriptor = meta_function_helper_t<Type, std::remove_reference_t<Candidate>>;
if constexpr(std::is_invocable_v<std::remove_reference_t<Candidate>, const Type &, type_list_element_t<Index, typename descriptor::args_type>...>) {
if(const auto *const clazz = instance->try_cast<const Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
return meta_invoke_with_args<Type, Policy>(std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
}
} else if constexpr(std::is_invocable_v<decltype(Candidate), Type &, type_list_element_t<Index, typename descriptor::args_type>...>) {
if(auto * const clazz = instance->try_cast<Type>(); clazz && ((args+Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
return invoke(*clazz, (args+Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
} else if constexpr(std::is_invocable_v<std::remove_reference_t<Candidate>, Type &, type_list_element_t<Index, typename descriptor::args_type>...>) {
if(auto *const clazz = instance->try_cast<Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
return meta_invoke_with_args<Type, Policy>(std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
}
} else {
if(((args+Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
return invoke((args+Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
if(((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
return meta_invoke_with_args<Type, Policy>(std::forward<Candidate>(candidate), (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
}
}
return meta_any{};
}
template<typename Type, typename... Args, std::size_t... Index>
[[nodiscard]] meta_any meta_construct(meta_any *const args, std::index_sequence<Index...>) {
if(((args + Index)->allow_cast<Args>() && ...)) {
return meta_any{std::in_place_type<Type>, (args + Index)->cast<Args>()...};
}
return meta_any{};
}
} // namespace internal
/**
* @brief Invokes a function given a list of erased parameters, if possible.
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Tries to _invoke_ an object given a list of erased parameters.
* @tparam Type Reflected type to which the object to _invoke_ is associated.
* @tparam Policy Optional policy (no policy set by default).
* @tparam Candidate The type of the actual object to _invoke_.
* @param instance An opaque instance of the underlying type, if required.
* @param candidate The actual object to _invoke_.
* @param args Parameters to use to _invoke_ the object.
* @return A meta any containing the returned value, if any.
*/
template<typename Type, typename Policy = as_is_t, typename Candidate>
[[nodiscard]] meta_any meta_invoke([[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *const args) {
return internal::meta_invoke<Type, Policy>(std::move(instance), std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
}
/**
* @brief Tries to invoke a function given a list of erased parameters.
* @tparam Type Reflected type to which the function is associated.
* @tparam Candidate The actual function to invoke.
* @tparam Policy Optional policy (no policy set by default).
* @tparam Index Indexes to use to extract erased arguments from their list.
* @param instance An opaque instance of the underlying type, if required.
* @param args Parameters to use to invoke the function.
* @return A meta any containing the returned value, if any.
*/
template<typename Type, auto Candidate, typename Policy = as_is_t, std::size_t... Index>
[[nodiscard]] std::enable_if_t<std::is_invocable_v<decltype(Candidate)>, meta_any> meta_invoke(meta_handle, meta_any *, std::index_sequence<Index...>) {
if constexpr(std::is_void_v<decltype(Candidate())>) {
Candidate();
return meta_any{std::in_place_type<void>};
template<typename Type, auto Candidate, typename Policy = as_is_t>
[[nodiscard]] meta_any meta_invoke(meta_handle instance, meta_any *const args) {
return internal::meta_invoke<Type, Policy>(std::move(instance), Candidate, args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<decltype(Candidate)>>::args_type::size>{});
}
/**
* @brief Tries to construct an instance given a list of erased parameters.
* @tparam Type Actual type of the instance to construct.
* @tparam Args Types of arguments expected.
* @param args Parameters to use to construct the instance.
* @return A meta any containing the new instance, if any.
*/
template<typename Type, typename... Args>
[[nodiscard]] meta_any meta_construct(meta_any *const args) {
return internal::meta_construct<Type, Args...>(args, std::index_sequence_for<Args...>{});
}
/**
* @brief Tries to construct an instance given a list of erased parameters.
* @tparam Type Reflected type to which the object to _invoke_ is associated.
* @tparam Policy Optional policy (no policy set by default).
* @tparam Candidate The type of the actual object to _invoke_.
* @param args Parameters to use to _invoke_ the object.
* @param candidate The actual object to _invoke_.
* @return A meta any containing the returned value, if any.
*/
template<typename Type, typename Policy = as_is_t, typename Candidate>
[[nodiscard]] meta_any meta_construct(Candidate &&candidate, meta_any *const args) {
if constexpr(meta_function_helper_t<Type, Candidate>::is_static) {
return internal::meta_invoke<Type, Policy>({}, std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
} else {
return meta_dispatch<Policy>(Candidate());
return internal::meta_invoke<Type, Policy>(*args, std::forward<Candidate>(candidate), args + 1u, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
}
}
/**
* @brief Tries to construct an instance given a list of erased parameters.
* @tparam Type Reflected type to which the function is associated.
* @tparam Candidate The actual function to invoke.
* @tparam Policy Optional policy (no policy set by default).
* @param args Parameters to use to invoke the function.
* @return A meta any containing the returned value, if any.
*/
template<typename Type, auto Candidate, typename Policy = as_is_t>
[[nodiscard]] meta_any meta_construct(meta_any *const args) {
return meta_construct<Type, Policy>(Candidate, args);
}
} // namespace entt
#endif

View File

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

View File

@@ -1,26 +1,21 @@
#ifndef ENTT_POLY_FWD_HPP
#define ENTT_POLY_FWD_HPP
#include <cstdint>
#include <type_traits>
namespace entt {
template<typename, std::size_t Len, std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)>
template<typename, std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)>
class basic_poly;
/**
* @brief Alias declaration for the most common use case.
* @tparam Concept Concept descriptor.
*/
template<typename Concept>
using poly = basic_poly<Concept, sizeof(double[2])>;
}
using poly = basic_poly<Concept>;
} // namespace entt
#endif

View File

@@ -1,7 +1,6 @@
#ifndef ENTT_POLY_POLY_HPP
#define ENTT_POLY_POLY_HPP
#include <cstddef>
#include <functional>
#include <tuple>
@@ -13,17 +12,15 @@
#include "../core/type_traits.hpp"
#include "fwd.hpp"
namespace entt {
/*! @brief Inspector class used to infer the type of the virtual table. */
struct poly_inspector {
/**
* @brief Generic conversion operator (definition only).
* @tparam Type Type to which conversion is requested.
*/
template <class Type>
template<class Type>
operator Type &&() const;
/**
@@ -33,15 +30,14 @@ struct poly_inspector {
* @param args The arguments to pass to the function.
* @return A poly inspector convertible to any type.
*/
template<auto Member, typename... Args>
poly_inspector invoke(Args &&... args) const;
template<std::size_t Member, typename... Args>
poly_inspector invoke(Args &&...args) const;
/*! @copydoc invoke */
template<auto Member, typename... Args>
poly_inspector invoke(Args &&... args);
template<std::size_t Member, typename... Args>
poly_inspector invoke(Args &&...args);
};
/**
* @brief Static virtual table factory.
* @tparam Concept Concept descriptor.
@@ -53,35 +49,35 @@ class poly_vtable {
using inspector = typename Concept::template type<poly_inspector>;
template<typename Ret, typename... Args>
static auto vtable_entry(Ret(*)(inspector &, Args...)) -> Ret(*)(basic_any<Len, Align> &, Args...);
static auto vtable_entry(Ret (*)(inspector &, Args...)) -> Ret (*)(basic_any<Len, Align> &, Args...);
template<typename Ret, typename... Args>
static auto vtable_entry(Ret(*)(const inspector &, Args...)) -> Ret(*)(const basic_any<Len, Align> &, Args...);
static auto vtable_entry(Ret (*)(const inspector &, Args...)) -> Ret (*)(const basic_any<Len, Align> &, Args...);
template<typename Ret, typename... Args>
static auto vtable_entry(Ret(*)(Args...)) -> Ret(*)(const basic_any<Len, Align> &, Args...);
static auto vtable_entry(Ret (*)(Args...)) -> Ret (*)(const basic_any<Len, Align> &, Args...);
template<typename Ret, typename... Args>
static auto vtable_entry(Ret(inspector:: *)(Args...)) -> Ret(*)(basic_any<Len, Align> &, Args...);
static auto vtable_entry(Ret (inspector::*)(Args...)) -> Ret (*)(basic_any<Len, Align> &, Args...);
template<typename Ret, typename... Args>
static auto vtable_entry(Ret(inspector:: *)(Args...) const) -> Ret(*)(const basic_any<Len, Align> &, Args...);
static auto vtable_entry(Ret (inspector::*)(Args...) const) -> Ret (*)(const basic_any<Len, Align> &, Args...);
template<auto... Candidate>
static auto make_vtable(value_list<Candidate...>)
-> decltype(std::make_tuple(vtable_entry(Candidate)...));
static auto make_vtable(value_list<Candidate...>) ENTT_NOEXCEPT
-> decltype(std::make_tuple(vtable_entry(Candidate)...));
template<typename... Func>
[[nodiscard]] static constexpr auto make_vtable(type_list<Func...>) {
if constexpr(sizeof...(Func) == 0) {
[[nodiscard]] static constexpr auto make_vtable(type_list<Func...>) ENTT_NOEXCEPT {
if constexpr(sizeof...(Func) == 0u) {
return decltype(make_vtable(typename Concept::template impl<inspector>{})){};
} else if constexpr((std::is_function_v<Func> && ...)) {
return decltype(std::make_tuple(vtable_entry(std::declval<Func inspector:: *>())...)){};
return decltype(std::make_tuple(vtable_entry(std::declval<Func inspector::*>())...)){};
}
}
template<typename Type, auto Candidate, typename Ret, typename Any, typename... Args>
static void fill_vtable_entry(Ret(* &entry)(Any &, Args...)) {
static void fill_vtable_entry(Ret (*&entry)(Any &, Args...)) ENTT_NOEXCEPT {
if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Args...>) {
entry = +[](Any &, Args... args) -> Ret {
return std::invoke(Candidate, std::forward<Args>(args)...);
@@ -94,15 +90,18 @@ class poly_vtable {
}
template<typename Type, auto... Index>
[[nodiscard]] static auto fill_vtable(std::index_sequence<Index...>) {
type impl{};
[[nodiscard]] static auto fill_vtable(std::index_sequence<Index...>) ENTT_NOEXCEPT {
vtable_type impl{};
(fill_vtable_entry<Type, value_list_element_v<Index, typename Concept::template impl<Type>>>(std::get<Index>(impl)), ...);
return impl;
}
using vtable_type = decltype(make_vtable(Concept{}));
static constexpr bool is_mono_v = std::tuple_size_v<vtable_type> == 1u;
public:
/*! @brief Virtual table type. */
using type = decltype(make_vtable(Concept{}));
using type = std::conditional_t<is_mono_v, std::tuple_element_t<0u, vtable_type>, const vtable_type *>;
/**
* @brief Returns a static virtual table for a specific concept and type.
@@ -110,14 +109,18 @@ public:
* @return A static virtual table for the given concept and type.
*/
template<typename Type>
[[nodiscard]] static const auto * instance() {
[[nodiscard]] static type instance() ENTT_NOEXCEPT {
static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Type differs from its decayed form");
static const auto vtable = fill_vtable<Type>(std::make_index_sequence<Concept::template impl<Type>::size>{});
return &vtable;
static const vtable_type vtable = fill_vtable<Type>(std::make_index_sequence<Concept::template impl<Type>::size>{});
if constexpr(is_mono_v) {
return std::get<0>(vtable);
} else {
return &vtable;
}
}
};
/**
* @brief Poly base class used to inject functionalities into concepts.
* @tparam Poly The outermost poly class.
@@ -132,21 +135,31 @@ struct poly_base {
* @param args The arguments to pass to the function.
* @return The return value of the invoked function, if any.
*/
template<auto Member, typename... Args>
[[nodiscard]] decltype(auto) invoke(const poly_base &self, Args &&... args) const {
template<std::size_t Member, typename... Args>
[[nodiscard]] decltype(auto) invoke(const poly_base &self, Args &&...args) const {
const auto &poly = static_cast<const Poly &>(self);
return std::get<Member>(*poly.vtable)(poly.storage, std::forward<Args>(args)...);
if constexpr(std::is_function_v<std::remove_pointer_t<decltype(poly.vtable)>>) {
return poly.vtable(poly.storage, std::forward<Args>(args)...);
} else {
return std::get<Member>(*poly.vtable)(poly.storage, std::forward<Args>(args)...);
}
}
/*! @copydoc invoke */
template<auto Member, typename... Args>
[[nodiscard]] decltype(auto) invoke(poly_base &self, Args &&... args) {
template<std::size_t Member, typename... Args>
[[nodiscard]] decltype(auto) invoke(poly_base &self, Args &&...args) {
auto &poly = static_cast<Poly &>(self);
return std::get<Member>(*poly.vtable)(poly.storage, std::forward<Args>(args)...);
if constexpr(std::is_function_v<std::remove_pointer_t<decltype(poly.vtable)>>) {
static_assert(Member == 0u, "Unknown member");
return poly.vtable(poly.storage, std::forward<Args>(args)...);
} else {
return std::get<Member>(*poly.vtable)(poly.storage, std::forward<Args>(args)...);
}
}
};
/**
* @brief Shortcut for calling `poly_base<Type>::invoke`.
* @tparam Member Index of the function to invoke.
@@ -156,12 +169,11 @@ struct poly_base {
* @param args The arguments to pass to the function.
* @return The return value of the invoked function, if any.
*/
template<auto Member, typename Poly, typename... Args>
decltype(auto) poly_call(Poly &&self, Args &&... args) {
template<std::size_t Member, typename Poly, typename... Args>
decltype(auto) poly_call(Poly &&self, Args &&...args) {
return std::forward<Poly>(self).template invoke<Member>(self, std::forward<Args>(args)...);
}
/**
* @brief Static polymorphism made simple and within everyone's reach.
*
@@ -182,17 +194,16 @@ class basic_poly: private Concept::template type<poly_base<basic_poly<Concept, L
/*! @brief A poly base is allowed to snoop into a poly object. */
friend struct poly_base<basic_poly>;
using vtable_type = typename poly_vtable<Concept, Len, Align>::type;
public:
/*! @brief Concept type. */
using concept_type = typename Concept::template type<poly_base<basic_poly>>;
/*! @brief Virtual table type. */
using vtable_type = typename poly_vtable<Concept, Len, Align>::type;
/*! @brief Default constructor. */
basic_poly() ENTT_NOEXCEPT
: storage{},
vtable{}
{}
vtable{} {}
/**
* @brief Constructs a poly by directly initializing the new object.
@@ -201,10 +212,9 @@ public:
* @param args Parameters to use to construct the instance.
*/
template<typename Type, typename... Args>
explicit basic_poly(std::in_place_type_t<Type>, Args &&... 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_cv_t<std::remove_reference_t<Type>>>()} {}
/**
* @brief Constructs a poly from a given value.
@@ -213,40 +223,13 @@ public:
*/
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, basic_poly>>>
basic_poly(Type &&value) ENTT_NOEXCEPT
: basic_poly{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)}
{}
: basic_poly{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)} {}
/**
* @brief Copy constructor.
* @param other The instance to copy from.
* @brief Returns the object type if any, `type_id<void>()` otherwise.
* @return The object type if any, `type_id<void>()` otherwise.
*/
basic_poly(const basic_poly &other) = default;
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
basic_poly(basic_poly &&other) ENTT_NOEXCEPT
: basic_poly{}
{
swap(*this, other);
}
/**
* @brief Assignment operator.
* @param other The instance to assign from.
* @return This poly object.
*/
basic_poly & operator=(basic_poly other) {
swap(other, *this);
return *this;
}
/**
* @brief Returns the type of the contained object.
* @return The type of the contained object, if any.
*/
[[nodiscard]] type_info type() const ENTT_NOEXCEPT {
[[nodiscard]] const type_info &type() const ENTT_NOEXCEPT {
return storage.type();
}
@@ -254,12 +237,12 @@ public:
* @brief Returns an opaque pointer to the contained instance.
* @return An opaque pointer the contained instance, if any.
*/
[[nodiscard]] const void * data() const ENTT_NOEXCEPT {
[[nodiscard]] const void *data() const ENTT_NOEXCEPT {
return storage.data();
}
/*! @copydoc data */
[[nodiscard]] void * data() ENTT_NOEXCEPT {
[[nodiscard]] void *data() ENTT_NOEXCEPT {
return storage.data();
}
@@ -270,13 +253,15 @@ public:
* @param args Parameters to use to construct the instance.
*/
template<typename Type, typename... Args>
void emplace(Args &&... args) {
*this = basic_poly{std::in_place_type<Type>, std::forward<Args>(args)...};
void emplace(Args &&...args) {
storage.template emplace<Type>(std::forward<Args>(args)...);
vtable = poly_vtable<Concept, Len, Align>::template instance<std::remove_cv_t<std::remove_reference_t<Type>>>();
}
/*! @brief Destroys contained object */
void reset() {
*this = basic_poly{};
storage.reset();
vtable = {};
}
/**
@@ -284,40 +269,30 @@ public:
* @return False if the poly is empty, true otherwise.
*/
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
return !(vtable == nullptr);
return static_cast<bool>(storage);
}
/**
* @brief Returns a pointer to the underlying concept.
* @return A pointer to the underlying concept.
*/
[[nodiscard]] concept_type * operator->() ENTT_NOEXCEPT {
[[nodiscard]] concept_type *operator->() ENTT_NOEXCEPT {
return this;
}
/*! @copydoc operator-> */
[[nodiscard]] const concept_type * operator->() const ENTT_NOEXCEPT {
[[nodiscard]] const concept_type *operator->() const ENTT_NOEXCEPT {
return this;
}
/**
* @brief Swaps two poly objects.
* @param lhs A valid poly object.
* @param rhs A valid poly object.
*/
friend void swap(basic_poly &lhs, basic_poly &rhs) {
using std::swap;
swap(lhs.storage, rhs.storage);
swap(lhs.vtable, rhs.vtable);
}
/**
* @brief Aliasing constructor.
* @return A poly that shares a reference to an unmanaged object.
*/
[[nodiscard]] basic_poly as_ref() ENTT_NOEXCEPT {
basic_poly ref = std::as_const(*this).as_ref();
basic_poly ref{};
ref.storage = storage.as_ref();
ref.vtable = vtable;
return ref;
}
@@ -331,11 +306,9 @@ public:
private:
basic_any<Len, Align> storage;
const vtable_type *vtable;
vtable_type vtable;
};
}
} // namespace entt
#endif

View File

@@ -1,15 +1,13 @@
#ifndef ENTT_PROCESS_PROCESS_HPP
#define ENTT_PROCESS_PROCESS_HPP
#include <utility>
#include <cstdint>
#include <type_traits>
#include <utility>
#include "../config/config.h"
namespace entt {
/**
* @brief Base class for processes.
*
@@ -71,44 +69,44 @@ namespace entt {
*/
template<typename Derived, typename Delta>
class process {
enum class state: unsigned int {
UNINITIALIZED = 0,
RUNNING,
PAUSED,
SUCCEEDED,
FAILED,
ABORTED,
FINISHED,
REJECTED
enum class state : std::uint8_t {
uninitialized = 0,
running,
paused,
succeeded,
failed,
aborted,
finished,
rejected
};
template<typename Target = Derived>
auto next(std::integral_constant<state, state::UNINITIALIZED>)
-> decltype(std::declval<Target>().init(), void()) {
auto next(std::integral_constant<state, state::uninitialized>)
-> decltype(std::declval<Target>().init(), void()) {
static_cast<Target *>(this)->init();
}
template<typename Target = Derived>
auto next(std::integral_constant<state, state::RUNNING>, Delta delta, void *data)
-> decltype(std::declval<Target>().update(delta, data), void()) {
auto next(std::integral_constant<state, state::running>, Delta delta, void *data)
-> decltype(std::declval<Target>().update(delta, data), void()) {
static_cast<Target *>(this)->update(delta, data);
}
template<typename Target = Derived>
auto next(std::integral_constant<state, state::SUCCEEDED>)
-> decltype(std::declval<Target>().succeeded(), void()) {
auto next(std::integral_constant<state, state::succeeded>)
-> decltype(std::declval<Target>().succeeded(), void()) {
static_cast<Target *>(this)->succeeded();
}
template<typename Target = Derived>
auto next(std::integral_constant<state, state::FAILED>)
-> decltype(std::declval<Target>().failed(), void()) {
auto next(std::integral_constant<state, state::failed>)
-> decltype(std::declval<Target>().failed(), void()) {
static_cast<Target *>(this)->failed();
}
template<typename Target = Derived>
auto next(std::integral_constant<state, state::ABORTED>)
-> decltype(std::declval<Target>().aborted(), void()) {
auto next(std::integral_constant<state, state::aborted>)
-> decltype(std::declval<Target>().aborted(), void()) {
static_cast<Target *>(this)->aborted();
}
@@ -123,7 +121,7 @@ protected:
*/
void succeed() ENTT_NOEXCEPT {
if(alive()) {
current = state::SUCCEEDED;
current = state::succeeded;
}
}
@@ -135,7 +133,7 @@ protected:
*/
void fail() ENTT_NOEXCEPT {
if(alive()) {
current = state::FAILED;
current = state::failed;
}
}
@@ -146,8 +144,8 @@ protected:
* running.
*/
void pause() ENTT_NOEXCEPT {
if(current == state::RUNNING) {
current = state::PAUSED;
if(current == state::running) {
current = state::paused;
}
}
@@ -158,8 +156,8 @@ protected:
* paused.
*/
void unpause() ENTT_NOEXCEPT {
if(current == state::PAUSED) {
current = state::RUNNING;
if(current == state::paused) {
current = state::running;
}
}
@@ -168,7 +166,7 @@ public:
using delta_type = Delta;
/*! @brief Default destructor. */
virtual ~process() {
virtual ~process() ENTT_NOEXCEPT {
static_assert(std::is_base_of_v<process, Derived>, "Incorrect use of the class template");
}
@@ -182,7 +180,7 @@ public:
*/
void abort(const bool immediately = false) {
if(alive()) {
current = state::ABORTED;
current = state::aborted;
if(immediately) {
tick({});
@@ -195,7 +193,7 @@ public:
* @return True if the process is still alive, false otherwise.
*/
[[nodiscard]] bool alive() const ENTT_NOEXCEPT {
return current == state::RUNNING || current == state::PAUSED;
return current == state::running || current == state::paused;
}
/**
@@ -203,7 +201,7 @@ public:
* @return True if the process is terminated, false otherwise.
*/
[[nodiscard]] bool finished() const ENTT_NOEXCEPT {
return current == state::FINISHED;
return current == state::finished;
}
/**
@@ -211,7 +209,7 @@ public:
* @return True if the process is paused, false otherwise.
*/
[[nodiscard]] bool paused() const ENTT_NOEXCEPT {
return current == state::PAUSED;
return current == state::paused;
}
/**
@@ -219,7 +217,7 @@ public:
* @return True if the process terminated with errors, false otherwise.
*/
[[nodiscard]] bool rejected() const ENTT_NOEXCEPT {
return current == state::REJECTED;
return current == state::rejected;
}
/**
@@ -228,13 +226,13 @@ public:
* @param data Optional data.
*/
void tick(const Delta delta, void *data = nullptr) {
switch (current) {
case state::UNINITIALIZED:
next(std::integral_constant<state, state::UNINITIALIZED>{});
current = state::RUNNING;
switch(current) {
case state::uninitialized:
next(std::integral_constant<state, state::uninitialized>{});
current = state::running;
break;
case state::RUNNING:
next(std::integral_constant<state, state::RUNNING>{}, delta, data);
case state::running:
next(std::integral_constant<state, state::running>{}, delta, data);
break;
default:
// suppress warnings
@@ -243,17 +241,17 @@ public:
// if it's dead, it must be notified and removed immediately
switch(current) {
case state::SUCCEEDED:
next(std::integral_constant<state, state::SUCCEEDED>{});
current = state::FINISHED;
case state::succeeded:
next(std::integral_constant<state, state::succeeded>{});
current = state::finished;
break;
case state::FAILED:
next(std::integral_constant<state, state::FAILED>{});
current = state::REJECTED;
case state::failed:
next(std::integral_constant<state, state::failed>{});
current = state::rejected;
break;
case state::ABORTED:
next(std::integral_constant<state, state::ABORTED>{});
current = state::REJECTED;
case state::aborted:
next(std::integral_constant<state, state::aborted>{});
current = state::rejected;
break;
default:
// suppress warnings
@@ -262,10 +260,9 @@ public:
}
private:
state current{state::UNINITIALIZED};
state current{state::uninitialized};
};
/**
* @brief Adaptor for lambdas and functors to turn them into processes.
*
@@ -313,9 +310,8 @@ struct process_adaptor: process<process_adaptor<Func, Delta>, Delta>, private Fu
* @param args Parameters to use to initialize the actual process.
*/
template<typename... Args>
process_adaptor(Args &&... args)
: Func{std::forward<Args>(args)...}
{}
process_adaptor(Args &&...args)
: Func{std::forward<Args>(args)...} {}
/**
* @brief Updates a process and its internal state if required.
@@ -323,12 +319,14 @@ struct process_adaptor: process<process_adaptor<Func, Delta>, Delta>, private Fu
* @param data Optional data.
*/
void update(const Delta delta, void *data) {
Func::operator()(delta, data, [this]() { this->succeed(); }, [this]() { this->fail(); });
Func::operator()(
delta,
data,
[this]() { this->succeed(); },
[this]() { this->fail(); });
}
};
}
} // namespace entt
#endif

View File

@@ -1,19 +1,17 @@
#ifndef ENTT_PROCESS_SCHEDULER_HPP
#define ENTT_PROCESS_SCHEDULER_HPP
#include <vector>
#include <memory>
#include <utility>
#include <algorithm>
#include <iterator>
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
#include "../config/config.h"
#include "process.hpp"
namespace entt {
/**
* @brief Cooperative scheduler for processes.
*
@@ -43,9 +41,9 @@ namespace entt {
template<typename Delta>
class scheduler {
struct process_handler {
using instance_type = std::unique_ptr<void, void(*)(void *)>;
using update_fn_type = bool(process_handler &, Delta, void *);
using abort_fn_type = void(process_handler &, bool);
using instance_type = std::unique_ptr<void, void (*)(void *)>;
using update_fn_type = bool(scheduler &, std::size_t, Delta, void *);
using abort_fn_type = void(scheduler &, std::size_t, bool);
using next_type = std::unique_ptr<process_handler>;
instance_type instance;
@@ -55,12 +53,11 @@ class scheduler {
};
struct continuation {
continuation(process_handler *ref)
: handler{ref}
{}
continuation(process_handler *ref) ENTT_NOEXCEPT
: handler{ref} {}
template<typename Proc, typename... Args>
continuation then(Args &&... args) {
continuation then(Args &&...args) {
static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type");
auto proc = typename process_handler::instance_type{new Proc{std::forward<Args>(args)...}, &scheduler::deleter<Proc>};
handler->next.reset(new process_handler{std::move(proc), &scheduler::update<Proc>, &scheduler::abort<Proc>, nullptr});
@@ -78,17 +75,17 @@ class scheduler {
};
template<typename Proc>
[[nodiscard]] static bool update(process_handler &handler, const Delta delta, void *data) {
auto *process = static_cast<Proc *>(handler.instance.get());
[[nodiscard]] static bool update(scheduler &owner, std::size_t pos, const Delta delta, void *data) {
auto *process = static_cast<Proc *>(owner.handlers[pos].instance.get());
process->tick(delta, data);
if(process->rejected()) {
return true;
} else if(process->finished()) {
if(handler.next) {
if(auto &&handler = owner.handlers[pos]; handler.next) {
handler = std::move(*handler.next);
// forces the process to exit the uninitialized state
return handler.update(handler, {}, nullptr);
return handler.update(owner, pos, {}, nullptr);
}
return true;
@@ -98,8 +95,8 @@ class scheduler {
}
template<typename Proc>
static void abort(process_handler &handler, const bool immediately) {
static_cast<Proc *>(handler.instance.get())->abort(immediately);
static void abort(scheduler &owner, std::size_t pos, const bool immediately) {
static_cast<Proc *>(owner.handlers[pos].instance.get())->abort(immediately);
}
template<typename Proc>
@@ -118,7 +115,7 @@ public:
scheduler(scheduler &&) = default;
/*! @brief Default move assignment operator. @return This scheduler. */
scheduler & operator=(scheduler &&) = default;
scheduler &operator=(scheduler &&) = default;
/**
* @brief Number of processes currently scheduled.
@@ -172,13 +169,13 @@ public:
* @return An opaque object to use to concatenate processes.
*/
template<typename Proc, typename... Args>
auto attach(Args &&... args) {
auto attach(Args &&...args) {
static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type");
auto proc = typename process_handler::instance_type{new Proc{std::forward<Args>(args)...}, &scheduler::deleter<Proc>};
process_handler handler{std::move(proc), &scheduler::update<Proc>, &scheduler::abort<Proc>, nullptr};
auto &&ref = handlers.emplace_back(process_handler{std::move(proc), &scheduler::update<Proc>, &scheduler::abort<Proc>, nullptr});
// forces the process to exit the uninitialized state
handler.update(handler, {}, nullptr);
return continuation{&handlers.emplace_back(std::move(handler))};
ref.update(*this, handlers.size() - 1u, {}, nullptr);
return continuation{&handlers.back()};
}
/**
@@ -250,17 +247,14 @@ public:
* @param data Optional data.
*/
void update(const Delta delta, void *data = nullptr) {
auto sz = handlers.size();
for(auto pos = handlers.size(); pos; --pos) {
auto &handler = handlers[pos-1];
const auto curr = pos - 1u;
if(const auto dead = handler.update(handler, delta, data); dead) {
std::swap(handler, handlers[--sz]);
if(const auto dead = handlers[curr].update(*this, curr, delta, data); dead) {
std::swap(handlers[curr], handlers.back());
handlers.pop_back();
}
}
handlers.erase(handlers.begin() + sz, handlers.end());
}
/**
@@ -274,23 +268,16 @@ public:
* @param immediately Requests an immediate operation.
*/
void abort(const bool immediately = false) {
decltype(handlers) exec;
exec.swap(handlers);
for(auto &&handler: exec) {
handler.abort(handler, immediately);
for(auto pos = handlers.size(); pos; --pos) {
const auto curr = pos - 1u;
handlers[curr].abort(*this, curr, immediately);
}
std::move(handlers.begin(), handlers.end(), std::back_inserter(exec));
handlers.swap(exec);
}
private:
std::vector<process_handler> handlers{};
};
}
} // namespace entt
#endif

View File

@@ -1,52 +1,288 @@
#ifndef ENTT_RESOURCE_CACHE_HPP
#define ENTT_RESOURCE_CACHE_HPP
#ifndef ENTT_RESOURCE_RESOURCE_CACHE_HPP
#define ENTT_RESOURCE_RESOURCE_CACHE_HPP
#include <cstddef>
#include <functional>
#include <iterator>
#include <memory>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include "../config/config.h"
#include "../container/dense_map.hpp"
#include "../core/compressed_pair.hpp"
#include "../core/fwd.hpp"
#include "handle.hpp"
#include "loader.hpp"
#include "../core/iterator.hpp"
#include "../core/utility.hpp"
#include "fwd.hpp"
#include "loader.hpp"
#include "resource.hpp"
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename Type, typename It>
class resource_cache_iterator final {
template<typename, typename>
friend class resource_cache_iterator;
public:
using value_type = std::pair<id_type, resource<Type>>;
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
resource_cache_iterator() ENTT_NOEXCEPT = default;
resource_cache_iterator(const It iter) ENTT_NOEXCEPT
: it{iter} {}
template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
resource_cache_iterator(const resource_cache_iterator<std::remove_const_t<Type>, Other> &other) ENTT_NOEXCEPT
: it{other.it} {}
resource_cache_iterator &operator++() ENTT_NOEXCEPT {
return ++it, *this;
}
resource_cache_iterator operator++(int) ENTT_NOEXCEPT {
resource_cache_iterator orig = *this;
return ++(*this), orig;
}
resource_cache_iterator &operator--() ENTT_NOEXCEPT {
return --it, *this;
}
resource_cache_iterator operator--(int) ENTT_NOEXCEPT {
resource_cache_iterator orig = *this;
return operator--(), orig;
}
resource_cache_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
it += value;
return *this;
}
resource_cache_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
resource_cache_iterator copy = *this;
return (copy += value);
}
resource_cache_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
return (*this += -value);
}
resource_cache_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
return (*this + -value);
}
[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT {
return {it[value].first, resource<Type>{it[value].second}};
}
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
return (*this)[0];
}
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
return operator*();
}
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
friend std::ptrdiff_t operator-(const resource_cache_iterator<TLhs, ILhs> &, const resource_cache_iterator<TRhs, IRhs> &) ENTT_NOEXCEPT;
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
friend bool operator==(const resource_cache_iterator<TLhs, ILhs> &, const resource_cache_iterator<TRhs, IRhs> &) ENTT_NOEXCEPT;
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
friend bool operator<(const resource_cache_iterator<TLhs, ILhs> &, const resource_cache_iterator<TRhs, IRhs> &) ENTT_NOEXCEPT;
private:
It it;
};
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
[[nodiscard]] std::ptrdiff_t operator-(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) ENTT_NOEXCEPT {
return lhs.it - rhs.it;
}
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
[[nodiscard]] bool operator==(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) ENTT_NOEXCEPT {
return lhs.it == rhs.it;
}
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
[[nodiscard]] bool operator!=(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) ENTT_NOEXCEPT {
return !(lhs == rhs);
}
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
[[nodiscard]] bool operator<(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) ENTT_NOEXCEPT {
return lhs.it < rhs.it;
}
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
[[nodiscard]] bool operator>(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) ENTT_NOEXCEPT {
return rhs < lhs;
}
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
[[nodiscard]] bool operator<=(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) ENTT_NOEXCEPT {
return !(lhs > rhs);
}
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
[[nodiscard]] bool operator>=(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) ENTT_NOEXCEPT {
return !(lhs < rhs);
}
} // namespace internal
/**
* @brief Simple cache for resources of a given type.
*
* Minimal implementation of a cache for resources of a given type. It doesn't
* offer much functionalities but it's suitable for small or medium sized
* applications and can be freely inherited to add targeted functionalities for
* large sized applications.
*
* @tparam Resource Type of resources managed by a cache.
* Internal details not to be documented.
* @endcond
*/
template<typename Resource>
struct resource_cache {
/**
* @brief Basic cache for resources of any type.
* @tparam Type Type of resources managed by a cache.
* @tparam Loader Type of loader used to create the resources.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Type, typename Loader, typename Allocator>
class resource_cache {
using alloc_traits = typename std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
using container_allocator = typename alloc_traits::template rebind_alloc<std::pair<const id_type, typename Loader::result_type>>;
using container_type = dense_map<id_type, typename Loader::result_type, identity, std::equal_to<id_type>, container_allocator>;
public:
/*! @brief Resource type. */
using value_type = Type;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Type of resources managed by a cache. */
using resource_type = Resource;
/*! @brief Loader type. */
using loader_type = Loader;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Input iterator type. */
using iterator = internal::resource_cache_iterator<Type, typename container_type::iterator>;
/*! @brief Constant input iterator type. */
using const_iterator = internal::resource_cache_iterator<const Type, typename container_type::const_iterator>;
/*! @brief Default constructor. */
resource_cache() = default;
resource_cache()
: resource_cache{loader_type{}} {}
/**
* @brief Constructs an empty cache with a given allocator.
* @param allocator The allocator to use.
*/
explicit resource_cache(const allocator_type &allocator)
: resource_cache{loader_type{}, allocator} {}
/**
* @brief Constructs an empty cache with a given allocator and loader.
* @param callable The loader to use.
* @param allocator The allocator to use.
*/
explicit resource_cache(const loader_type &callable, const allocator_type &allocator = allocator_type{})
: pool{container_type{allocator}, callable} {}
/*! @brief Default copy constructor. */
resource_cache(const resource_cache &) = default;
/**
* @brief Allocator-extended copy constructor.
* @param other The instance to copy from.
* @param allocator The allocator to use.
*/
resource_cache(const resource_cache &other, const allocator_type &allocator)
: pool{std::piecewise_construct, std::forward_as_tuple(other.pool.first(), allocator), std::forward_as_tuple(other.pool.second())} {}
/*! @brief Default move constructor. */
resource_cache(resource_cache &&) = default;
/*! @brief Default move assignment operator. @return This cache. */
resource_cache & operator=(resource_cache &&) = default;
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
resource_cache(resource_cache &&other, const allocator_type &allocator)
: pool{std::piecewise_construct, std::forward_as_tuple(std::move(other.pool.first()), allocator), std::forward_as_tuple(std::move(other.pool.second()))} {}
/**
* @brief Number of resources managed by a cache.
* @return Number of resources currently stored.
* @brief Default copy assignment operator.
* @return This cache.
*/
[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
return resources.size();
resource_cache &operator=(const resource_cache &) = default;
/**
* @brief Default move assignment operator.
* @return This cache.
*/
resource_cache &operator=(resource_cache &&) = default;
/**
* @brief Returns the associated allocator.
* @return The associated allocator.
*/
[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
return pool.first().get_allocator();
}
/**
* @brief Returns an iterator to the beginning.
*
* The returned iterator points to the first instance of the cache. If the
* cache is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first instance of the internal cache.
*/
[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
return pool.first().begin();
}
/*! @copydoc cbegin */
[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
return cbegin();
}
/*! @copydoc begin */
[[nodiscard]] iterator begin() ENTT_NOEXCEPT {
return pool.first().begin();
}
/**
* @brief Returns an iterator to the end.
*
* The returned iterator points to the element following the last instance
* of the cache. Attempting to dereference the returned iterator results in
* undefined behavior.
*
* @return An iterator to the element following the last instance of the
* internal cache.
*/
[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
return pool.first().end();
}
/*! @copydoc cend */
[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
return cend();
}
/*! @copydoc end */
[[nodiscard]] iterator end() ENTT_NOEXCEPT {
return pool.first().end();
}
/**
@@ -54,115 +290,79 @@ struct resource_cache {
* @return True if the cache contains no resources, false otherwise.
*/
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
return resources.empty();
return pool.first().empty();
}
/**
* @brief Clears a cache and discards all its resources.
*
* Handles are not invalidated and the memory used by a resource isn't
* freed as long as at least a handle keeps the resource itself alive.
* @brief Number of resources managed by a cache.
* @return Number of resources currently stored.
*/
[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
return pool.first().size();
}
/*! @brief Clears a cache. */
void clear() ENTT_NOEXCEPT {
resources.clear();
pool.first().clear();
}
/**
* @brief Loads the resource that corresponds to a given identifier.
* @brief Loads a resource, if its identifier does not exist.
*
* In case an identifier isn't already present in the cache, it loads its
* resource and stores it aside for future uses. Arguments are forwarded
* directly to the loader in order to construct properly the requested
* resource.
*
* @note
* If the identifier is already present in the cache, this function does
* nothing and the arguments are simply discarded.
* Arguments are forwarded directly to the loader and _consumed_ only if the
* resource doesn't already exist.
*
* @warning
* If the resource cannot be loaded correctly, the returned handle will be
* If the resource isn't loaded correctly, the returned handle could be
* invalid and any use of it will result in undefined behavior.
*
* @tparam Loader Type of loader to use to load the resource if required.
* @tparam Args Types of arguments to use to load the resource if required.
* @param id Unique resource identifier.
* @param args Arguments to use to load the resource if required.
* @return A pair consisting of an iterator to the inserted element (or to
* the element that prevented the insertion) and a bool denoting whether the
* insertion took place.
*/
template<typename... Args>
std::pair<iterator, bool> load(const id_type id, Args &&...args) {
if(auto it = pool.first().find(id); it != pool.first().end()) {
return {it, false};
}
return pool.first().emplace(id, pool.second()(std::forward<Args>(args)...));
}
/**
* @brief Force loads a resource, if its identifier does not exist.
* @copydetails load
*/
template<typename... Args>
std::pair<iterator, bool> force_load(const id_type id, Args &&...args) {
return {pool.first().insert_or_assign(id, pool.second()(std::forward<Args>(args)...)).first, true};
}
/**
* @brief Returns a handle for a given resource identifier.
*
* @warning
* There is no guarantee that the returned handle is valid.<br/>
* If it is not, any use will result in indefinite behavior.
*
* @param id Unique resource identifier.
* @return A handle for the given resource.
*/
template<typename Loader, typename... Args>
resource_handle<Resource> load(const id_type id, Args &&... args) {
if(auto it = resources.find(id); it == resources.cend()) {
if(auto handle = temp<Loader>(std::forward<Args>(args)...); handle) {
return (resources[id] = std::move(handle));
}
} else {
return it->second;
[[nodiscard]] resource<const value_type> operator[](const id_type id) const {
if(auto it = pool.first().find(id); it != pool.first().cend()) {
return resource<const value_type>{it->second};
}
return {};
}
/**
* @brief Reloads a resource or loads it for the first time if not present.
*
* Equivalent to the following snippet (pseudocode):
*
* @code{.cpp}
* cache.discard(id);
* cache.load(id, args...);
* @endcode
*
* Arguments are forwarded directly to the loader in order to construct
* properly the requested resource.
*
* @warning
* If the resource cannot be loaded correctly, the returned handle will be
* invalid and any use of it will result in undefined behavior.
*
* @tparam Loader Type of loader to use to load the resource.
* @tparam Args Types of arguments to use to load the resource.
* @param id Unique resource identifier.
* @param args Arguments to use to load the resource.
* @return A handle for the given resource.
*/
template<typename Loader, typename... Args>
resource_handle<Resource> reload(const id_type id, Args &&... args) {
return (discard(id), load<Loader>(id, std::forward<Args>(args)...));
}
/**
* @brief Creates a temporary handle for a resource.
*
* Arguments are forwarded directly to the loader in order to construct
* properly the requested resource. The handle isn't stored aside and the
* cache isn't in charge of the lifetime of the resource itself.
*
* @tparam Loader Type of loader to use to load the resource.
* @tparam Args Types of arguments to use to load the resource.
* @param args Arguments to use to load the resource.
* @return A handle for the given resource.
*/
template<typename Loader, typename... Args>
[[nodiscard]] resource_handle<Resource> temp(Args &&... args) const {
return Loader{}.get(std::forward<Args>(args)...);
}
/**
* @brief Creates a handle for a given resource identifier.
*
* A resource handle can be in a either valid or invalid state. In other
* terms, a resource handle is properly initialized with a resource if the
* cache contains the resource itself. Otherwise the returned handle is
* uninitialized and accessing it results in undefined behavior.
*
* @sa resource_handle
*
* @param id Unique resource identifier.
* @return A handle for the given resource.
*/
[[nodiscard]] resource_handle<Resource> handle(const id_type id) const {
if(auto it = resources.find(id); it != resources.cend()) {
return it->second;
/*! @copydoc operator[] */
[[nodiscard]] resource<value_type> operator[](const id_type id) {
if(auto it = pool.first().find(id); it != pool.first().end()) {
return resource<value_type>{it->second};
}
return {};
@@ -174,64 +374,51 @@ struct resource_cache {
* @return True if the cache contains the resource, false otherwise.
*/
[[nodiscard]] bool contains(const id_type id) const {
return (resources.find(id) != resources.cend());
return pool.first().contains(id);
}
/**
* @brief Discards the resource that corresponds to a given identifier.
*
* Handles are not invalidated and the memory used by the resource isn't
* freed as long as at least a handle keeps the resource itself alive.
*
* @brief Removes an element from a given position.
* @param pos An iterator to the element to remove.
* @return An iterator following the removed element.
*/
iterator erase(const_iterator pos) {
const auto it = pool.first().begin();
return pool.first().erase(it + (pos - const_iterator{it}));
}
/**
* @brief Removes the given elements from a cache.
* @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 An iterator following the last removed element.
*/
iterator erase(const_iterator first, const_iterator last) {
const auto it = pool.first().begin();
return pool.first().erase(it + (first - const_iterator{it}), it + (last - const_iterator{it}));
}
/**
* @brief Removes the given elements from a cache.
* @param id Unique resource identifier.
* @return Number of resources erased (either 0 or 1).
*/
void discard(const id_type id) {
if(auto it = resources.find(id); it != resources.end()) {
resources.erase(it);
}
size_type erase(const id_type id) {
return pool.first().erase(id);
}
/**
* @brief Iterates all resources.
*
* The function object is invoked for each element. It is provided with
* either the resource identifier, the resource handle or both of them.<br/>
* The signature of the function must be equivalent to one of the following
* forms:
*
* @code{.cpp}
* void(const entt::id_type);
* void(entt::resource_handle<Resource>);
* void(const entt::id_type, entt::resource_handle<Resource>);
* @endcode
*
* @tparam Func Type of the function object to invoke.
* @param func A valid function object.
* @brief Returns the loader used to create resources.
* @return The loader used to create resources.
*/
template <typename Func>
void each(Func func) const {
auto begin = resources.begin();
auto end = resources.end();
while(begin != end) {
auto curr = begin++;
if constexpr(std::is_invocable_v<Func, id_type>) {
func(curr->first);
} else if constexpr(std::is_invocable_v<Func, resource_handle<Resource>>) {
func(curr->second);
} else {
func(curr->first, curr->second);
}
}
[[nodiscard]] loader_type loader() const {
return pool.second();
}
private:
std::unordered_map<id_type, resource_handle<Resource>> resources;
compressed_pair<container_type, loader_type> pool;
};
}
} // namespace entt
#endif

View File

@@ -1,23 +1,19 @@
#ifndef ENTT_RESOURCE_FWD_HPP
#define ENTT_RESOURCE_FWD_HPP
#include <memory>
namespace entt {
template<typename>
struct resource_loader;
template<typename Type, typename = resource_loader<Type>, typename = std::allocator<Type>>
class resource_cache;
template<typename>
struct resource_cache;
template<typename>
class resource_handle;
template<typename, typename>
class resource_loader;
}
class resource;
} // namespace entt
#endif

View File

@@ -1,189 +0,0 @@
#ifndef ENTT_RESOURCE_HANDLE_HPP
#define ENTT_RESOURCE_HANDLE_HPP
#include <memory>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "fwd.hpp"
namespace entt {
/**
* @brief Shared resource handle.
*
* A shared resource handle is a small class that wraps a resource and keeps it
* alive even if it's deleted from the cache. It can be either copied or
* moved. A handle shares a reference to the same resource with all the other
* handles constructed for the same identifier.<br/>
* As a rule of thumb, resources should never be copied nor moved. Handles are
* the way to go to keep references to them.
*
* @tparam Resource Type of resource managed by a handle.
*/
template<typename Resource>
class resource_handle {
/*! @brief Resource handles are friends with each other. */
template<typename>
friend class resource_handle;
public:
/*! @brief Default constructor. */
resource_handle() ENTT_NOEXCEPT = default;
/**
* @brief Creates a handle from a shared pointer, namely a resource.
* @param res A pointer to a properly initialized resource.
*/
resource_handle(std::shared_ptr<Resource> res) ENTT_NOEXCEPT
: resource{std::move(res)}
{}
/**
* @brief Copy constructor.
* @param other The instance to copy from.
*/
resource_handle(const resource_handle<Resource> &other) ENTT_NOEXCEPT = default;
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
resource_handle(resource_handle<Resource> &&other) ENTT_NOEXCEPT = default;
/**
* @brief Copy constructs a handle which shares ownership of the resource.
* @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<!std::is_same_v<Other, Resource> && std::is_base_of_v<Resource, Other>>>
resource_handle(const resource_handle<Other> &other) ENTT_NOEXCEPT
: resource{other.resource}
{}
/**
* @brief Move constructs a handle which takes ownership of the resource.
* @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<!std::is_same_v<Other, Resource> && std::is_base_of_v<Resource, Other>>>
resource_handle(resource_handle<Other> &&other) ENTT_NOEXCEPT
: resource{std::move(other.resource)}
{}
/**
* @brief Copy assignment operator.
* @param other The instance to copy from.
* @return This resource handle.
*/
resource_handle & operator=(const resource_handle<Resource> &other) ENTT_NOEXCEPT = default;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This resource handle.
*/
resource_handle & operator=(resource_handle<Resource> &&other) ENTT_NOEXCEPT = default;
/**
* @brief Copy assignment operator from foreign handle.
* @tparam Other Type of resource managed by the received handle.
* @param other The handle to copy from.
* @return This resource handle.
*/
template<typename Other>
std::enable_if_t<!std::is_same_v<Other, Resource> && std::is_base_of_v<Resource, Other>, resource_handle &>
operator=(const resource_handle<Other> &other) ENTT_NOEXCEPT {
resource = other.resource;
return *this;
}
/**
* @brief Move assignment operator from foreign handle.
* @tparam Other Type of resource managed by the received handle.
* @param other The handle to move from.
* @return This resource handle.
*/
template<typename Other>
std::enable_if_t<!std::is_same_v<Other, Resource> && std::is_base_of_v<Resource, Other>, resource_handle &>
operator=(resource_handle<Other> &&other) ENTT_NOEXCEPT {
resource = std::move(other.resource);
return *this;
}
/**
* @brief Gets a reference to the managed resource.
*
* @warning
* The behavior is undefined if the handle doesn't contain a resource.
*
* @return A reference to the managed resource.
*/
[[nodiscard]] const Resource & get() const ENTT_NOEXCEPT {
ENTT_ASSERT(static_cast<bool>(resource), "Invalid resource");
return *resource;
}
/*! @copydoc get */
[[nodiscard]] Resource & get() ENTT_NOEXCEPT {
return const_cast<Resource &>(std::as_const(*this).get());
}
/*! @copydoc get */
[[nodiscard]] operator const Resource & () const ENTT_NOEXCEPT {
return get();
}
/*! @copydoc get */
[[nodiscard]] operator Resource & () ENTT_NOEXCEPT {
return get();
}
/*! @copydoc get */
[[nodiscard]] const Resource & operator *() const ENTT_NOEXCEPT {
return get();
}
/*! @copydoc get */
[[nodiscard]] Resource & operator *() ENTT_NOEXCEPT {
return get();
}
/**
* @brief Gets a pointer to the managed resource.
*
* @warning
* The behavior is undefined if the handle doesn't contain a resource.
*
* @return A pointer to the managed resource or `nullptr` if the handle
* contains no resource at all.
*/
[[nodiscard]] const Resource * operator->() const ENTT_NOEXCEPT {
return resource.get();
}
/*! @copydoc operator-> */
[[nodiscard]] Resource * operator->() ENTT_NOEXCEPT {
return const_cast<Resource *>(std::as_const(*this).operator->());
}
/**
* @brief Returns true if a handle contains a resource, false otherwise.
* @return True if the handle contains a resource, false otherwise.
*/
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
return static_cast<bool>(resource);
}
private:
std::shared_ptr<Resource> resource;
};
}
#endif

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