Compare commits

..

645 Commits

Author SHA1 Message Date
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
Michele Caini
2d5a3f24aa doc: updated documentation 2021-07-21 00:07:02 +02:00
Michele Caini
1639429edd registry: minor changes 2021-07-01 15:32:30 +02:00
Michele Caini
f81abf4883 add cpp.hint to .gitignore 2021-07-01 12:32:18 +02:00
Michele Caini
dcddb7d50e registry: allow non-const types in const overloads of registry context variable getters (close #720) 2021-06-30 09:35:36 +02:00
Michele Caini
42a763031c storage: minor changes 2021-06-30 08:55:21 +02:00
Michele Caini
0964ca5918 storage: turn to assure_at_least based on position to avoid errors at call site 2021-06-29 16:36:00 +02:00
Michele Caini
4e870b83cd test: suppress warnings due to suggested braces 2021-06-29 15:17:58 +02:00
Michele Caini
0b19c9be0b storage: add override specifier 2021-06-29 15:10:41 +02:00
Michele Caini
ac655902a0 storage:
* internal review for memory management
* added support for nosy destructors to ::in_place_pop
* optimized ::emplace/::insert
2021-06-29 15:05:03 +02:00
Michele Caini
e4fb293b55 sparse_set: added ::emplace_back 2021-06-29 12:37:39 +02:00
Michele Caini
d3df64ef4b sparse_set: emplace returns the slot used for insertion 2021-06-28 18:51:28 +02:00
Michele Caini
93e3d14f34 sparse_set: added ::slot to return the next slot available for insertion 2021-06-28 18:46:02 +02:00
Michele Caini
bc687d412d test: suppress some warnings due to [[nodiscard]] 2021-06-28 18:02:43 +02:00
Michele Caini
71e85b44b0 sparse_set: make ::policy [[nodiscard]] 2021-06-28 18:00:40 +02:00
Michele Caini
d4b59aff97 sparse_set: tombstone/null debug check in ::contains 2021-06-28 16:51:24 +02:00
Michele Caini
e3c21e1f3d sparse_set/storage (close #707):
* improved perf on erase/remove (and therefore also registry::destroy)
* allow to inhibit a pop from derived classes
* removed about_to_pop (virtual) function
2021-06-28 15:53:59 +02:00
Michele Caini
e012250e0a type_traits: minor changes 2021-06-28 11:43:27 +02:00
Michele Caini
c6bf82664c doc: added a note about optimized range-destroy 2021-06-28 11:20:40 +02:00
Michele Caini
c61e98009f registry/benchmark: rollback optimized remove/erase (benchmarks have shown that they are not worth it) 2021-06-28 10:19:27 +02:00
Michele Caini
44bccaaad6 basic_registry:
* added fast path to destroy
* added fast path to remove
* added fast path to erase
* minor changes
2021-06-28 09:24:12 +02:00
Michele Caini
aebca14dea view: added member type iterator_type to internal class view_iterator 2021-06-28 09:10:52 +02:00
Michele Caini
4576f27f6e sparse_set: minor changes 2021-06-28 09:10:12 +02:00
Michele Caini
0c213fca40 type_traits:
* added entt::is_iterator[_v]
* added entt::is_iterator_type[_v]
2021-06-28 09:09:55 +02:00
Michele Caini
891f596191 view: minor changes 2021-06-25 17:26:22 +02:00
Michele Caini
eb2b724902 benchmark: tombstone policy runs 2021-06-24 14:04:35 +02:00
Michele Caini
1f8c896181 registry:
* deprecated ::destroyed (use ::released instead)
* added ::release overloads (aka force-destroy)
2021-06-24 10:51:03 +02:00
Michele Caini
12b39e1fbe doc: added missing tokens 2021-06-23 16:35:00 +02:00
Michele Caini
206d31f27a registry: const ::view/::group also support non-const types (close #715) 2021-06-23 15:37:46 +02:00
Michele Caini
6709331182 resource_handle: conversion functions (close #706) 2021-06-22 18:54:27 +02:00
Michele Caini
9460e04ea5 entt_traits: review/cleanup 2021-06-22 15:39:15 +02:00
Michele Caini
462bfea733 entt_traits: review to reduce the risk of ambiguous calls 2021-06-22 13:03:32 +02:00
Michele Caini
a2d61dfaeb runtime_view: suppress warning for shadow parameter 2021-06-22 09:58:19 +02:00
Michele Caini
538425e35c doc: in-place delete and pointer stability 2021-06-22 09:53:56 +02:00
Michele Caini
2e86c1f1a2 component: add basic_component_traits aside component_traits 2021-06-22 09:52:43 +02:00
Michele Caini
8aa4d46ce0 runtime_view: support for storage policy (tombstones) 2021-06-21 15:18:13 +02:00
Michele Caini
cdf67a1421 sparse_set: public ::policy method for opaque runtime detection 2021-06-21 14:44:09 +02:00
Michele Caini
93d905d93d updated TODO 2021-06-21 13:04:46 +02:00
Michele Caini
2c35203647 view:
* split view iterator from multi-type view
* stable/packed storage policy support
2021-06-21 13:04:37 +02:00
Michele Caini
216871fe56 runtime_view: minor changes 2021-06-21 12:59:44 +02:00
Michele Caini
03511f39b1 registry:
* prevent groups from being used with tombstones
* support to compact one, several or all pools
2021-06-21 12:59:26 +02:00
Michele Caini
5ad0832b22 group: minor changes 2021-06-21 12:57:35 +02:00
Michele Caini
d49e7ba4b2 sparse_set/storage:
* added component traits (component.hpp)
* support for in-place delete (tombstones)
2021-06-21 12:56:33 +02:00
Michele Caini
a621b36389 type_traits: removed internal is_empty[_v] 2021-06-21 12:44:50 +02:00
Michele Caini
d093df02ac test: minor changes 2021-06-21 12:39:18 +02:00
Michele Caini
2e1529e78d entity:
* Updated doc
* No const-ref for basic types
2021-06-21 12:32:30 +02:00
Michele Caini
90be1db402 entity:
* extended entt_traits<T>::to_type support
* added null_t::operator|
* added tombstone_t::operator|
2021-06-04 11:01:26 +02:00
Michele Caini
29265e4181 doc: updated doxy.in file 2021-06-03 10:44:13 +02:00
Michele Caini
2689a7ef13 registry: minor changes 2021-06-03 09:06:38 +02:00
Michele Caini
88b58cf23d doc: added a note about tombstones 2021-06-03 09:06:36 +02:00
Michele Caini
e2e433480b entity/registry:
* added entt_traits<T>::reserved()
* added tombstone_t type and tombstone constant
* create-with-hint also accepts null entities (discarded)
* registry skips over tombstone versions and rejects them
2021-06-03 09:06:34 +02:00
Michele Caini
b8bc3e4e94 test: exception safety for sparse set and storage 2021-06-03 08:44:48 +02:00
Michele Caini
c28f52b816 sparse_set/storage: exception safety (kind of) 2021-06-03 08:44:43 +02:00
Michele Caini
ca34309f75 config: removed ENTT_NOEXCEPT, added ENTT_NOEXCEPTION 2021-06-03 08:44:30 +02:00
Michele Caini
ea614a0f3f entity: review of entt_traits<T>::to_type 2021-05-28 08:59:50 +02:00
Michele Caini
3e7dc7af29 entity:
* review of entt_traits design
* added static constexpr member function entt_traits::to_integral
* added static constexpr member function entt_traits::to_entity
* added static constexpr member function entt_traits::to_version
* added static constexpr member function entt_traits::to_type
* custom class identifiers must expose member type entity_field
* it's no longer required to specialize entt_traits (breaking change)
2021-05-28 08:59:50 +02:00
Michele Caini
0b3e3fd19a doc: updated links (close #709) and TODO file 2021-05-28 08:58:36 +02:00
Michele Caini
d3372dc05c organizer: wrap std::min to trick windows.h (close #710) 2021-05-28 08:54:41 +02:00
Michele Caini
7d141cb183 storage: fix move ctor/op 2021-05-27 16:16:45 +02:00
Michele Caini
755699c31b config:
* removed entt::page_size
* renamed ENTT_PAGE_SIZE to ENTT_SPARSE_PAGE
* added ENTT_PACKED_PAGE (page size for component arrays)
2021-05-25 10:07:20 +02:00
Michele Caini
acf3d4cd74 sparse_set/storage: suppress a couple of warnings 2021-05-25 10:06:55 +02:00
Michele Caini
92414c91b5 sparse_set/storage: review memory management for fancy allocators and the like 2021-05-24 11:53:45 +02:00
Michele Caini
ff67f402bb sparse_set/storage:
* better performance on insertion
* exception guarantee for nothrow default constructible entity types
2021-05-24 10:36:33 +02:00
Michele Caini
f048a3f1d8 sparse_set: minor changes 2021-05-24 10:27:30 +02:00
Michele Caini
9d39cb51cf storage/registry: review range ::insert (breaking change) 2021-05-24 10:27:30 +02:00
Michele Caini
7624a9d34c sparse_set/storage:
* exception safety guarantee (sort of)
* ::insert optimization for non random access iterators
2021-05-24 10:27:30 +02:00
Michele Caini
0fe73fb6ed storage: free memory in move assignment operator (with tests) 2021-05-24 10:27:10 +02:00
Michele Caini
ae11652493 sparse_set: free memory in move assignment operator (with tests) 2021-05-24 09:52:09 +02:00
Michele Caini
0ab46870ae updated TODO list 2021-05-21 09:00:18 +02:00
Michele Caini
a5381374b8 updated workflow 2021-05-20 11:00:42 +02:00
Michele Caini
7fae655651 updated TODO file 2021-05-19 10:49:23 +02:00
Michele Caini
3d9959c8e5 sparse_set/storage: small review and optimizations 2021-05-19 09:29:37 +02:00
Michele Caini
bc86576aa8 storage: improved performance when emplacing 2021-05-17 12:34:42 +02:00
Michele Caini
7c2bce8baf core: added entt::page_size 2021-05-17 12:07:48 +02:00
Michele Caini
b062bbf58d storage/view/group: reintroduce (opaque) ::raw functions as it ought to be 2021-05-17 11:59:51 +02:00
Michele Caini
8e5a048913 storage: updated pointer type for storage iterator 2021-05-17 10:10:54 +02:00
Michele Caini
ff642ffff7 sparse_set: updated pointer type for sparse set iterator 2021-05-17 10:09:50 +02:00
Michele Caini
656c12cebd view/group: opaque pointer type for ::data 2021-05-17 00:00:06 +02:00
Michele Caini
48f3fcf3bd doc: updated links (close #704) 2021-05-16 22:33:04 +02:00
Michele Caini
d797af695b registry: much faster range ::create 2021-05-16 00:26:57 +02:00
Michele Caini
75e0161c5f view :minor changes 2021-05-15 23:01:50 +02:00
Michele Caini
c0662816f1 updated TODO file 2021-05-14 18:24:09 +02:00
Michele Caini
415a31ce23 storage: forward allocator to base class 2021-05-14 15:12:29 +02:00
Michele Caini
6e603e2e51 sparse_set: suppress conversion warning 2021-05-14 15:12:26 +02:00
Michele Caini
9af318a767 test: minor changes 2021-05-14 15:12:23 +02:00
Michele Caini
30827120f6 registry: cleanup 2021-05-14 15:12:20 +02:00
Michele Caini
6194e12616 entity:
* allocator support for sparse set and storage
* pointer stability when assigning components
* removed ::raw functions (registry, view, group)
* to_entity returns null for invalid components
2021-05-14 15:12:16 +02:00
Michele Caini
59a27fb652 registry: deprecated ::reserve_pools 2021-05-13 11:01:46 +02:00
Michele Caini
f8ec57c2f6 *: cleanup 2021-05-11 10:50:38 +02:00
Michele Caini
844ef5e232 meta: avoid warning for unused variable 2021-05-10 09:49:44 +02:00
Michele Caini
5bf3f7b77e meta: suppress warning for extra ; 2021-05-08 17:19:06 +02:00
Michele Caini
00c0afd093 meta: avoid using y_combinator to clean up types (close #700) 2021-05-08 17:13:52 +02:00
Michele Caini
5228739c87 meta: make it work also with VS2017 2021-05-08 17:13:52 +02:00
Michele Caini
0f6d1268c7 meta: further reduced number of instantiations/size of symbols 2021-05-08 17:13:52 +02:00
Michele Caini
b2a4515d2b any: minor changes 2021-05-08 17:13:51 +02:00
Michele Caini
3576c80d33 meta_any: drastically reduce the number of instantiations/size of vtable 2021-05-08 17:13:51 +02:00
Michele Caini
77afd2d36c any: ::owner function (useful to also review meta_any) 2021-05-08 17:13:51 +02:00
Michele Caini
e6ddd5d9c2 any: drastically reduced the number of instantiations/size of vtable 2021-05-08 17:13:51 +02:00
Michele Caini
b9bf1a234e config: added ENTT_LAUNDER 2021-05-08 17:13:51 +02:00
Michele Caini
0ce9122449 any: reduce vtable size, simplify ref/cref creation 2021-05-08 17:13:51 +02:00
Michele Caini
dc41657872 any: empty vtable for void type 2021-05-08 17:13:51 +02:00
Michele Caini
64d9380031 *: updated messages for deprecated functions 2021-05-08 17:13:51 +02:00
Michele Caini
eca7484e30 doc: updated doc for any/meta_any 2021-05-08 17:13:51 +02:00
Michele Caini
2b5c393a13 meta: forward_as_meta 2021-05-08 17:13:51 +02:00
Michele Caini
0817d416a3 meta: make_meta_any -> make_meta 2021-05-08 17:13:51 +02:00
Michele Caini
ccff753305 any: forward_as_any 2021-05-08 17:13:51 +02:00
Michele Caini
11481a430a meta: minor changes 2021-05-08 17:13:51 +02:00
Michele Caini
e252b22735 test: code coverage 2021-05-08 17:13:51 +02:00
Michele Caini
a32ca8eb1d doc: updated doc for any/meta_any/poly 2021-05-08 17:13:50 +02:00
Michele Caini
1979a2279f poly: removed std::reference_wrapper support (incomplete, breaking change) 2021-05-08 17:13:50 +02:00
Michele Caini
1434f942dd *: make it work with VS2017 2021-05-08 17:13:50 +02:00
Michele Caini
955d325f07 meta_any: deprecated std::reference_wrapper support 2021-05-08 17:13:50 +02:00
Michele Caini
3fac3fe2d7 any: deprecate std::reference_wrapper support 2021-05-08 17:13:50 +02:00
Michele Caini
6ff217e74e meta: removed any reference to std::reference_wrapper/std::ref/std::cref 2021-05-08 17:13:50 +02:00
Michele Caini
cd667fe34b meta: make_meta_any 2021-05-08 17:13:50 +02:00
Michele Caini
3d6202ecfd any: make_any 2021-05-08 17:13:50 +02:00
Michele Caini
c40f0ef2bb type traits: is_equality_comparable support for nlohmann json like types (where T::value_type is T) - close #701 2021-05-08 10:43:27 +02:00
Michele Caini
c4b169edd1 registry: make ::remove and ::erase work also with iterators returned by views 2021-05-07 11:16:32 +02:00
Michele Caini
c6bba98828 registry: ::destroy review 2021-05-06 16:34:36 +02:00
Michele Caini
e5d4f1bb58 entity: deprecate handle::remove_all and registry::remove_all 2021-05-06 16:33:43 +02:00
Michele Caini
cf522d60ca doc: updated doc for handle 2021-05-06 10:31:43 +02:00
Michele Caini
daf72a7c61 entity: registry ::remove/::erase entity/range and safe/unsafe versions (close #486) 2021-05-04 11:36:02 +02:00
Michele Caini
f116ad0594 test: use iterators in the poly storage example 2021-05-03 22:29:39 +02:00
Michele Caini
e59d40834d any: reduce vtable options 2021-04-29 11:17:15 +02:00
Michele Caini
d06328af7f any: small review 2021-04-29 10:24:18 +02:00
Michele Caini
baa9d7d836 any: avoid invalid state from assignment operator for non-copyable types 2021-04-29 10:05:01 +02:00
Michele Caini
b030df55ee registry: clean up 2021-04-22 08:55:20 +02:00
Michele Caini
64eb8a2d0d sparse_set: ::remove returns number of actually removed elements 2021-04-22 08:42:35 +02:00
Michele Caini
7d7c36e0c7 registry: clean up ::remove_all 2021-04-21 16:50:33 +02:00
Michele Caini
9ad5768050 registry: clean up ::clear 2021-04-21 16:46:39 +02:00
Michele Caini
1cafbcff38 sparse_set: remove (and range-remove) if exists 2021-04-21 16:39:51 +02:00
Michele Caini
4bbf93fd0c sparse_set: remove -> erase 2021-04-21 15:46:02 +02:00
Michele Caini
120918bc37 any: refine the aggregate support to make any closer to its standard counterpart 2021-04-21 13:45:51 +02:00
Michele Caini
253448cffb sparse_set: suppress warnings for unused variables 2021-04-20 12:38:06 +02:00
Michele Caini
26f19fb90e storage: cleanup 2021-04-20 12:37:50 +02:00
Michele Caini
e8f982e909 view/group: raw() isn't bound to pointer types 2021-04-19 16:05:08 +02:00
Michele Caini
0fa433187e storage: valid state on component removal (allows component destructors to access pools) 2021-04-19 15:34:41 +02:00
Michele Caini
b0069299ea meta_any: avoid dereferencing C-style arrays 2021-04-15 11:45:10 +02:00
Michele Caini
375d5d3e9b doc: cleanup 2021-04-14 15:44:33 +02:00
Michele Caini
2d9398ae1a entity: handle review 2021-04-14 15:44:27 +02:00
Michele Caini
fc69e91636 test: refined custom id example 2021-04-14 15:44:17 +02:00
Michele Caini
5c56cbd672 process: dead -> finished 2021-04-13 09:02:29 +02:00
Michele Caini
a42255158d config: added error messages to ENTT_ASSERT 2021-04-13 09:02:29 +02:00
Michele Caini
f3d10a97df now working on v3.8.0 2021-04-13 09:02:26 +02:00
184 changed files with 55262 additions and 27130 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

View File

@@ -9,34 +9,36 @@ jobs:
strategy:
matrix:
compiler: [
g++-7, g++-8, g++-9, g++,
clang++-8, clang++-9, clang++
]
compiler:
- pkg: g++-7
exe: g++-7
- pkg: g++-8
exe: g++-8
- pkg: g++-9
exe: g++-9
- pkg: g++
exe: g++
- pkg: clang-8
exe: clang++-8
- pkg: clang-9
exe: clang++-9
- pkg: clang-10
exe: clang++-10
- pkg: clang
exe: clang++
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
sudo apt-get install ${{ matrix.compiler.pkg }} -y
- 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
@@ -44,27 +46,52 @@ jobs:
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 10 -C Debug -j4
run: ctest --timeout 30 -C Debug -j4
linux-extra:
timeout-minutes: 10
strategy:
matrix:
compiler: [g++, clang++]
id_type: [uint32, uint64]
cxx_std: [cxx_std_17, cxx_std_20]
include:
- id_type: uint64
id_type_variable: -DENTT_ID_TYPE=std::uint64_t
- cxx_std: cxx_std_20
cxx_std_variable: -DENTT_CXX_STD=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 ${{ matrix.id_type_variable }} ${{ matrix.cxx_std_variable }} ..
make -j4
- name: Run tests
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 30 -C Debug -j4
windows:
timeout-minutes: 10
strategy:
matrix:
os: [windows-latest, windows-2016]
toolset: [clang-cl, default, v141]
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
runs-on: ${{ matrix.os }}
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
@@ -77,7 +104,35 @@ 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: 10
strategy:
matrix:
id_type: [uint32, uint64]
cxx_std: [cxx_std_17, cxx_std_20]
include:
- id_type: uint64
id_type_variable: -DENTT_ID_TYPE=std::uint64_t
- cxx_std: cxx_std_20
cxx_std_variable: -DENTT_CXX_STD=cxx_std_20
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- name: Compile tests
working-directory: build
run: |
cmake -DENTT_BUILD_TESTING=ON ${{ matrix.id_type_variable }} ${{ matrix.cxx_std_variable }} ..
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
@@ -94,4 +149,32 @@ 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: 10
strategy:
matrix:
id_type: [uint32, uint64]
cxx_std: [cxx_std_17, cxx_std_20]
include:
- id_type: uint64
id_type_variable: -DENTT_ID_TYPE=std::uint64_t
- cxx_std: cxx_std_20
cxx_std_variable: -DENTT_CXX_STD=cxx_std_20
runs-on: macOS-latest
steps:
- uses: actions/checkout@v2
- name: Compile tests
working-directory: build
run: |
cmake -DENTT_BUILD_TESTING=ON ${{ matrix.id_type_variable }} ${{ matrix.cxx_std_variable }} ..
make -j4
- name: Run tests
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 30 -C Debug -j4

View File

@@ -22,7 +22,7 @@ jobs:
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 10 -C Debug -j4
run: ctest --timeout 30 -C Debug -j4
- name: Collect data
working-directory: build
run: |
@@ -30,9 +30,9 @@ jobs:
lcov -c -d . -o coverage.info
lcov -l coverage.info
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
uses: codecov/codecov-action@v2
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: build/coverage.info
files: build/coverage.info
name: EnTT
fail_ci_if_error: true

View File

@@ -10,6 +10,13 @@ jobs:
strategy:
matrix:
compiler: [clang++]
id_type: [uint32, uint64]
cxx_std: [cxx_std_17, cxx_std_20]
include:
- id_type: uint64
id_type_variable: -DENTT_ID_TYPE=std::uint64_t
- cxx_std: cxx_std_20
cxx_std_variable: -DENTT_CXX_STD=cxx_std_20
runs-on: ubuntu-latest
@@ -20,10 +27,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 ${{ matrix.id_type_variable }} ${{ matrix.cxx_std_variable }} ..
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

1
.gitignore vendored
View File

@@ -7,6 +7,7 @@ conan/test_package/build
.vscode
.vs
CMakeSettings.json
cpp.hint
# Bazel
/bazel-*

View File

@@ -30,7 +30,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)
@@ -42,37 +42,64 @@ message(VERBOSE "* ${PROJECT_NAME} v${PROJECT_VERSION} (${CMAKE_BUILD_TYPE})")
message(VERBOSE "* Copyright (c) 2017-2021 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 availbale." OFF)
option(ENTT_USE_SANITIZER "Enable sanitizers by adding -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined flags if availbale." 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 +112,101 @@ 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/version.h>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/container/dense_hash_map.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/container/dense_hash_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/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/handle.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/resource/loader.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 +215,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 +293,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,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2017-2021 Michele Caini
Copyright (c) 2017-2021 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

@@ -49,6 +49,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,30 +74,33 @@ 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 **dense 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
**Breaking news**:
* The ECS allows attaching multiple components of the same type to an entity.
* All tools work perfectly across boundaries (DLL-friendly)!!
:slightly_smiling_face:
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.
It is also known that `EnTT` is used in **Minecraft**.<br/>
@@ -246,6 +250,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 +277,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

40
TODO
View File

@@ -1,31 +1,25 @@
* long term feature: shared_ptr less locator and resource cache
* custom allocators and EnTT allocator-aware in general (long term feature, I don't actually need it at the moment) - see #22
* 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...)
WIP:
* HP: see msvc only issue at https://godbolt.org/z/8KW9PhG4c
* HP: scheduler, use any (or poly?) instead of unique_ptr
* HP: any and the like: remove constructor that accepts reference wrapper, allow only in-place T& and entt::make_any<T>(...)
* HP: resource, forward the id to the loader from the cache and if constexpr the call to load, update doc and describe customization points
* HP: 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
* HP: add user data to type_info
* HP: make pools available (registry/view/group), review operator| for views
* HP: any_vector for context variables
* HP: make const registry::view thread safe, switch to a view<T...>{registry} model (long term goal)
* HP: weak reference wrapper example with custom storage
* HP: paginate pools
* HP: headless (sparse set only) view
* HP: write documentation for custom storages and views!!
* HP: registry: use a poly object for pools, no more pool_data type.
* HP: make runtime views use opaque storage and therefore return also elements.
* HP: add exclude-only views to combine with packs
* HP: entity-aware observer, add observer functions aside observer class
* add the possibility of disabling entities without deleting components thanks to the new full check
* add registry::storage id -> basic_sparse_set (no template arg), add example of use
* fast-contains for sparse sets (low prio but nice-to-have)
* runtime events (dispatcher/emitter), runtime context variables...
* runtime_view/registry, remove reference to basic_sparse_set<E>
* dedicated entity storage, in-place O(1) release/destroy for non-orphaned entities, out-of-sync model
* custom allocators all over
WIP:
* customizable any_vtable, sfinae-friendly definition and op::custom for user-def
* resource, forward the id to the loader from the cache and if constexpr the call to load, update doc and describe customization points
* add user data to type_info
* 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
* pagination doesn't work nicely across boundaries probably, give it a look. RO operations are fine, adding components maybe not.
* snapshot: support for range-based archives
* page size 0 -> page less mode
* 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

View File

@@ -1,4 +1,4 @@
# Doxyfile 1.8.20
# Doxyfile 1.9.1
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
@@ -313,7 +313,10 @@ OPTIMIZE_OUTPUT_SLICE = NO
# Note: For files without extension you can use no_extension as a placeholder.
#
# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
# the files are not read by doxygen.
# the files are not read by doxygen. When specifying no_extension you should add
# * to the FILE_PATTERNS.
#
# Note see also the list of default file extension mappings.
EXTENSION_MAPPING =
@@ -523,6 +526,13 @@ EXTRACT_LOCAL_METHODS = NO
EXTRACT_ANON_NSPACES = NO
# If this flag is set to YES, the name of an unnamed parameter in a declaration
# will be determined by the corresponding definition. By default unnamed
# parameters remain unnamed in the output.
# The default value is: YES.
RESOLVE_UNNAMED_PARAMS = YES
# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
# undocumented members inside documented classes or files. If set to NO these
# members will be included in the various overviews, but no documentation
@@ -560,11 +570,18 @@ HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = NO
# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
# names in lower-case letters. If set to YES, upper-case letters are also
# allowed. This is useful if you have classes or files whose names only differ
# in case and if your file system supports case sensitive file names. Windows
# (including Cygwin) and Mac users are advised to set this option to NO.
# With the correct setting of option CASE_SENSE_NAMES doxygen will better be
# able to match the capabilities of the underlying filesystem. In case the
# filesystem is case sensitive (i.e. it supports files in the same directory
# whose names only differ in casing), the option must be set to YES to properly
# deal with such files in case they appear in the input. For filesystems that
# are not case sensitive the option should be be set to NO to properly deal with
# output files written for symbols that only differ in casing, such as for two
# classes, one named CLASS and the other named Class, and to also support
# references to files without having to specify the exact matching casing. On
# Windows (including Cygwin) and MacOS, users should typically set this option
# to NO, whereas on Linux or other Unix flavors it should typically be set to
# YES.
# The default value is: system dependent.
CASE_SENSE_NAMES = YES
@@ -803,7 +820,10 @@ WARN_IF_DOC_ERROR = YES
WARN_NO_PARAMDOC = YES
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
# a warning is encountered.
# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
# at the end of the doxygen process doxygen will return with a non-zero status.
# Possible values are: NO, YES and FAIL_ON_WARNINGS.
# The default value is: NO.
WARN_AS_ERROR = NO
@@ -841,8 +861,8 @@ INPUT = @DOXY_SOURCE_DIRECTORY@ \
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
# documentation (see: https://www.gnu.org/software/libiconv/) for the list of
# possible encodings.
# documentation (see:
# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
# The default value is: UTF-8.
INPUT_ENCODING = UTF-8
@@ -855,13 +875,15 @@ INPUT_ENCODING = UTF-8
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# read by doxygen.
#
# Note the list of default checked file patterns might differ from the list of
# default file extension mappings.
#
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen
# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
# *.vhdl, *.ucf, *.qsf and *.ice.
# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl,
# *.ucf, *.qsf and *.ice.
FILE_PATTERNS = *.h \
*.hpp \
@@ -1079,16 +1101,22 @@ USE_HTAGS = NO
VERBATIM_HEADERS = YES
# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
# cost of reduced performance. This can be particularly helpful with template
# rich C++ code for which doxygen's built-in parser lacks the necessary type
# information.
# clang parser (see:
# http://clang.llvm.org/) for more accurate parsing at the cost of reduced
# performance. This can be particularly helpful with template rich C++ code for
# which doxygen's built-in parser lacks the necessary type information.
# Note: The availability of this option depends on whether or not doxygen was
# generated with the -Duse_libclang=ON option for CMake.
# The default value is: NO.
CLANG_ASSISTED_PARSING = NO
# If clang assisted parsing is enabled and the CLANG_ADD_INC_PATHS tag is set to
# YES then doxygen will add the directory of each input to the include path.
# The default value is: YES.
CLANG_ADD_INC_PATHS = YES
# If clang assisted parsing is enabled you can provide the compiler with command
# line options that you would normally use when invoking the compiler. Note that
# the include paths will already be set by doxygen for the files and directories
@@ -1102,7 +1130,7 @@ CLANG_OPTIONS =
# file is the compilation database (see:
# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the
# options used when the source files were built. This is equivalent to
# specifying the "-p" option to a clang tool, such as clang-check. These options
# specifying the -p option to a clang tool, such as clang-check. These options
# will then be passed to the parser. Any options specified with CLANG_OPTIONS
# will be added as well.
# Note: The availability of this option depends on whether or not doxygen was
@@ -1121,13 +1149,6 @@ CLANG_DATABASE_PATH =
ALPHABETICAL_INDEX = YES
# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
# which the alphabetical index list will be split.
# Minimum value: 1, maximum value: 20, default value: 5.
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
COLS_IN_ALPHA_INDEX = 5
# In case all classes in a project start with a common prefix, all classes will
# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
# can be used to specify a prefix (or a list of prefixes) that should be ignored
@@ -1298,10 +1319,11 @@ HTML_INDEX_NUM_ENTRIES = 100
# If the GENERATE_DOCSET tag is set to YES, additional index files will be
# generated that can be used as input for Apple's Xcode 3 integrated development
# environment (see: https://developer.apple.com/xcode/), introduced with OSX
# 10.5 (Leopard). To create a documentation set, doxygen will generate a
# Makefile in the HTML output directory. Running make will produce the docset in
# that directory and running make install will install the docset in
# environment (see:
# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To
# create a documentation set, doxygen will generate a Makefile in the HTML
# output directory. Running make will produce the docset in that directory and
# running make install will install the docset in
# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
# genXcode/_index.html for more information.
@@ -1343,8 +1365,8 @@ DOCSET_PUBLISHER_NAME = Publisher
# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on
# Windows.
# (see:
# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows.
#
# The HTML Help Workshop contains a compiler that can convert all HTML output
# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
@@ -1419,7 +1441,8 @@ QCH_FILE =
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
# Project output. For more information please see Qt Help Project / Namespace
# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
# (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_QHP is set to YES.
@@ -1427,8 +1450,8 @@ QHP_NAMESPACE = org.doxygen.Project
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
# Help Project output. For more information please see Qt Help Project / Virtual
# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
# folders).
# Folders (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).
# The default value is: doc.
# This tag requires that the tag GENERATE_QHP is set to YES.
@@ -1436,16 +1459,16 @@ QHP_VIRTUAL_FOLDER = doc
# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
# filter to add. For more information please see Qt Help Project / Custom
# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
# filters).
# Filters (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_NAME =
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
# custom filter to add. For more information please see Qt Help Project / Custom
# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
# filters).
# Filters (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_ATTRS =
@@ -1457,9 +1480,9 @@ QHP_CUST_FILTER_ATTRS =
QHP_SECT_FILTER_ATTRS =
# The QHG_LOCATION tag can be used to specify the location of Qt's
# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
# generated .qhp file.
# The QHG_LOCATION tag can be used to specify the location (absolute path
# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to
# run qhelpgenerator on the generated .qhp file.
# This tag requires that the tag GENERATE_QHP is set to YES.
QHG_LOCATION =
@@ -1586,7 +1609,7 @@ USE_MATHJAX = NO
# When MathJax is enabled you can set the default output format to be used for
# the MathJax output. See the MathJax site (see:
# http://docs.mathjax.org/en/latest/output.html) for more details.
# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details.
# Possible values are: HTML-CSS (which is slower, but has the best
# compatibility), NativeMML (i.e. MathML) and SVG.
# The default value is: HTML-CSS.
@@ -1616,7 +1639,8 @@ MATHJAX_EXTENSIONS =
# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
# of code that will be used on startup of the MathJax code. See the MathJax site
# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
# (see:
# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an
# example see the documentation.
# This tag requires that the tag USE_MATHJAX is set to YES.
@@ -1663,7 +1687,8 @@ SERVER_BASED_SEARCH = NO
#
# Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library
# Xapian (see: https://xapian.org/).
# Xapian (see:
# https://xapian.org/).
#
# See the section "External Indexing and Searching" for details.
# The default value is: NO.
@@ -1676,8 +1701,9 @@ EXTERNAL_SEARCH = NO
#
# Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library
# Xapian (see: https://xapian.org/). See the section "External Indexing and
# Searching" for details.
# Xapian (see:
# https://xapian.org/). See the section "External Indexing and Searching" for
# details.
# This tag requires that the tag SEARCHENGINE is set to YES.
SEARCHENGINE_URL =
@@ -2277,7 +2303,7 @@ HIDE_UNDOC_RELATIONS = YES
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
# Bell Labs. The other options in this section have no effect if this option is
# set to NO
# The default value is: NO.
# The default value is: YES.
HAVE_DOT = YES
@@ -2356,10 +2382,32 @@ UML_LOOK = NO
# but if the number exceeds 15, the total amount of fields shown is limited to
# 10.
# Minimum value: 0, maximum value: 100, default value: 10.
# This tag requires that the tag HAVE_DOT is set to YES.
# This tag requires that the tag UML_LOOK is set to YES.
UML_LIMIT_NUM_FIELDS = 10
# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and
# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS
# tag is set to YES, doxygen will add type and arguments for attributes and
# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen
# will not generate fields with class member information in the UML graphs. The
# class diagrams will look similar to the default class diagrams but using UML
# notation for the relationships.
# Possible values are: NO, YES and NONE.
# The default value is: NO.
# This tag requires that the tag UML_LOOK is set to YES.
DOT_UML_DETAILS = NO
# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
# to display on a single line. If the actual line length exceeds this threshold
# significantly it will wrapped across multiple lines. Some heuristics are apply
# to avoid ugly line breaks.
# Minimum value: 0, maximum value: 1000, default value: 17.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_WRAP_THRESHOLD = 17
# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
# collaboration graphs will show the relations between templates and their
# instances.
@@ -2433,7 +2481,9 @@ DIRECTORY_GRAPH = YES
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
# to make the SVG files visible in IE 9+ (other browsers do not have this
# requirement).
# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo,
# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
# png:gdiplus:gdiplus.
# The default value is: png.
@@ -2549,9 +2599,11 @@ DOT_MULTI_TARGETS = NO
GENERATE_LEGEND = YES
# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate
# files that are used to generate the various graphs.
#
# Note: This setting is not only used for dot files but also for msc and
# plantuml temporary files.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_CLEANUP = YES

View File

@@ -7,10 +7,11 @@
* [Introduction](#introduction)
* [Definitions](#definitions)
* [ENTT_NOEXCEPT](#entt_noexcept)
* [ENTT_NOEXCEPTION](#entt_noexcept)
* [ENTT_USE_ATOMIC](#entt_use_atomic)
* [ENTT_ID_TYPE](#entt_id_type)
* [ENTT_PAGE_SIZE](#entt_page_size)
* [ENTT_SPARSE_PAGE](#entt_sparse_page)
* [ENTT_PACKED_PAGE](#entt_packed_page)
* [ENTT_ASSERT](#entt_assert)
* [ENTT_DISABLE_ASSERT](#entt_disable_assert)
* [ENTT_NO_ETO](#entt_no_eto)
@@ -37,11 +38,11 @@ Each parameter can result in internal library definitions. It's not recommended
to try to also modify these definitions, since there is no guarantee that they
will remain stable over time unlike the options below.
## ENTT_NOEXCEPT
## ENTT_NOEXCEPTION
The purpose of this parameter is to suppress the use of `noexcept` by this
library.<br/>
To do this, simply define the variable without assigning any value to it.
This parameter can be used to switch off exception handling in `EnTT`.<br/>
To do this, simply define the variable without assigning any value to it. This
is roughly equivalent to setting the compiler flag `-ff-noexceptions`.
## ENTT_USE_ATOMIC
@@ -59,14 +60,23 @@ the library.<br/>
By default, its type is `std::uint32_t`. However, users can define a different
default type if necessary.
## ENTT_PAGE_SIZE
## ENTT_SPARSE_PAGE
As is known, the ECS module of `EnTT` is based on _sparse sets_. What is less
known perhaps is that these are paged to reduce memory consumption.<br/>
It's known that the ECS module of `EnTT` is based on _sparse sets_. What is less
known perhaps is that the sparse arrays are paged to reduce memory usage.<br/>
Default size of pages (that is, the number of elements they contain) is 4096 but
users can adjust it if appropriate. In all case, the chosen value **must** be a
power of 2.
## ENTT_PACKED_PAGE
Similar to sparse arrays, packed arrays of components are paginated as well. In
However, int this case the aim isn't to reduce memory usage but to have pointer
stability upon component creation.<br/>
Default size of pages (that is, the number of elements they contain) is 1024 but
users can adjust it if appropriate. In all case, the chosen value **must** be a
power of 2.
## ENTT_ASSERT
For performance reasons, `EnTT` doesn't use exceptions or any other control

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

@@ -0,0 +1,56 @@
# Crash Course: containers
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [Containers](#containers)
* [Dense hash map](#dense-hash-map)
* [Dense hash set](#dense-hash-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 hash map
The dense hash map made available in `EnTT` is a map that aims to return a
packed array of elements, so as to reduce the number of jumps in memory during
the iteration.<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_map`.<br/>
Therefore, there is no need to go into the API description.
## Dense hash set
The dense hash set made available in `EnTT` is a set that aims to return a
packed array of elements, so as to reduce the number of jumps in memory during
the iteration.<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

@@ -17,7 +17,8 @@
* [Small buffer optimization](#small-buffer-optimization)
* [Alignment requirement](#alignment-requirement)
* [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 +28,8 @@
* [Integral constant](#integral-constant)
* [Tag](#tag)
* [Type list and value list](#type-list-and-value-list)
* [Compressed pair](#compressed-pair)
* [Enum as bitmask](#enum-as-bitmask)
* [Utilities](#utilities)
<!--
@endcond TURN_OFF_DOXYGEN
@@ -247,15 +250,44 @@ entt::any any{0};
entt::any in_place{std::in_place_type<int>, 42};
```
The `any` class takes the burden of destroying the contained element when
required, regardless of the storage strategy used for the specific object.<br/>
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/>
the one contained, so as to be able to handle the new instance.
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
entt::any any{42};
entt::any value{3};
// assigns by copy
any.assign(value);
// assigns by move
any.assign(std::move(value));
```
The `any` class will also perform a check on the type information and whether or
not the original type was copy or move assignable, as appropriate.<br/>
In all cases, the `assign` function returns a boolean value to indicate the
success or failure of the operation.
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:
`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) { /* ... */ }
@@ -263,7 +295,7 @@ if(any == empty) { /* ... */ }
In this case, before proceeding with a comparison, it's verified that the _type_
of the two objects is actually the same.<br/>
Refer to the `EnTT` type system documentation for more details on how
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
@@ -272,20 +304,17 @@ an opaque container for const and non-const references:
```cpp
int value = 42;
// reference construction
entt::any any{std::ref(value)};
entt::any cany{std::cref(value)};
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);
// alias construction
int value = 42;
entt::any in_place{std::in_place_type<int &>, &value};
any.emplace<const int &>(value);
```
In other words, whenever `any` intercepts a `reference_wrapper` or is explicitly
told that users want 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/>
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:
@@ -362,33 +391,22 @@ that won't be able to interoperate with each other.
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.
@@ -402,13 +420,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();
}
@@ -421,21 +439,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
@@ -445,18 +455,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();
```
@@ -484,10 +488,71 @@ 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.
A type info object is an opaque class that is also copy and move constructible.
Objects of this class are returned by the `type_id` function template:
```cpp
auto info = entt::type_id<a_type>();
```
These are the information made available by a `type_info` object:
* 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_const_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_const_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_const_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
@@ -628,7 +693,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.
@@ -639,6 +704,86 @@ 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.
# 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.
# 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

@@ -77,45 +77,30 @@ not different techniques depending on how the data are laid out.
I tried to describe some of the techniques that fit well with the model of
`EnTT`. [Here](https://skypjack.github.io/2019-06-25-ecs-baf-part-4/) is the
first post of a series that tries to explore the problem. More will probably
come in future.
Long story short, you can always define a tree where the nodes expose implicit
lists of children by means of the following type:
```cpp
struct relationship {
std::size_t children{};
entt::entity first{entt::null};
entt::entity prev{entt::null};
entt::entity next{entt::null};
entt::entity parent{entt::null};
// ... other data members ...
};
```
The sort functionalities of `EnTT`, the groups and all the other features of the
library can help then to get the best in terms of data locality and therefore
performance from this component.
come in future.<br/>
In addition, `EnTT` also offers the possibility to create stable storage types
and therefore have pointer stability for one, all or some components. This is by
far the most convenient solution when it comes to creating hierarchies and
whatnot. See the documentation for the ECS part of the library and in particular
what concerns the `component_traits` class for further details.
## Custom entity identifiers: yay or nay?
Custom entity identifiers are definitely a good idea in two cases at least:
* If `std::uint32_t` is too large or isn't large enough for your purposes, since
this is the underlying type of `entt::entity`.
* If `std::uint32_t` isn't large enough for your purposes, since this is the
underlying type of `entt::entity`.
* If you want to avoid conflicts when using multiple registries.
Identifiers can be defined through enum classes and custom types for which a
specialization of `entt_traits` exists. For this purpose, `entt_traits` is also
defined as a _sfinae-friendly_ class template.<br/>
Identifiers can be defined through enum classes and class types that define an
`entity_type` member of type `std::uint32_t` or `std::uint64_t`.<br/>
In fact, this is a definition equivalent to that of `entt::entity`:
```cpp
enum class entity: std::uint32_t {};
```
In theory, integral types can also be used as entity identifiers, even though
this may break in future and isn't recommended in general.
There is no limit to the number of identifiers that can be defined.
## Warning C4307: integral constant overflow

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

@@ -23,11 +23,11 @@ I hope this list can grow much more in the future:
* [TiltedOnline](https://github.com/tiltedphoques/TiltedOnline) by
[Tilted Phoques](https://github.com/tiltedphoques): Skyrim and Fallout 4 mod
to play online.
* [Apparently](https://www.youtube.com/watch?v=P8xvOA3ikrQ&t=1105s)
[Call of Duty: Vanguard](https://www.callofduty.com/vanguard): I can neither
confirm nor deny but there is a license I know in the credits.
* [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 +70,16 @@ 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.
* Engines and the like:
* [Aether Engine](https://hadean.com/spatial-simulation/)
@@ -92,6 +100,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
@@ -112,6 +123,23 @@ I hope this list can grow much more in the future:
modular, tiny and fast C++ game engine, aimed to develop 3D desktop games.
* [Spike](https://github.com/FahimFuad/Spike): a powerful game engine which
can run on a toaster.
* [Helena Framework](https://github.com/NIKEA-SOFT/HelenaFramework): a modern
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.
* Articles, videos and blog posts:
* [Some posts](https://skypjack.github.io/tags/#entt) on my personal
@@ -176,6 +204,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

@@ -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
@@ -210,9 +218,10 @@ Among the few relevant differences, `meta_any` adds support for containers and
pointer-like types (see the following sections for more details), while `any`
does not.<br/>
Similar to `any`, this class can also be used to create _aliases_ for unmanaged
objects either upon construction using `std::ref` and `std::cref` or from an
existing instance by means of the `as_ref` function. However, unlike `any`,
`meta_any` treats an empty instance and one initialized with `void` differently:
objects either with `forward_as_meta` or using the `std::in_place_type<T &>`
disambiguation tag, as well as from an existing object by means of the `as_ref`
member function. However, unlike `any`, `meta_any` treats an empty instance and
one initialized with `void` differently:
```cpp
entt::meta_any empty{};
@@ -266,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
@@ -327,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()) {
// ...
}
```
@@ -386,7 +382,7 @@ object for a sequence container:
```cpp
std::vector<int> vec{1, 2, 3};
entt::meta_any any{std::ref(vec)};
entt::meta_any any = entt::forward_as_meta(vec);
if(any.type().is_sequence_container()) {
if(auto view = any.as_sequence_container(); view) {
@@ -714,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
@@ -725,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();
@@ -741,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
@@ -770,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.
@@ -784,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/>
@@ -880,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
@@ -941,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

@@ -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); }
};
// ...
@@ -312,13 +312,13 @@ instance->draw();
The `poly` class template offers a wide range of constructors, from the default
one (which will return an uninitialized `poly` object) to the copy and move
constructor, as well as the ability to create objects in-place.<br/>
Among others, there is a constructor that allows users to wrap unmanaged objects
in a `poly` instance (either const or non-const ones):
constructors, as well as the ability to create objects in-place.<br/>
Among others, there is also a constructor that allows users to wrap unmanaged
objects in a `poly` instance (either const or non-const ones):
```cpp
circle shape;
drawable instance{std::ref(shape)};
drawable instance{std::in_place_type<circle &>, shape};
```
Similarly, it's possible to create non-owning copies of `poly` from an existing

View File

@@ -28,9 +28,9 @@ A typical process must inherit from the `process` class template that stays true
to the CRTP idiom. Moreover, derived classes must specify what's the intended
type for elapsed times.
A process should expose publicly the following member functions whether
required (note that it isn't required to define a function unless the derived
class wants to _override_ the default behavior):
A process should expose publicly the following member functions whether needed
(note that it isn't required to define a function unless the derived class wants
to _override_ the default behavior):
* `void update(Delta, void *);`

View File

@@ -44,11 +44,19 @@ 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

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_hash_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_hash_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].element</ValueNode>
</IndexListItems>
</Expand>
</Type>
</AutoVisualizer>

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

@@ -0,0 +1,31 @@
<?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="str != nullptr">{{ hash={ hash } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[data]">str,na</Item>
</Expand>
</Type>
<Type Name="entt::type_info">
<DisplayString>{{ name={ alias,na } }}</DisplayString>
<Expand>
<Item Name="[hash]">identifier</Item>
<Item Name="[index]">seq</Item>
</Expand>
</Type>
</AutoVisualizer>

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

@@ -0,0 +1,119 @@
<?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.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.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>
<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>
</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,3 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
</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]">data</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::dispatcher::pool_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,62 +1,69 @@
#ifndef ENTT_CONFIG_CONFIG_H
#define ENTT_CONFIG_CONFIG_H
#ifndef ENTT_NOEXCEPT
# define ENTT_NOEXCEPT noexcept
#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
# define ENTT_NOEXCEPT noexcept
# 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)
#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
#endif
#ifndef ENTT_USE_ATOMIC
# define ENTT_MAYBE_ATOMIC(Type) Type
# define ENTT_MAYBE_ATOMIC(Type) Type
#else
# include <atomic>
# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
# include <atomic>
# define ENTT_MAYBE_ATOMIC(Type) std::atomic<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_PAGE_SIZE
static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0), "ENTT_PAGE_SIZE must be a power of two");
#else
# define ENTT_PAGE_SIZE 4096
#ifndef ENTT_SPARSE_PAGE
# define ENTT_SPARSE_PAGE 4096
#endif
#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
#ifndef ENTT_NO_ETO
# include <type_traits>
# define ENTT_IS_EMPTY(Type) std::is_empty<Type>
#ifdef ENTT_NO_ETO
# define ENTT_IGNORE_IF_EMPTY false
#else
# include <type_traits>
# define ENTT_IS_EMPTY(Type) std::false_type
# define ENTT_IGNORE_IF_EMPTY true
#endif
#ifndef ENTT_STANDARD_CPP
# 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
#endif

View File

@@ -1,10 +1,8 @@
#ifndef ENTT_CONFIG_VERSION_H
#define ENTT_CONFIG_VERSION_H
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 7
#define ENTT_VERSION_PATCH 1
#define ENTT_VERSION_MINOR 9
#define ENTT_VERSION_PATCH 0
#endif

View File

@@ -0,0 +1,959 @@
#ifndef ENTT_CONTAINER_DENSE_HASH_MAP_HPP
#define ENTT_CONTAINER_DENSE_HASH_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/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_hash_map_node final {
template<typename... Args>
dense_hash_map_node(const std::size_t pos, Args &&...args)
: next{pos},
element{std::forward<Args>(args)...} {}
std::size_t next;
std::pair<Key, Type> element;
};
template<typename It>
class dense_hash_map_iterator {
friend dense_hash_map_iterator<const std::remove_pointer_t<It> *>;
using iterator_traits = std::iterator_traits<decltype(std::addressof(std::declval<It>()->element))>;
public:
using value_type = typename iterator_traits::value_type;
using pointer = typename iterator_traits::pointer;
using reference = typename iterator_traits::reference;
using difference_type = typename iterator_traits::difference_type;
using iterator_category = std::random_access_iterator_tag;
dense_hash_map_iterator() ENTT_NOEXCEPT = default;
dense_hash_map_iterator(const It iter) ENTT_NOEXCEPT
: it{iter} {}
template<bool Const = std::is_const_v<std::remove_pointer_t<It>>, typename = std::enable_if_t<Const>>
dense_hash_map_iterator(const dense_hash_map_iterator<std::remove_const_t<std::remove_pointer_t<It>> *> &other)
: it{other.it} {}
dense_hash_map_iterator &operator++() ENTT_NOEXCEPT {
return ++it, *this;
}
dense_hash_map_iterator operator++(int) ENTT_NOEXCEPT {
dense_hash_map_iterator orig = *this;
return ++(*this), orig;
}
dense_hash_map_iterator &operator--() ENTT_NOEXCEPT {
return --it, *this;
}
dense_hash_map_iterator operator--(int) ENTT_NOEXCEPT {
dense_hash_map_iterator orig = *this;
return operator--(), orig;
}
dense_hash_map_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
it += value;
return *this;
}
dense_hash_map_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
dense_hash_map_iterator copy = *this;
return (copy += value);
}
dense_hash_map_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
return (*this += -value);
}
dense_hash_map_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
return (*this + -value);
}
[[nodiscard]] reference operator[](const difference_type value) const {
return it[value].element;
}
[[nodiscard]] pointer operator->() const {
return std::addressof(it->element);
}
[[nodiscard]] reference operator*() const {
return *operator->();
}
template<typename ILhs, typename IRhs>
friend auto operator-(const dense_hash_map_iterator<ILhs> &, const dense_hash_map_iterator<IRhs> &) ENTT_NOEXCEPT;
template<typename ILhs, typename IRhs>
friend bool operator==(const dense_hash_map_iterator<ILhs> &, const dense_hash_map_iterator<IRhs> &) ENTT_NOEXCEPT;
template<typename ILhs, typename IRhs>
friend bool operator<(const dense_hash_map_iterator<ILhs> &, const dense_hash_map_iterator<IRhs> &) ENTT_NOEXCEPT;
private:
It it;
};
template<typename ILhs, typename IRhs>
[[nodiscard]] auto operator-(const dense_hash_map_iterator<ILhs> &lhs, const dense_hash_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return lhs.it - rhs.it;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator==(const dense_hash_map_iterator<ILhs> &lhs, const dense_hash_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return lhs.it == rhs.it;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator!=(const dense_hash_map_iterator<ILhs> &lhs, const dense_hash_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return !(lhs == rhs);
}
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator<(const dense_hash_map_iterator<ILhs> &lhs, const dense_hash_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return lhs.it < rhs.it;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator>(const dense_hash_map_iterator<ILhs> &lhs, const dense_hash_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return rhs < lhs;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator<=(const dense_hash_map_iterator<ILhs> &lhs, const dense_hash_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return !(lhs > rhs);
}
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator>=(const dense_hash_map_iterator<ILhs> &lhs, const dense_hash_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return !(lhs < rhs);
}
template<typename It>
class dense_hash_map_local_iterator {
friend dense_hash_map_local_iterator<const std::remove_pointer_t<It> *>;
using iterator_traits = std::iterator_traits<decltype(std::addressof(std::declval<It>()->element))>;
public:
using value_type = typename iterator_traits::value_type;
using pointer = typename iterator_traits::pointer;
using reference = typename iterator_traits::reference;
using difference_type = typename iterator_traits::difference_type;
using iterator_category = std::forward_iterator_tag;
dense_hash_map_local_iterator() ENTT_NOEXCEPT = default;
dense_hash_map_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT
: it{iter},
offset{pos} {}
template<bool Const = std::is_const_v<std::remove_pointer_t<It>>, typename = std::enable_if_t<Const>>
dense_hash_map_local_iterator(const dense_hash_map_local_iterator<std::remove_const_t<std::remove_pointer_t<It>> *> &other)
: it{other.it},
offset{other.offset} {}
dense_hash_map_local_iterator &operator++() ENTT_NOEXCEPT {
return offset = it[offset].next, *this;
}
dense_hash_map_local_iterator operator++(int) ENTT_NOEXCEPT {
dense_hash_map_local_iterator orig = *this;
return ++(*this), orig;
}
[[nodiscard]] pointer operator->() const {
return std::addressof(it[offset].element);
}
[[nodiscard]] reference operator*() const {
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_hash_map_local_iterator<ILhs> &lhs, const dense_hash_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return lhs.index() == rhs.index();
}
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator!=(const dense_hash_map_local_iterator<ILhs> &lhs, const dense_hash_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_hash_map final {
static constexpr float default_threshold = 0.875f;
static constexpr std::size_t minimum_capacity = 8u;
using allocator_traits = std::allocator_traits<Allocator>;
using alloc = typename allocator_traits::template rebind_alloc<std::pair<const Key, Type>>;
using alloc_traits = typename std::allocator_traits<alloc>;
using node_type = internal::dense_hash_map_node<const Key, 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>>;
[[nodiscard]] std::size_t hash_to_bucket(const std::size_t hash) const ENTT_NOEXCEPT {
return fast_mod(hash, 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() + 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() + it.index();
}
}
return cend();
}
template<typename... Args>
[[nodiscard]] auto get_or_emplace(const Key &key, Args &&...args) {
const auto hash = sparse.second()(key);
auto index = hash_to_bucket(hash);
if(auto it = constrained_find(key, index); it != end()) {
return std::make_pair(it, false);
}
if(const auto count = size() + 1u; count > (bucket_count() * max_load_factor())) {
rehash(bucket_count() * 2u);
index = hash_to_bucket(hash);
}
packed.first().emplace_back(sparse.first()[index], std::forward<Args>(args)...);
// update goes after emplace to enforce exception guarantees
sparse.first()[index] = size() - 1u;
return std::make_pair(--end(), true);
}
void move_and_pop(const std::size_t pos) {
if(const auto last = size() - 1u; pos != last) {
size_type *curr = sparse.first().data() + bucket(packed.first().back().element.first);
for(; *curr != last; curr = &packed.first()[*curr].next) {}
*curr = pos;
using node_allocator_traits = typename alloc_traits::template rebind_traits<decltype(node_type::element)>;
typename node_allocator_traits::allocator_type allocator = packed.first().get_allocator();
auto *ptr = std::addressof(packed.first()[pos].element);
std::destroy_at(ptr);
packed.first()[pos].next = packed.first().back().next;
// no exception guarantees when mapped type has a throwing move constructor (we're technically doomed)
node_allocator_traits::construct(allocator, ptr, std::move(packed.first().back().element));
}
packed.first().pop_back();
}
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 Random access iterator type. */
using iterator = internal::dense_hash_map_iterator<typename packed_container_type::pointer>;
/*! @brief Constant random access iterator type. */
using const_iterator = internal::dense_hash_map_iterator<typename packed_container_type::const_pointer>;
/*! @brief Forward iterator type. */
using local_iterator = internal::dense_hash_map_local_iterator<typename packed_container_type::pointer>;
/*! @brief Constant forward iterator type. */
using const_local_iterator = internal::dense_hash_map_local_iterator<typename packed_container_type::const_pointer>;
/*! @brief Default constructor. */
dense_hash_map()
: dense_hash_map(minimum_capacity) {}
/**
* @brief Constructs an empty container with a given allocator.
* @param allocator The allocator to use.
*/
explicit dense_hash_map(const allocator_type &allocator)
: dense_hash_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_hash_map(const size_type bucket_count, const allocator_type &allocator)
: dense_hash_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_hash_map(const size_type bucket_count, const hasher &hash, const allocator_type &allocator)
: dense_hash_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_hash_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 Copy constructor.
* @param other The instance to copy from.
*/
dense_hash_map(const dense_hash_map &other)
: dense_hash_map{other, alloc_traits::select_on_container_copy_construction(other.get_allocator())} {}
/**
* @brief Allocator-extended copy constructor.
* @param other The instance to copy from.
* @param allocator The allocator to use.
*/
dense_hash_map(const dense_hash_map &other, const allocator_type &allocator)
: sparse{sparse_container_type{other.sparse.first(), allocator}, other.sparse.second()},
// cannot copy the container directly due to a nasty issue of apple clang :(
packed{packed_container_type{other.packed.first().begin(), other.packed.first().end(), allocator}, other.packed.second()},
threshold{other.threshold} {
}
/**
* @brief Default move constructor.
* @param other The instance to move from.
*/
dense_hash_map(dense_hash_map &&other) ENTT_NOEXCEPT = default;
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
dense_hash_map(dense_hash_map &&other, const allocator_type &allocator) ENTT_NOEXCEPT
: sparse{sparse_container_type{std::move(other.sparse.first()), allocator}, std::move(other.sparse.second())},
// cannot move the container directly due to a nasty issue of apple clang :(
packed{packed_container_type{std::make_move_iterator(other.packed.first().begin()), std::make_move_iterator(other.packed.first().end()), allocator}, std::move(other.packed.second())},
threshold{other.threshold} {}
/*! @brief Default destructor. */
~dense_hash_map() = default;
/**
* @brief Copy assignment operator.
* @param other The instance to copy from.
* @return This container.
*/
dense_hash_map &operator=(const dense_hash_map &other) {
threshold = other.threshold;
sparse.first().clear();
packed.first().clear();
rehash(other.bucket_count());
packed.first().reserve(other.packed.first().size());
insert(other.cbegin(), other.cend());
return *this;
}
/**
* @brief Default move assignment operator.
* @param other The instance to move from.
* @return This container.
*/
dense_hash_map &operator=(dense_hash_map &&other) ENTT_NOEXCEPT = 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().data();
}
/*! @copydoc cbegin */
[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
return cbegin();
}
/*! @copydoc begin */
[[nodiscard]] iterator begin() ENTT_NOEXCEPT {
return packed.first().data();
}
/**
* @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().data() + size();
}
/*! @copydoc cend */
[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
return cend();
}
/*! @copydoc end */
[[nodiscard]] iterator end() ENTT_NOEXCEPT {
return packed.first().data() + size();
}
/**
* @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 emplace(value);
}
/*! @copydoc insert */
std::pair<iterator, bool> insert(value_type &&value) {
return emplace(std::move(value));
}
/**
* @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 emplace(std::forward<Arg>(value));
}
/**
* @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) {
emplace(*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) {
auto result = try_emplace(key, std::forward<Arg>(value));
if(!result.second) {
result.first->second = std::forward<Arg>(value);
}
return result;
}
/*! @copydoc insert_or_assign */
template<typename Arg>
std::pair<iterator, bool> insert_or_assign(key_type &&key, Arg &&value) {
auto result = try_emplace(std::move(key), std::forward<Arg>(value));
if(!result.second) {
result.first->second = std::forward<Arg>(value);
}
return result;
}
/**
* @brief Constructs an element in-place, if the key does not exist.
* @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) == 0u) {
return get_or_emplace(key_type{});
} else if constexpr(sizeof...(Args) == 1u) {
return get_or_emplace(args.first..., std::forward<Args>(args)...);
} else if constexpr(sizeof...(Args) == 2u) {
return get_or_emplace(std::get<0u>(std::tie(args...)), std::forward<Args>(args)...);
} else {
static_assert(sizeof...(Args) == 3u, "Invalid arguments");
return emplace(std::pair<key_type, mapped_type>{std::forward<Args>(args)...});
}
}
/**
* @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 get_or_emplace(key, std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple(std::forward<Args>(args)...));
}
/*! @copydoc try_emplace */
template<typename... Args>
std::pair<iterator, bool> try_emplace(key_type &&key, Args &&...args) {
return get_or_emplace(key, std::piecewise_construct, std::forward_as_tuple(std::move(key)), std::forward_as_tuple(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 dist = std::distance(cbegin(), pos);
erase(pos->first);
return begin() + dist;
}
/**
* @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 = std::distance(cbegin(), first);
for(auto rfirst = std::make_reverse_iterator(last), rlast = std::make_reverse_iterator(first); rfirst != rlast; ++rfirst) {
erase(rfirst->first);
}
return dist > static_cast<decltype(dist)>(size()) ? end() : (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() + 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 true;
}
}
return false;
}
/**
* @brief Exchanges the contents with those of a given container.
* @param other Container to exchange the content with.
*/
void swap(dense_hash_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]] Type &at(const key_type &key) {
auto it = find(key);
ENTT_ASSERT(it != end(), "Invalid key");
return it->second;
}
/*! @copydoc at */
[[nodiscard]] const 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]] Type &operator[](const key_type &key) {
return try_emplace(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]] Type &operator[](key_type &&key) {
return try_emplace(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, bucket(key));
}
/*! @copydoc find */
[[nodiscard]] const_iterator find(const key_type &key) const {
return constrained_find(key, 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, 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, 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().data(), 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().data(), 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().data(), 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().data(), 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 hash_to_bucket(sparse.second()(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 = 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
#endif

View File

@@ -0,0 +1,849 @@
#ifndef ENTT_CONTAINER_DENSE_HASH_SET_HPP
#define ENTT_CONTAINER_DENSE_HASH_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 Type>
struct dense_hash_set_node final {
template<typename... Args>
dense_hash_set_node(const std::size_t pos, Args &&...args)
: next{pos},
element{std::forward<Args>(args)...} {}
std::size_t next;
Type element;
};
template<typename It>
class dense_hash_set_iterator {
friend dense_hash_set_iterator<const std::remove_pointer_t<It> *>;
using iterator_traits = std::iterator_traits<decltype(std::addressof(std::as_const(std::declval<It>()->element)))>;
public:
using value_type = typename iterator_traits::value_type;
using pointer = typename iterator_traits::pointer;
using reference = typename iterator_traits::reference;
using difference_type = typename iterator_traits::difference_type;
using iterator_category = std::random_access_iterator_tag;
dense_hash_set_iterator() ENTT_NOEXCEPT = default;
dense_hash_set_iterator(const It iter) ENTT_NOEXCEPT
: it{iter} {}
template<bool Const = std::is_const_v<std::remove_pointer_t<It>>, typename = std::enable_if_t<Const>>
dense_hash_set_iterator(const dense_hash_set_iterator<std::remove_const_t<std::remove_pointer_t<It>> *> &other)
: it{other.it} {}
dense_hash_set_iterator &operator++() ENTT_NOEXCEPT {
return ++it, *this;
}
dense_hash_set_iterator operator++(int) ENTT_NOEXCEPT {
dense_hash_set_iterator orig = *this;
return ++(*this), orig;
}
dense_hash_set_iterator &operator--() ENTT_NOEXCEPT {
return --it, *this;
}
dense_hash_set_iterator operator--(int) ENTT_NOEXCEPT {
dense_hash_set_iterator orig = *this;
return operator--(), orig;
}
dense_hash_set_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
it += value;
return *this;
}
dense_hash_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
dense_hash_set_iterator copy = *this;
return (copy += value);
}
dense_hash_set_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
return (*this += -value);
}
dense_hash_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
return (*this + -value);
}
[[nodiscard]] reference operator[](const difference_type value) const {
return it[value].element;
}
[[nodiscard]] pointer operator->() const {
return std::addressof(it->element);
}
[[nodiscard]] reference operator*() const {
return *operator->();
}
template<typename ILhs, typename IRhs>
friend auto operator-(const dense_hash_set_iterator<ILhs> &, const dense_hash_set_iterator<IRhs> &) ENTT_NOEXCEPT;
template<typename ILhs, typename IRhs>
friend bool operator==(const dense_hash_set_iterator<ILhs> &, const dense_hash_set_iterator<IRhs> &) ENTT_NOEXCEPT;
template<typename ILhs, typename IRhs>
friend bool operator<(const dense_hash_set_iterator<ILhs> &, const dense_hash_set_iterator<IRhs> &) ENTT_NOEXCEPT;
private:
It it;
};
template<typename ILhs, typename IRhs>
[[nodiscard]] auto operator-(const dense_hash_set_iterator<ILhs> &lhs, const dense_hash_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return lhs.it - rhs.it;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator==(const dense_hash_set_iterator<ILhs> &lhs, const dense_hash_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return lhs.it == rhs.it;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator!=(const dense_hash_set_iterator<ILhs> &lhs, const dense_hash_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return !(lhs == rhs);
}
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator<(const dense_hash_set_iterator<ILhs> &lhs, const dense_hash_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return lhs.it < rhs.it;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator>(const dense_hash_set_iterator<ILhs> &lhs, const dense_hash_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return rhs < lhs;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator<=(const dense_hash_set_iterator<ILhs> &lhs, const dense_hash_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return !(lhs > rhs);
}
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator>=(const dense_hash_set_iterator<ILhs> &lhs, const dense_hash_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return !(lhs < rhs);
}
template<typename It>
class dense_hash_set_local_iterator {
friend dense_hash_set_local_iterator<const std::remove_pointer_t<It> *>;
using iterator_traits = std::iterator_traits<decltype(std::addressof(std::as_const(std::declval<It>()->element)))>;
public:
using value_type = typename iterator_traits::value_type;
using pointer = typename iterator_traits::pointer;
using reference = typename iterator_traits::reference;
using difference_type = typename iterator_traits::difference_type;
using iterator_category = std::forward_iterator_tag;
dense_hash_set_local_iterator() ENTT_NOEXCEPT = default;
dense_hash_set_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT
: it{iter},
offset{pos} {}
template<bool Const = std::is_const_v<std::remove_pointer_t<It>>, typename = std::enable_if_t<Const>>
dense_hash_set_local_iterator(const dense_hash_set_local_iterator<std::remove_const_t<std::remove_pointer_t<It>> *> &other)
: it{other.it},
offset{other.offset} {}
dense_hash_set_local_iterator &operator++() ENTT_NOEXCEPT {
return offset = it[offset].next, *this;
}
dense_hash_set_local_iterator operator++(int) ENTT_NOEXCEPT {
dense_hash_set_local_iterator orig = *this;
return ++(*this), orig;
}
[[nodiscard]] pointer operator->() const {
return std::addressof(it[offset].element);
}
[[nodiscard]] reference operator*() const {
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_hash_set_local_iterator<ILhs> &lhs, const dense_hash_set_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
return lhs.index() == rhs.index();
}
template<typename ILhs, typename IRhs>
[[nodiscard]] bool operator!=(const dense_hash_set_local_iterator<ILhs> &lhs, const dense_hash_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_hash_set final {
static constexpr float default_threshold = 0.875f;
static constexpr std::size_t minimum_capacity = 8u;
using allocator_traits = std::allocator_traits<Allocator>;
using alloc = typename allocator_traits::template rebind_alloc<Type>;
using alloc_traits = typename std::allocator_traits<alloc>;
using node_type = internal::dense_hash_set_node<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>>;
[[nodiscard]] std::size_t hash_to_bucket(const std::size_t hash) const ENTT_NOEXCEPT {
return fast_mod(hash, 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() + 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() + it.index();
}
}
return cend();
}
template<typename Arg>
[[nodiscard]] auto get_or_emplace(Arg &&arg) {
const auto hash = sparse.second()(arg);
auto index = hash_to_bucket(hash);
if(auto it = constrained_find(arg, index); it != end()) {
return std::make_pair(it, false);
}
if(const auto count = size() + 1u; count > (bucket_count() * max_load_factor())) {
rehash(bucket_count() * 2u);
index = hash_to_bucket(hash);
}
packed.first().emplace_back(sparse.first()[index], std::forward<Arg>(arg));
// update goes after emplace to enforce exception guarantees
sparse.first()[index] = size() - 1u;
return std::make_pair(--end(), true);
}
template<typename Other>
bool do_erase(const Other &value) {
for(size_type *curr = sparse.first().data() + bucket(value); *curr != std::numeric_limits<size_type>::max(); curr = &packed.first()[*curr].next) {
if(packed.second()(packed.first()[*curr].element, value)) {
const auto index = *curr;
*curr = packed.first()[*curr].next;
move_and_pop(index);
return true;
}
}
return false;
}
void move_and_pop(const std::size_t pos) {
if(const auto last = size() - 1u; pos != last) {
size_type *curr = sparse.first().data() + bucket(packed.first().back().element);
for(; *curr != last; curr = &packed.first()[*curr].next) {}
*curr = pos;
// basic exception guarantees when value type has a throwing move constructor
packed.first()[pos] = std::move(packed.first().back());
}
packed.first().pop_back();
}
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_hash_set_iterator<typename packed_container_type::pointer>;
/*! @brief Constant random access iterator type. */
using const_iterator = internal::dense_hash_set_iterator<typename packed_container_type::const_pointer>;
/*! @brief Forward iterator type. */
using local_iterator = internal::dense_hash_set_local_iterator<typename packed_container_type::pointer>;
/*! @brief Constant forward iterator type. */
using const_local_iterator = internal::dense_hash_set_local_iterator<typename packed_container_type::const_pointer>;
/*! @brief Default constructor. */
dense_hash_set()
: dense_hash_set(minimum_capacity) {}
/**
* @brief Constructs an empty container with a given allocator.
* @param allocator The allocator to use.
*/
explicit dense_hash_set(const allocator_type &allocator)
: dense_hash_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_hash_set(const size_type bucket_count, const allocator_type &allocator)
: dense_hash_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_hash_set(const size_type bucket_count, const hasher &hash, const allocator_type &allocator)
: dense_hash_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_hash_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 Copy constructor.
* @param other The instance to copy from.
*/
dense_hash_set(const dense_hash_set &other)
: dense_hash_set{other, alloc_traits::select_on_container_copy_construction(other.get_allocator())} {}
/**
* @brief Allocator-extended copy constructor.
* @param other The instance to copy from.
* @param allocator The allocator to use.
*/
dense_hash_set(const dense_hash_set &other, const allocator_type &allocator)
: sparse{sparse_container_type{other.sparse.first(), allocator}, other.sparse.second()},
// cannot copy the container directly due to a nasty issue of apple clang :(
packed{packed_container_type{other.packed.first().begin(), other.packed.first().end(), allocator}, other.packed.second()},
threshold{other.threshold} {
}
/**
* @brief Default move constructor.
* @param other The instance to move from.
*/
dense_hash_set(dense_hash_set &&other) ENTT_NOEXCEPT = default;
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
dense_hash_set(dense_hash_set &&other, const allocator_type &allocator) ENTT_NOEXCEPT
: sparse{sparse_container_type{std::move(other.sparse.first()), allocator}, std::move(other.sparse.second())},
// cannot move the container directly due to a nasty issue of apple clang :(
packed{packed_container_type{std::make_move_iterator(other.packed.first().begin()), std::make_move_iterator(other.packed.first().end()), allocator}, std::move(other.packed.second())},
threshold{other.threshold} {}
/*! @brief Default destructor. */
~dense_hash_set() = default;
/**
* @brief Copy assignment operator.
* @param other The instance to copy from.
* @return This container.
*/
dense_hash_set &operator=(const dense_hash_set &other) {
threshold = other.threshold;
sparse.first().clear();
packed.first().clear();
rehash(other.bucket_count());
packed.first().reserve(other.packed.first().size());
insert(other.cbegin(), other.cend());
return *this;
}
/**
* @brief Default move assignment operator.
* @param other The instance to move from.
* @return This container.
*/
dense_hash_set &operator=(dense_hash_set &&other) ENTT_NOEXCEPT = 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().data();
}
/*! @copydoc cbegin */
[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
return cbegin();
}
/*! @copydoc begin */
[[nodiscard]] iterator begin() ENTT_NOEXCEPT {
return packed.first().data();
}
/**
* @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().data() + size();
}
/*! @copydoc cend */
[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
return cend();
}
/*! @copydoc end */
[[nodiscard]] iterator end() ENTT_NOEXCEPT {
return packed.first().data() + size();
}
/**
* @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 emplace(value);
}
/*! @copydoc insert */
std::pair<iterator, bool> insert(value_type &&value) {
return emplace(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) {
emplace(*first);
}
}
/**
* @brief Constructs an element in-place, if it does not exist.
* @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_const_t<std::remove_reference_t<Args>>, value_type>)) {
return get_or_emplace(std::forward<Args>(args)...);
} else {
return get_or_emplace(value_type{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 dist = std::distance(cbegin(), pos);
erase(*pos);
return begin() + dist;
}
/**
* @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 = std::distance(cbegin(), first);
for(auto rfirst = std::make_reverse_iterator(last), rlast = std::make_reverse_iterator(first); rfirst != rlast; ++rfirst) {
erase(*rfirst);
}
return dist > static_cast<decltype(dist)>(size()) ? end() : (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) {
return do_erase(value);
}
/**
* @brief Exchanges the contents with those of a given container.
* @param other Container to exchange the content with.
*/
void swap(dense_hash_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, bucket(value));
}
/*! @copydoc find */
[[nodiscard]] const_iterator find(const value_type &value) const {
return constrained_find(value, 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, 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, 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().data(), 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().data(), 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().data(), 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().data(), 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 hash_to_bucket(sparse.second()(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 = bucket(packed.first()[pos].element);
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 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,25 @@
#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_hash_map;
template<
typename Type,
typename = std::hash<Type>,
typename = std::equal_to<Type>,
typename = std::allocator<Type>>
class dense_hash_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,22 +1,18 @@
#ifndef ENTT_CORE_ANY_HPP
#define ENTT_CORE_ANY_HPP
#include <cstddef>
#include <functional>
#include <memory>
#include <new>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/utility.hpp"
#include "fwd.hpp"
#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,7 +20,21 @@ namespace entt {
*/
template<std::size_t Len, std::size_t Align>
class basic_any {
enum class operation { COPY, MOVE, DTOR, COMP, ADDR, CADDR, REF, CREF, TYPE };
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 &, const void *);
@@ -33,158 +43,82 @@ class basic_any {
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 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);
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_reference_t<std::remove_const_t<Type>>, Type>, "Invalid type");
const Type *instance = nullptr;
if constexpr(in_situ<Type>) {
instance = (value.mode == policy::owner) ? ENTT_LAUNDER(reinterpret_cast<const Type *>(&value.storage)) : static_cast<const Type *>(value.instance);
} else {
return lhs == rhs;
instance = static_cast<const Type *>(value.instance);
}
}
template<typename Type>
static Type & as(const void *to) {
return *const_cast<Type *>(static_cast<const Type *>(to));
}
template<typename Type>
static const void * basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &from, [[maybe_unused]] const void *to) {
if constexpr(std::is_void_v<Type>) {
switch(op) {
case operation::COPY:
case operation::MOVE:
case operation::REF:
case operation::CREF:
as<basic_any>(to).vtable = from.vtable;
break;
default:
break;
switch(op) {
case operation::copy:
if constexpr(std::is_copy_constructible_v<Type>) {
static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*instance);
}
} else if constexpr(std::is_lvalue_reference_v<Type>) {
using base_type = std::decay_t<Type>;
switch(op) {
case operation::COPY:
if constexpr(std::is_copy_constructible_v<base_type>) {
as<basic_any>(to) = *static_cast<const base_type *>(from.instance);
break;
case operation::move:
if constexpr(in_situ<Type>) {
if(value.mode == policy::owner) {
return new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(instance))};
}
break;
case operation::MOVE:
as<basic_any>(to).instance = from.instance;
as<basic_any>(to).vtable = from.vtable;
[[fallthrough]];
case operation::DTOR:
break;
case operation::COMP:
return compare<base_type>(from.instance, to) ? to : nullptr;
case operation::ADDR:
return std::is_const_v<std::remove_reference_t<Type>> ? nullptr : from.instance;
case operation::CADDR:
return from.instance;
case operation::REF:
as<basic_any>(to).instance = from.instance;
as<basic_any>(to).vtable = basic_vtable<Type>;
break;
case operation::CREF:
as<basic_any>(to).instance = from.instance;
as<basic_any>(to).vtable = basic_vtable<const base_type &>;
break;
case operation::TYPE:
as<type_info>(to) = type_id<base_type>();
break;
}
} else if constexpr(in_situ<Type>) {
#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L
auto *instance = const_cast<Type *>(std::launder(reinterpret_cast<const Type *>(&from.storage)));
#else
auto *instance = const_cast<Type *>(reinterpret_cast<const Type *>(&from.storage));
#endif
switch(op) {
case operation::COPY:
if constexpr(std::is_copy_constructible_v<Type>) {
new (&as<basic_any>(to).storage) Type{std::as_const(*instance)};
as<basic_any>(to).vtable = from.vtable;
}
break;
case operation::MOVE:
new (&as<basic_any>(to).storage) Type{std::move(*instance)};
as<basic_any>(to).vtable = from.vtable;
break;
case operation::DTOR:
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 *>(instance) = 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 *>(instance) = *static_cast<const Type *>(other);
return other;
}
break;
case operation::destroy:
if constexpr(in_situ<Type>) {
instance->~Type();
break;
case operation::COMP:
return compare<Type>(instance, to) ? to : nullptr;
case operation::ADDR:
case operation::CADDR:
return instance;
case operation::REF:
as<basic_any>(to).instance = instance;
as<basic_any>(to).vtable = basic_vtable<Type &>;
break;
case operation::CREF:
as<basic_any>(to).instance = instance;
as<basic_any>(to).vtable = basic_vtable<const Type &>;
break;
case operation::TYPE:
as<type_info>(to) = type_id<Type>();
break;
} else if constexpr(std::is_array_v<Type>) {
delete[] instance;
} else {
delete instance;
}
} else {
switch(op) {
case operation::COPY:
if constexpr(std::is_copy_constructible_v<Type>) {
as<basic_any>(to).instance = new Type{*static_cast<const Type *>(from.instance)};
as<basic_any>(to).vtable = from.vtable;
}
break;
case operation::MOVE:
as<basic_any>(to).instance = std::exchange(as<basic_any>(&from).instance, nullptr);
as<basic_any>(to).vtable = from.vtable;
break;
case operation::DTOR:
if constexpr(std::is_array_v<Type>) {
delete[] static_cast<const Type *>(from.instance);
} else {
delete static_cast<const Type *>(from.instance);
}
break;
case operation::COMP:
return compare<Type>(from.instance, to) ? to : nullptr;
case operation::ADDR:
case operation::CADDR:
return from.instance;
case operation::REF:
as<basic_any>(to).instance = from.instance;
as<basic_any>(to).vtable = basic_vtable<Type &>;
break;
case operation::CREF:
as<basic_any>(to).instance = from.instance;
as<basic_any>(to).vtable = basic_vtable<const Type &>;
break;
case operation::TYPE:
as<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 *>(instance) == *static_cast<const Type *>(other) ? other : nullptr;
} else {
return (instance == other) ? other : nullptr;
}
case operation::get:
return instance;
}
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>) {
vtable = basic_vtable<std::remove_const_t<std::remove_reference_t<Type>>>;
info = &type_id<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(std::is_aggregate_v<Type>) {
new (&storage) Type{std::forward<Args>(args)...};
if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) {
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(std::is_aggregate_v<Type>) {
if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) {
instance = new Type{std::forward<Args>(args)...};
} else {
instance = new Type(std::forward<Args>(args)...);
@@ -193,54 +127,57 @@ 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} {}
public:
/*! @brief Size of the internal storage. */
static constexpr auto length = Len;
/*! @brief Alignment requirement. */
static constexpr auto alignment = Align;
/*! @brief Default constructor. */
basic_any() ENTT_NOEXCEPT
: basic_any{std::in_place_type<void>}
{}
: instance{},
info{&type_id<void>()},
vtable{},
mode{policy::owner} {}
/**
* @brief Constructs an any by directly initializing the new object.
* @brief Constructs a wrapper by directly initializing the new object.
* @tparam Type Type of object to use to initialize the wrapper.
* @tparam Args Types of arguments to use to construct the new instance.
* @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<Type>}
{
explicit basic_any(std::in_place_type_t<Type>, Args &&...args)
: basic_any{} {
initialize<Type>(std::forward<Args>(args)...);
}
/**
* @brief Constructs an any 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{std::in_place_type<Type &>, value.get()}
{}
/**
* @brief Constructs an any from a given value.
* @brief Constructs a wrapper from a given value.
* @tparam Type Type of object to use to initialize the wrapper.
* @param value An instance of an object to use to initialize the wrapper.
*/
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
basic_any(Type &&value)
: basic_any{std::in_place_type<std::decay_t<Type>>, std::forward<Type>(value)}
{}
: basic_any{} {
initialize<std::decay_t<Type>>(std::forward<Type>(value));
}
/**
* @brief Copy constructor.
* @param other The instance to copy from.
*/
basic_any(const basic_any &other)
: basic_any{std::in_place_type<void>}
{
other.vtable(operation::COPY, other, this);
: basic_any{} {
if(other.vtable) {
other.vtable(operation::copy, other, this);
}
}
/**
@@ -248,14 +185,20 @@ public:
* @param other The instance to move from.
*/
basic_any(basic_any &&other) ENTT_NOEXCEPT
: basic_any{std::in_place_type<void>}
{
other.vtable(operation::MOVE, other, this);
: instance{},
info{other.info},
vtable{other.vtable},
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 && mode == policy::owner) {
vtable(operation::destroy, *this, nullptr);
}
}
/**
@@ -263,9 +206,13 @@ public:
* @param other The instance to copy from.
* @return This any object.
*/
basic_any & operator=(const basic_any &other) {
vtable(operation::DTOR, *this, nullptr);
other.vtable(operation::COPY, other, this);
basic_any &operator=(const basic_any &other) {
reset();
if(other.vtable) {
other.vtable(operation::copy, other, this);
}
return *this;
}
@@ -274,21 +221,16 @@ public:
* @param other The instance to move from.
* @return This any object.
*/
basic_any & operator=(basic_any &&other) {
vtable(operation::DTOR, *this, nullptr);
other.vtable(operation::MOVE, other, this);
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>
basic_any & operator=(std::reference_wrapper<Type> value) ENTT_NOEXCEPT {
emplace<Type &>(value.get());
return *this;
}
@@ -306,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;
}
/**
@@ -335,14 +296,50 @@ public:
* @param args Parameters to use to construct the instance.
*/
template<typename Type, typename... Args>
void emplace(Args &&... args) {
std::exchange(vtable, &basic_vtable<Type>)(operation::DTOR, *this, nullptr);
void emplace(Args &&...args) {
reset();
initialize<Type>(std::forward<Args>(args)...);
}
/**
* @brief Copy 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;
}
/**
* @brief Move 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(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);
if(vtable && mode == policy::owner) {
vtable(operation::destroy, *this, nullptr);
}
info = &type_id<void>();
vtable = nullptr;
mode = policy::owner;
}
/**
@@ -350,7 +347,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;
}
/**
@@ -359,32 +356,44 @@ public:
* @return False if the two objects differ in their content, true otherwise.
*/
bool operator==(const basic_any &other) const ENTT_NOEXCEPT {
return type() == other.type() && (vtable(operation::COMP, *this, other.data()) == other.data());
if(vtable && *info == *other.info) {
return (vtable(operation::compare, *this, other.data()) != nullptr);
}
return (!vtable && !other.vtable);
}
/**
* @brief Aliasing constructor.
* @return An any that shares a reference to an unmanaged object.
* @return A wrapper that shares a reference to an unmanaged object.
*/
[[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT {
basic_any ref{};
vtable(operation::REF, *this, &ref);
return ref;
return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)};
}
/*! @copydoc as_ref */
[[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT {
basic_any ref{};
vtable(operation::CREF, *this, &ref);
return ref;
return basic_any{*this, policy::cref};
}
/**
* @brief Returns true if a wrapper owns its object, false otherwise.
* @return True if the wrapper owns its object, false otherwise.
*/
[[nodiscard]] bool owner() const ENTT_NOEXCEPT {
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.
@@ -398,7 +407,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.
@@ -409,48 +417,78 @@ 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);
ENTT_ASSERT(instance);
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);
ENTT_ASSERT(instance);
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);
return static_cast<Type>(std::move(*instance));
if constexpr(std::is_copy_constructible_v<std::remove_const_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_const_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_const_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.
* @tparam Len Size of the storage reserved for the small buffer optimization.
* @tparam Align Optional alignment requirement.
* @tparam Args Types of arguments to use to construct the new instance.
* @param args Parameters to use to construct the instance.
* @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) {
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.
* @tparam Align Optional alignment requirement.
* @tparam Type Type of argument to use to construct the new instance.
* @param value Parameter to use to construct the instance.
* @return A properly initialized and not necessarily owning wrapper.
*/
template<std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename Type>
basic_any<Len, Align> forward_as_any(Type &&value) {
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,269 @@
#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_const_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::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_const_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::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) ENTT_NOEXCEPT = 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) ENTT_NOEXCEPT = 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 {
template<typename First, typename Second>
struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_t, 2u> {};
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

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

@@ -0,0 +1,112 @@
#ifndef ENTT_CORE_FLAG_HPP
#define ENTT_CORE_FLAG_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::true_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<std::is_enum_v<Type> && entt::enum_as_bitmask_v<Type>, Type>
operator|(const Type lhs, const Type rhs) ENTT_NOEXCEPT {
return 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<std::is_enum_v<Type> && entt::enum_as_bitmask_v<Type>, Type>
operator&(const Type lhs, const Type rhs) ENTT_NOEXCEPT {
return 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<std::is_enum_v<Type> && entt::enum_as_bitmask_v<Type>, Type>
operator^(const Type lhs, const Type rhs) ENTT_NOEXCEPT {
return 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<std::is_enum_v<Type> && entt::enum_as_bitmask_v<Type>, Type>
operator~(const Type value) ENTT_NOEXCEPT {
return Type{~static_cast<std::underlying_type_t<Type>>(value)};
}
/*! @copydoc operator~ */
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<std::is_enum_v<Type> && 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<std::is_enum_v<Type> && 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<std::is_enum_v<Type> && 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<std::is_enum_v<Type> && entt::enum_as_bitmask_v<Type>, Type &>
operator^=(Type &lhs, const Type rhs) ENTT_NOEXCEPT {
return (lhs = (lhs ^ rhs));
}
/*! @copydoc operator| */
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<std::is_enum_v<Type> && entt::enum_as_bitmask_v<Type>, Type>
operator==(const Type lhs, const Type rhs) ENTT_NOEXCEPT {
return (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<std::is_enum_v<Type> && entt::enum_as_bitmask_v<Type>, Type>
operator!=(const Type lhs, const Type rhs) ENTT_NOEXCEPT {
return !(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,20 @@
#ifndef ENTT_CORE_FWD_HPP
#define ENTT_CORE_FWD_HPP
#include <type_traits>
#include "../config/config.h"
namespace entt {
template<std::size_t Len, std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)>
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<sizeof(double[2])>;
}
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,16 +32,13 @@ struct fnv1a_traits<std::uint64_t> {
static constexpr std::uint64_t prime = 1099511628211ull;
};
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Zero overhead unique identifier.
*
@@ -58,11 +48,15 @@ struct fnv1a_traits<std::uint64_t> {
* 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>;
using hs_traits = internal::fnv1a_traits<id_type>;
struct const_wrapper {
// non-explicit constructor on purpose
@@ -72,10 +66,10 @@ class basic_hashed_string {
// FowlerNollVo hash function v. 1a - the good
[[nodiscard]] static constexpr id_type helper(const Char *curr) ENTT_NOEXCEPT {
auto value = traits_type::offset;
auto value = hs_traits::offset;
while(*curr != 0) {
value = (value ^ static_cast<traits_type::type>(*(curr++))) * traits_type::prime;
value = (value ^ static_cast<hs_traits::type>(*(curr++))) * hs_traits::prime;
}
return value;
@@ -94,8 +88,8 @@ public:
* @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; }
id_type partial{hs_traits::offset};
while(size--) { partial = (partial ^ (str++)[0]) * hs_traits::prime; }
return partial;
}
@@ -107,7 +101,7 @@ public:
* characters.<br/>
* Example of use:
* @code{.cpp}
* const auto value = basic_hashed_string<char>::to_value("my.png");
* const auto value = basic_hashed_string<char>::value("my.png");
* @endcode
*
* @tparam N Number of characters of the identifier.
@@ -130,8 +124,8 @@ public:
/*! @brief Constructs an empty hashed string. */
constexpr basic_hashed_string() ENTT_NOEXCEPT
: str{nullptr}, hash{}
{}
: str{nullptr},
hash{} {}
/**
* @brief Constructs a hashed string from an array of const characters.
@@ -149,23 +143,27 @@ public:
*/
template<std::size_t N>
constexpr basic_hashed_string(const value_type (&curr)[N]) ENTT_NOEXCEPT
: str{curr}, hash{helper(curr)}
{}
: str{curr},
hash{helper(curr)} {}
/**
* @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)}
{}
: str{wrapper.str},
hash{helper(wrapper.str)} {}
/**
* @brief Returns the human-readable representation of a hashed string.
* @return The string used to initialize the instance.
*/
[[nodiscard]] constexpr const value_type * data() const ENTT_NOEXCEPT {
[[nodiscard]] constexpr const value_type *data() const ENTT_NOEXCEPT {
return str;
}
@@ -178,21 +176,16 @@ public:
}
/*! @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.
*/
[[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:
@@ -200,7 +193,6 @@ private:
hash_type hash;
};
/**
* @brief Deduction guide.
*
@@ -212,9 +204,7 @@ private:
* @param str Human-readable identifer.
*/
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,22 +214,81 @@ 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.
@@ -249,7 +298,6 @@ inline namespace literals {
return entt::hashed_string{str};
}
/**
* @brief User defined literal for hashed wstrings.
* @param str The literal without its suffix.
@@ -259,11 +307,8 @@ inline namespace literals {
return entt::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.
*
@@ -57,8 +54,6 @@ public:
static constexpr identifier_type type = get<std::decay_t<Type>>(std::index_sequence_for<Types...>{});
};
}
} // namespace entt
#endif

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

@@ -0,0 +1,116 @@
#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 = decltype(std::addressof(std::declval<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 iterators.
*/
template<typename It>
struct iterable_adaptor final {
/*! @brief Type of the objects returned during iteration. */
using value_type = typename std::iterator_traits<It>::value_type;
/*! @brief Iterator type. */
using iterator = It;
/*! @brief Const iterator type. */
using const_iterator = iterator;
/*! @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(It from, It to)
: first{from},
last{to} {}
/**
* @brief Returns an iterator to the beginning.
* @return An iterator to the first element of the range.
*/
[[nodiscard]] const_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]] const_iterator end() const ENTT_NOEXCEPT {
return last;
}
/*! @copydoc begin */
[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
return begin();
}
/*! @copydoc end */
[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
return end();
}
private:
It first;
It last;
};
} // namespace entt
#endif

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

@@ -0,0 +1,108 @@
#ifndef ENTT_CORE_MEMORY_HPP
#define ENTT_CORE_MEMORY_HPP
#include <cstddef>
#include <limits>
#include <memory>
#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_const_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);
}
} // 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,136 @@ 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;
template<typename Type>
friend const 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}
{}
template<typename Type>
constexpr type_info(std::in_place_type_t<Type>) ENTT_NOEXCEPT
: seq{type_index<std::remove_reference_t<std::remove_const_t<Type>>>::value()},
identifier{type_hash<std::remove_reference_t<std::remove_const_t<Type>>>::value()},
alias{type_name<std::remove_reference_t<std::remove_const_t<Type>>>::value()} {}
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;
/**
* @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 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 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<std::remove_cv_t<std::remove_reference_t<Type>>>};
return instance;
} else {
return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
}
}
} // namespace entt
#endif

View File

@@ -1,17 +1,15 @@
#ifndef ENTT_CORE_TYPE_TRAITS_HPP
#define ENTT_CORE_TYPE_TRAITS_HPP
#include <cstddef>
#include <utility>
#include <iterator>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "fwd.hpp"
namespace entt {
/**
* @brief Utility class to disambiguate overloaded functions.
* @tparam N Number of choices available.
@@ -19,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.
@@ -37,7 +31,6 @@ struct choice_t<0> {};
template<std::size_t N>
inline constexpr choice_t<N> choice{};
/**
* @brief Identity type trait.
*
@@ -52,7 +45,6 @@ struct type_identity {
using type = Type;
};
/**
* @brief Helper type.
* @tparam Type A type.
@@ -60,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.
@@ -69,21 +60,17 @@ 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>
inline constexpr auto size_of_v = size_of<Type>::value;
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
@@ -91,8 +78,7 @@ inline constexpr auto size_of_v = size_of<Type>::value;
* @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
@@ -100,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.
@@ -110,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`.
@@ -118,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.
@@ -131,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.
@@ -145,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.
@@ -160,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.
@@ -169,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.
@@ -177,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<> {
@@ -192,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.
@@ -205,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.
@@ -216,7 +191,6 @@ struct type_list_cat<type_list<Type...>> {
using type = type_list<Type...>;
};
/**
* @brief Helper type.
* @tparam List Type lists to concatenate.
@@ -224,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.
@@ -241,11 +213,9 @@ struct type_list_unique<type_list<Type, Other...>> {
using type = std::conditional_t<
std::disjunction_v<std::is_same<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<>> {
@@ -253,7 +223,6 @@ struct type_list_unique<type_list<>> {
using type = type_list<>;
};
/**
* @brief Helper type.
* @tparam Type A type list.
@@ -261,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.
@@ -271,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.
@@ -280,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>
inline constexpr auto type_list_contains_v = type_list_contains<List, Type>::value;
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.
@@ -306,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.
@@ -314,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.
@@ -327,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.
@@ -341,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.
@@ -356,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.
@@ -365,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.
@@ -373,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<> {
@@ -388,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.
@@ -401,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.
@@ -412,7 +366,6 @@ struct value_list_cat<value_list<Value...>> {
using type = value_list<Value...>;
};
/**
* @brief Helper type.
* @tparam List Value lists to concatenate.
@@ -420,70 +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>()) {
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 Potentially equality comparable type.
*/
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 Potentially equality comparable type.
*/
template<class Type>
inline constexpr auto 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.
@@ -493,31 +386,27 @@ 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.
* @tparam Tuple Tuple-like type.
* @tparam Args The list of arguments to use to probe the function type.
*/
* @copybrief is_applicable
* @tparam Func A valid function type.
* @tparam Tuple Tuple-like type.
* @tparam Args The list of arguments to use to probe the function type.
*/
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.
* @tparam Args The list of arguments to use to probe the function type.
*/
template<typename Func, typename Args>
inline constexpr auto is_applicable_v = is_applicable<Func, Args>::value;
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
@@ -528,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
@@ -537,47 +425,198 @@ struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret,
* @tparam Args The list of arguments to use to probe the function type.
*/
template<typename Ret, typename Func, typename Args>
inline constexpr auto is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value;
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.
* @tparam Type Potential complete type.
*/
* @brief Provides the member constant `value` to true if a given type is
* complete, false otherwise.
* @tparam Type The type to test.
*/
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 Potential complete type.
*/
* @brief Helper variable template.
* @tparam Type The type to test.
*/
template<typename Type>
inline constexpr auto is_complete_v = is_complete<Type>::value;
inline constexpr bool is_complete_v = is_complete<Type>::value;
/**
* @brief Provides the member constant `value` to true if a given type is empty
* and the empty type optimization is enabled, false otherwise.
* @tparam Type Potential empty type.
* @brief Provides the member constant `value` to true if a given type is an
* iterator, false otherwise.
* @tparam Type The type to test.
*/
template<typename Type, typename = void>
struct is_empty: ENTT_IS_EMPTY(Type) {};
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::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.
* @tparam Type Potential empty type.
* @tparam Type The type to test.
*/
template<typename Type>
inline constexpr auto is_empty_v = is_empty<Type>::value;
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.
*/
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> {};
/**
* @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;
/**
* @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>
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.
*/
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.
@@ -590,7 +629,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> {
@@ -598,7 +636,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.
@@ -607,7 +644,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.
@@ -617,20 +653,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.
@@ -638,8 +673,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

@@ -0,0 +1,37 @@
#ifndef ENTT_ENTITY_COMPONENT_HPP
#define ENTT_ENTITY_COMPONENT_HPP
#include <type_traits>
#include "../config/config.h"
namespace entt {
/*! @brief Commonly used default traits for all types. */
struct basic_component_traits {
/*! @brief Pointer stability, default is `false`. */
static constexpr auto in_place_delete = false;
/*! @brief Empty type optimization, default is `ENTT_IGNORE_IF_EMPTY`. */
static constexpr auto ignore_if_empty = ENTT_IGNORE_IF_EMPTY;
/*! @brief Page size, default is `ENTT_PACKED_PAGE`. */
static constexpr auto page_size = ENTT_PACKED_PAGE;
};
/**
* @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 {
static_assert(std::is_same_v<std::decay_t<Type>, Type>, "Unsupported type");
};
/**
* @brief Helper variable template.
* @tparam Type Type of component.
*/
template<class Type>
inline constexpr bool ignore_as_empty_v = component_traits<Type>::ignore_if_empty &&std::is_empty_v<Type>;
} // namespace entt
#endif

View File

@@ -1,195 +1,339 @@
#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 {
/**
* @brief Entity traits.
*
* Primary template isn't defined on purpose. All the specializations give a
* compile-time error unless the template parameter is an accepted entity type.
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename, typename = void>
struct entt_traits;
/**
* @brief Entity traits for enumeration types.
* @tparam Type The type to check.
*/
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> {};
/**
* @brief Entity traits for a 32 bits entity identifier.
*
* A 32 bits entity identifier guarantees:
*
* * 20 bits for the entity number (suitable for almost all the games).
* * 12 bit for the version (resets in [0-4095]).
*/
template<>
struct entt_traits<std::uint32_t> {
/*! @brief Underlying entity type. */
using entity_type = std::uint32_t;
/*! @brief Underlying version type. */
using version_type = std::uint16_t;
/*! @brief Difference type. */
using difference_type = std::int64_t;
/*! @brief Mask to use to get the entity number out of an identifier. */
static constexpr entity_type entity_mask = 0xFFFFF;
/*! @brief Mask to use to get the version out of an identifier. */
static constexpr entity_type version_mask = 0xFFF;
/*! @brief Extent of the entity number within an identifier. */
static constexpr std::size_t entity_shift = 20u;
};
/**
* @brief Entity traits for a 64 bits entity identifier.
*
* A 64 bits entity identifier guarantees:
*
* * 32 bits for the entity number (an indecently large number).
* * 32 bit for the version (an indecently large number).
*/
template<>
struct entt_traits<std::uint64_t> {
/*! @brief Underlying entity type. */
using entity_type = std::uint64_t;
/*! @brief Underlying version type. */
using version_type = std::uint32_t;
/*! @brief Difference type. */
using difference_type = std::int64_t;
/*! @brief Mask to use to get the entity number out of an identifier. */
static constexpr entity_type entity_mask = 0xFFFFFFFF;
/*! @brief Mask to use to get the version out of an identifier. */
static constexpr entity_type version_mask = 0xFFFFFFFF;
/*! @brief Extent of the entity number within an identifier. */
static constexpr std::size_t entity_shift = 32u;
};
/**
* @brief Converts an entity type to its underlying type.
* @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 static_cast<typename entt_traits<Entity>::entity_type>(entity);
}
/*! @brief Null object for all entity identifiers. */
struct null_t {
/**
* @brief Converts the null object to identifiers of any type.
* @tparam Entity Type of entity identifier.
* @return The null representation for the given identifier.
*/
template<typename Entity>
[[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT {
return Entity{entt_traits<Entity>::entity_mask};
}
/**
* @brief Compares two null objects.
* @return True in all cases.
*/
[[nodiscard]] constexpr bool operator==(const null_t &) const ENTT_NOEXCEPT {
return true;
}
/**
* @brief Compares two null objects.
* @return False in all cases.
*/
[[nodiscard]] constexpr bool operator!=(const null_t &) const ENTT_NOEXCEPT {
return false;
}
/**
* @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.
* @return False if the two elements differ, true otherwise.
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator==(const Entity &entity) const ENTT_NOEXCEPT {
return (to_integral(entity) & entt_traits<Entity>::entity_mask) == to_integral(static_cast<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.
* @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 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.
* @param other A null object yet to be converted.
* @return False if the two elements differ, true otherwise.
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator==(const Entity &entity, const null_t &other) ENTT_NOEXCEPT {
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.
* @param other A null object yet to be converted.
* @return True if the two elements differ, false otherwise.
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator!=(const Entity &entity, const null_t &other) ENTT_NOEXCEPT {
return !(other == entity);
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Entity traits.
* @tparam Type Type of identifier.
*/
template<typename Type>
class entt_traits: internal::entt_traits<Type> {
using base_type = internal::entt_traits<Type>;
public:
/*! @brief Value type. */
using value_type = Type;
/*! @brief Underlying entity type. */
using entity_type = typename base_type::entity_type;
/*! @brief Underlying version type. */
using version_type = typename base_type::version_type;
/*! @brief Reserved identifier. */
static constexpr entity_type reserved = base_type::entity_mask | (base_type::version_mask << base_type::entity_shift);
/*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */
static constexpr auto page_size = ENTT_SPARSE_PAGE;
/**
* @brief Converts an entity to its underlying type.
* @param value The value to convert.
* @return The integral representation of the given value.
*/
[[nodiscard]] static constexpr entity_type to_integral(const value_type value) ENTT_NOEXCEPT {
return static_cast<entity_type>(value);
}
/**
* @brief Returns the entity part once converted to the underlying type.
* @param value The value to convert.
* @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) & base_type::entity_mask);
}
/**
* @brief Returns the version part once converted to the underlying type.
* @param value The value to convert.
* @return The integral representation of the version part.
*/
[[nodiscard]] static constexpr version_type to_version(const value_type value) ENTT_NOEXCEPT {
return (to_integral(value) >> base_type::entity_shift);
}
/**
* @brief Constructs an identifier from its parts.
*
* If the version part is not provided, a tombstone is returned.<br/>
* If the entity part is not provided, a null identifier is returned.
*
* @param entity The entity part of the identifier.
* @param version The version part of the identifier.
* @return A properly constructed identifier.
*/
[[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)};
}
};
/**
* @copydoc entt_traits<Entity>::to_integral
* @tparam Entity The value type.
*/
template<typename 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);
}
/**
* @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 identifier.
* @return The null representation for the given type.
*/
template<typename Entity>
[[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT {
using entity_traits = entt_traits<Entity>;
return entity_traits::combine(entity_traits::reserved, entity_traits::reserved);
}
/**
* @brief Compares two null objects.
* @param other A null object.
* @return True in all cases.
*/
[[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const ENTT_NOEXCEPT {
return true;
}
/**
* @brief Compares two null objects.
* @param other A null object.
* @return False in all cases.
*/
[[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const ENTT_NOEXCEPT {
return false;
}
/**
* @brief Compares a null object and an identifier of any type.
* @tparam Entity Type of identifier.
* @param entity Identifier with which to compare.
* @return False if the two elements differ, true otherwise.
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT {
using entity_traits = entt_traits<Entity>;
return entity_traits::to_entity(entity) == entity_traits::to_entity(*this);
}
/**
* @brief Compares a null object and an identifier of any type.
* @tparam Entity Type of identifier.
* @param entity Identifier with which to compare.
* @return True if the two elements differ, false otherwise.
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT {
return !(entity == *this);
}
};
/**
* @brief Compares a null object and an identifier of any type.
* @tparam Entity Type of identifier.
* @param entity Identifier with which to compare.
* @param other A null object yet to be converted.
* @return False if the two elements differ, true otherwise.
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator==(const Entity entity, const null_t other) ENTT_NOEXCEPT {
return other.operator==(entity);
}
/**
* @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.
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator!=(const Entity entity, const null_t other) ENTT_NOEXCEPT {
return !(other == entity);
}
/*! @brief Tombstone object for all identifiers. */
struct tombstone_t {
/**
* @brief Converts the tombstone object to identifiers of any type.
* @tparam Entity Type of identifier.
* @return The tombstone representation for the given type.
*/
template<typename Entity>
[[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT {
using entity_traits = entt_traits<Entity>;
return entity_traits::combine(entity_traits::reserved, entity_traits::reserved);
}
/**
* @brief Compares two tombstone objects.
* @param other A tombstone object.
* @return True in all cases.
*/
[[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const ENTT_NOEXCEPT {
return true;
}
/**
* @brief Compares two tombstone objects.
* @param other A tombstone object.
* @return False in all cases.
*/
[[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const ENTT_NOEXCEPT {
return false;
}
/**
* @brief Compares a tombstone object and an identifier of any type.
* @tparam Entity Type of identifier.
* @param entity Identifier with which to compare.
* @return False if the two elements differ, true otherwise.
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT {
using entity_traits = entt_traits<Entity>;
return entity_traits::to_version(entity) == entity_traits::to_version(*this);
}
/**
* @brief Compares a tombstone object and an identifier of any type.
* @tparam Entity Type of identifier.
* @param entity Identifier with which to compare.
* @return True if the two elements differ, false otherwise.
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT {
return !(entity == *this);
}
};
/**
* @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.
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator==(const Entity entity, const tombstone_t other) ENTT_NOEXCEPT {
return other.operator==(entity);
}
/**
* @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.
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator!=(const Entity entity, const tombstone_t other) ENTT_NOEXCEPT {
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 comparision 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 identifiers of any
* allowed type. Similarly, there exist comparision operators between the
* tombstone entity and any other identifier.
*/
inline constexpr tombstone_t tombstone{};
}
} // namespace entt
#endif

View File

@@ -1,69 +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>
template<typename Entity, typename = std::allocator<Entity>>
class basic_sparse_set;
template<typename, typename, typename>
template<typename, typename Type, typename = std::allocator<Type>, typename = void>
class basic_storage;
template<typename>
class basic_registry;
template<typename...>
template<typename, typename, typename, typename = void>
class basic_view;
template<typename>
class 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.
@@ -71,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.
@@ -99,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.
@@ -107,31 +85,26 @@ 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>;
/**
* @brief Alias declaration for the most common use case.
* @tparam Args Other template parameters.
@@ -139,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,113 +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...>> final {
/*! @brief A registry is allowed to create groups. */
friend class basic_registry<Entity>;
template<typename Component>
using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Component>>::storage_type, Component>;
template<typename Comp>
using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Comp>>::storage_type, Comp>;
class iterable_group final {
friend class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>>;
using basic_common_type = std::common_type_t<typename storage_type<Get>::base_type...>;
template<typename It>
class iterable_group_iterator final {
friend class iterable_group;
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_group_iterator(It from, const std::tuple<storage_type<Get> *...> &args) ENTT_NOEXCEPT
: it{from},
pools{args}
{}
extended_group_iterator() = default;
public:
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;
extended_group_iterator(typename basic_common_type::iterator from, const std::tuple<storage_type<Get> *...> &args) ENTT_NOEXCEPT
: it{from},
pools{args} {}
iterable_group_iterator & operator++() ENTT_NOEXCEPT {
return ++it, *this;
}
iterable_group_iterator operator++(int) ENTT_NOEXCEPT {
iterable_group_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_group_iterator &other) const ENTT_NOEXCEPT {
return other.it == it;
}
[[nodiscard]] bool operator!=(const iterable_group_iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
private:
It it;
std::tuple<storage_type<Get> *...> pools;
};
iterable_group(basic_sparse_set<Entity> * const ref, const std::tuple<storage_type<Get> *...> &cpools)
: handler{ref},
pools{cpools}
{}
public:
using iterator = iterable_group_iterator<typename basic_sparse_set<Entity>::iterator>;
using reverse_iterator = iterable_group_iterator<typename basic_sparse_set<Entity>::reverse_iterator>;
[[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_sparse_set<Entity> * const handler;
const std::tuple<storage_type<Get> *...> pools;
typename basic_common_type::iterator it;
std::tuple<storage_type<Get> *...> pools;
};
basic_group(basic_sparse_set<Entity> &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_sparse_set<Entity>::iterator;
using iterator = typename base_type::iterator;
/*! @brief Reversed iterator type. */
using reverse_iterator = typename basic_sparse_set<Entity>::reverse_iterator;
using reverse_iterator = typename base_type::reverse_iterator;
/*! @brief Iterable group type. */
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.
@@ -200,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]] const entity_type * data() const ENTT_NOEXCEPT {
return *this ? handler->data() : nullptr;
}
/**
* @brief Returns an iterator to the first entity of the group.
*
@@ -287,7 +272,7 @@ public:
/**
* @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.
*/
@@ -315,7 +300,7 @@ 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 {
@@ -333,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));
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)...);
}
}
@@ -384,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
@@ -396,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}};
}
/**
@@ -421,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.
@@ -436,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)...);
}
}
}
@@ -468,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_sparse_set<entity_type> * const handler;
base_type *const handler;
const std::tuple<storage_type<Get> *...> pools;
};
/**
* @brief Owning group.
*
@@ -525,144 +512,114 @@ 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...>> final {
/*! @brief A registry is allowed to create groups. */
friend class basic_registry<Entity>;
template<typename Component>
using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Component>>::storage_type, Component>;
template<typename Comp>
using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Comp>>::storage_type, Comp>;
class iterable_group final {
friend class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>, Owned...>;
using basic_common_type = std::common_type_t<typename storage_type<Owned>::base_type..., typename storage_type<Get>::base_type...>;
template<typename, typename>
class iterable_group_iterator;
template<typename>
struct extended_group_iterator;
template<typename It, typename... OIt>
class iterable_group_iterator<It, type_list<OIt...>> final {
friend class iterable_group;
template<typename... OIt>
struct extended_group_iterator<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 = input_iterator_pointer<value_type>;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
template<typename... Other>
iterable_group_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}
{}
extended_group_iterator() = default;
public:
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>
extended_group_iterator(typename basic_common_type::iterator 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_group_iterator & operator++() ENTT_NOEXCEPT {
return ++it, (++std::get<OIt>(owned), ...), *this;
}
iterable_group_iterator operator++(int) ENTT_NOEXCEPT {
iterable_group_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_group_iterator &other) const ENTT_NOEXCEPT {
return other.it == it;
}
[[nodiscard]] bool operator!=(const iterable_group_iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
private:
It it;
std::tuple<OIt...> owned;
std::tuple<storage_type<Get> *...> get;
};
iterable_group(std::tuple<storage_type<Owned> *..., storage_type<Get> *...> cpools, const std::size_t * const extent)
: pools{cpools},
length{extent}
{}
public:
using iterator = iterable_group_iterator<
typename basic_sparse_set<Entity>::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_group_iterator<
typename basic_sparse_set<Entity>::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())>>...>
>;
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
return length ? iterator{
std::get<0>(pools)->basic_sparse_set<Entity>::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)...)};
extended_group_iterator &operator++() ENTT_NOEXCEPT {
return ++it, (++std::get<OIt>(owned), ...), *this;
}
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
return length ? iterator{
std::get<0>(pools)->basic_sparse_set<Entity>::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_sparse_set<Entity>::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),
std::forward_as_tuple(*std::get<OIt>(owned)...),
std::get<storage_type<Get> *>(get)->get_as_tuple(*it)...);
}
[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
return length ? reverse_iterator{
std::get<0>(pools)->basic_sparse_set<Entity>::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<OIt...> owned;
std::tuple<storage_type<Get> *...> get;
};
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_sparse_set<Entity>::iterator;
using iterator = typename base_type::iterator;
/*! @brief Reversed iterator type. */
using reverse_iterator = typename basic_sparse_set<Entity>::reverse_iterator;
using reverse_iterator = typename base_type::reverse_iterator;
/*! @brief Iterable group type. */
using iterable = iterable_adaptor<extended_group_iterator<type_list_cat_t<std::conditional_t<ignore_as_empty_v<std::remove_const_t<Owned>>, type_list<>, type_list<decltype(std::declval<storage_type<Owned>>().end())>>...>>>;
/*! @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.
@@ -680,38 +637,6 @@ public:
return !*this || !*length;
}
/**
* @brief Direct access to the list of components of a given pool.
*
* The returned pointer is such that range
* `[raw<Component>(), raw<Component>() + size())` is always a valid range,
* even if the container is empty.<br/>
*
* @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]] Component * 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() : nullptr;
}
/**
* @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]] const entity_type * data() const ENTT_NOEXCEPT {
return *this ? std::get<0>(pools)->data() : nullptr;
}
/**
* @brief Returns an iterator to the first entity of the group.
*
@@ -721,7 +646,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_sparse_set<entity_type>::end() - *length) : iterator{};
return *this ? (std::get<0>(pools)->base_type::end() - *length) : iterator{};
}
/**
@@ -735,7 +660,7 @@ public:
* group.
*/
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
return *this ? std::get<0>(pools)->basic_sparse_set<entity_type>::end() : iterator{};
return *this ? std::get<0>(pools)->base_type::end() : iterator{};
}
/**
@@ -747,7 +672,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_sparse_set<entity_type>::rbegin() : reverse_iterator{};
return *this ? std::get<0>(pools)->base_type::rbegin() : reverse_iterator{};
}
/**
@@ -762,7 +687,7 @@ public:
* reversed group.
*/
[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
return *this ? (std::get<0>(pools)->basic_sparse_set<entity_type>::rbegin() + *length) : reverse_iterator{};
return *this ? (std::get<0>(pools)->base_type::rbegin() + *length) : reverse_iterator{};
}
/**
@@ -787,7 +712,7 @@ public:
/**
* @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.
*/
@@ -815,7 +740,7 @@ 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 {
@@ -833,20 +758,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));
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)...);
}
}
@@ -878,13 +803,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
@@ -896,8 +821,12 @@ 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 {
using extended_iterator_type = typename iterable::iterator;
iterator last = length ? std::get<0>(pools)->basic_common_type::end() : iterator{};
auto from = extended_iterator_type{last - *length, std::make_tuple((std::get<storage_type<Owned> *>(pools)->end() - *length)...), std::make_tuple(std::get<storage_type<Get> *>(pools)...)};
auto to = extended_iterator_type{last, std::make_tuple((std::get<storage_type<Owned> *>(pools)->end())...), std::make_tuple(std::get<storage_type<Get> *>(pools)...)};
return {std::move(from), std::move(to)};
}
/**
@@ -922,14 +851,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.
@@ -937,39 +866,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.
*
@@ -24,36 +21,28 @@ namespace entt {
*/
template<typename Entity, typename... Type>
struct basic_handle {
/*! @brief Underlying entity identifier. */
using entity_type = std::remove_const_t<Entity>;
/*! @brief Type of registry accepted by the handle. */
using registry_type = constness_as_t<basic_registry<entity_type>, Entity>;
using registry_type = constness_as_t<basic_registry<std::remove_const_t<Entity>>, Entity>;
/*! @brief Underlying entity identifier. */
using entity_type = typename registry_type::entity_type;
/*! @brief Underlying version type. */
using version_type = typename registry_type::version_type;
/*! @brief Unsigned integer type. */
using size_type = typename registry_type::size_type;
/*! @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.
@@ -64,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();
@@ -101,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;
}
@@ -126,7 +112,7 @@ struct basic_handle {
* @sa basic_registry::destroy
* @param version A desired version upon destruction.
*/
void destroy(const typename registry_type::version_type version) {
void destroy(const version_type version) {
reg->destroy(entt, version);
}
@@ -139,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)...);
}
@@ -153,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)...);
}
@@ -167,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)...);
}
@@ -181,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)...);
}
@@ -190,32 +176,23 @@ struct basic_handle {
* @brief Removes the given components from a handle.
* @sa basic_registry::remove
* @tparam Component Types of components to remove.
*/
template<typename... Component>
void remove() const {
static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type");
reg->template remove<Component...>(entt);
}
/**
* @brief Removes the given components from a handle.
* @sa basic_registry::remove_if_exists
* @tparam Component Types of components to remove.
* @return The number of components actually removed.
*/
template<typename... Component>
decltype(auto) remove_if_exists() const {
size_type remove() const {
static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type");
return reg->template remove_if_exists<Component...>(entt);
return reg->template remove<Component...>(entt);
}
/**
* @brief Removes all the components from a handle and makes it orphaned.
* @sa basic_registry::remove_all
* @brief Erases the given components from a handle.
* @sa basic_registry::erase
* @tparam Component Types of components to erase.
*/
void remove_all() const {
static_assert(sizeof...(Type) == 0, "Invalid operation");
reg->remove_all(entt);
template<typename... Component>
void erase() const {
static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type");
reg->template erase<Component...>(entt);
}
/**
@@ -262,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)...);
}
@@ -288,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:
@@ -303,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,18 +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).
@@ -37,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{});
}
@@ -45,7 +43,6 @@ private:
registry_type &reg;
};
/**
* @brief Deduction guide.
* @tparam Entity A valid entity type (see entt_traits for more details).
@@ -53,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).
@@ -61,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).
@@ -81,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 {
@@ -99,7 +94,6 @@ private:
registry_type &reg;
};
/**
* @brief Deduction guide.
* @tparam Entity A valid entity type (see entt_traits for more details).
@@ -107,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).
@@ -115,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.
@@ -132,23 +123,34 @@ void invoke(basic_registry<Entity> &reg, const Entity entt) {
func(reg, entt);
}
/**
* @brief Returns the entity associated with a given component.
*
* @warning
* Currently, this function only works correctly with the default pool as it
* makes assumptions about how the components are laid out.
*
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Component Type of component.
* @param reg A registry that contains the given entity and its components.
* @param component A valid component instance.
* @param instance A valid component instance.
* @return The entity associated with the given component.
*/
template<typename Entity, typename Component>
Entity to_entity(const basic_registry<Entity> &reg, const Component &component) {
const auto view = reg.template view<const Component>();
return *(view.data() + (&component - view.raw()));
}
Entity to_entity(const basic_registry<Entity> &reg, const Component &instance) {
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 = 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 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.
*
@@ -191,7 +184,7 @@ class basic_observer {
template<std::size_t Index>
static void discard_if(basic_observer &obs, basic_registry<Entity> &, const Entity entt) {
if(obs.storage.contains(entt) && !(obs.storage.get(entt) &= (~(1 << Index)))) {
obs.storage.remove(entt);
obs.storage.erase(entt);
}
}
@@ -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);
}
@@ -234,7 +228,7 @@ class basic_observer {
template<std::size_t Index>
static void discard_if(basic_observer &obs, basic_registry<Entity> &, const Entity entt) {
if(obs.storage.contains(entt) && !(obs.storage.get(entt) &= (~(1 << Index)))) {
obs.storage.remove(entt);
obs.storage.erase(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_hash_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,20 +52,17 @@ 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;
@@ -84,32 +74,28 @@ struct resource<type_list<Args...>, type_list<Req...>> {
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<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> free_function_to_resource(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<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource(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<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource(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);
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();
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Utility class for creating a static task graph.
*
@@ -123,9 +109,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 +121,7 @@ class basic_organizer final {
callback_type *callback{};
dependency_type *dependency;
prepare_type *prepare{};
type_info info{};
const type_info *info{};
};
template<typename Type>
@@ -155,12 +141,12 @@ 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 auto length = std::min(count, sizeof...(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;
}
@@ -262,8 +248,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 +257,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 +268,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 +300,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 +316,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 +324,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 +332,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;
}
@@ -381,18 +366,18 @@ public:
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));
}
/**
@@ -414,22 +399,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));
}
/**
@@ -445,18 +426,17 @@ public:
using resource_type = internal::resource<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 +478,10 @@ public:
}
private:
std::unordered_map<entt::id_type, std::vector<std::pair<std::size_t, bool>>> dependencies;
dense_hash_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,19 +1,99 @@
#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 "sparse_set.hpp"
#include "entity.hpp"
#include "fwd.hpp"
#include "sparse_set.hpp"
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename Type>
class runtime_view_iterator final {
[[nodiscard]] bool valid() const {
return 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 iterator_type = typename Type::iterator;
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 = default;
runtime_view_iterator(const std::vector<const Type *> &cpools, const std::vector<const Type *> &ignore, iterator_type curr) ENTT_NOEXCEPT
: pools{&cpools},
filter{&ignore},
it{curr} {
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--() ENTT_NOEXCEPT {
while(--it != (*pools)[0]->begin() && !valid()) {}
return *this;
}
runtime_view_iterator operator--(int) ENTT_NOEXCEPT {
runtime_view_iterator orig = *this;
return operator--(), orig;
}
[[nodiscard]] pointer operator->() const {
return it.operator->();
}
[[nodiscard]] reference operator*() const {
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 Type *> *pools;
const std::vector<const Type *> *filter;
iterator_type it;
};
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Runtime view.
@@ -55,76 +135,7 @@ namespace entt {
*/
template<typename Entity>
class basic_runtime_view final {
using underlying_iterator = typename basic_sparse_set<Entity>::iterator;
class view_iterator final {
friend class basic_runtime_view<Entity>;
view_iterator(const std::vector<const basic_sparse_set<Entity> *> &cpools, const std::vector<const basic_sparse_set<Entity> *> &ignore, underlying_iterator curr) ENTT_NOEXCEPT
: pools{&cpools},
filter{&ignore},
it{curr}
{
if(it != (*pools)[0]->end() && !valid()) {
++(*this);
}
}
[[nodiscard]] bool valid() const {
return 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 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 & 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_sparse_set<Entity> *> *pools;
const std::vector<const basic_sparse_set<Entity> *> *filter;
underlying_iterator it;
};
using basic_common_type = basic_sparse_set<Entity>;
[[nodiscard]] bool valid() const {
return !pools.empty() && pools.front();
@@ -136,29 +147,27 @@ public:
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Bidirectional iterator type. */
using iterator = view_iterator;
using iterator = internal::runtime_view_iterator<basic_common_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.
*/
basic_runtime_view(std::vector<const basic_sparse_set<Entity> *> cpools, std::vector<const basic_sparse_set<Entity> *> epools) ENTT_NOEXCEPT
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)}
{
const auto it = std::min_element(pools.begin(), pools.end(), [](const auto *lhs, const auto *rhs) {
filter{std::move(epools)} {
auto candidate = std::min_element(pools.begin(), pools.end(), [](const auto *lhs, const auto *rhs) {
return (!lhs && rhs) || (lhs && rhs && lhs->size() < rhs->size());
});
// brings the best candidate (if any) on front of the vector
std::rotate(pools.begin(), it, pools.end());
std::rotate(pools.begin(), candidate, pools.end());
}
/**
@@ -200,12 +209,12 @@ public:
/**
* @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); });
&& std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); });
}
/**
@@ -231,12 +240,10 @@ public:
}
private:
std::vector<const basic_sparse_set<Entity> *> pools;
std::vector<const basic_sparse_set<Entity> *> filter;
std::vector<const basic_common_type *> pools;
std::vector<const basic_common_type *> filter;
};
}
} // 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_hash_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;
/*! @brief Default move assignment operator. @return This snapshot. */
basic_snapshot & operator=(basic_snapshot &&) = default;
basic_snapshot &operator=(basic_snapshot &&) = 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->destroyed());
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,20 +159,20 @@ 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{};
archive(length);
entity_type entt{};
if constexpr(std::tuple_size_v<decltype(reg->template view<Type>().get({}))> == 0) {
if constexpr(ignore_as_empty_v<std::remove_const_t<Type>>) {
while(length--) {
archive(entt);
const auto entity = reg->valid(entt) ? entt : reg->create(entt);
ENTT_ASSERT(entity == entt);
ENTT_ASSERT(entity == entt, "Entity not available for use");
reg->template emplace<Type>(entity);
}
} else {
@@ -186,7 +181,7 @@ class basic_snapshot_loader {
while(length--) {
archive(entt, instance);
const auto entity = reg->valid(entt) ? entt : reg->create(entt);
ENTT_ASSERT(entity == entt);
ENTT_ASSERT(entity == entt, "Entity not available for use");
reg->template emplace<Type>(entity, 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());
ENTT_ASSERT(reg->empty(), "Registry must be empty");
}
/*! @brief Default move constructor. */
basic_snapshot_loader(basic_snapshot_loader &&) = default;
/*! @brief Default move assignment operator. @return This loader. */
basic_snapshot_loader & operator=(basic_snapshot_loader &&) = default;
basic_snapshot_loader &operator=(basic_snapshot_loader &&) = 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->destroy(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()) {
@@ -330,7 +322,7 @@ class basic_continuous_loader {
template<typename Container>
auto update(int, Container &container)
-> decltype(typename Container::mapped_type{}, void()) {
-> decltype(typename Container::mapped_type{}, void()) {
// map like container
Container other;
@@ -353,7 +345,7 @@ class basic_continuous_loader {
template<typename Container>
auto update(char, Container &container)
-> decltype(typename Container::value_type{}, void()) {
-> 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 +355,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>) {
@@ -380,19 +372,19 @@ class basic_continuous_loader {
const auto local = ref.second.first;
if(reg->valid(local)) {
reg->template remove_if_exists<Component>(local);
reg->template remove<Component>(local);
}
}
}
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{};
archive(length);
entity_type entt{};
if constexpr(std::tuple_size_v<decltype(reg->template view<Other>().get({}))> == 0) {
if constexpr(ignore_as_empty_v<std::remove_const_t<Other>>) {
while(length--) {
archive(entt);
restore(entt);
@@ -419,14 +411,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 +430,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 = (to_integral(entt) & traits_type::entity_mask); 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 +471,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 +485,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 +517,11 @@ public:
*
* @return A non-const reference to this loader.
*/
basic_continuous_loader & orphans() {
reg->orphans([this](const auto entt) {
reg->destroy(entt);
basic_continuous_loader &orphans() {
reg->each([this](const auto entt) {
if(reg->orphan(entt)) {
reg->release(entt);
}
});
return *this;
@@ -537,7 +529,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 +538,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 +553,10 @@ public:
}
private:
std::unordered_map<entity_type, std::pair<entity_type, bool>> remloc;
dense_hash_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,21 +1,28 @@
#include "config/version.h"
#include "container/dense_hash_map.hpp"
#include "container/dense_hash_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"
#include "entity/component.hpp"
#include "entity/entity.hpp"
#include "entity/group.hpp"
#include "entity/handle.hpp"
#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/snapshot.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,15 +1,12 @@
#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.
*
@@ -67,7 +64,7 @@ struct service_locator {
*
* @return A reference to the service implementation currently set, if any.
*/
[[nodiscard]] static Service & ref() ENTT_NOEXCEPT {
[[nodiscard]] static Service &ref() ENTT_NOEXCEPT {
return *service;
}
@@ -78,7 +75,7 @@ struct service_locator {
* @param args Parameters to use to construct the service.
*/
template<typename Impl = Service, typename... Args>
static void set(Args &&... args) {
static void set(Args &&...args) {
service = std::make_shared<Impl>(std::forward<Args>(args)...);
}
@@ -87,7 +84,7 @@ struct service_locator {
* @param ptr Service to use to replace the current one.
*/
static void set(std::shared_ptr<Service> ptr) {
ENTT_ASSERT(static_cast<bool>(ptr));
ENTT_ASSERT(static_cast<bool>(ptr), "Null service not allowed");
service = std::move(ptr);
}
@@ -104,8 +101,6 @@ private:
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,196 @@
#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_hash_map.hpp"
#include "../container/dense_hash_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 &) {
[[nodiscard]] static bool clear([[maybe_unused]] any &container) {
if constexpr(is_dynamic_sequence_container<Type>::value) {
if(auto *const cont = any_cast<Type>(&container); cont) {
cont->clear();
return true;
}
}
return false;
}
/**
* @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 begin(any &container) {
if(auto *const cont = any_cast<Type>(&container); cont) {
return iterator{std::begin(*cont)};
}
return iterator{std::begin(any_cast<const Type &>(container))};
}
/**
* @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 end(any &container) {
if(auto *const cont = any_cast<Type>(&container); cont) {
return iterator{std::end(*cont)};
}
return iterator{std::end(any_cast<const Type &>(container))};
}
[[nodiscard]] static iterator insert([[maybe_unused]] any &container, [[maybe_unused]] iterator it, [[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>>();
return iterator{cont->insert(any_cast<const typename Type::iterator &>(it.base()), element ? *element : value.cast<typename Type::value_type>())};
}
}
}
return {};
}
[[nodiscard]] static iterator erase([[maybe_unused]] any &container, [[maybe_unused]] iterator it) {
if constexpr(is_dynamic_sequence_container<Type>::value) {
if(auto *const cont = any_cast<Type>(&container); cont) {
return iterator{cont->erase(any_cast<const typename Type::iterator &>(it.base()))};
}
}
return {};
}
[[nodiscard]] static meta_any get(any &container, size_type pos) {
if(auto *const cont = any_cast<Type>(&container); cont) {
return meta_any{std::in_place_type<typename Type::reference>, (*cont)[pos]};
}
return meta_any{std::in_place_type<typename Type::const_reference>, any_cast<const Type &>(container)[pos]};
}
};
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 begin(any &container) {
if(auto *const cont = any_cast<Type>(&container); cont) {
return iterator{std::integral_constant<bool, key_only>{}, cont->begin()};
}
return iterator{std::integral_constant<bool, key_only>{}, std::begin(any_cast<const Type &>(container))};
}
[[nodiscard]] static iterator end(any &container) {
if(auto *const cont = any_cast<Type>(&container); cont) {
return iterator{std::integral_constant<bool, key_only>{}, cont->end()};
}
return iterator{std::integral_constant<bool, key_only>{}, std::end(any_cast<const Type &>(container))};
}
[[nodiscard]] static bool insert(any &container, meta_any &key, [[maybe_unused]] meta_any &value) {
if(auto *const cont = any_cast<Type>(&container); cont && key.allow_cast<const typename Type::key_type &>()) {
if constexpr(is_key_only_meta_associative_container<Type>::value) {
return cont->insert(key.cast<const typename Type::key_type &>()).second;
} else {
if(value.allow_cast<const typename Type::mapped_type &>()) {
return cont->emplace(key.cast<const typename Type::key_type &>(), value.cast<const typename Type::mapped_type &>()).second;
}
}
}
return false;
}
[[nodiscard]] static bool erase(any &container, meta_any &key) {
if(auto *const cont = any_cast<Type>(&container); cont && key.allow_cast<const typename Type::key_type &>()) {
return (cont->erase(key.cast<const typename Type::key_type &>()) != cont->size());
}
return false;
}
[[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 +199,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 +208,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 +218,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 +229,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 +238,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 +248,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_hash_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_hash_map<Key, Type, Args...>>
: internal::basic_meta_associative_container_traits<dense_hash_map<Key, Type, Args...>> {};
}
/**
* @brief Meta associative container traits for `dense_hash_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_hash_set<Type, Args...>>
: internal::basic_meta_associative_container_traits<dense_hash_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) {
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) {
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) {
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) {
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>) {}
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));
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) {
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) {
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) {
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) {
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) {
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{
{},
nullptr,
nullptr,
Setter::size,
/* 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,
internal::meta_node<std::remove_const_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()
: 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();
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));
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};
}
/**
@@ -213,53 +237,21 @@ 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 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;
}
return meta_factory<Type>{};
}
/**
* @brief Assigns a meta conversion function to a meta type.
*
* The given type must be such that an instance of the reflected type can be
* converted to it.
*
* @tparam To Type of the conversion function to assign to the meta type.
* @return A meta factory for the parent 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));
}
};
if(!internal::find_if(&node, type->conv)) {
node.next = type->conv;
type->conv = &node;
}
link_base_if_required(node);
return meta_factory<Type>{};
}
@@ -277,23 +269,40 @@ struct meta_factory<Type> {
*/
template<auto Candidate>
auto 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 std::invoke(Candidate, *static_cast<const Type *>(instance));
internal::meta_node<std::remove_const_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;
}
link_conv_if_required(node);
return meta_factory<Type>{};
}
/**
* @brief Assigns a meta conversion function to a meta type.
*
* The given type must be such that an instance of the reflected type can be
* converted to it.
*
* @tparam To Type of the conversion function to assign to the meta type.
* @return A meta factory for the parent type.
*/
template<typename To>
auto conv() ENTT_NOEXCEPT {
static_assert(std::is_convertible_v<Type, To>, "Could not convert to the required type");
static internal::meta_conv_node node{
nullptr,
internal::meta_node<std::remove_const_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
};
link_conv_if_required(node);
return meta_factory<Type>{};
}
@@ -313,28 +322,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(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy");
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 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>{};
}
/**
@@ -349,40 +349,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.
*
@@ -392,12 +384,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) {
std::invoke(Func, *static_cast<Type *>(instance));
};
owner->dtor = [](void *instance) { std::invoke(Func, *static_cast<Type *>(instance)); };
return meta_factory<Type>{};
}
@@ -417,31 +404,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,
nullptr,
nullptr,
std::is_same_v<Type, data_type> || std::is_const_v<data_type>,
true,
&internal::meta_info<data_type>::resolve,
1u,
/* this is never static */
std::is_const_v<data_type> ? internal::meta_traits::is_const : internal::meta_traits::is_none,
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));
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{
{},
nullptr,
nullptr,
1u,
((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,
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};
}
}
@@ -468,30 +465,68 @@ 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{
{},
nullptr,
nullptr,
0u,
/* this is never static */
internal::meta_traits::is_const,
internal::meta_node<std::remove_const_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));
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{
{},
nullptr,
nullptr,
1u,
/* this is never static nor const */
internal::meta_traits::is_none,
internal::meta_node<std::remove_const_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 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>{});
}
/**
@@ -510,44 +545,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,
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>{});
}
(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),
internal::meta_node<std::conditional_t<std::is_same_v<Policy, as_void_t>, void, std::remove_const_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.
@@ -562,13 +580,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,160 @@
#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);
meta_data_node *next;
meta_prop_node *prop;
const size_type arity;
const meta_traits traits;
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;
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 *);
const meta_traits traits;
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;
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;
const meta_traits traits;
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 +163,73 @@ 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>(),
{},
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)
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),
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_const_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) {
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,68 @@
#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 {
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 = default;
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 +70,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 {
/*! @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;
@@ -69,32 +86,39 @@ public:
* @param head The underlying node with which to construct the range.
*/
meta_range(node_type *head)
: node{head}
{}
: 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,10 +17,9 @@ namespace entt {
*/
template<typename Type>
[[nodiscard]] meta_type resolve() ENTT_NOEXCEPT {
return internal::meta_info<Type>::resolve();
return internal::meta_node<std::remove_const_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.
@@ -32,15 +28,14 @@ template<typename Type>
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,18 +1,14 @@
#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>
struct meta_class_template_tag {};
/**
* @brief General purpose traits class for generating meta template information.
* @tparam Clazz Type of class template.
@@ -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_const_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_const_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_const_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,25 +196,21 @@ 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)>>> || std::is_member_function_pointer_v<decltype(Data)>) {
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<!std::is_member_function_pointer_v<decltype(Data)>, typename descriptor::args_type>;
using data_type = type_list_element_t<descriptor::is_static, typename descriptor::args_type>;
if(auto * const clazz = instance->try_cast<Type>(); clazz) {
if(value.allow_cast<data_type>()) {
std::invoke(Data, *clazz, value.cast<data_type>());
return true;
}
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) {
if(value.allow_cast<data_type>()) {
std::invoke(Data, clazz) = value.cast<data_type>();
return true;
}
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 {
@@ -194,7 +228,6 @@ template<typename Type, auto Data>
return false;
}
/**
* @brief Gets the value of a given variable.
* @tparam Type Reflected type to which the variable is associated.
@@ -205,92 +238,159 @@ template<typename Type, auto Data>
*/
template<typename Type, auto Data, typename Policy = as_is_t>
[[nodiscard]] meta_any meta_getter([[maybe_unused]] meta_handle instance) {
[[maybe_unused]] auto dispatch = [](auto &&value) {
if constexpr(std::is_same_v<Policy, as_void_t>) {
return meta_any{std::in_place_type<void>, std::forward<decltype(value)>(value)};
} else if constexpr(std::is_same_v<Policy, as_ref_t>) {
return meta_any{std::reference_wrapper{std::forward<decltype(value)>(value)}};
} else if constexpr(std::is_same_v<Policy, as_cref_t>) {
return meta_any{std::cref(std::forward<decltype(value)>(value))};
} else {
static_assert(std::is_same_v<Policy, as_is_t>, "Policy not supported");
return meta_any{std::forward<decltype(value)>(value)};
}
};
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_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>> || 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 ? dispatch(std::invoke(Data, *clazz)) : 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)>>>) {
return meta_any{};
} else {
if(auto * clazz = instance->try_cast<Type>(); clazz) {
return dispatch(std::invoke(Data, *clazz));
} else {
auto * fallback = instance->try_cast<const Type>();
return fallback ? dispatch(std::invoke(Data, *fallback)) : meta_any{};
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));
}
}
}
return meta_any{};
} else if constexpr(std::is_pointer_v<decltype(Data)>) {
if constexpr(std::is_array_v<std::remove_pointer_t<decltype(Data)>>) {
return meta_any{};
} else {
return dispatch(*Data);
return meta_dispatch<Policy>(*Data);
}
} else {
return dispatch(Data);
return meta_dispatch<Policy>(Data);
}
}
/**
* @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]] 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)>;
auto dispatch = [](auto &&... params) {
if constexpr(std::is_void_v<std::remove_cv_t<typename descriptor::return_type>> || std::is_same_v<Policy, as_void_t>) {
std::invoke(Candidate, std::forward<decltype(params)>(params)...);
return meta_any{std::in_place_type<void>};
} else if constexpr(std::is_same_v<Policy, as_ref_t>) {
return meta_any{std::reference_wrapper{std::invoke(Candidate, std::forward<decltype(params)>(params)...)}};
} else if constexpr(std::is_same_v<Policy, as_cref_t>) {
return meta_any{std::cref(std::invoke(Candidate, std::forward<decltype(params)>(params)...))};
} else {
static_assert(std::is_same_v<Policy, as_is_t>, "Policy not supported");
return meta_any{std::invoke(Candidate, std::forward<decltype(params)>(params)...)};
}
};
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 dispatch(*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 dispatch(*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 dispatch((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
/**
* 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).
* @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>
[[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 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,17 +1,13 @@
#ifndef ENTT_POLY_FWD_HPP
#define ENTT_POLY_FWD_HPP
#include <type_traits>
namespace entt {
template<typename, std::size_t Len, 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.
@@ -19,8 +15,6 @@ class basic_poly;
template<typename Concept>
using poly = basic_poly<Concept, sizeof(double[2])>;
}
} // 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;
/**
@@ -34,14 +31,13 @@ struct poly_inspector {
* @return A poly inspector convertible to any type.
*/
template<auto Member, typename... Args>
poly_inspector invoke(Args &&... args) const;
poly_inspector invoke(Args &&...args) const;
/*! @copydoc invoke */
template<auto Member, typename... Args>
poly_inspector invoke(Args &&... 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)...));
-> 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...>) {
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...)) {
if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Args...>) {
entry = +[](Any &, Args... args) -> Ret {
return std::invoke(Candidate, std::forward<Args>(args)...);
@@ -95,14 +91,17 @@ class poly_vtable {
template<typename Type, auto... Index>
[[nodiscard]] static auto fill_vtable(std::index_sequence<Index...>) {
type impl{};
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() {
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.
@@ -133,20 +136,29 @@ struct poly_base {
* @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 {
[[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) {
[[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)>>) {
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.
@@ -157,11 +169,10 @@ struct poly_base {
* @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) {
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 +193,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,20 +211,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>>>()}
{}
/**
* @brief Constructs a poly that holds an unmanaged object.
* @tparam Type Type of object to use to initialize the poly.
* @param value An instance of an object to use to initialize the poly.
*/
template<typename Type>
basic_poly(std::reference_wrapper<Type> value)
: basic_poly{std::in_place_type<Type &>, value.get()}
{}
vtable{poly_vtable<Concept, Len, Align>::template instance<std::remove_const_t<std::remove_reference_t<Type>>>()} {}
/**
* @brief Constructs a poly from a given value.
@@ -223,40 +222,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();
}
@@ -264,12 +236,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();
}
@@ -280,13 +252,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_const_t<std::remove_reference_t<Type>>>();
}
/*! @brief Destroys contained object */
void reset() {
*this = basic_poly{};
storage.reset();
vtable = {};
}
/**
@@ -294,40 +268,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;
}
@@ -341,11 +305,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;
}
}
@@ -182,7 +180,7 @@ public:
*/
void abort(const bool immediately = false) {
if(alive()) {
current = state::ABORTED;
current = state::aborted;
if(immediately) {
tick({});
@@ -195,15 +193,15 @@ 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;
}
/**
* @brief Returns true if a process is already terminated.
* @return True if the process is terminated, false otherwise.
*/
[[nodiscard]] bool dead() const ENTT_NOEXCEPT {
return current == state::FINISHED;
[[nodiscard]] bool finished() const ENTT_NOEXCEPT {
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,16 @@
#ifndef ENTT_PROCESS_SCHEDULER_HPP
#define ENTT_PROCESS_SCHEDULER_HPP
#include <vector>
#include <memory>
#include <utility>
#include <algorithm>
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
#include "../config/config.h"
#include "process.hpp"
namespace entt {
/**
* @brief Cooperative scheduler for processes.
*
@@ -43,7 +40,7 @@ namespace entt {
template<typename Delta>
class scheduler {
struct process_handler {
using instance_type = std::unique_ptr<void, void(*)(void *)>;
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 next_type = std::unique_ptr<process_handler>;
@@ -56,13 +53,10 @@ class scheduler {
struct continuation {
continuation(process_handler *ref)
: handler{ref}
{
ENTT_ASSERT(handler);
}
: 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});
@@ -86,7 +80,7 @@ class scheduler {
if(process->rejected()) {
return true;
} else if(process->dead()) {
} else if(process->finished()) {
if(handler.next) {
handler = std::move(*handler.next);
// forces the process to exit the uninitialized state
@@ -120,7 +114,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.
@@ -174,7 +168,7 @@ 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};
@@ -255,7 +249,7 @@ public:
auto sz = handlers.size();
for(auto pos = handlers.size(); pos; --pos) {
auto &handler = handlers[pos-1];
auto &handler = handlers[pos - 1];
if(const auto dead = handler.update(handler, delta, data); dead) {
std::swap(handler, handlers[--sz]);
@@ -291,8 +285,6 @@ private:
std::vector<process_handler> handlers{};
};
}
} // namespace entt
#endif

View File

@@ -1,20 +1,18 @@
#ifndef ENTT_RESOURCE_CACHE_HPP
#define ENTT_RESOURCE_CACHE_HPP
#include <type_traits>
#include <unordered_map>
#include <utility>
#include "../config/config.h"
#include "../container/dense_hash_map.hpp"
#include "../core/fwd.hpp"
#include "../core/utility.hpp"
#include "fwd.hpp"
#include "handle.hpp"
#include "loader.hpp"
#include "fwd.hpp"
namespace entt {
/**
* @brief Simple cache for resources of a given type.
*
@@ -26,7 +24,10 @@ namespace entt {
* @tparam Resource Type of resources managed by a cache.
*/
template<typename Resource>
struct resource_cache {
class resource_cache {
static_assert(std::is_same_v<Resource, std::remove_const_t<std::remove_reference_t<Resource>>>, "Invalid resource type");
public:
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Type of resources managed by a cache. */
@@ -39,7 +40,7 @@ struct resource_cache {
resource_cache(resource_cache &&) = default;
/*! @brief Default move assignment operator. @return This cache. */
resource_cache & operator=(resource_cache &&) = default;
resource_cache &operator=(resource_cache &&) = default;
/**
* @brief Number of resources managed by a cache.
@@ -90,9 +91,7 @@ struct resource_cache {
* @return A handle for the given resource.
*/
template<typename Loader, typename... Args>
resource_handle<Resource> load(const id_type id, Args &&... args) {
static_assert(std::is_base_of_v<resource_loader<Loader, Resource>, Loader>, "Invalid loader type");
resource_handle<resource_type> 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));
@@ -128,7 +127,7 @@ struct resource_cache {
* @return A handle for the given resource.
*/
template<typename Loader, typename... Args>
resource_handle<Resource> reload(const id_type id, Args &&... args) {
resource_handle<resource_type> reload(const id_type id, Args &&...args) {
return (discard(id), load<Loader>(id, std::forward<Args>(args)...));
}
@@ -145,7 +144,7 @@ struct resource_cache {
* @return A handle for the given resource.
*/
template<typename Loader, typename... Args>
[[nodiscard]] resource_handle<Resource> temp(Args &&... args) const {
[[nodiscard]] resource_handle<resource_type> temp(Args &&...args) const {
return Loader{}.get(std::forward<Args>(args)...);
}
@@ -162,7 +161,7 @@ struct resource_cache {
* @param id Unique resource identifier.
* @return A handle for the given resource.
*/
[[nodiscard]] resource_handle<Resource> handle(const id_type id) const {
[[nodiscard]] resource_handle<const resource_type> handle(const id_type id) const {
if(auto it = resources.find(id); it != resources.cend()) {
return it->second;
}
@@ -170,6 +169,15 @@ struct resource_cache {
return {};
}
/*! @copydoc handle */
[[nodiscard]] resource_handle<resource_type> handle(const id_type id) {
if(auto it = resources.find(id); it != resources.end()) {
return it->second;
}
return {};
}
/**
* @brief Checks if a cache contains a given identifier.
* @param id Unique resource identifier.
@@ -203,14 +211,14 @@ struct resource_cache {
*
* @code{.cpp}
* void(const entt::id_type);
* void(entt::resource_handle<Resource>);
* void(const entt::id_type, entt::resource_handle<Resource>);
* void(entt::resource_handle<const resource_type>);
* void(const entt::id_type, entt::resource_handle<const resource_type>);
* @endcode
*
* @tparam Func Type of the function object to invoke.
* @param func A valid function object.
*/
template <typename Func>
template<typename Func>
void each(Func func) const {
auto begin = resources.begin();
auto end = resources.end();
@@ -220,7 +228,42 @@ struct resource_cache {
if constexpr(std::is_invocable_v<Func, id_type>) {
func(curr->first);
} else if constexpr(std::is_invocable_v<Func, resource_handle<Resource>>) {
} else if constexpr(std::is_invocable_v<Func, resource_handle<const resource_type>>) {
func(resource_handle<const resource_type>{curr->second});
} else {
func(curr->first, resource_handle<const resource_type>{curr->second});
}
}
}
/**
* @copybrief each
*
* 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_type>);
* void(const entt::id_type, entt::resource_handle<resource_type>);
* @endcode
*
* @tparam Func Type of the function object to invoke.
* @param func A valid function object.
*/
template<typename Func>
void each(Func func) {
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_type>>) {
func(curr->second);
} else {
func(curr->first, curr->second);
@@ -229,11 +272,9 @@ struct resource_cache {
}
private:
std::unordered_map<id_type, resource_handle<Resource>> resources;
dense_hash_map<id_type, resource_handle<resource_type>, identity> resources;
};
}
} // namespace entt
#endif

View File

@@ -1,23 +1,17 @@
#ifndef ENTT_RESOURCE_FWD_HPP
#define ENTT_RESOURCE_FWD_HPP
namespace entt {
template<typename>
class resource_cache;
template<typename>
struct resource_cache;
template<typename>
struct resource_handle;
class resource_handle;
template<typename, typename>
class resource_loader;
}
} // namespace entt
#endif

View File

@@ -1,16 +1,14 @@
#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.
*
@@ -24,7 +22,17 @@ namespace entt {
* @tparam Resource Type of resource managed by a handle.
*/
template<typename Resource>
struct resource_handle {
class resource_handle {
/*! @brief Resource handles are friends with each other. */
template<typename>
friend class resource_handle;
public:
/*! @brief Unsigned integer type. */
using size_type = long;
/*! @brief Type of resources managed by a cache. */
using resource_type = Resource;
/*! @brief Default constructor. */
resource_handle() ENTT_NOEXCEPT = default;
@@ -32,9 +40,88 @@ struct resource_handle {
* @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)}
{}
resource_handle(std::shared_ptr<resource_type> res) ENTT_NOEXCEPT
: resource{std::move(res)} {}
/**
* @brief Copy constructor.
* @param other The instance to copy from.
*/
resource_handle(const resource_handle &other) ENTT_NOEXCEPT = default;
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
resource_handle(resource_handle &&other) ENTT_NOEXCEPT = default;
/**
* @brief Aliasing constructor.
* @tparam Other Type of resource managed by the received handle.
* @param other The handle with which to share ownership information.
* @param res Unrelated and unmanaged resources.
*/
template<typename Other>
resource_handle(const resource_handle<Other> &other, resource_type &res) noexcept
: resource{other.resource, std::addressof(res)} {}
/**
* @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<resource_type, Other> && std::is_base_of_v<resource_type, 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<resource_type, Other> && std::is_base_of_v<resource_type, 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 &other) ENTT_NOEXCEPT = default;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This resource handle.
*/
resource_handle &operator=(resource_handle &&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<resource_type, Other> && std::is_base_of_v<resource_type, 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<resource_type, Other> && std::is_base_of_v<resource_type, 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.
@@ -44,33 +131,17 @@ struct resource_handle {
*
* @return A reference to the managed resource.
*/
[[nodiscard]] const Resource & get() const ENTT_NOEXCEPT {
ENTT_ASSERT(static_cast<bool>(resource));
[[nodiscard]] resource_type &get() const ENTT_NOEXCEPT {
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 {
[[nodiscard]] operator resource_type &() 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 {
[[nodiscard]] resource_type &operator*() const ENTT_NOEXCEPT {
return get();
}
@@ -83,16 +154,10 @@ struct resource_handle {
* @return A pointer to the managed resource or `nullptr` if the handle
* contains no resource at all.
*/
[[nodiscard]] const Resource * operator->() const ENTT_NOEXCEPT {
ENTT_ASSERT(static_cast<bool>(resource));
[[nodiscard]] resource_type *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.
@@ -101,12 +166,18 @@ struct resource_handle {
return static_cast<bool>(resource);
}
/**
* @brief Returns the number of handles pointing the same resource.
* @return The number of handles pointing the same resource.
*/
[[nodiscard]] size_type use_count() const ENTT_NOEXCEPT {
return resource.use_count();
}
private:
std::shared_ptr<Resource> resource;
std::shared_ptr<resource_type> resource;
};
}
} // namespace entt
#endif

View File

@@ -1,14 +1,11 @@
#ifndef ENTT_RESOURCE_LOADER_HPP
#define ENTT_RESOURCE_LOADER_HPP
#include "fwd.hpp"
#include "handle.hpp"
namespace entt {
/**
* @brief Base class for resource loaders.
*
@@ -44,7 +41,8 @@ namespace entt {
template<typename Loader, typename Resource>
class resource_loader {
/*! @brief Resource loaders are friends of their caches. */
friend struct resource_cache<Resource>;
template<typename Other>
friend class resource_cache;
/**
* @brief Loads the resource and returns it.
@@ -53,13 +51,11 @@ class resource_loader {
* @return The resource just loaded or an empty pointer in case of errors.
*/
template<typename... Args>
[[nodiscard]] resource_handle<Resource> get(Args &&... args) const {
[[nodiscard]] resource_handle<Resource> get(Args &&...args) const {
return static_cast<const Loader *>(this)->load(std::forward<Args>(args)...);
}
};
}
} // namespace entt
#endif

View File

@@ -1,77 +1,61 @@
#ifndef ENTT_SIGNAL_DELEGATE_HPP
#define ENTT_SIGNAL_DELEGATE_HPP
#include <tuple>
#include <cstddef>
#include <utility>
#include <functional>
#include <tuple>
#include <type_traits>
#include "../core/type_traits.hpp"
#include <utility>
#include "../config/config.h"
#include "../core/type_traits.hpp"
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename Ret, typename... Args>
auto function_pointer(Ret(*)(Args...)) -> Ret(*)(Args...);
auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...);
template<typename Ret, typename Type, typename... Args, typename Other>
auto function_pointer(Ret(*)(Type, Args...), Other &&) -> Ret(*)(Args...);
auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...);
template<typename Class, typename Ret, typename... Args, typename... Other>
auto function_pointer(Ret(Class:: *)(Args...), Other &&...) -> Ret(*)(Args...);
auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...);
template<typename Class, typename Ret, typename... Args, typename... Other>
auto function_pointer(Ret(Class:: *)(Args...) const, Other &&...) -> Ret(*)(Args...);
auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...);
template<typename Class, typename Type, typename... Other>
auto function_pointer(Type Class:: *, Other &&...) -> Type(*)();
auto function_pointer(Type Class::*, Other &&...) -> Type (*)();
template<typename... Type>
using function_pointer_t = decltype(internal::function_pointer(std::declval<Type>()...));
template<typename... Class, typename Ret, typename... Args>
[[nodiscard]] constexpr auto index_sequence_for(Ret(*)(Args...)) {
[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) {
return std::index_sequence_for<Class..., Args...>{};
}
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @brief Used to wrap a function or a member of a specified type. */
template<auto>
struct connect_arg_t {};
/*! @brief Constant of type connect_arg_t used to disambiguate calls. */
template<auto Func>
inline constexpr connect_arg_t<Func> connect_arg{};
/**
* @brief Basic delegate implementation.
*
@@ -81,7 +65,6 @@ inline constexpr connect_arg_t<Func> connect_arg{};
template<typename>
class delegate;
/**
* @brief Utility class to use to send around functions and members.
*
@@ -132,8 +115,8 @@ public:
/*! @brief Default constructor. */
delegate() ENTT_NOEXCEPT
: fn{nullptr}, data{nullptr}
{}
: fn{nullptr},
data{nullptr} {}
/**
* @brief Constructs a delegate and connects a free function or an unbound
@@ -271,7 +254,7 @@ public:
* @brief Returns the instance or the payload linked to a delegate, if any.
* @return An opaque pointer to the underlying data.
*/
[[nodiscard]] const void * instance() const ENTT_NOEXCEPT {
[[nodiscard]] const void *instance() const ENTT_NOEXCEPT {
return data;
}
@@ -288,7 +271,7 @@ public:
* @return The value returned by the underlying function.
*/
Ret operator()(Args... args) const {
ENTT_ASSERT(static_cast<bool>(*this));
ENTT_ASSERT(static_cast<bool>(*this), "Uninitialized delegate");
return fn(data, std::forward<Args>(args)...);
}
@@ -315,7 +298,6 @@ private:
const void *data;
};
/**
* @brief Compares the contents of two delegates.
* @tparam Ret Return type of a function type.
@@ -329,15 +311,12 @@ template<typename Ret, typename... Args>
return !(lhs == rhs);
}
/**
* @brief Deduction guide.
* @tparam Candidate Function or member to connect to the delegate.
*/
template<auto Candidate>
delegate(connect_arg_t<Candidate>)
-> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate)>>>;
delegate(connect_arg_t<Candidate>) -> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate)>>>;
/**
* @brief Deduction guide.
@@ -345,9 +324,7 @@ delegate(connect_arg_t<Candidate>)
* @tparam Type Type of class or type of payload.
*/
template<auto Candidate, typename Type>
delegate(connect_arg_t<Candidate>, Type &&)
-> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate), Type>>>;
delegate(connect_arg_t<Candidate>, Type &&) -> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate), Type>>>;
/**
* @brief Deduction guide.
@@ -355,11 +332,8 @@ delegate(connect_arg_t<Candidate>, Type &&)
* @tparam Args Types of arguments of a function type.
*/
template<typename Ret, typename... Args>
delegate(Ret(*)(const void *, Args...), const void * = nullptr)
-> delegate<Ret(Args...)>;
}
delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate<Ret(Args...)>;
} // namespace entt
#endif

View File

@@ -1,21 +1,20 @@
#ifndef ENTT_SIGNAL_DISPATCHER_HPP
#define ENTT_SIGNAL_DISPATCHER_HPP
#include <cstddef>
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../container/dense_hash_map.hpp"
#include "../core/fwd.hpp"
#include "../core/type_info.hpp"
#include "../core/utility.hpp"
#include "sigh.hpp"
namespace entt {
/**
* @brief Basic dispatcher implementation.
*
@@ -50,29 +49,29 @@ class dispatcher {
signal.publish(events[pos]);
}
events.erase(events.cbegin(), events.cbegin()+length);
events.erase(events.cbegin(), events.cbegin() + length);
}
void disconnect(void *instance) override {
sink().disconnect(instance);
bucket().disconnect(instance);
}
void clear() ENTT_NOEXCEPT override {
events.clear();
}
[[nodiscard]] sink_type sink() ENTT_NOEXCEPT {
return entt::sink{signal};
[[nodiscard]] sink_type bucket() ENTT_NOEXCEPT {
return sink_type{signal};
}
template<typename... Args>
void trigger(Args &&... args) {
void trigger(Args &&...args) {
Event instance{std::forward<Args>(args)...};
signal.publish(instance);
}
template<typename... Args>
void enqueue(Args &&... args) {
void enqueue(Args &&...args) {
if constexpr(std::is_aggregate_v<Event>) {
events.push_back(Event{std::forward<Args>(args)...});
} else {
@@ -86,18 +85,14 @@ class dispatcher {
};
template<typename Event>
[[nodiscard]] pool_handler<Event> & assure() {
const auto index = type_seq<Event>::value();
if(!(index < pools.size())) {
pools.resize(std::size_t(index)+1u);
[[nodiscard]] pool_handler<Event> &assure() {
if(auto &&ptr = pools[type_hash<Event>::value()]; !ptr) {
auto *cpool = new pool_handler<Event>{};
ptr.reset(cpool);
return *cpool;
} else {
return static_cast<pool_handler<Event> &>(*ptr);
}
if(!pools[index]) {
pools[index].reset(new pool_handler<Event>{});
}
return static_cast<pool_handler<Event> &>(*pools[index]);
}
public:
@@ -108,7 +103,7 @@ public:
dispatcher(dispatcher &&) = default;
/*! @brief Default move assignment operator. @return This dispatcher. */
dispatcher & operator=(dispatcher &&) = default;
dispatcher &operator=(dispatcher &&) = default;
/**
* @brief Returns a sink object for the given event.
@@ -129,7 +124,7 @@ public:
*/
template<typename Event>
[[nodiscard]] auto sink() {
return assure<Event>().sink();
return assure<Event>().bucket();
}
/**
@@ -143,7 +138,7 @@ public:
* @param args Arguments to use to construct the event.
*/
template<typename Event, typename... Args>
void trigger(Args &&... args) {
void trigger(Args &&...args) {
assure<Event>().trigger(std::forward<Args>(args)...);
}
@@ -172,7 +167,7 @@ public:
* @param args Arguments to use to construct the event.
*/
template<typename Event, typename... Args>
void enqueue(Args &&... args) {
void enqueue(Args &&...args) {
assure<Event>().enqueue(std::forward<Args>(args)...);
}
@@ -210,9 +205,7 @@ public:
template<typename Type>
void disconnect(Type *value_or_instance) {
for(auto &&cpool: pools) {
if(cpool) {
cpool->disconnect(value_or_instance);
}
cpool.second->disconnect(value_or_instance);
}
}
@@ -228,9 +221,7 @@ public:
void clear() {
if constexpr(sizeof...(Event) == 0) {
for(auto &&cpool: pools) {
if(cpool) {
cpool->clear();
}
cpool.second->clear();
}
} else {
(assure<Event>().clear(), ...);
@@ -259,19 +250,15 @@ public:
* to reduce at a minimum the time spent in the bodies of the listeners.
*/
void update() const {
for(auto pos = pools.size(); pos; --pos) {
if(auto &&cpool = pools[pos-1]; cpool) {
cpool->publish();
}
for(auto &&cpool: pools) {
cpool.second->publish();
}
}
private:
std::vector<std::unique_ptr<basic_pool>> pools;
dense_hash_map<id_type, std::unique_ptr<basic_pool>, identity> pools;
};
}
} // namespace entt
#endif

View File

@@ -1,7 +1,6 @@
#ifndef ENTT_SIGNAL_EMITTER_HPP
#define ENTT_SIGNAL_EMITTER_HPP
#include <algorithm>
#include <functional>
#include <iterator>
@@ -9,15 +8,14 @@
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../container/dense_hash_map.hpp"
#include "../core/fwd.hpp"
#include "../core/type_info.hpp"
#include "../core/utility.hpp"
namespace entt {
/**
* @brief General purpose event emitter.
*
@@ -59,8 +57,8 @@ class emitter {
[[nodiscard]] bool empty() const ENTT_NOEXCEPT override {
auto pred = [](auto &&element) { return element.first; };
return std::all_of(once_list.cbegin(), once_list.cend(), pred) &&
std::all_of(on_list.cbegin(), on_list.cend(), pred);
return std::all_of(once_list.cbegin(), once_list.cend(), pred)
&& std::all_of(on_list.cbegin(), on_list.cend(), pred);
}
void clear() ENTT_NOEXCEPT override {
@@ -122,24 +120,20 @@ class emitter {
};
template<typename Event>
[[nodiscard]] pool_handler<Event> * assure() {
const auto index = type_seq<Event>::value();
if(!(index < pools.size())) {
pools.resize(std::size_t(index)+1u);
[[nodiscard]] pool_handler<Event> *assure() {
if(auto &&ptr = pools[type_hash<Event>::value()]; !ptr) {
auto *cpool = new pool_handler<Event>{};
ptr.reset(cpool);
return cpool;
} else {
return static_cast<pool_handler<Event> *>(ptr.get());
}
if(!pools[index]) {
pools[index].reset(new pool_handler<Event>{});
}
return static_cast<pool_handler<Event> *>(pools[index].get());
}
template<typename Event>
[[nodiscard]] const pool_handler<Event> * assure() const {
const auto index = type_seq<Event>::value();
return (!(index < pools.size()) || !pools[index]) ? nullptr : static_cast<const pool_handler<Event> *>(pools[index].get());
[[nodiscard]] const pool_handler<Event> *assure() const {
const auto it = pools.find(type_hash<Event>::value());
return (it == pools.cend()) ? nullptr : static_cast<const pool_handler<Event> *>(it->second.get());
}
public:
@@ -169,8 +163,7 @@ public:
* @param conn A connection object to wrap.
*/
connection(typename pool_handler<Event>::connection_type conn)
: pool_handler<Event>::connection_type{std::move(conn)}
{}
: pool_handler<Event>::connection_type{std::move(conn)} {}
};
/*! @brief Default constructor. */
@@ -185,7 +178,7 @@ public:
emitter(emitter &&) = default;
/*! @brief Default move assignment operator. @return This emitter. */
emitter & operator=(emitter &&) = default;
emitter &operator=(emitter &&) = default;
/**
* @brief Emits the given event.
@@ -199,7 +192,7 @@ public:
* @param args Parameters to use to initialize the event.
*/
template<typename Event, typename... Args>
void publish(Args &&... args) {
void publish(Args &&...args) {
Event instance{std::forward<Args>(args)...};
assure<Event>()->publish(instance, *static_cast<Derived *>(this));
}
@@ -289,9 +282,7 @@ public:
*/
void clear() ENTT_NOEXCEPT {
for(auto &&cpool: pools) {
if(cpool) {
cpool->clear();
}
cpool.second->clear();
}
}
@@ -312,16 +303,14 @@ public:
*/
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
return std::all_of(pools.cbegin(), pools.cend(), [](auto &&cpool) {
return !cpool || cpool->empty();
return cpool.second->empty();
});
}
private:
std::vector<std::unique_ptr<basic_pool>> pools{};
dense_hash_map<id_type, std::unique_ptr<basic_pool>, identity> pools{};
};
}
} // namespace entt
#endif

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