Compare commits

..

834 Commits

Author SHA1 Message Date
Michele Caini
fc1ff11352 updated single include file 2020-08-31 22:47:23 +02:00
Michele Caini
0595ba384e *: suppress some warnings 2020-08-29 21:59:44 +02:00
Michele Caini
f80f5e9955 doc: updated links 2020-08-29 16:14:11 +02:00
Michele Caini
f689014c3e emitter: the event isn't const by default anymore (close #547) 2020-08-21 18:09:05 +02:00
Michele Caini
9b24655bbf dispatcher: the event isn't const by default anymore (see #547) 2020-08-21 18:02:41 +02:00
Michele Caini
c3201070a1 doc: updated FAQs (close #543) 2020-08-21 16:16:40 +02:00
Michele Caini
8a146209e4 sigh: updated documentation 2020-08-21 15:59:56 +02:00
Michele Caini
2b719726cd view: suppress warnings 2020-08-21 12:24:34 +02:00
Michele Caini
27209e4836 doc: updated links 2020-08-19 16:39:24 +02:00
Michele Caini
0497762f94 doc: typo 2020-08-19 10:41:01 +02:00
Michele Caini
b38f1e744b doc: updated README - EnTT runs everywhere 2020-08-19 10:15:16 +02:00
Michele Caini
481c49e18e doc: added a new link to similar projects 2020-08-19 10:06:38 +02:00
Michele Caini
69cf9115b5 doc: added a new link to the list of showcases (close #546) 2020-08-19 10:02:31 +02:00
Michele Caini
3eedd89efe config/core/entity: added is_eto_eligible[_v] and used it where it makes sense 2020-08-17 18:21:21 +02:00
Michele Caini
ad262ea624 entity/*: suppress some warnings here and there 2020-08-16 12:10:55 +02:00
Michele Caini
08cd8c95f7 updated todo list 2020-08-16 11:25:28 +02:00
Michele Caini
c86790c6b8 test: minor changes 2020-08-16 11:24:57 +02:00
Michele Caini
e466d9252f view: rebind the view only when possible 2020-08-14 12:12:57 +02:00
Michele Caini
02f8a0baa6 view: stable candidate computed on construction 2020-08-14 11:58:10 +02:00
Michele Caini
9f7bbe3f9d doc: updated links 2020-08-14 11:02:37 +02:00
Michele Caini
e81e0e97e0 meta: workaround for an issue of VS2019 (see #314) 2020-08-12 09:05:20 +02:00
Michele Caini
52554c3972 test: suppress a bunch of warnings 2020-08-11 16:53:03 +02:00
Michele Caini
48b6c4876a runtime view: suppress warnings for shadow variables 2020-08-11 16:52:24 +02:00
Michele Caini
99e89c2540 registry: suppress warnings for shadow variables 2020-08-11 16:52:06 +02:00
Michele Caini
0e8453d4ed entt_traits: updated difference types 2020-08-11 15:27:41 +02:00
Michele Caini
c750b746b3 meta: explicit return type to get around an issue with VS2017 2020-08-10 18:16:26 +02:00
Michele Caini
0ac07e2e83 meta: reset is now part of meta_type 2020-08-10 18:15:11 +02:00
Michele Caini
ead6cb9ca3 test: suppress warnings 2020-08-10 10:04:14 +02:00
Michele Caini
c401f9211d test: minor changes 2020-08-10 10:04:14 +02:00
Michele Caini
abb72c0a9a view: rbegin/rend 2020-08-10 10:04:13 +02:00
Michele Caini
bc9172bd43 doc: updated links 2020-08-10 10:04:13 +02:00
Michele Caini
b205a5e49d group: rbegin/rend 2020-08-10 10:04:13 +02:00
Michele Caini
fccf9642d8 view: prepare multi component views for reverse iterators 2020-08-10 10:04:13 +02:00
Michele Caini
96dd68f52b doc: typo 2020-08-10 10:04:12 +02:00
Michele Caini
7f923bb389 doc: added more details about observers (close #536) 2020-08-10 10:04:12 +02:00
Michele Caini
7ab7ebb3f6 doc: added a note about in-place sorting 2020-08-10 10:04:12 +02:00
Michele Caini
4eacb58a3e doc: minor changes 2020-08-10 10:04:12 +02:00
Michele Caini
24248832d1 group/view: explicit proxy object 2020-08-10 10:04:11 +02:00
Michele Caini
a53f0900bd view/group: make range return types explicit in preparation for faster/unsafe reverse iterators 2020-08-10 10:04:11 +02:00
Michele Caini
6a1a5a0f74 sparse set: minor changes 2020-08-10 10:04:10 +02:00
Michele Caini
65aa4e145e registry: minor changes 2020-08-10 10:04:10 +02:00
Michele Caini
cf903aa082 storage: added reverse iterators 2020-08-10 10:04:09 +02:00
Michele Caini
75dd86e8ef sparse set: added reverse iterators 2020-08-10 10:04:09 +02:00
Michele Caini
70a3d62c14 doc: udpated links 2020-08-10 10:04:08 +02:00
Michele Caini
291e3cd229 view: minor changes 2020-08-10 10:04:08 +02:00
Michele Caini
9b28c1fdd2 runtime view: suppress warning (close #540) 2020-08-10 10:01:09 +02:00
Michele Caini
d1d73da039 doc: fixed typo (close #534) 2020-07-28 21:51:59 +02:00
Michele Caini
59f041b9a9 view: minor changes 2020-07-27 10:50:11 +02:00
Michele Caini
fbf3b9ce4d doc: help doxygen not to go crazy (thanks to @erez-o for pointing this out) 2020-07-27 00:07:11 +02:00
Michele Caini
9be2e1c4c9 sigh: don't use std::forward with fake forwarding references 2020-07-26 01:09:58 +02:00
Michele Caini
a06c9f890c meta: cleanup 2020-07-25 23:35:52 +02:00
cugone
fc8e8874a9 *: fix C4003 NOMINMAX issue (#531) 2020-07-25 23:09:26 +02:00
Michele Caini
09e0f68616 view: suppress shadow warnings 2020-07-25 23:02:52 +02:00
Michele Caini
0e7e36f80d view: chunked iteration for multi-component views (close #462) 2020-07-25 18:47:20 +02:00
Michele Caini
8484dcd332 doc: updated links 2020-07-24 11:00:33 +02:00
Michele Caini
8a46296b48 meta: simplified iterators for meta containers 2020-07-24 11:00:33 +02:00
Michele Caini
b748a910b2 meta: fix an issue with meta_associative_container::meta_iterator::value 2020-07-24 11:00:33 +02:00
Michele Caini
932603eea3 *: minor changes 2020-07-24 11:00:33 +02:00
Michele Caini
5056972f79 meta: ctors and funcs also accept a meta_any * + size pair (close #528) 2020-07-24 11:00:33 +02:00
Michele Caini
d99ccca291 meta: minor changes 2020-07-24 11:00:33 +02:00
Sackhorn
1d9b26d9ec doc: backslash typo in FAQ (#532) 2020-07-24 11:00:12 +02:00
Michele Caini
3a64707c6e doc: updated links 2020-07-21 15:43:58 +02:00
Michele Caini
6f4071bd75 view: check filter array correctly (close #527) 2020-07-21 13:12:51 +02:00
Michele Caini
1354fdc613 *: suppress some warnings here and there (close #525) 2020-07-20 00:20:55 +02:00
Michele Caini
991244655a dispatcher: opaque disconnect (close #523) 2020-07-19 12:18:15 +02:00
Michele Caini
0d07d935bf *: removed redundant namespace names 2020-07-19 12:13:51 +02:00
Michele Caini
bd56b642a7 doc: updated links 2020-07-19 11:03:06 +02:00
Sackhorn
d989f0edf2 Update entity.md (#524)
Small typo fix in wiki
2020-07-19 10:20:52 +02:00
Michele Caini
f561cf9c8f build system: suppress wrong warning 2020-07-16 18:03:16 +02:00
Michele Caini
13502e5467 entity: explicit casts to suppress misleading warnings 2020-07-16 17:48:29 +02:00
Michele Caini
045c2a6f05 delegate: removed redundant calls to delegate constructor 2020-07-15 18:13:44 +02:00
Michele Caini
725e9feb68 entity: removed unused constructors from range iterators (being default constructible isn't a requirement for input iterators) 2020-07-15 17:33:21 +02:00
Michele Caini
6e45126f53 test: added explicit test for ENTT_NO_ETO 2020-07-15 16:09:41 +02:00
Michele Caini
6ebc39e038 entity: added sfinae-friendly utility for component-to-pool conversions 2020-07-14 18:25:01 +02:00
Michele Caini
8b0a7c302b storage: workaround for an issue of vs2017 2020-07-14 17:46:08 +02:00
Michele Caini
0c3e50da2f build system: try to reproduce an issue of vs2019, toolset v141 2020-07-14 15:11:39 +02:00
Michele Caini
d220cf4acc platform: minor changes 2020-07-13 23:56:01 +02:00
Innokentiy Alaytsev
eb1ce7e927 meta: Deduplicate the STL-compatible container traits (#518) 2020-07-13 23:55:19 +02:00
Michele Caini
46f6f41a6b entity: remove asserts from to_entity, it's potentially UB as from the standard 2020-07-12 23:53:20 +02:00
Michele Caini
2fa59fc43c entity: added debug asserts to to_entity 2020-07-12 16:45:00 +02:00
Michele Caini
b7763b3887 test: minor changes 2020-07-11 17:59:23 +02:00
Michele Caini
8c002b67d5 runtime view: support for excluded components (close #512) 2020-07-11 17:26:20 +02:00
Michele Caini
7692c4a377 test: ENTT_ID_TYPE -> entt::id_type 2020-07-11 15:56:30 +02:00
Michele Caini
1f0acec06c doc: minor changes 2020-07-11 15:07:23 +02:00
Michele Caini
1b5295a8fe helper: added to_entity function that returns the entity associated with a given component 2020-07-11 12:32:58 +02:00
Michele Caini
0b699a3f20 view: fixed the constrained range-each 2020-07-10 17:54:07 +02:00
Michele Caini
409d7a499d test: code coverage 2020-07-10 17:45:17 +02:00
Michele Caini
ba7428f3d6 group: small changes to get around an issue of g++ v7.5 2020-07-10 17:35:57 +02:00
Michele Caini
c12c32d26e view/group: updated range iterators (thanks to @Lawrencemm for pointing this out) 2020-07-10 15:37:43 +02:00
Michele Caini
0ca5ea0150 test: minor changes 2020-07-10 15:09:40 +02:00
Michele Caini
11ca7c7b3e group: get around an issue with g++ 2020-07-09 19:20:09 +02:00
Michele Caini
8f6c47527d doc: added a note about ranges for views and groups (close #502) 2020-07-09 18:40:41 +02:00
Michele Caini
dd28882b27 group: optimized ranges 2020-07-09 18:18:07 +02:00
Michele Caini
1b93a449b2 view: improved view ranges 2020-07-09 18:09:38 +02:00
Michele Caini
7cf2ec09b7 group: added iterable object to visit entities and components at once 2020-07-09 16:58:26 +02:00
Michele Caini
4d1952a406 view: added iterable object to visit entities and components at once 2020-07-09 16:58:07 +02:00
Michele Caini
b8d25e2327 doc: typos 2020-07-08 18:07:26 +02:00
Michele Caini
89d7c7e572 handle: better API 2020-07-06 09:39:23 +02:00
Michele Caini
75b19230eb entity: make null_t public 2020-07-06 09:24:56 +02:00
Indiana Kernick
7889ca1ca7 Non-owning entt::handle (#513) 2020-07-04 18:00:44 +02:00
Michele Caini
cec1b932bd example: custom identifier (and some fixes for the purpose) 2020-07-02 16:56:49 +02:00
Michele Caini
2c7455ea2b benchmark: use entt::id_type instead of ENTT_ID_TYPE 2020-07-02 15:07:20 +02:00
Michele Caini
edc9cc9278 type_traits: removed ENTT_OPAQUE_TYPE 2020-07-01 18:41:20 +02:00
Michele Caini
51eafaeb4f doc: mention custom types for entity identifiers 2020-07-01 18:41:00 +02:00
Michele Caini
556373d331 registry: better support for integral entity identifiers (even if not recommended) 2020-07-01 18:39:34 +02:00
Michele Caini
804cfb7482 test: use entt::to_integral instead of std::underlying_type_t for entity identifiers 2020-07-01 17:17:33 +02:00
Michele Caini
143eae0729 test: avoid using underlying_type_t with entt_traits 2020-07-01 17:10:42 +02:00
Michele Caini
29d4846de7 entity: bind to_integral to entt_traits instead of a specific enum class 2020-07-01 16:28:56 +02:00
Michele Caini
306178f371 doc: minor changes 2020-07-01 16:06:08 +02:00
Michele Caini
371b541fbc entity: make entt_traits sfinae-friendly and avoid using std::underlying_type_t to define the entity traits types 2020-07-01 15:32:04 +02:00
Oortonaut
902658fd21 Properly case entity version for 64-bit support. (#516) 2020-07-01 10:51:26 +02:00
Michele Caini
0ca8dff974 test: code coverage 2020-06-30 17:50:36 +02:00
Michele Caini
a212b054e7 platform: added support file for android ndk r17 2020-06-30 16:47:27 +02:00
Michele Caini
cccd1baa2f meta: minor changes 2020-06-26 16:04:09 +02:00
Michele Caini
5dd25aed4d meta: assoc container's ::erase/::find accept also convertible keys 2020-06-26 14:59:35 +02:00
Michele Caini
6ddc725b75 meta: minor changes 2020-06-26 12:51:44 +02:00
Michele Caini
36ab7444a0 meta: removed some deprecated functions (semantics had changed, it was already a breaking change in itself) 2020-06-26 12:43:51 +02:00
Michele Caini
a8d004334a meta: minor changes 2020-06-26 12:24:28 +02:00
Michele Caini
900398a632 resource: added prefix resource_ to all classes 2020-06-25 23:37:50 +02:00
Michele Caini
f24bb8737d deprecate actor 2020-06-25 22:55:35 +02:00
Michele Caini
cb5e9b197a doc: added links to docsforge (close #510) 2020-06-24 09:44:54 +02:00
Michele Caini
450e20ae78 meta: minor changes 2020-06-23 11:31:04 +02:00
Michele Caini
5d42a76fe5 meta: added support for convertible types to meta containers ::insert functions 2020-06-23 10:55:40 +02:00
Michele Caini
c5075a3db0 doc: minor changes (see #510) 2020-06-23 08:49:17 +02:00
Michele Caini
ad5bb5198b meta: fixed key_only member function for meta associative containers 2020-06-22 17:02:45 +02:00
Michele Caini
d306bc4a9a test: code coverage 2020-06-22 12:47:10 +02:00
Michele Caini
5c3b956542 doc: fixed some problems with doxygen (see #510) 2020-06-22 12:11:36 +02:00
Michele Caini
ade8533f28 meta: dedicate iterators for different types of meta containers 2020-06-22 11:07:57 +02:00
Michele Caini
ad3413d6ad meta: added is_key_only_meta_associative_container[_v] to type traits 2020-06-22 10:19:06 +02:00
skypjack
f3d0e3f4dd test: fixed tests for meta containers 2020-06-22 09:32:00 +02:00
Michele Caini
6e2e030ba7 meta: updated documentation 2020-06-22 01:05:48 +02:00
Michele Caini
613f993638 core/type_traits: removed is_dereferenceable[_v] 2020-06-21 23:42:07 +02:00
Michele Caini
2566e3631b meta: support for pointer-like types 2020-06-21 23:39:26 +02:00
Michele Caini
4ad6c0a8a9 meta: meta_sequence_container_traits_t -> meta_sequence_container_traits 2020-06-21 23:13:20 +02:00
Michele Caini
0369ac450e meta: added meta sequence container resize + [[nodiscard]] here and there 2020-06-21 23:08:14 +02:00
Michele Caini
ab16680cc8 type_traits: removed container detectors 2020-06-20 01:20:22 +02:00
Michele Caini
8a344cd9b2 meta: meta container support review 2020-06-20 01:17:14 +02:00
Michele Caini
4e66fb4589 test: minor changes, code coverage 2020-06-19 11:52:47 +02:00
Michele Caini
0043e3d623 meta: breaking change, meta_any::operator* dereferences the internal pointer (if any) 2020-06-19 00:15:01 +02:00
Michele Caini
ed168015bf meta: review meta_handle 2020-06-18 18:49:52 +02:00
Michele Caini
1d450ef27e meta: deprecate meta_[any|handle]::operator*, use ::ref instead 2020-06-18 18:21:12 +02:00
Michele Caini
932e681d22 meta: get rid of meta_dtor_node 2020-06-18 16:44:04 +02:00
Michele Caini
032bcd9fbe meta: minor changes 2020-06-18 16:02:26 +02:00
Michele Caini
e113fa53a4 meta: split meta_storage from meta_any 2020-06-18 14:31:06 +02:00
Michele Caini
da212dc2a4 meta: added meta_type::is_dereferenceable 2020-06-18 11:34:44 +02:00
Michele Caini
d1c5a62a9d core: type_traits, added is_dereferenceable[_v] 2020-06-18 11:30:55 +02:00
Michele Caini
0c777e10e5 doc: meta, container support 2020-06-18 10:17:50 +02:00
Michele Caini
b919b172b1 *: suppress some warnings here and there 2020-06-16 23:36:31 +02:00
Michele Caini
5dd8d05114 meta: get around an issue of msvc 2020-06-16 23:29:50 +02:00
Michele Caini
3400b32015 meta: containers support (close #499) 2020-06-16 23:21:45 +02:00
Michele Caini
2e4becad60 type_traits: sequence/associative container traits 2020-06-16 23:18:55 +02:00
Michele Caini
3d485858d5 meta: get around a bug of gcc-7, meta factory has no longer only static members 2020-06-15 00:08:46 +02:00
Michele Caini
be93643808 view: non-const excluded types are always accepted (close #507) 2020-06-14 23:42:15 +02:00
Michele Caini
f704352bae type_traits: unpack_as_t/unpack_as_v 2020-06-14 23:28:35 +02:00
Michele Caini
6683b46b07 meta: factory<Type> has now only static functions 2020-06-14 17:32:28 +02:00
Michele Caini
a1fe458338 meta: removed dependency on <algorithm> from internal.hpp 2020-06-14 16:37:38 +02:00
Michele Caini
e6328e3207 meta: minor changes 2020-06-12 17:47:39 +02:00
Michele Caini
8b1f703397 doc: updated missing description 2020-06-12 16:18:12 +02:00
Michele Caini
583c8add7e meta: make meta_handle copyable 2020-06-12 11:42:05 +02:00
Michele Caini
0266cc8e99 meta: make instances meta handles all around the codebase 2020-06-12 11:36:13 +02:00
Michele Caini
fac4132cb1 meta: meta_type rank/extent 2020-06-12 10:10:43 +02:00
Michele Caini
bf3864b2da meta: arrays are no longer indexable via meta_type::set/get 2020-06-12 09:30:29 +02:00
Michele Caini
7c21042858 sparse_set: improved performance (see #506) - thanks @Kerndog73 2020-06-12 08:19:44 +02:00
Michele Caini
d0fd9e4618 meta: range_iterator are now part of ranges 2020-06-11 19:50:32 +02:00
Michele Caini
9b53230f83 meta: cleanup 2020-06-11 17:59:29 +02:00
Michele Caini
d814b7b11e test: code coverage 2020-06-11 17:03:45 +02:00
Michele Caini
9f5421d3d4 build system: solved an issue with codecov 2020-06-11 16:48:25 +02:00
Michele Caini
6b9e5db3bf meta: updated resolve function(s) 2020-06-11 15:40:16 +02:00
Michele Caini
9eac3faf3d meta: added meta_range and meta_iterator 2020-06-11 15:27:56 +02:00
Michele Caini
252de444fc test: meta, minor changes 2020-06-11 11:00:20 +02:00
Michele Caini
c29b9cc937 test: updated tests for meta 2020-06-11 10:46:40 +02:00
Michele Caini
6ac582a771 test: more on meta_type 2020-06-11 10:10:07 +02:00
Michele Caini
04b0c9f2cf test: more on meta_func 2020-06-11 09:55:49 +02:00
Michele Caini
6b2468f033 test: more on meta_data 2020-06-11 09:53:35 +02:00
Michele Caini
2737acb114 test: added meta_type 2020-06-11 09:45:55 +02:00
Michele Caini
d03e9c48c5 tests: minor changes 2020-06-11 09:01:33 +02:00
Michele Caini
08d9cfe82c test: added meta_func 2020-06-10 17:37:21 +02:00
Michele Caini
e8ea8f91c3 test: added meta_data 2020-06-10 17:22:48 +02:00
Michele Caini
e7cfa7fda5 test: added meta_ctor 2020-06-10 16:43:02 +02:00
Michele Caini
b0b504d002 test: added meta_conv 2020-06-10 15:59:12 +02:00
Michele Caini
1da07ba60b test: added meta_base 2020-06-10 15:51:10 +02:00
Michele Caini
cedebf8145 test: added meta_prop 2020-06-10 15:42:47 +02:00
Michele Caini
29832bb2c0 test: added meta_any 2020-06-10 15:32:30 +02:00
Michele Caini
320d815d60 test: added meta_basic 2020-06-10 15:05:26 +02:00
Michele Caini
9f18401581 meta: split fixture and tests 2020-06-10 14:56:01 +02:00
Michele Caini
b3a2f0e3a3 meta: internal range/iterator support 2020-06-10 11:34:45 +02:00
Michele Caini
c00dcd31d6 meta: small review 2020-06-09 16:41:12 +02:00
Michele Caini
b836be2c84 doc: minor changes 2020-06-09 16:13:47 +02:00
Michele Caini
f89c5cc072 doc: added section pool to entity.md (close #505) 2020-06-09 09:57:57 +02:00
Michele Caini
ab7fa56bb3 doc: updated documentation for groups and nested groups 2020-06-09 09:32:36 +02:00
Michele Caini
7e624159ee registry/group: pool<T>::super is no longer required 2020-06-09 00:55:48 +02:00
Michele Caini
857a791d68 registry: sort/sortable no longer requires pool<T>::super 2020-06-08 00:44:02 +02:00
Michele Caini
171463faf5 [[nodiscard]]: try to get around an issue with VS2017 (see #501) 2020-06-07 17:34:38 +02:00
Michele Caini
2cafb49ffe entity: [[nodiscard]] (close #501) 2020-06-07 00:51:45 +02:00
Michele Caini
ae12fed5fe meta: [[nodiscard]] (see #501) 2020-06-07 00:13:20 +02:00
Michele Caini
be5547de53 core: [[nodiscard]] (see #501) 2020-06-06 23:51:08 +02:00
Michele Caini
cac50ef566 locator: [[nodiscard]] (see #501) 2020-06-06 23:43:56 +02:00
Michele Caini
57a5736942 process: [[nodiscard]] (see #501) 2020-06-06 23:42:48 +02:00
Michele Caini
167eadf699 resource: [[nodiscard]] (see #501) 2020-06-06 23:39:56 +02:00
Michele Caini
5676e420a6 signal: [[nodiscard]] (see #501) 2020-06-06 23:36:31 +02:00
Michele Caini
f5edc9e973 doc: core, type_info<T>::name 2020-06-05 14:57:24 +02:00
Michele Caini
4a305e1568 type_info: added ::name 2020-06-05 00:32:15 +02:00
Michele Caini
748ab221dd delegate: renaming of exported types 2020-06-03 23:59:58 +02:00
Michele Caini
7d786cea8f test: pass ENTT_STANDALONE definition to all targets 2020-06-03 23:42:05 +02:00
Michele Caini
1bf734338d doc: suppress a couple of warnings from doxygen 2020-06-03 17:17:10 +02:00
Michele Caini
98df1d6004 config: type index based lookup as opt-in alternative (close #490) 2020-06-03 17:13:11 +02:00
Michele Caini
290e942f3e doc: updated documentation for meta 2020-06-01 19:38:07 +02:00
Michele Caini
46d54f7e01 factory: try to get around an issue of GCC 2020-05-31 17:23:56 +02:00
Michele Caini
16e1715d06 factory: extended ::data support (close #496) 2020-05-31 17:13:21 +02:00
Michele Caini
52faa1bec5 meta: allow read-only data registration in meta (close #496) 2020-05-29 00:54:39 +02:00
Michele Caini
c8a6465688 meta: reduce instantiations 2020-05-29 00:30:20 +02:00
Alex Ames
0273b7606e snapshot: components no longer need to be copyable (#498)
Replaced calls to std::as_const with std::move so that components can
be moved into place instead of copied when reading them in from a
snapshot.
2020-05-27 23:56:48 +02:00
Michele Caini
51022cb132 meta: reduce instantiations 2020-05-27 00:51:42 +02:00
Michele Caini
78f9d87ac3 test: suppress shadow warnings 2020-05-25 23:03:28 +02:00
Michele Caini
e57135eaab delegate: support for non-capturing lambda functions (see #495) 2020-05-25 22:59:02 +02:00
Michele Caini
5fbb663529 *: added meaningful messages (?) to static asserts (close #493) 2020-05-24 00:32:08 +02:00
Michele Caini
7db0f540bd dispatcher: enqueue works fine with aggregates now (close #491) 2020-05-23 00:40:42 +02:00
Michele Caini
c8f0afb775 test: ugly workaround for an ICE of VS2017 that fails to compile valid C++ code when combined with ENTT_API 2020-05-22 00:33:17 +02:00
Michele Caini
51a5815098 registry: suppress shadow warnings 2020-05-21 22:50:38 +02:00
Michele Caini
18bd2eb729 doc: removed references to patreon account 2020-05-21 08:44:24 +02:00
Michele Caini
f04fd61879 doc: added config section 2020-05-19 00:36:45 +02:00
Michele Caini
c1527cc4c8 doc: more details on the order of components and entities when iterating through pointers returned from raw/data (close #489) 2020-05-17 23:48:43 +02:00
Michele Caini
cf1c1ed126 group: const correctness for constructor arguments 2020-05-17 22:57:16 +02:00
Michele Caini
c4ba8f96e6 view: try to get around a bug in MSVC 2020-05-17 00:44:06 +02:00
Michele Caini
350884c627 view: const correctness for constructor arguments 2020-05-17 00:18:52 +02:00
Michele Caini
aa756f6d68 doc: minor changes 2020-05-17 00:12:43 +02:00
Michele Caini
1a77bf49f5 registry: const correctness 2020-05-16 00:52:28 +02:00
Michele Caini
1b509c56fe meta: cleanup 2020-05-16 00:52:23 +02:00
Joël Lamotte
038b10c74d Added build2 package info (#484) 2020-05-14 00:38:36 +02:00
Michele Caini
39baa59625 registry: remove_if_exists returns the number of components actually removed (close #483) 2020-05-13 00:20:44 +02:00
Michele Caini
3fc091ebb2 view: removed deprecated functions 2020-05-12 00:24:39 +02:00
Michele Caini
a557e133d3 group: removed deprecated functions 2020-05-12 00:18:42 +02:00
Michele Caini
7d4f10ccc6 registry: removed deprecated functions 2020-05-12 00:12:49 +02:00
Michele Caini
39a15bef12 snapshot: removed deprecated functions 2020-05-11 16:38:12 +02:00
Michele Caini
92c59f3ea1 registry: removed the dependency on the snapshot stuff 2020-05-11 16:38:12 +02:00
Michele Caini
226bd44e2c meta: removed deprecated functions 2020-05-11 16:38:12 +02:00
Michele Caini
78d43a49c6 observer: removed deprecated functions 2020-05-11 16:38:12 +02:00
Michele Caini
471651ac5a storage: removed deprecated functions 2020-05-11 16:38:12 +02:00
Michele Caini
89515b0e30 sparse set: removed deprecated functions 2020-05-11 16:38:12 +02:00
Michele Caini
1d61c0d970 build system: updated version, now working on v3.5.0 2020-05-11 16:38:12 +02:00
Michele Caini
35c3acbade doc: added a link to the discord server 2020-05-11 16:37:18 +02:00
Michele Caini
58a2c6a13a updated single include file 2020-05-10 16:14:27 +02:00
Michele Caini
bf0160adbd registry: removed useless check 2020-05-08 23:27:53 +02:00
Michele Caini
dfbc92dd3e test: code coverage 2020-05-08 23:17:36 +02:00
Michele Caini
85b0bbfd55 snapshot: suppress shadow warnings 2020-05-07 17:20:25 +02:00
Michele Caini
fb2a93dc05 snapshot: updated documentation 2020-05-07 16:25:51 +02:00
Michele Caini
4ac46bf19d snapshot: registry friendship is no longer required 2020-05-07 16:02:49 +02:00
Michele Caini
4d73adb540 snapshot: cleanup 2020-05-07 16:02:49 +02:00
Michele Caini
bdfeb1ae22 registry: destroy with suggested version 2020-05-07 16:02:49 +02:00
Michele Caini
3fc116ab53 minor changes 2020-05-07 16:02:49 +02:00
Michele Caini
8db61a38cc sparse set: review 2020-05-07 16:02:49 +02:00
Michele Caini
0a70979934 observer: deprecated .replace, use .update instead 2020-05-07 16:02:49 +02:00
Michele Caini
1a2ccdf991 Update README.md 2020-05-07 16:01:12 +02:00
Michele Caini
0adee56d76 meta: as_alias[_t] -> as_ref[_t] (see #479) 2020-04-30 23:59:17 +02:00
Michele Caini
fedd50efe4 meta: added meta_any::ref as an alias to meta_any operator* (close #479) 2020-04-30 23:53:02 +02:00
Michele Caini
d7de7dbe6b build system: minor changes (close #478) 2020-04-30 23:24:50 +02:00
Michele Caini
9fb8939efd meta: removed redundant assert 2020-04-30 14:28:54 +02:00
Michele Caini
fe8f671137 build system: USE_LIBCPP option is set to ON by default (see #478) 2020-04-30 14:28:34 +02:00
Michele Caini
1a4de1482b build system: minor changes (close #478) 2020-04-29 23:10:42 +02:00
Michele Caini
090a2595e1 config/type_id: minor changes 2020-04-29 01:05:17 +02:00
Michele Caini
89dc7f817f meta: added resolve_type utility 2020-04-25 17:36:57 +02:00
Michele Caini
fe0f26ce9c meta: workaround for an issue of clang 7 (close #472) 2020-04-25 16:48:21 +02:00
Michele Caini
11349629f4 meta: get around an issue of VS2017 2020-04-23 14:56:39 +02:00
Michele Caini
9a67652c0c meta: separate resolve.hpp file 2020-04-22 20:05:24 +02:00
Michele Caini
b35521dfcb meta: deprecated resolve by id, added entt::resolve_id 2020-04-21 18:32:35 +02:00
Michele Caini
9faf306d1e meta: added entt::resolve_if 2020-04-20 23:25:10 +02:00
Michele Caini
d8393dace6 meta: deprecated *::alias(), use ::id() instead 2020-04-20 22:34:30 +02:00
Michele Caini
692e5275a3 entity: the null entity works fine also with C++20 (close #467) 2020-04-20 22:34:30 +02:00
Michele Caini
1cfe517db8 meta: meta_type::id -> meta_type::type_id 2020-04-20 22:34:30 +02:00
Michele Caini
e99e1dc2ac meta: factory::alias -> factory::type (with type_info<T>::id as a default identifier) 2020-04-20 22:34:30 +02:00
Michele Caini
1bf4ebcabb suppress warnings for shadow parameters 2020-04-20 22:34:30 +02:00
Michele Caini
ec4b264868 meta: export also the meta_node class template (close #464) 2020-04-20 22:34:30 +02:00
Michele Caini
efb125b2d5 updated list of contributors 2020-04-20 22:34:16 +02:00
Elias Daler
1c0fdbed59 Fix typo in docs (s/comp/velocity) (#468) 2020-04-20 22:32:52 +02:00
Michele Caini
37bb10e564 doc: updated examples (close #463) 2020-04-17 00:23:04 +02:00
Michele Caini
1507d5d85f updated single include file (close #461) 2020-04-14 18:37:59 +02:00
Michele Caini
2a86acfc50 doc: updated documentation for the core part 2020-04-14 18:35:03 +02:00
Michele Caini
e0ee01b92e doc: fixed typo 2020-04-14 18:29:25 +02:00
Michele Caini
6006917622 storage: assert on requirements (see #458) 2020-04-09 19:42:45 +02:00
Michele Caini
4ffcf6bf3b meta: added the possibility to detach meta types from contexts 2020-04-09 00:26:02 +02:00
Michele Caini
49cde59c4e meta: minor changes 2020-04-08 23:45:48 +02:00
Michele Caini
ed7382995c registry: added ::remove_all to orphan entities (close #446) 2020-04-07 23:48:07 +02:00
Michele Caini
a7faab53b9 basic_continuous_loader: deprecated ::has, use ::contains instead 2020-04-07 00:00:57 +02:00
Michele Caini
a6aad25117 sparse_set/storage: deprecated ::destroy, use ::erase instead 2020-04-06 23:57:20 +02:00
Michele Caini
5f06084b19 sparse_set: deprecated ::has, use ::contains instead 2020-04-06 23:51:59 +02:00
Michele Caini
8151fbcb2e core: tag is no longer deprecated (close #457) 2020-04-06 23:21:17 +02:00
Michele Caini
5a8e46a6b8 doc: updated documentation for the resource module 2020-04-06 23:20:46 +02:00
Michele Caini
c4997c52e2 doc: updated documentation for the entity module 2020-04-06 23:20:28 +02:00
Michele Caini
ff12c22f50 test: cleanup 2020-04-05 23:36:00 +02:00
Michele Caini
ed7f09ab80 registry: updated tests (lib) 2020-04-05 23:32:10 +02:00
Michele Caini
f68941992a emitter: updated tests (lib) 2020-04-05 23:22:16 +02:00
Michele Caini
afcd9285e6 dispatcher: updated tests (lib) 2020-04-05 23:16:16 +02:00
Michele Caini
22f78d5b33 doc: updated documentation for making EnTT work nicely across boundaries 2020-04-05 22:58:07 +02:00
Michele Caini
5259142eb6 registry: use fast path if available, standard path otherwise 2020-04-05 22:07:35 +02:00
Michele Caini
dc403ce8ab emitter: use fast path if available, standard path otherwise 2020-04-05 22:07:23 +02:00
Michele Caini
a6fddd6a2c dispatcher: use fast path if available, standard path otherwise 2020-04-05 22:07:12 +02:00
Michele Caini
819cd623f9 multiple modules: minor changes 2020-04-05 22:04:57 +02:00
Michele Caini
f185494de3 type info: added has_type_index[_v] 2020-04-05 22:03:52 +02:00
Michele Caini
628a17ae72 config: fixed ENTT_IS_EMPTY macro definition 2020-04-03 00:49:47 +02:00
Michele Caini
08169b9180 emitter: reintroduced type_id for the internal pool 2020-04-03 00:22:01 +02:00
Michele Caini
1e79f2441c dispatcher: reintroduced type_id for the internal pool 2020-04-03 00:21:53 +02:00
Michele Caini
8680c0ba39 config: ENTT_DISABLE_ETO -> ENTT_NO_ETO 2020-04-02 18:21:42 +02:00
Michele Caini
8a78b0dbd6 registry: use only sequential indexes for component types now 2020-04-01 22:17:10 +02:00
Michele Caini
0232035e34 emitter: use only sequential indexes now 2020-04-01 21:27:59 +02:00
Michele Caini
74f3df83db dispatcher: use only sequential indexes now 2020-04-01 21:19:38 +02:00
Michele Caini
53fa0d2fe8 registry: ::assure sets a constraint on the type 2020-04-01 00:16:35 +02:00
Michele Caini
52aa9ab761 type_info: split type_info::index to type_index::value 2020-04-01 00:14:28 +02:00
Michele Caini
92cf6195b2 iterators: standard-ish names for better integration 2020-03-30 16:11:10 +02:00
Michele Caini
86a392991a test: type_info::index 2020-03-28 17:05:23 +01:00
Michele Caini
30d426d667 doc: updated core section 2020-03-28 17:01:04 +01:00
Michele Caini
5702c50680 type_info: added ::index to generate type indexes 2020-03-28 17:00:48 +01:00
Michele Caini
e38d2e156d doc: updated list of showcases 2020-03-27 00:25:30 +01:00
Michele Caini
dfc18619a2 build system: updated installation process (close #451) 2020-03-27 00:04:48 +01:00
Michele Caini
9e8f2c52a0 doc: updated section for on_update/on_replace (close #452) 2020-03-25 15:11:14 +01:00
Michele Caini
1c8f5b98f1 build system: updated installation process (close #451) 2020-03-25 00:56:43 +01:00
Michele Caini
972470ad42 benchmark: minor changes 2020-03-24 22:40:39 +01:00
Michele Caini
f7e3a055fb registry: minor changes 2020-03-21 23:35:45 +01:00
Michele Caini
34f05fb8dc doc: updated wiki for meta (close #448) 2020-03-21 16:56:29 +01:00
Michele Caini
709fd34264 registry/storage: ::insert supports all types of iterators 2020-03-21 16:48:31 +01:00
Michele Caini
b888757092 entity/*: iterators review 2020-03-20 23:53:43 +01:00
Michele Caini
0f42827047 entity/*: stop using deprecated functions internally 2020-03-20 16:50:04 +01:00
Michele Caini
e69d1556e1 config: renamed ENTT_ENABLE_ETO to ENTT_IS_EMPTY 2020-03-20 00:04:26 +01:00
Michele Caini
141fbf7472 type_traits: deprecated entt::tag because of the more general purpose alternative entt::integral_constant 2020-03-19 23:41:49 +01:00
Michele Caini
c9a3ae8149 type_traits: added shortcut entt::integral_constant 2020-03-19 23:38:10 +01:00
Michele Caini
e16a8d29ea links: added the Vim of game engines (close #447) 2020-03-19 16:05:31 +01:00
Michele Caini
fff50d0e50 registry: workaround for an issue with MSVC 2020-03-19 16:00:11 +01:00
Michele Caini
360734b447 doc: workaround for some idiosyncracies of doxygen 2020-03-19 00:49:49 +01:00
Michele Caini
7e5edad32b registry: workaround for an issue of gcc7 2020-03-19 00:33:14 +01:00
Michele Caini
fc47b47850 doc: updated doc for empty types 2020-03-18 23:59:44 +01:00
Michele Caini
c7de058e1d storage/registry: empty types are no longer a thing 2020-03-18 00:11:53 +01:00
Michele Caini
0fff3c905c view: deprecated ::less, ::each returns only non-empty types now 2020-03-16 00:30:33 +01:00
Michele Caini
2fcb055c43 group: deprecated ::less, ::each returns only non-empty types now 2020-03-15 23:50:02 +01:00
Michele Caini
97e0d63102 storage: consume args for empty types in all cases 2020-03-13 23:08:59 +01:00
Michele Caini
5e04f0accd registry: strict check on ::insert (see #438) 2020-03-13 00:02:05 +01:00
Michele Caini
16835e3928 registry: get_or_assign -> get_or_emplace 2020-03-12 23:41:58 +01:00
Michele Caini
22e0ef1354 doc: updated examples, wiki and inline documentation 2020-03-12 23:33:08 +01:00
Michele Caini
0141dc519c doc: added similar projects (close #443) 2020-03-11 17:57:36 +01:00
Michele Caini
9aadc30b94 registry: disambiguate calls to ::assign and ::assign_or_replace (close #438) 2020-03-11 16:19:41 +01:00
Michele Caini
23a73fcb7c doc: suppressed some warnings due to missing parameters 2020-03-11 15:36:14 +01:00
Michele Caini
9a3b585110 registry: disambiguate calls to pool_handler<T>::construct (see #438) 2020-03-11 14:34:47 +01:00
Michele Caini
09ee46860a sparse_set: disambiguate calls to ::construct (see #438) 2020-03-11 00:07:41 +01:00
Michele Caini
d8f289182d storage: disambiguate calls to ::construct (see #438) 2020-03-11 00:05:04 +01:00
Michele Caini
dc8fa2153b registry: on_replace becomes on_update 2020-03-09 14:46:38 +01:00
Michele Caini
32fb335832 registry: added ::patch, reintroduced ::replace from arguments (close #437) 2020-03-09 14:34:28 +01:00
Michele Caini
58885854f1 doc: added a warning to registry::replace 2020-03-08 23:36:18 +01:00
Michele Caini
3e87788541 registry: ::replace always returns the component, no matter what 2020-03-08 16:42:17 +01:00
Michele Caini
5746df4d74 doc: added a note on registry::assign 2020-03-08 15:54:07 +01:00
Michele Caini
71d86c44e0 registry: cleanup 2020-03-08 00:56:00 +01:00
Michele Caini
c5e50289e1 workflow: unlock deploy to homebrew-entt (close #397) 2020-03-07 23:26:47 +01:00
Michele Caini
e62f1edada registry: minor changes 2020-03-07 23:26:39 +01:00
Michele Caini
50e8db28b8 minor changes all over the codebase (final) 2020-03-07 00:41:21 +01:00
Michele Caini
ef7c572018 core: define entt::id_type alias for ENTT_ID_TYPE (close #416) 2020-03-07 00:41:21 +01:00
Michele Caini
1e962754cc entity module: added utility.hpp, cleaned up fwd.hpp 2020-03-07 00:41:21 +01:00
Michele Caini
0240453b07 registry: review context variables 2020-03-07 00:41:21 +01:00
Michele Caini
38a80a95f2 registry: reduce instantiations 2020-03-07 00:41:21 +01:00
Michele Caini
b5e411d251 registry: removed deprecated functions 2020-03-07 00:41:21 +01:00
Michele Caini
71a623276e registry: removed deprecated overload for replace 2020-03-07 00:41:21 +01:00
Michele Caini
1e76703144 meta: removed deprecated functions 2020-03-07 00:41:21 +01:00
Michele Caini
6ffaf11226 helper: added a shortcut to invoke members on components from callbacks (close #385) 2020-03-07 00:41:21 +01:00
Michele Caini
fc0caec1a4 build system: updated version, now working on v3.4.0 2020-03-07 00:41:21 +01:00
Michele Caini
043f9a5025 updated single include file 2020-03-07 00:41:15 +01:00
Michele Caini
822264a65e workflow: --dry-run for the deploy.yml debut (see #397) 2020-03-07 00:30:24 +01:00
Michele Caini
8e9a6a4f06 registry: fixed a bug that affects late group initialization (close #440) 2020-03-07 00:30:21 +01:00
Michele Caini
76f3909ec9 workflow: update homebrew-entt (see #397) 2020-03-07 00:30:19 +01:00
Michele Caini
23e839b40e build system: updated install process (close #421) 2020-03-07 00:30:16 +01:00
Michele Caini
6a560fc7bf observer: use any<T...> rather than a fold expression with has<T> 2020-03-07 00:30:08 +01:00
Michele Caini
709d1c93a4 registry: fixed a bug that affects late group initialization (close #436) 2020-03-07 00:29:57 +01:00
Michele Caini
8d4b5f4bb7 sparse_set: remove copy ctor/assignment operator (use range-construct instead) 2020-03-07 00:11:30 +01:00
Paul Gruenbacher
9eb5a85e9e doc: update clone example to handle empty types (#441) 2020-03-07 00:09:19 +01:00
Paul Gruenbacher
e7521445e9 doc: update stamp example (#442) 2020-03-07 00:09:15 +01:00
Michele Caini
bb050e2660 actor: (has<T>(e) && ...) -> has<T...>(e) 2020-03-07 00:07:56 +01:00
Michele Caini
bc3b0eb491 links: added references to Chrysalis (project and blog post) 2020-03-07 00:06:39 +01:00
Michele Caini
7cea05d376 doc: updated documentation for snapshot archives (close #431) 2020-03-07 00:06:32 +01:00
Michele Caini
b8a3bdf6b5 build system: minor changes 2020-03-07 00:06:25 +01:00
Michele Caini
638b6dba17 build system: updated version, now working on v3.3.2 2020-03-06 23:56:55 +01:00
Michele Caini
50ba8c6c39 updated single include file 2020-02-28 23:27:20 +01:00
Michele Caini
94d15ebbef config: being empty isn't enough for ETO, type must be default constructible 2020-02-28 23:18:12 +01:00
Michele Caini
e150882231 doc: udpated doc (see #431) 2020-02-28 23:18:09 +01:00
Michele Caini
80a659c90c doc: typo 2020-02-28 23:18:03 +01:00
Michele Caini
17d96427ea registry: assign_or_replace works also with aggregates now (close #429) 2020-02-28 23:17:58 +01:00
Michele Caini
7fdda788af registry: minor changes (close #424) 2020-02-28 23:17:53 +01:00
Ezekiel Warren
182adbd9d9 Re-added bazel support (#430) 2020-02-28 23:17:45 +01:00
Michele Caini
4931c9cd9b build system: updated version, now working on v3.3.1 2020-02-28 23:17:24 +01:00
Michele Caini
a112409735 updated single include file (close #425) 2020-02-23 16:54:02 +01:00
Michele Caini
7aaa6dd986 build system: suppress a few other warnings here and there 2020-02-19 16:12:36 +01:00
Michele Caini
3c39cfe645 build system: trigger more warnings (at least with MSVC) and suppress as many as possible (close #420, close #394) 2020-02-19 15:01:13 +01:00
Michele Caini
6a46325e7e registry: deprecate ::clone/::stamp 2020-02-18 12:51:19 +01:00
Michele Caini
15c9688a5a entity module: minor changes 2020-02-18 11:50:33 +01:00
Michele Caini
d9f93ccc11 moved tags from entity/helper to core/type_traits (see #419) 2020-02-18 09:58:51 +01:00
Michele Caini
652e569afc registry: cleanup 2020-02-17 13:36:57 +01:00
Michele Caini
c55372459f view: cleanup 2020-02-17 13:36:46 +01:00
Michele Caini
09f36e32f7 type traits: added member_class and member_class_t 2020-02-17 08:18:58 +01:00
Michele Caini
688d435d8e meta: fixed a bug on meta_type::construct with no arguments (close #413) 2020-02-16 21:54:48 +01:00
Michele Caini
e7403d8551 registry: in-place ::replace, the signal no longer receives an extra argument (close #406) 2020-02-15 22:32:50 +01:00
Indiana Kernick
60039441a1 Remove UTF-8 BOM (#414) 2020-02-15 15:40:09 +01:00
Michele Caini
97f4414cc4 minor changes 2020-02-15 00:54:40 +01:00
Michele Caini
df9595bc0d build system: clean up 2020-02-14 23:15:26 +01:00
Michele Caini
93533b7bc6 build system: make it work properly (again) with clang-cl 2020-02-14 23:06:46 +01:00
Michele Caini
a99afa2ddf build system: make gtest compile (again) on macos 2020-02-14 22:56:37 +01:00
Michele Caini
1e651a5145 tests: code coverage 2020-02-14 17:32:05 +01:00
Michele Caini
79ecfa2573 build system: suppress deprecated warnings 2020-02-14 16:43:27 +01:00
Michele Caini
e2b676d54c cleanup 2020-02-13 12:30:01 +01:00
Michele Caini
2b73bf35b6 meta: meta_type_node::id -> meta_type_node::type_id 2020-02-13 12:05:45 +01:00
Michele Caini
2137b3a879 doc: added DungeonSlayer to links 2020-02-11 23:29:50 +01:00
Michele Caini
e52b3fd5bc meta: ::identifier renamed to ::alias, added meta_type::id to get the id of the underlying type (close #412) 2020-02-11 12:30:55 +01:00
Michele Caini
0dfd2aa714 build system: minor changes to make tests compile with VS 2020-02-10 22:57:48 +01:00
Michele Caini
5d63c4c981 storage: basic_storage no longer exists (close #411) 2020-02-10 22:49:23 +01:00
Michele Caini
84d3f9ab9a registry: added missing assert (close #409) 2020-02-10 22:46:49 +01:00
Michele Caini
1133dba9d8 attribute: correct include guard (close #410) 2020-02-10 22:45:30 +01:00
Michele Caini
cd2fdc2ffe build system: use cmake fetch content rather than external add 2020-02-10 22:43:42 +01:00
Michele Caini
57d017ed8e build system: the version is read from cmake rather than written 2020-02-10 22:25:35 +01:00
Michele Caini
3ad2c559f1 build system: better support for IDEs 2020-02-09 23:16:59 +01:00
Michele Caini
c3facfa925 build system: minor changes 2020-02-09 20:07:32 +01:00
Michele Caini
db8d9ebc72 suppress shadow warnings 2020-02-08 16:31:01 +01:00
Michele Caini
3aef00af18 registry: suppress warnings on ::replace for empty components (close #407, close #408) 2020-02-08 15:56:01 +01:00
Michele Caini
a4d576bae8 registry: visit functionality for context variables (close #390) 2020-02-08 15:34:32 +01:00
Michele Caini
9320365ef6 registry: const-correctness for ::visit 2020-02-08 15:34:28 +01:00
Michele Caini
0be7494042 group: added front/back (close #393) 2020-02-08 15:34:22 +01:00
Michele Caini
df55f338ca view: added front/back (see #393) 2020-02-08 15:34:18 +01:00
Michele Caini
8210efb548 delegate: suppress warnings on parameters used for tag dispatching 2020-02-08 15:34:14 +01:00
Michele Caini
43503e2fc9 test: enforce requirements for non default constructible components (close #405) 2020-02-08 15:34:08 +01:00
Michele Caini
448c3ae425 registry/storage: assign review 2020-02-08 15:34:03 +01:00
Michele Caini
f7684a8a3e cleanup/minor changes 2020-02-08 15:33:59 +01:00
Michele Caini
4f200ac71e cleanup 2020-02-08 15:32:09 +01:00
Michele Caini
4772559e8b test: code coverage 2020-02-02 22:12:41 +01:00
Michele Caini
a984ce5bba registry: temporary changes to remove args from assure 2020-02-02 21:51:13 +01:00
Michele Caini
eee8a338e2 doc: updated examples (close #401) 2020-02-02 20:52:14 +01:00
Michele Caini
8894c0ef48 typo 2020-02-01 01:01:03 +01:00
Michele Caini
c5b8577d94 meta: redefined constructors where it matters (close #404) 2020-02-01 00:59:23 +01:00
Michele Caini
ce9def4a56 registry: avoid redundant instantiations 2020-02-01 00:50:13 +01:00
Michele Caini
894601aad2 cleanup/suppress warnings 2020-01-31 23:40:52 +01:00
Michele Caini
285c91e81b config: ENTT_DISABLE_ASSERT no longer exists, ENTT_ASSERT can be redefined now (close #403) 2020-01-31 23:39:37 +01:00
Michele Caini
ba8ca93afc doc: updated the doc for the ecs part (close #390) 2020-01-31 23:32:09 +01:00
Michele Caini
ad0dab2493 registry: added ::visit to get all components in a registry of owned by an entity 2020-01-31 22:43:25 +01:00
Michele Caini
8b66316180 doc: updated README file 2020-01-31 22:42:36 +01:00
Michele Caini
da4ee38914 type_info: minor changes 2020-01-30 20:03:51 +01:00
Michele Caini
62fde229e2 cleanup 2020-01-29 00:26:38 +01:00
Michele Caini
676115d214 delegate/sigh: reduced number of instantiations, moved instantiations out of the way from sigh::publish 2020-01-28 12:20:52 +01:00
Michele Caini
f36d838df1 hashed_string: non-recursive helper (close #400) 2020-01-27 22:46:24 +01:00
Michele Caini
f32f3ab895 config: avoid using pretty function with gcc < 9 (see #396) 2020-01-25 23:19:38 +01:00
Michele Caini
fc0432df8c registry: minor changes 2020-01-24 23:52:39 +01:00
Michele Caini
797b5bcb53 type_info: force constexpr-ness (close #396) 2020-01-24 23:29:12 +01:00
Michele Caini
b816f203cd doc: minor changes (close #398) 2020-01-24 23:29:06 +01:00
Michele Caini
714e2f86a1 cleanup 2020-01-24 16:02:45 +01:00
Michele Caini
ff75085f15 doc: updated links 2020-01-23 22:55:03 +01:00
Michele Caini
dbc8b18b80 registry: added any<T...> (close #371) 2020-01-21 12:52:59 +01:00
Michele Caini
efb2bbe8b2 cleanup: removed entity/utility.hpp 2020-01-21 12:33:37 +01:00
Michele Caini
8278c8eedd doc: minor changes 2020-01-21 12:24:02 +01:00
Michele Caini
6f696a026b registry: callbacks receive the registry as first argument (better dependencies) 2020-01-20 16:09:25 +01:00
Michele Caini
f558797246 workaround for an issue of clang 6 2020-01-20 00:01:10 +01:00
Michele Caini
c8671d1c2b doc: updated signal.md (see #385) 2020-01-19 23:43:57 +01:00
Michele Caini
7fd41e48b9 sigh: support for unbound members 2020-01-19 19:18:33 +01:00
Michele Caini
409d5f5966 delegate: support for unbound members 2020-01-19 15:16:38 +01:00
Michele Caini
5d1802faf7 doc: updated links 2020-01-18 23:05:42 +01:00
Michele Caini
a580bac385 registry: range-assign with optional init function(s) 2020-01-18 01:11:30 +01:00
Michele Caini
c43214543f storage: removed batch construct by copy 2020-01-18 00:57:27 +01:00
Michele Caini
b3c568fb6f registry: faster group initialization 2020-01-16 00:02:22 +01:00
Michele Caini
b34fe3200c storage: batch construct by copy 2020-01-15 23:55:28 +01:00
Michele Caini
dada8dbdeb storage/registry: batch add no longer returns an iterator 2020-01-15 23:21:34 +01:00
Michele Caini
e7d4077065 storage/registry: batch add default-constructs the component 2020-01-15 23:07:35 +01:00
Michele Caini
b1e54bfd86 storage: minor changes 2020-01-15 22:42:17 +01:00
Michele Caini
85ad4c4766 registry (and a few others): callbacks no longer receive components, see #386 2020-01-13 23:11:24 +01:00
Michele Caini
3dce560759 test: updated benchmarks 2020-01-13 23:08:42 +01:00
Michele Caini
969b8f0a7a test: minor changes 2020-01-13 00:27:15 +01:00
Michele Caini
ba8e738f40 doc: updated ecs part 2020-01-13 00:12:22 +01:00
Michele Caini
4bc80fae8d storage: minor changes 2020-01-13 00:06:17 +01:00
Michele Caini
ee5156704d registry: ::assign to assign entities to an empty registry 2020-01-12 00:31:53 +01:00
Michele Caini
986cd03732 registry: added ::data() to get the list of entities of a registry 2020-01-11 23:56:50 +01:00
Michele Caini
937fdabaa9 algorithm: missing include (close #389) 2020-01-11 23:56:11 +01:00
Michele Caini
c0fa97510e minor changes that are aimed mainly at suppressing warnings 2020-01-11 23:06:30 +01:00
Michele Caini
2fc6fe442d workaround of an bug of gcc-7 2020-01-11 21:56:08 +01:00
Michele Caini
7ba14f5a57 registry: only opaque stamp is allowed, use assign/assign_or_replace for components 2020-01-11 01:23:57 +01:00
Michele Caini
352e4576fc registry: ::reset doesn't exist anymore, use ::remove and ::clear instead 2020-01-11 01:11:48 +01:00
Michele Caini
210eebc0dc sparse_set/storage: ::reset is now ::clear 2020-01-09 22:51:35 +01:00
Michele Caini
5904941361 registry: stable assign (close #386) 2020-01-09 22:06:33 +01:00
Michele Caini
b352815cf8 registry: minor chages 2020-01-07 23:58:23 +01:00
Michele Caini
38a2751cd9 tests: code coverage back to 100% 2020-01-07 00:16:46 +01:00
Michele Caini
6388607d11 doc: added PopHead to the list of showcases (close #384) 2020-01-06 23:54:05 +01:00
Michele Caini
469276b8be registry: create no longer accepts a list of default constructible types to assign to entities 2020-01-06 23:46:58 +01:00
Michele Caini
2a863ee851 registry: minor changes 2020-01-06 00:50:03 +01:00
Michele Caini
b6dae2fe57 doc: updated entity.md 2020-01-06 00:40:45 +01:00
Michele Caini
d202bd3e50 registry: create with hint (close #303) 2020-01-06 00:40:22 +01:00
Michele Caini
dc2e44b2c0 doc: added sequentity to the list of showcases 2020-01-06 00:39:33 +01:00
Michele Caini
32c481c28c doc: stomp -> stamp 2020-01-04 22:59:46 +01:00
Michele Caini
8519c99706 updated copyright 2020-01-04 00:17:16 +01:00
Michele Caini
5b1bac4c19 doc: more details on entt::null (close #375) 2020-01-02 23:58:12 +01:00
Michele Caini
0a95a0674b build system: the conan-center-index makes the custom deploy step useless (close #380) 2020-01-02 22:25:12 +01:00
Michele Caini
2f11f6dc37 workaround for an issue of MSVC 2020-01-01 22:39:01 +01:00
Michele Caini
af66274c72 registry: const/non-const review (close #381) 2020-01-01 22:29:13 +01:00
Michele Caini
c5679b208f registry: improved ::group 2019-12-31 16:45:49 +01:00
Michele Caini
020fc179a5 traits: to_integral uses static_cast now 2019-12-31 15:45:36 +01:00
Michele Caini
dc9c1fc762 doc: minor changes 2019-12-31 15:21:53 +01:00
Michele Caini
f0938d70b2 registry: stomp -> stamp 2019-12-31 00:54:10 +01:00
Michele Caini
072761c5ba updated doc 2019-12-31 00:37:52 +01:00
Michele Caini
8aacd4d022 aob: removed mod stuff 2019-12-29 15:53:52 +01:00
Michele Caini
b6f9ca0021 updated TODO 2019-12-29 15:36:36 +01:00
Michele Caini
a43f354511 doc: minor changes 2019-12-29 15:18:20 +01:00
Michele Caini
43766406ae registry/dispatcher/emitter: removed ::discard, updated test and doc 2019-12-29 15:16:12 +01:00
Michele Caini
37dd1ae363 registry: group reworking 2019-12-29 15:16:12 +01:00
Michele Caini
5a3effaef5 group: faster contains 2019-12-29 15:16:12 +01:00
Michele Caini
cd4bcce70f view: faster contains 2019-12-29 15:16:12 +01:00
Michele Caini
c1e7549b61 registry: group reworking 2019-12-29 15:16:12 +01:00
Michele Caini
036ef25da9 registry: uses views to initialize groups 2019-12-29 15:16:12 +01:00
Michele Caini
9884c37ef4 view: gets rid of filter tuple 2019-12-29 15:16:12 +01:00
Michele Caini
23069d76eb registry: minor changes 2019-12-29 15:16:12 +01:00
Michele Caini
b2d0e7fae2 registry: assure returns a reference now 2019-12-29 15:16:12 +01:00
Michele Caini
30a039a031 minor changes: shadow variables, dead doc, todo list 2019-12-29 15:16:12 +01:00
Michele Caini
8d67bb726c test: guarantee that entt::entity{} != entt::null (close #375) 2019-12-29 15:16:12 +01:00
Michele Caini
c4dec4cd4d meta: test review 2019-12-29 15:16:12 +01:00
Michele Caini
f17b975fb9 meta: meta_any support for non-copyable types (close #336) 2019-12-29 15:16:12 +01:00
Michele Caini
7a3b7593d0 doc: updated documentation 2019-12-29 15:16:12 +01:00
Michele Caini
bad342b840 test: lib plugin std cpp 2019-12-29 15:16:12 +01:00
Michele Caini
e3968a8f9d test: lib std cpp 2019-12-29 15:16:12 +01:00
Michele Caini
f577183c24 build system: BUILD_PLUGIN is no longer required, plugins are libs 2019-12-29 15:16:12 +01:00
Michele Caini
be58f2e68a minor changes 2019-12-29 15:16:12 +01:00
Michele Caini
99f81e82d5 build system: removed support for bazel (no longer maintained) 2019-12-29 15:16:12 +01:00
Michele Caini
7cfd3957de test_info:🆔 make the family-like fallback work across boundaries 2019-12-29 15:16:12 +01:00
Michele Caini
c51c88a17c include guards and macros in general: removed superfluous comments 2019-12-29 15:16:12 +01:00
Michele Caini
e09af98dc5 test: minor changes 2019-12-29 15:16:12 +01:00
Michele Caini
89635f6583 type_info:🆔 family-like fallback 2019-12-29 15:16:12 +01:00
Michele Caini
14034642f1 test: review 2019-12-29 15:16:12 +01:00
Michele Caini
4af7d975c1 type_id -> type info + allow also fully runtime ids 2019-12-29 15:16:12 +01:00
Michele Caini
d9884917fb registry: added discard to free explicitly a pool 2019-12-29 15:16:12 +01:00
Michele Caini
95fd4e4c99 emitter: added discard to free explicitly a pool 2019-12-29 15:16:12 +01:00
Michele Caini
9457a55910 dispatcher: added discard to free explicitly a pool 2019-12-29 15:16:12 +01:00
Michele Caini
56e7dd0de0 dispatcher::discard becomes dispatcher::clear 2019-12-29 15:16:12 +01:00
Michele Caini
f0dc882ae9 minor changes here and there 2019-12-29 15:16:12 +01:00
Michele Caini
acb70d4440 meta: uses type_id_v rather than other techniques 2019-12-29 15:16:12 +01:00
Michele Caini
167721bf8c removed ENTT_API from ENTT_OPAQUE_ID 2019-12-29 15:16:12 +01:00
Michele Caini
4f438e5228 family: back to the old-fashioned model 2019-12-29 15:16:12 +01:00
Michele Caini
0f0aeb6d47 meta: prepare migration to entt::type_id 2019-12-29 15:16:12 +01:00
Michele Caini
d1a6edad8a ENTT_OPAQUE_TYPE: to_integer -> to_integral 2019-12-29 15:16:12 +01:00
Michele Caini
388feb7edb docs: updated doxy.in 2019-12-29 15:16:12 +01:00
Michele Caini
962b068c92 test: pass NOMINMAX where cr.h is included 2019-12-29 15:16:12 +01:00
Michele Caini
a32373bc3a meta: updated tests, CR_UNLOAD isn't passed on close 2019-12-29 15:16:12 +01:00
Michele Caini
94a5a4ac1e doc: minor changes 2019-12-29 15:16:12 +01:00
Michele Caini
ed8eea12bd entt::component no longer exists 2019-12-29 15:16:12 +01:00
Michele Caini
83bea8b654 registry: uses type_id_v rather than families 2019-12-29 15:16:12 +01:00
Michele Caini
d4d2db228e minor changes: noexcept-ness review (close #372) 2019-12-29 15:16:12 +01:00
Michele Caini
f8b8c91fe8 type_id: sfinae friendly version to allow extreme customization 2019-12-29 15:16:12 +01:00
Michele Caini
8451301a5a dispatcher/emitter: constrained assure 2019-12-29 15:16:12 +01:00
Michele Caini
449b03f6bd type_id: built-in variadic accepts only single types 2019-12-29 15:16:12 +01:00
Michele Caini
3853ff725f dispatcher/emitter: better assure 2019-12-29 15:16:12 +01:00
Michele Caini
ee66e3ef9e added missing include 2019-12-29 15:16:12 +01:00
Michele Caini
2643500957 type_id: removed redundant check 2019-12-29 15:16:12 +01:00
Michele Caini
88467a87a4 emitter: uses type_id_v rather than a family 2019-12-29 15:16:12 +01:00
Michele Caini
d1cdeb4a2d dispatcher: uses type_id_v rather than a family 2019-12-29 15:16:12 +01:00
Michele Caini
f5ced7fe39 core: (customizable) type_id[_v] 2019-12-29 15:16:12 +01:00
Michele Caini
023267ecab static hashed_string::to_value -> hashed_string::value 2019-12-29 15:16:12 +01:00
Michele Caini
3d515a760c attribute.h: lib -> core 2019-12-29 15:16:12 +01:00
Michele Caini
026c5987dd suppress some warnings here and there 2019-12-29 15:16:12 +01:00
Michele Caini
84fb3694f2 test: meta and plugins 2019-12-29 15:16:12 +01:00
Michele Caini
169dcbcd74 coverage: enabled plugins 2019-12-29 15:16:12 +01:00
Michele Caini
d28b6fbf1e test: use MODULE rather than SHARED for plugins 2019-12-29 15:16:12 +01:00
Michele Caini
9f7a2ef84e updated TODO 2019-12-29 15:16:12 +01:00
Michele Caini
cc5ea60d2b test: minor changes (for code coverage) 2019-12-29 15:16:12 +01:00
Michele Caini
8bc63494bc hashed string: added missing include 2019-12-29 15:16:12 +01:00
Michele Caini
9acdeeed04 CI: enabled plugins 2019-12-29 15:16:12 +01:00
Michele Caini
d2cdb2a209 build system: refine macro SETUP_PLUGIN_TEST 2019-12-29 15:16:12 +01:00
Michele Caini
36bad31355 signal: updated fwd.hpp 2019-12-29 15:16:12 +01:00
Michele Caini
427587e591 test: ENTT_API_* definitions are set by means of cmake 2019-12-29 15:16:12 +01:00
Michele Caini
4f028d8201 test: prepare plugin stuff 2019-12-29 15:16:12 +01:00
Michele Caini
d59c052554 build system: added fungos/cr to the list of dependencies required to compile lib tests 2019-12-29 15:16:12 +01:00
Michele Caini
93ebe91bc5 test: removed useless FAIL() 2019-12-29 15:16:12 +01:00
Michele Caini
fadb8f695e tests: minor changes 2019-12-29 15:16:12 +01:00
Michele Caini
8bb1518d09 registry: (almost) transparent dll/.so support with ENTT_API 2019-12-29 15:16:12 +01:00
Michele Caini
e8d9d663a7 registry: removed superfluous friend declaration 2019-12-29 15:16:12 +01:00
Michele Caini
2d2648cf57 emitter: : (almost) transparent dll/.so support with ENTT_API 2019-12-29 15:16:12 +01:00
Michele Caini
231036784d dispatcher: (almost) transparent dll/.so support with ENTT_API 2019-12-29 15:16:12 +01:00
Michele Caini
882b91b221 family: review to make it work with dllimport/dllexport 2019-12-29 15:16:12 +01:00
Michele Caini
df346ab380 lib, test: minor changes 2019-12-29 15:16:12 +01:00
Michele Caini
c7b8e82ada meta: (almost) transparent dll/.so support with ENTT_API 2019-12-29 15:16:12 +01:00
Michele Caini
b8784863f2 meta: context no longer exists 2019-12-29 15:16:12 +01:00
Michele Caini
3f67054f03 meta: compare function shouldn't be static 2019-12-29 15:16:12 +01:00
Michele Caini
36d1b0a4bc emitter: updated inline doc 2019-12-29 15:16:12 +01:00
Michele Caini
748777b8eb registry: removed superfluous ::template from unset() 2019-12-29 15:16:12 +01:00
Michele Caini
acd8e0bce5 registry: updated ::prepare 2019-12-29 15:16:12 +01:00
Michele Caini
d2d068edff registry: removed superfluous ::template from type() 2019-12-29 15:16:12 +01:00
Michele Caini
e2a7a7ce20 test: cleanup 2019-12-29 15:16:12 +01:00
Michele Caini
b25b1c45fb named types (traits and macros) are no longer available 2019-12-29 15:16:12 +01:00
Michele Caini
c3b0fa6c93 registry no longer uses named types 2019-12-29 15:16:12 +01:00
Michele Caini
63a4e67174 cleanup 2019-12-29 15:16:12 +01:00
Michele Caini
88e37438fa emitter: const/non-const assure 2019-12-29 15:16:12 +01:00
Michele Caini
5be1bf7d82 meta no longer uses named types 2019-12-29 15:16:12 +01:00
Michele Caini
eb4689d2f1 dispatcher no longer uses named types 2019-12-29 15:16:12 +01:00
Michele Caini
e81549e5fd emitter no longer uses named types 2019-12-29 15:16:12 +01:00
Michele Caini
becaef36ff disabled tests for shared objects/named types 2019-12-29 15:16:12 +01:00
Michele Caini
6aefa40ca2 updated TODO (changes requried to get rid of named types) 2019-12-29 15:16:12 +01:00
Michele Caini
250ef50361 added attribute.h 2019-12-29 15:16:12 +01:00
Michele Caini
5a1af60357 view/group: all iterators are at least bidirectional (close #370) 2019-12-29 15:16:12 +01:00
Michele Caini
46db75308c test: modularized lib tests 2019-12-29 15:16:12 +01:00
Michele Caini
33f5e13b29 use std::for_each instead of std::for_each_n (the latter isn't supported by older compilers) 2019-12-29 15:16:12 +01:00
Michele Caini
54bb4797db cleanup 2019-12-29 15:16:12 +01:00
Michele Caini
7a14fbb221 registry: stomp doesn't work anymore with foreign registries 2019-12-29 15:16:12 +01:00
Michele Caini
18349f5ee4 small improvements 2019-12-29 15:16:12 +01:00
Michele Caini
22b93cdd0a meta: updated doc (see #361) 2019-12-29 15:16:12 +01:00
Michele Caini
63bc0b2ba1 entity: updated doc (close #364) 2019-12-29 15:16:12 +01:00
Michele Caini
c57a7c745d meta: improved perf and compilation time, reduced loc 2019-12-29 15:16:12 +01:00
Michele Caini
f2831b5f5c updated doc (close #363) 2019-12-29 15:16:12 +01:00
Michele Caini
96f793f91b meta_any: unifying assignment operator 2019-12-29 15:16:12 +01:00
Michele Caini
4e2a0d6e58 noexcept-ness review (close #362) 2019-12-29 15:16:12 +01:00
Michele Caini
442c7f1f09 meta: review 2019-12-29 15:16:12 +01:00
Michele Caini
dbff4af7c6 cleanup 2019-12-29 15:16:12 +01:00
Michele Caini
93b09836da updated workflow (close #359) 2019-12-29 15:16:12 +01:00
Michele Caini
1782dc1565 emitter: cleanup (close #360) 2019-12-29 15:16:12 +01:00
Michele Caini
cc16874d58 cleanup 2019-12-29 15:16:12 +01:00
Michele Caini
c8925b2ae3 using atomics is no longer the default: ENTT_NO_ATOMIC -> ENTT_USE_ATOMIC 2019-12-29 15:16:12 +01:00
Michele Caini
fc043b9fcd small perf improvements 2019-12-29 15:16:12 +01:00
Michele Caini
ea86d33bc1 meta: removed reset-all function (it was logically broken across boundaries) 2019-12-29 15:16:12 +01:00
Michele Caini
82f33b82e3 meta: allow assigning properties to opaque meta types 2019-12-29 15:16:12 +01:00
Michele Caini
c645cb83a2 meta: reset-all functionality (see #358) 2019-12-29 15:16:12 +01:00
Michele Caini
d6d79a2aa5 code coverage 2019-12-29 15:16:12 +01:00
Michele Caini
786568fd2f updated workflow 2019-12-29 15:16:12 +01:00
Michele Caini
434f77a058 sigh: review connection/scoped_connection 2019-12-29 15:16:12 +01:00
Michele Caini
02f777a143 cleanup 2019-12-29 15:16:12 +01:00
Michele Caini
8025a84aeb updated workflow 2019-12-29 15:16:12 +01:00
Innokentiy Alaytsev
c37a50d3b8 updated faq.md (#351) - close #348 2019-12-29 15:16:12 +01:00
Michele Caini
6dbbb265d1 registry: added range remove 2019-12-29 15:16:12 +01:00
Michele Caini
02ce2ff74c registry: multi component remove 2019-12-29 15:16:12 +01:00
Michele Caini
28e5267132 registry::stomp supports foreign registries (close #315) 2019-12-29 15:16:12 +01:00
Michele Caini
f498b8a049 registry::prepare: strictier check 2019-12-29 15:16:12 +01:00
Michele Caini
b31afe5c8c safer registry::prepare 2019-12-29 15:16:12 +01:00
Michele Caini
eac944dbea get rid of registry::skip_family_pools 2019-12-29 15:16:12 +01:00
Michele Caini
965b20c37a review: dispatcher::assure/emitter::assure 2019-12-29 15:16:12 +01:00
Michele Caini
fba85754d7 review: registry::clone 2019-12-29 15:16:12 +01:00
Michele Caini
3c69f98451 range registry::stomp is no longer available 2019-12-29 15:16:12 +01:00
Michele Caini
cad8a90124 spawning registry::create is no longer available 2019-12-29 15:16:12 +01:00
Michele Caini
b8d888b17f minor changes 2019-12-29 15:16:12 +01:00
Michele Caini
942d783e4e cleanup 2019-12-29 15:16:12 +01:00
Michele Caini
0e352cb466 single component registry::prepare with arguments (suitable for custom pools) 2019-12-29 15:16:12 +01:00
Michele Caini
50af38b952 registry::clone/stomp review (close #350) 2019-12-29 15:16:12 +01:00
Michele Caini
1459c59cd0 updated TODO 2019-12-29 15:16:12 +01:00
Michele Caini
e74f5b2991 cleanup/refactoring 2019-12-29 15:16:12 +01:00
Michele Caini
0dec05fd70 no longer _each (it was too ugly for me) 2019-12-29 15:16:12 +01:00
Michele Caini
3cff21b4d0 updated doc (close #349) 2019-12-29 15:16:12 +01:00
Michele Caini
c380da7214 range stomp is now named stomp_each for consistency 2019-12-29 15:16:12 +01:00
Michele Caini
e2bf903c49 registry::assign_each validates entities 2019-12-29 15:16:12 +01:00
Michele Caini
9cf64ba881 range destroy is now named destroy_each for consistency 2019-12-29 15:16:12 +01:00
Michele Caini
74d901b7ae registry: range assign 2019-12-29 15:16:12 +01:00
Michele Caini
b15cb46a6d now working on v3.3.0 2019-12-29 15:16:12 +01:00
Michele Caini
89dc76e3b2 doc: added SgOgl to the EnTT in Action list (close #378) 2019-12-29 15:12:13 +01:00
Michele Caini
000b7d2651 updated single include file 2019-12-19 15:14:33 +01:00
Michele Caini
9ffa372ba2 workaround for an issue of MSVC (close #373) 2019-12-19 09:19:37 +01:00
Michele Caini
510cfcf1fc now working on v3.2.2 2019-12-19 09:18:41 +01:00
Michele Caini
e7f6d93a1e cleanup 2019-12-01 15:59:25 +01:00
Michele Caini
a17c449075 observer: minor changes 2019-12-01 15:58:05 +01:00
Michele Caini
c4da7a87f9 typo 2019-12-01 15:57:51 +01:00
Michele Caini
4a8b2f826d cleanup 2019-12-01 15:46:28 +01:00
Stefano Fiorentino
4901e4fb80 adding git_shallow to cmake deps helps reducing building time from scratch (#367)
very useful in CI/CD pipelines
2019-12-01 15:45:20 +01:00
Michele Caini
ce3a0157cb updated version.h to v3.2.1 2019-11-26 11:20:54 +01:00
Michele Caini
a146b06e4b updated single include file to v3.2.1 (close #366) 2019-11-26 11:18:41 +01:00
Michele Caini
dc78a3e56d version: 3.2.1 2019-11-26 11:17:13 +01:00
Michele Caini
86c524dade fix: sorting empty types (close #365) 2019-11-26 11:16:02 +01:00
Innokentiy Alaytsev
6889edcfc4 meta, fix: meta_prop::next lazy initialization 2019-11-26 11:11:42 +01:00
Michele Caini
3cd5934bad typo 2019-11-26 11:11:26 +01:00
Michele Caini
064104f23a suppress **all** shadow warnings (close #346) 2019-11-26 11:11:19 +01:00
Michele Caini
e80b1c264c family: it no longer dacays types 2019-11-26 11:11:13 +01:00
Michele Caini
03c3ee514b updated doc 2019-10-25 00:03:58 +02:00
Michele Caini
c93d0f0d59 registry::prepare to force create pools explicitly 2019-10-24 23:10:03 +02:00
Michele Caini
85793d38bc meta: a static assertion forbids using non-copyable types with meta_any (close #336) 2019-10-24 22:55:07 +02:00
David Demelier
49b4a85d55 FAQ: add note about non-copyable objects (#344) 2019-10-24 14:59:22 +02:00
Michele Caini
5e8a9340f1 meta: added meta_type::remove_extent (close #345) 2019-10-23 23:29:25 +02:00
Michele Caini
2b97c01bd6 named_type_traits<Type>::value -> named_type_traits_v<Type> 2019-10-21 22:52:49 +02:00
Michele Caini
39e3a5a708 make macros require a trailing semicolon 2019-10-21 14:23:22 +02:00
Michele Caini
df13d993f7 test: more on type_traits 2019-10-21 13:43:51 +02:00
Michele Caini
36c62763e5 updated TODO 2019-10-20 18:45:49 +02:00
Michele Caini
ad5651ffb2 ENTT_DISABLE_ETO to disable empty type optimization (close #330) 2019-10-20 17:32:10 +02:00
Michele Caini
717b091b49 meta: internal and external indexing to disambiguate property overloads 2019-10-20 15:44:53 +02:00
Michele Caini
4376bbe40b workaround for an issue with clang 6 2019-10-19 22:08:11 +02:00
Michele Caini
2382d2e21c meta: invocable properties (aka annotations - see #299) 2019-10-19 16:25:35 +02:00
Michele Caini
520ccfcd06 meta: support tuple as properties 2019-10-19 16:09:03 +02:00
Michele Caini
0bdc5dfe95 minor changes: make_index_sequence -> index_sequence_for 2019-10-19 15:54:32 +02:00
Michele Caini
6a656a1b0a type_traits: updated choice utility (template variable) 2019-10-19 15:51:27 +02:00
Michele Caini
5ad4079dbf meta: std::get<1>-table properties are no longer accepted, only std::pair is supported 2019-10-19 15:19:58 +02:00
Innokentiy Alaytsev
bfa46b795f Property list (#338)
* Fix typo in comment
* Implemented a function for adding a list of properties to meta-item
2019-10-19 15:13:43 +02:00
Michele Caini
ecaa9c275c typo 2019-10-18 21:45:17 +02:00
Michele Caini
93fd1757f8 meta: more on properties (close #341) 2019-10-18 17:30:32 +02:00
Michele Caini
71d0958398 type_traits: added support for the choice trick 2019-10-18 16:38:23 +02:00
Michele Caini
ed89d94d7a meta: support for key-only properties 2019-10-17 17:23:25 +02:00
Stephan Z
135132e5f0 Snapshot loader can auto-update entities stored in maps now (#335) - close #334
* Detect if member variable is of type map and update the contained entities if needed
* issue 334: tests for identifier update on maps in snapshots
2019-10-16 23:35:29 +02:00
Michele Caini
1ad75f5c1b meta: updated doc (close #324) 2019-10-16 13:43:29 +02:00
Michele Caini
21c6f9e394 meta: updated documentation 2019-10-16 13:43:29 +02:00
Michele Caini
5a7e954aee meta: context review 2019-10-16 13:43:29 +02:00
Michele Caini
c1117e260c meta: meta_node<>::ctx -> meta_node<>::global 2019-10-16 13:43:29 +02:00
Michele Caini
b0ea150e94 meta: properties can be also runtime objects 2019-10-16 13:43:29 +02:00
Michele Caini
0e3bdc02ea meta: meta_factory::type supports named types (the identifier isn't required in this case) 2019-10-16 13:43:29 +02:00
Michele Caini
e5a075a329 meta: minor changes 2019-10-16 13:43:29 +02:00
Michele Caini
30c7a64619 meta: simplified T::prop 2019-10-16 13:43:29 +02:00
Michele Caini
ab12f29ebf meta: introduce extended meta factory 2019-10-16 13:43:29 +02:00
Michele Caini
a0c3a82c76 meta: cleanup and renaming 2019-10-16 13:43:29 +02:00
Michele Caini
085a281f8a meta: some tests across boundaries 2019-10-16 13:43:29 +02:00
Michele Caini
1f871c24b9 meta: plug-and-play mode 2019-10-16 13:43:29 +02:00
Michele Caini
9f46b51985 minor changes 2019-10-16 13:43:29 +02:00
Michele Caini
fc572b8717 updated TODO 2019-10-16 13:43:29 +02:00
Michele Caini
4404601fa3 meta: tests 2019-10-16 13:43:29 +02:00
Michele Caini
faf7e28119 meta: added meta context object 2019-10-16 13:43:29 +02:00
Michele Caini
0303facfd9 updated TODO 2019-10-16 13:43:29 +02:00
Michele Caini
86e056a736 meta: added underlying context 2019-10-16 13:43:29 +02:00
Michele Caini
75105dc1fc meta: cleanup 2019-10-16 13:43:29 +02:00
Michele Caini
14b4979c98 added is_equality_comparable[_v] trait 2019-10-16 13:43:29 +02:00
Michele Caini
f956c64765 meta: doc 2019-10-16 13:43:29 +02:00
Michele Caini
8b3f954ad8 meta: cleanup 2019-10-16 13:43:29 +02:00
Michele Caini
a013cfcd1e meta_type::meta is no longer available 2019-10-16 13:43:29 +02:00
Michele Caini
65553e6c43 meta_type::remove_pointer returns a pointer to an underlying node now 2019-10-16 13:43:29 +02:00
Michele Caini
401c881c00 meta_prop::meta is no longer available 2019-10-16 13:43:29 +02:00
Michele Caini
cf0e4d24a1 meta_func::meta is no longer available 2019-10-16 13:43:29 +02:00
Michele Caini
21810f3b39 meta_data::meta is no longer available 2019-10-16 13:43:29 +02:00
Michele Caini
0b211ab0ba meta_dtor::meta is no longer available 2019-10-16 13:43:29 +02:00
Michele Caini
30c7cf3512 meta_ctor::meta is no longer available 2019-10-16 13:43:29 +02:00
Michele Caini
bf8839a560 meta_base::meta is no longer available 2019-10-16 13:43:29 +02:00
Michele Caini
818998c532 meta_conv::meta is no longer available 2019-10-16 13:43:29 +02:00
Michele Caini
98f54029d0 some consts here and there 2019-10-16 13:43:29 +02:00
Michele Caini
d41f2c138b minor changes 2019-10-16 13:43:29 +02:00
Michele Caini
50f07a6a0d meta: cleanup 2019-10-16 13:43:29 +02:00
Michele Caini
b10116febb review: cache resource_type and id_type 2019-10-16 13:43:29 +02:00
Michele Caini
17b5d732e4 cleanup 2019-10-16 13:43:29 +02:00
Michele Caini
5b16b1d827 entt::resource_cache -> entt::cache 2019-10-16 13:43:29 +02:00
Michele Caini
e5fe962130 entt::resource_handle -> entt::handle 2019-10-16 13:43:29 +02:00
Michele Caini
00f03d9bb7 entt::resource_loader -> entt::loader 2019-10-16 13:43:29 +02:00
Michele Caini
f7c285af20 minor changes 2019-10-16 09:14:08 +02:00
Michele Caini
f6e3121c76 GH sponsorship 2019-10-08 22:56:26 +02:00
Michele Caini
a95aa81850 utility: overloaded and y_combinator 2019-10-01 15:02:18 +02:00
Michele Caini
ffa5825658 cleanup 2019-10-01 15:01:56 +02:00
Michele Caini
df5b90cc7c nested groups: documentation 2019-09-30 00:20:02 +02:00
Michele Caini
9dde644fd0 nested groups: impl + tests 2019-09-30 00:19:17 +02:00
Michele Caini
d525a1536c sink::[before|disconnect]: const void * -> Type * 2019-09-30 00:11:07 +02:00
Michele Caini
54f18f3f0d delegate/sink: pointer payload 2019-09-29 23:40:03 +02:00
Michele Caini
ce4f26a5d5 minor changes 2019-09-29 21:43:30 +02:00
Guillaume Haerinck
177a7429f1 [doc] Add link to Mirror lasers & Robots project (#329) 2019-09-29 21:31:49 +02:00
Michele Caini
f672d734f9 delegate: built-in support for pointer payload 2019-09-29 00:46:29 +02:00
Michele Caini
faf5a38f1c sink: before/disconnect and const void * 2019-09-28 22:56:47 +02:00
Michele Caini
03c4267b84 process: tests with both a complex data type and a fundamental type (close #326) 2019-09-28 00:25:25 +02:00
Michele Caini
ab08621808 updated .gitignore 2019-09-28 00:17:41 +02:00
Michele Caini
d06c0e5c5d updated doc 2019-09-28 00:12:47 +02:00
Griffin Downs
32e76298e4 Add vcpkg installation instructions (#327) 2019-09-28 00:08:38 +02:00
Michele Caini
752163a5c0 workaround for an issue of VS2019 (close #314) 2019-09-27 23:49:43 +02:00
Michele Caini
6568fa23f8 updated TODO 2019-09-27 23:06:26 +02:00
Michele Caini
9c69288304 added more showcases (close #328) 2019-09-27 23:03:45 +02:00
Michele Caini
3a3346e750 typo 2019-09-26 23:32:25 +02:00
Michele Caini
47ab73d9a4 updated doc 2019-09-26 23:03:44 +02:00
Michele Caini
9555385325 updated list of contributors 2019-09-26 16:11:19 +02:00
xiss burg
f80a03afc7 This should allow any type to be used as Delta... (#325)
* This should allow any type to be used as Delta, beyond native numerical types.
* Why write more code than you have to?
2019-09-26 16:09:56 +02:00
Michele Caini
c1b64770d1 updated tests 2019-09-25 23:54:53 +02:00
Michele Caini
400f106d62 updated coverage.yml 2019-09-25 13:52:39 +02:00
Michele Caini
1cc20716ae codecov badge 2019-09-25 12:58:43 +02:00
Michele Caini
fc7d123871 tests: review 2019-09-25 12:54:17 +02:00
Michele Caini
2a8a014ad0 tests: review 2019-09-25 12:37:20 +02:00
Michele Caini
861f452d26 remove coveralls 2019-09-25 11:57:48 +02:00
Michele Caini
64a7d5e4fd codecov 2019-09-25 11:57:29 +02:00
Michele Caini
7c45423969 GH actions: reattach HEAD (workaround for an issue with the checkout action) 2019-09-25 10:13:13 +02:00
Michele Caini
480f70d7e2 GH actions: badges 2019-09-24 17:10:43 +02:00
Michele Caini
7f182896ca deleted appveyor/travis files 2019-09-24 17:01:45 +02:00
Michele Caini
1319fe2eb5 GH actions: conan 2019-09-24 16:45:26 +02:00
Michele Caini
f5c231aa80 GH actions: split CI and coveralls 2019-09-24 11:41:22 +02:00
Michele Caini
84d468333a github actions 2019-09-24 11:36:33 +02:00
Michele Caini
dfdbb0a061 class -> struct 2019-09-24 00:12:49 +02:00
Michele Caini
27365abbe3 registry: always assure and mutable list of pools 2019-09-23 23:44:14 +02:00
Michele Caini
f3aed46c94 minor changes 2019-09-23 23:16:29 +02:00
Michele Caini
7e0ea176cf cleanup 2019-09-23 22:58:47 +02:00
Michele Caini
791e13e7bf updated README 2019-09-23 16:01:50 +02:00
Michele Caini
9dff3ac749 sigh::disconnect and sigh::before support for pointers to instances 2019-09-22 23:42:04 +02:00
Michele Caini
45f207164d minor changes 2019-09-22 16:11:09 +02:00
Michele Caini
bea6f3ec14 sigh: filter out null opaque pointers 2019-09-22 15:19:27 +02:00
Michele Caini
1edc883ca2 cleanup 2019-09-21 17:00:44 +02:00
Michele Caini
116825d604 slightly faster sparse_set::arrange (apply permutation in O(N) now) 2019-09-21 15:29:11 +02:00
Michele Caini
26bc5ef091 workaround for an issue of VS 2019-09-20 17:27:52 +02:00
Michele Caini
8c228287aa doc: sink::before 2019-09-20 15:22:19 +02:00
Michele Caini
5c7c682d41 test: sink::before 2019-09-20 15:20:31 +02:00
Michele Caini
bfc2eca041 sink::before (overloaded function) 2019-09-20 14:53:10 +02:00
Michele Caini
b043b48e6e sparse_set::arrange: more tests (see #323) 2019-09-20 08:33:31 +02:00
Michele Caini
d7c03d7356 fixed an issue with sparse_set::arrange (close #323) 2019-09-19 23:50:30 +02:00
Michele Caini
c41f6aebf4 sorting: more tests 2019-09-19 22:39:13 +02:00
Michele Caini
5e6cda7c5e registry: fixed signal race on groups (close #320) 2019-09-19 14:09:53 +02:00
Carl Findahl
9c71b53abc Add namespace qualifier in CMake (close #321) (#322) 2019-09-19 13:28:33 +02:00
Michele Caini
3f96e04a7b workaround for an issue with clang 6 2019-09-18 23:16:45 +02:00
Michele Caini
90eeeedf52 view supports exclusion list 2019-09-18 14:32:58 +02:00
Michele Caini
6649362fec minor changes 2019-09-18 00:04:45 +02:00
Michele Caini
39c8889585 cleanup 2019-09-18 00:04:22 +02:00
Michele Caini
b5b1a64f0c typo 2019-09-18 00:04:07 +02:00
Michele Caini
cd108b5f57 prepare view for exclusion list 2019-09-17 23:26:54 +02:00
Michele Caini
780f3f8552 updated TODO list 2019-09-17 16:53:52 +02:00
Michele Caini
afea5eb69e added @suVrik to the list of contributors 2019-09-17 16:49:36 +02:00
Michele Caini
b72cf5d364 review: observer 2019-09-17 16:48:54 +02:00
Andrej Suvoraŭ
c133686dde Fix entt::collector::where (#319)
* fix observer
* add chained entt::collector::where test
2019-09-17 16:02:11 +02:00
Michele Caini
e216fa98ef cleanup 2019-09-16 22:37:38 +02:00
Michele Caini
8f9d8e188f updated README 2019-09-16 15:51:00 +02:00
Michele Caini
800751cbe0 sorting no longer requires allocations and is much faster 2019-09-16 15:38:34 +02:00
Michele Caini
14bc73cde9 swap accepts entities, no longer raw positions 2019-09-14 17:49:30 +02:00
Michele Caini
41307046be updated doc 2019-09-14 17:47:24 +02:00
Michele Caini
c259c79384 typo 2019-09-12 23:01:49 +02:00
Michele Caini
49f69facaf cleanup 2019-09-12 22:52:41 +02:00
Michele Caini
da2b6acf91 minor changes - code coverage 2019-09-12 22:34:03 +02:00
Michele Caini
fbdbe848c5 workaround for an issue of VS2017 (close #316) 2019-09-12 22:11:08 +02:00
Michele Caini
cc41caede9 multi-component [registry|view|group]::empty 2019-09-12 14:16:06 +02:00
Michele Caini
ee373eb6fb multi-component registry::shrink_to_fit 2019-09-11 23:12:14 +02:00
Michele Caini
d4b18b1e27 multi-component registry::reserve 2019-09-11 23:10:24 +02:00
Michele Caini
94e2fe2cbb make empty components eat arguments on construction 2019-09-11 22:56:35 +02:00
Michał Janiszewski
c93658ee04 Use Ninja on AppVeyor (#310)
This brings up improved compilation speed and saner error messages.
2019-09-11 22:24:05 +02:00
Michele Caini
ad7ee0b716 mark cmake project explicitly as C++ (see #309) 2019-09-10 23:02:22 +02:00
Michele Caini
b2fad7a567 added dispatch::discard to clear pools of events (close #312) 2019-09-10 22:55:03 +02:00
Michele Caini
e80adc2799 cmake support for clang users on Windows (see #311) 2019-09-10 16:38:38 +02:00
Michele Caini
ac111224ae meta: all function arguments are taken by alias (close #301) 2019-09-10 16:10:09 +02:00
Michele Caini
0813bb1a28 updated doc 2019-09-10 16:10:09 +02:00
Michele Caini
4d1ad8f749 meta_handle no longer exposes try_cast 2019-09-10 16:10:09 +02:00
Michele Caini
80b444e531 added meta_any constructor for the unmanaged object taken from a meta_handle 2019-09-10 16:10:09 +02:00
Michele Caini
3a6468cd0a disable BUILD_MOD for the CI 2019-09-10 15:18:28 +02:00
Michele Caini
c9fdb215f3 C language required for duktape (see BUILD_MOD option for more details) 2019-09-10 15:12:59 +02:00
Michał Janiszewski
85ff5f2d10 Mark CMake project explicitly as C++ (#309)
This prevents checking of C compiler
2019-09-10 14:31:12 +02:00
Michele Caini
be3597524f added a static_assert in a test to suppress a warning from GCC 2019-09-08 22:53:48 +02:00
Michele Caini
5717dbda4f added [[maybe_unused]] to suppress a warning from GCC 2019-09-08 22:50:51 +02:00
Michele Caini
588eec4672 added meta_type::compare, meta_any no longer has to store aside comparison functions 2019-09-08 22:44:06 +02:00
Michele Caini
cc3d0d8211 no more references to the prototype class (close #307) 2019-09-08 00:11:44 +02:00
Michele Caini
1550efaf7e stomp & spawn: const source registry (close #308) 2019-09-08 00:09:43 +02:00
Michele Caini
007300b881 sigh: ordered calls 2019-09-08 00:05:26 +02:00
Michele Caini
454f072f13 review stomp & spawn (close #306) 2019-09-06 17:21:07 +02:00
Michele Caini
b3be6c75c3 cleanup 2019-09-06 16:42:42 +02:00
Michele Caini
f8774f314b sink::disconnect accepts also opaque pointers 2019-09-06 08:19:28 +02:00
Michele Caini
3db35ad343 added openblack to the links 2019-09-06 08:19:28 +02:00
Michele Caini
0e42a779e2 meta_any uses reference_wrapper instead of as_alias_t for aliasing 2019-09-06 08:19:28 +02:00
Michele Caini
d34e829811 perf improvement 2019-09-06 08:19:28 +02:00
Michele Caini
ebd988702e relaxed constraints on the order of types in the group definition 2019-09-06 08:19:28 +02:00
Michele Caini
1232398742 now working on EnTT v3.2.0 2019-09-06 08:19:28 +02:00
Michele Caini
6dbcd47143 updated single file 2019-09-06 08:18:34 +02:00
Michele Caini
0e07482e26 ready to cut v3.1.1 2019-09-05 15:24:08 +02:00
Michele Caini
6bb4800ecd fix batch creation (close #305) 2019-09-05 15:24:03 +02:00
Michele Caini
37e1ac71b0 minor changes 2019-09-05 15:22:24 +02:00
Michele Caini
869f96816c updated doc 2019-09-05 15:21:45 +02:00
Michele Caini
35a7008444 added noexcept specifier 2019-09-03 23:39:38 +02:00
Michele Caini
67f80ee111 the default ctor of entt::meta_factory should be public 2019-09-03 23:31:43 +02:00
Michele Caini
083a58753b typo 2019-09-03 23:31:39 +02:00
Michele Caini
b652357a5c review: group::sort 2019-09-02 16:48:47 +02:00
Michele Caini
49a52140b0 minor changes 2019-09-02 16:34:38 +02:00
Michele Caini
e37f84a227 avoid taking the position twice 2019-09-02 16:23:11 +02:00
Nicki
50fc83d478 Update AUTHORS (#302)
He's one of contributors to the idea of nested groups!! :)
2019-08-30 09:49:46 +02:00
179 changed files with 30204 additions and 21368 deletions

18
.github/FUNDING.yml vendored
View File

@@ -1,12 +1,12 @@
# These are supported funding model platforms
github: # [skypjack] available only for those enrolled in the github sponsor program
patreon: skypjack
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
github: skypjack
patreon:
open_collective:
ko_fi:
tidelift:
community_bridge:
liberapay:
issuehunt:
otechie:
custom: https://www.paypal.me/skypjack

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

@@ -0,0 +1,79 @@
name: build
on: [push, pull_request]
jobs:
linux:
timeout-minutes: 10
strategy:
matrix:
compiler: [g++, clang++]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Compile tests
working-directory: build
env:
CXX: ${{ matrix.compiler }}
run: |
cmake -DBUILD_LIB=ON -DBUILD_EXAMPLE=ON ..
make -j4
- name: Run tests
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 5 -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 }}
steps:
- uses: actions/checkout@v2
- name: Compile tests
working-directory: build
run: |
cmake -DBUILD_LIB=ON -DBUILD_EXAMPLE=ON ${{ matrix.toolset_option }} ..
cmake --build . -j 4
- name: Run tests
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 5 -C Debug -j4
macos:
timeout-minutes: 10
runs-on: macOS-latest
steps:
- uses: actions/checkout@v2
- name: Compile tests
working-directory: build
run: |
cmake -DBUILD_LIB=ON -DBUILD_EXAMPLE=ON ..
make -j4
- name: Run tests
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 5 -C Debug -j4

33
.github/workflows/coverage.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: coverage
on: [push, pull_request]
jobs:
codecov:
timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Compile tests
working-directory: build
env:
CXXFLAGS: "-O0 --coverage -fno-inline -fno-inline-small-functions -fno-default-inline"
CXX: g++
run: |
cmake -DBUILD_LIB=ON -DBUILD_EXAMPLE=ON ..
make -j4
- name: Run tests
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 5 -C Debug -j4
- name: Upload coverage to Codecov
working-directory: build
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
run: |
wget https://codecov.io/bash -O codecov
chmod +x codecov
./codecov -t $CODECOV_TOKEN -B $GITHUB_REF -s .

40
.github/workflows/deploy.yml vendored Normal file
View File

@@ -0,0 +1,40 @@
name: deploy
on:
release:
types: published
jobs:
homebrew-entt:
timeout-minutes: 5
runs-on: ubuntu-latest
env:
GH_REPO: homebrew-entt
FORMULA: entt.rb
steps:
- uses: actions/checkout@v2
- name: Clone repository
working-directory: build
env:
PERSONAL_ACCESS_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
run: |
git clone https://$GITHUB_ACTOR:$PERSONAL_ACCESS_TOKEN@github.com/$GITHUB_ACTOR/$GH_REPO.git
- name: Prepare formula
working-directory: build
run: |
cd $GH_REPO
curl "https://github.com/${{ github.repository }}/archive/${{ github.ref }}.tar.gz" --location --fail --silent --show-error --output archive.tar.gz
sed -i -e '/url/s/".*"/"'$(echo "https://github.com/${{ github.repository }}/archive/${{ github.ref }}.tar.gz" | sed -e 's/[\/&]/\\&/g')'"/' $FORMULA
sed -i -e '/sha256/s/".*"/"'$(openssl sha256 archive.tar.gz | cut -d " " -f 2)'"/' $FORMULA
- name: Update remote
working-directory: build
run: |
cd $GH_REPO
git config --local user.email "action@github.com"
git config --local user.name "$GITHUB_ACTOR"
git add $FORMULA
git commit -m "Update to ${{ github.ref }}"
git push origin master

6
.gitignore vendored
View File

@@ -1,8 +1,12 @@
*.user
# Conan
conan/test_package/build
# IDEs
*.user
.idea
.vscode
.vs
CMakeSettings.json
# Bazel
/bazel-*

View File

@@ -1,97 +0,0 @@
language: cpp
dist: trusty
sudo: false
env:
global:
- CONAN_USERNAME="skypjack"
- CONAN_PACKAGE_NAME="entt"
- CONAN_HEADER_ONLY="True"
- NON_CONAN_DEPLOYMENT="True"
conan-buildsteps: &conan-buildsteps
before_install:
# use this step if you desire to manipulate CONAN variables programmatically
- NON_CONAN_DEPLOYMENT="False"
install:
- chmod +x ./conan/ci/install.sh
- ./conan/ci/install.sh
script:
- chmod +x ./conan/ci/build.sh
- ./conan/ci/build.sh
# the following are dummies to overwrite default build steps
before_script:
- true
after_success:
- true
if: tag IS present
conan-linux: &conan-linux
os: linux
sudo: required
language: python
python: "3.6"
services:
- docker
<<: *conan-buildsteps
matrix:
include:
- os: linux
compiler: gcc
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-7']
env: COMPILER=g++-7
- os: linux
compiler: clang
addons:
apt:
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-6.0']
packages: ['clang-6.0', 'g++-7']
env: COMPILER=clang++-6.0
- os: osx
osx_image: xcode10
compiler: clang
env: COMPILER=clang++
- os: linux
compiler: gcc
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-7']
env:
- COMPILER=g++-7
- CXXFLAGS="-O0 --coverage -fno-inline -fno-inline-small-functions -fno-default-inline"
before_script:
- pip install --user cpp-coveralls
after_success:
- coveralls --gcov gcov-7 --gcov-options '\-lp' --root ${TRAVIS_BUILD_DIR} --build-root ${TRAVIS_BUILD_DIR}/build --extension cpp --extension hpp --exclude deps --include src
# Conan testing and uploading
- <<: *conan-linux
env: CONAN_GCC_VERSIONS=8 CONAN_DOCKER_IMAGE=conanio/gcc8
notifications:
email:
on_success: never
on_failure: always
install:
- echo ${PATH}
- cmake --version
- export CXX=${COMPILER}
- echo ${CXX}
- ${CXX} --version
- ${CXX} -v
script:
- mkdir -p build && cd build
- cmake -DBUILD_TESTING=ON -DBUILD_LIB=ON .. && make -j4
- CTEST_OUTPUT_ON_FAILURE=1 ctest --timeout 5 -C Debug -j4
deploy:
provider: script
script: scripts/update_packages.sh $TRAVIS_TAG
on:
tags: true
condition: “$NON_CONAN_DEPLOYMENT = “True”

13
AUTHORS
View File

@@ -4,33 +4,46 @@ skypjack
# Contributors
alexames
BenediktConze
bjadamson
ceeac
ColinH
corystegel
Croydon
cugone
dbacchet
dBagrat
djarek
DonKult
drglove
eliasdaler
erez-o
eugeneko
gale83
ghost
grdowns
Green-Sky
Innokentiy-Alaytsev
Kerndog73
Lawrencemm
markand
mhammerc
Milerius
morbo84
m-waka
NixAJ
Oortonaut
Paolo-Oliverio
pgruenbacher
prowolf
stefanofiorentino
suVrik
szunhammer
The5-1
vblanco20-1
willtunnels
WizardIke
w1th0utnam3
xissburg
zaucy

View File

@@ -1,9 +1,15 @@
load("//bazel:copts.bzl", "entt_copts")
_msvc_copts = ["/std:c++17"]
_gcc_copts = ["-std=c++17"]
cc_library(
name = "entt",
visibility = ["//visibility:public"],
strip_include_prefix = "src",
hdrs = glob(["src/**/*.h", "src/**/*.hpp"]),
copts = entt_copts,
copts = select({
"@bazel_tools//src/conditions:windows": _msvc_copts,
"@bazel_tools//src/conditions:windows_msvc": _msvc_copts,
"@bazel_tools//src/conditions:windows_msys": _msvc_copts,
"//conditions:default": _gcc_copts,
}),
)

View File

@@ -2,7 +2,7 @@
# EnTT
#
cmake_minimum_required(VERSION 3.7.2)
cmake_minimum_required(VERSION 3.12.4)
#
# Building in-tree is not allowed (we take care of your craziness).
@@ -12,37 +12,44 @@ if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
message(FATAL_ERROR "Prevented in-tree built. Please create a build directory outside of the source code and call cmake from there. Thank you.")
endif()
#
# Read project version
#
set(ENTT_VERSION_REGEX "#define ENTT_VERSION_.*[ \t]+(.+)")
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/src/entt/config/version.h" ENTT_VERSION REGEX ${ENTT_VERSION_REGEX})
list(TRANSFORM ENTT_VERSION REPLACE ${ENTT_VERSION_REGEX} "\\1")
string(JOIN "." ENTT_VERSION ${ENTT_VERSION})
#
# Project configuration
#
project(EnTT VERSION 3.1.0)
include(GNUInstallDirs)
project(
EnTT
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
)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug)
endif()
set(SETTINGS_ORGANIZATION "Michele Caini")
set(SETTINGS_APPLICATION ${PROJECT_NAME})
set(PROJECT_AUTHOR "Michele Caini")
set(PROJECT_AUTHOR_EMAIL "michele.caini@gmail.com")
message("*")
message("* ${PROJECT_NAME} v${PROJECT_VERSION} (${CMAKE_BUILD_TYPE})")
message("* Copyright (c) 2017-2019 ${PROJECT_AUTHOR} <${PROJECT_AUTHOR_EMAIL}>")
message("*")
message(VERBOSE "*")
message(VERBOSE "* ${PROJECT_NAME} v${PROJECT_VERSION} (${CMAKE_BUILD_TYPE})")
message(VERBOSE "* Copyright (c) 2017-2020 Michele Caini <michele.caini@gmail.com>")
message(VERBOSE "*")
option(USE_LIBCPP "Use libc++ by adding -stdlib=libc++ flag if availbale." ON)
option(USE_ASAN "Use address sanitizer by adding -fsanitize=address -fno-omit-frame-pointer flags" OFF)
option(USE_COMPILE_OPTIONS "Use compile options from EnTT." ON)
#
# Compiler stuff
#
if(NOT MSVC AND USE_LIBCPP)
if(NOT WIN32 AND USE_LIBCPP)
include(CheckCXXSourceCompiles)
include(CMakePushCheckState)
@@ -52,11 +59,11 @@ if(NOT MSVC AND USE_LIBCPP)
check_cxx_source_compiles("
#include<type_traits>
int main() { return std::is_same_v<int, int> ? 0 : 1; }
int main() { return std::is_same_v<int, char>; }
" HAS_LIBCPP)
if(NOT HAS_LIBCPP)
message(WARNING "The option USE_LIBCPP is set (by default) but libc++ is not available. The flag will not be added to the target.")
message(VERBOSE "The option USE_LIBCPP is set (by default) but libc++ is not available. The flag will not be added to the target.")
endif()
cmake_pop_check_state()
@@ -66,36 +73,21 @@ endif()
# Add EnTT target
#
add_library(EnTT INTERFACE)
include(GNUInstallDirs)
configure_file(${EnTT_SOURCE_DIR}/cmake/in/version.h.in ${EnTT_SOURCE_DIR}/src/entt/config/version.h @ONLY)
add_library(EnTT INTERFACE)
add_library(EnTT::EnTT ALIAS EnTT)
target_include_directories(
EnTT INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
target_compile_definitions(
EnTT
INTERFACE $<$<AND:$<CONFIG:Debug>,$<NOT:$<CXX_COMPILER_ID:MSVC>>>:DEBUG>
INTERFACE $<$<AND:$<CONFIG:Release>,$<NOT:$<CXX_COMPILER_ID:MSVC>>>:RELEASE>
INTERFACE
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
if(USE_ASAN)
target_compile_options(EnTT INTERFACE $<$<AND:$<CONFIG:Debug>,$<NOT:$<CXX_COMPILER_ID:MSVC>>>:-fsanitize=address -fno-omit-frame-pointer>)
target_link_libraries(EnTT INTERFACE $<$<AND:$<CONFIG:Debug>,$<NOT:$<CXX_COMPILER_ID:MSVC>>>:-fsanitize=address -fno-omit-frame-pointer>)
endif()
if(USE_COMPILE_OPTIONS)
target_compile_options(
EnTT
INTERFACE $<$<AND:$<CONFIG:Debug>,$<NOT:$<CXX_COMPILER_ID:MSVC>>>:-O0 -g>
# it seems that -O3 ruins a bit the performance when using clang ...
INTERFACE $<$<AND:$<CONFIG:Release>,$<CXX_COMPILER_ID:Clang>>:-O2>
# ... on the other side, GCC is incredibly comfortable with it.
INTERFACE $<$<AND:$<CONFIG:Release>,$<CXX_COMPILER_ID:GNU>>:-O3>
)
target_compile_options(EnTT INTERFACE $<$<CONFIG:Debug>:-fsanitize=address -fno-omit-frame-pointer>)
target_link_libraries(EnTT INTERFACE $<$<CONFIG:Debug>:-fsanitize=address -fno-omit-frame-pointer>)
endif()
if(HAS_LIBCPP)
@@ -108,88 +100,62 @@ target_compile_features(EnTT INTERFACE cxx_std_17)
# Install EnTT
#
if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
set(CUSTOM_INSTALL_CONFIGDIR cmake)
else()
set(CUSTOM_INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/entt)
endif()
install(DIRECTORY src/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(TARGETS EnTT EXPORT EnTTTargets)
export(EXPORT EnTTTargets FILE ${EnTT_BINARY_DIR}/EnTTTargets.cmake)
install(
EXPORT EnTTTargets
FILE EnTTTargets.cmake
DESTINATION ${CUSTOM_INSTALL_CONFIGDIR}
)
#
# Build tree package config file
#
configure_file(cmake/in/EnTTBuildConfig.cmake.in EnTTConfig.cmake @ONLY)
include(CMakePackageConfigHelpers)
#
# Install tree package config file
#
configure_package_config_file(
cmake/in/EnTTConfig.cmake.in
${CUSTOM_INSTALL_CONFIGDIR}/EnTTConfig.cmake
INSTALL_DESTINATION ${CUSTOM_INSTALL_CONFIGDIR}
PATH_VARS CMAKE_INSTALL_INCLUDEDIR
install(
TARGETS EnTT
EXPORT EnTTTargets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
write_basic_package_version_file(
${EnTT_BINARY_DIR}/EnTTConfigVersion.cmake
EnTTConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion
)
configure_package_config_file(
${EnTT_SOURCE_DIR}/cmake/in/EnTTConfig.cmake.in
EnTTConfig.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake
)
export(
EXPORT EnTTTargets
FILE ${CMAKE_CURRENT_BINARY_DIR}/EnTTTargets.cmake
NAMESPACE EnTT::
)
install(
EXPORT EnTTTargets
FILE EnTTTargets.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake
NAMESPACE EnTT::
)
install(
FILES
${EnTT_BINARY_DIR}/${CUSTOM_INSTALL_CONFIGDIR}/EnTTConfig.cmake
${EnTT_BINARY_DIR}/EnTTConfigVersion.cmake
DESTINATION ${CUSTOM_INSTALL_CONFIGDIR}
${PROJECT_BINARY_DIR}/EnTTConfig.cmake
${PROJECT_BINARY_DIR}/EnTTConfigVersion.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake
)
install(DIRECTORY src/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
export(PACKAGE EnTT)
#
# Tests
#
option(BUILD_TESTING "Enable testing with ctest." OFF)
include(CTest)
if(BUILD_TESTING)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
option(FIND_GTEST_PACKAGE "Enable finding gtest package." OFF)
if(FIND_GTEST_PACKAGE)
find_package(GTest REQUIRED)
else()
# gtest, gtest_main, gmock and gmock_main targets are available from now on
set(GOOGLETEST_DEPS_DIR ${EnTT_SOURCE_DIR}/deps/googletest)
configure_file(${EnTT_SOURCE_DIR}/cmake/in/googletest.in ${GOOGLETEST_DEPS_DIR}/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . WORKING_DIRECTORY ${GOOGLETEST_DEPS_DIR})
execute_process(COMMAND ${CMAKE_COMMAND} --build . WORKING_DIRECTORY ${GOOGLETEST_DEPS_DIR})
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
add_subdirectory(${GOOGLETEST_DEPS_DIR}/src ${GOOGLETEST_DEPS_DIR}/build)
target_compile_features(gmock_main PRIVATE $<TARGET_PROPERTY:EnTT,INTERFACE_COMPILE_FEATURES>)
target_compile_features(gmock PRIVATE $<TARGET_PROPERTY:EnTT,INTERFACE_COMPILE_FEATURES>)
add_library(GTest::Main ALIAS gtest_main)
endif()
option(BUILD_BENCHMARK "Build benchmark." OFF)
option(BUILD_LIB "Build lib example." OFF)
option(BUILD_MOD "Build mod example." OFF)
option(BUILD_SNAPSHOT "Build snapshot example." OFF)
option(BUILD_EXAMPLE "Build examples." OFF)
option(BUILD_LIB "Build lib tests." OFF)
option(BUILD_SNAPSHOT "Build snapshot test with Cereal." OFF)
enable_testing()
add_subdirectory(test)
@@ -214,13 +180,15 @@ endif()
#
add_custom_target(
entt_aob
aob
SOURCES
appveyor.yml
.github/workflows/build.yml
.github/workflows/coverage.yml
.github/workflows/deploy.yml
.github/FUNDING.yml
AUTHORS
CONTRIBUTING.md
LICENSE
README.md
TODO
.travis.yml
)

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2017-2019 Michele Caini
Copyright (c) 2017-2020 Michele Caini
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

211
README.md
View File

@@ -3,13 +3,14 @@
<!--
@cond TURN_OFF_DOXYGEN
-->
[![GitHub version](https://badge.fury.io/gh/skypjack%2Fentt.svg)](http://badge.fury.io/gh/skypjack%2Fentt)
[![LoC](https://tokei.rs/b1/github/skypjack/entt)](https://github.com/skypjack/entt)
[![Build Status](https://travis-ci.org/skypjack/entt.svg?branch=master)](https://travis-ci.org/skypjack/entt)
[![Build status](https://ci.appveyor.com/api/projects/status/rvhaabjmghg715ck?svg=true)](https://ci.appveyor.com/project/skypjack/entt)
[![Coverage Status](https://coveralls.io/repos/github/skypjack/entt/badge.svg?branch=master)](https://coveralls.io/github/skypjack/entt?branch=master)
[![GitHub version](https://badge.fury.io/gh/skypjack%2Fentt.svg)](https://github.com/skypjack/entt/releases)
[![Build Status](https://github.com/skypjack/entt/workflows/build/badge.svg)](https://github.com/skypjack/entt/actions)
[![Coverage](https://codecov.io/gh/skypjack/entt/branch/master/graph/badge.svg)](https://codecov.io/gh/skypjack/entt)
[![Try online](https://img.shields.io/badge/try-online-brightgreen)](https://godbolt.org/z/cOUcm1)
[![Documentation](https://img.shields.io/badge/docs-docsforge-blue)](http://entt.docsforge.com/)
[![Gitter chat](https://badges.gitter.im/skypjack/entt.png)](https://gitter.im/skypjack/entt)
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/skypjack)
[![Discord channel](https://img.shields.io/discord/707607951396962417?logo=discord)](https://discord.gg/5BjPWBd)
[![Donate](https://img.shields.io/badge/donate-paypal-blue.svg)](https://www.paypal.me/skypjack)
`EnTT` is a header-only, tiny and easy to use library for game programming and
much more written in **modern C++**, mainly known for its innovative
@@ -17,14 +18,17 @@ much more written in **modern C++**, mainly known for its innovative
[Among others](https://github.com/skypjack/entt/wiki/EnTT-in-Action), it's used
in [**Minecraft**](https://minecraft.net/en-us/attribution/) by Mojang and the
[**ArcGIS Runtime SDKs**](https://developers.arcgis.com/arcgis-runtime/) by
Esri. Open an issue or submit a PR if you don't see your project in the list!
Esri.<br/>
If you don't see your project in the list, please open an issue, submit a PR or
add the [#entt](https://github.com/topics/entt) tag to your _topics_! :+1:
---
Do you want to **keep up with changes** or do you have a **question** that
doesn't require you to open an issue?<br/>
Join the [gitter channel](https://gitter.im/skypjack/entt) and meet other users
like you. The more we are, the better for everyone.
Join the [gitter channel](https://gitter.im/skypjack/entt) or the
[discord server](https://discord.gg/5BjPWBd) and meet other users like you. The
more we are, the better for everyone.
Wondering why your **debug build** is so slow on Windows or how to represent a
**hierarchy** with components?<br/>
@@ -34,12 +38,11 @@ Check out the
your answers may already be there.
If you use `EnTT` and you want to say thanks or support the project, please
**consider becoming a patron**:
[![Patreon](https://c5.patreon.com/external/logo/become_a_patron_button.png)](https://www.patreon.com/bePatron?c=1772573)
[Many thanks](https://skypjack.github.io/patreon/) to those who supported me and
still support me today.
**consider becoming a
[sponsor](https://github.com/users/skypjack/sponsorship)**.<br/>
You can help me make the difference.
[Many thanks](https://skypjack.github.io/sponsorship/) to those who supported me
and still support me today.
# Table of Contents
@@ -57,9 +60,6 @@ still support me today.
* [Contributors](#contributors)
* [License](#license)
* [Support](#support)
* [Patreon](#patreon)
* [Donation](#donation)
* [Hire me](#hire-me)
<!--
@endcond TURN_OFF_DOXYGEN
-->
@@ -102,10 +102,10 @@ Here is a brief, yet incomplete list of what it offers today:
Consider this list a work in progress as well as the project. The whole API is
fully documented in-code for those who are brave enough to read it.
Currently, `EnTT` is tested on Linux, Microsoft Windows and OSX. It has proven
to work also on both Android and iOS.<br/>
Most likely it won't be problematic on other systems as well, but it hasn't been
sufficiently tested so far.
It is also known that `EnTT` (version 3) is used in **Minecraft**.<br/>
Given that the game is available literally everywhere, I can confidently say
that the library has been sufficiently tested on every platform that can come to
mind.
## Code Example
@@ -155,8 +155,8 @@ int main() {
for(auto i = 0; i < 10; ++i) {
auto entity = registry.create();
registry.assign<position>(entity, i * 1.f, i * 1.f);
if(i % 2 == 0) { registry.assign<velocity>(entity, i * .1f, i * .1f); }
registry.emplace<position>(entity, i * 1.f, i * 1.f);
if(i % 2 == 0) { registry.emplace<velocity>(entity, i * .1f, i * .1f); }
}
update(dt, registry);
@@ -182,48 +182,30 @@ amazing set of features. And even more, of course.
## Performance
As it stands right now, `EnTT` is just fast enough for my requirements when
compared to my first choice (it was already amazingly fast actually).<br/>
Below is a comparison between the two (both of them compiled with GCC 7.3.0 on a
Dell XPS 13 from mid 2014):
The proposed entity-component system is incredibly fast to iterate entities and
components, this is a fact. Some compilers make a lot of optimizations because
of how `EnTT` works, some others aren't that good. In general, if we consider
real world cases, `EnTT` is somewhere between a bit and much faster than many of
the other solutions around, although I couldn't check them all for obvious
reasons.
| Benchmark | EntityX (compile-time) | EnTT |
|-----------|-------------|-------------|
| Create 1M entities | 0.0147s | **0.0046s** |
| Destroy 1M entities | 0.0053s | **0.0045s** |
| 1M entities, one component | 0.0012s | **1.9e-07s** |
| 1M entities, two components | 0.0012s | **3.8e-07s** |
| 1M entities, two components<br/>Half of the entities have all the components | 0.0009s | **3.8e-07s** |
| 1M entities, two components<br/>One of the entities has all the components | 0.0008s | **1.0e-06s** |
| 1M entities, five components | 0.0010s | **7.0e-07s** |
| 1M entities, ten components | 0.0011s | **1.2e-06s** |
| 1M entities, ten components<br/>Half of the entities have all the components | 0.0010s | **1.2e-06s** |
| 1M entities, ten components<br/>One of the entities has all the components | 0.0008s | **1.2e-06s** |
| Sort 150k entities, one component<br/>Arrays are in reverse order | - | **0.0036s** |
| Sort 150k entities, enforce permutation<br/>Arrays are in reverse order | - | **0.0005s** |
| Sort 150k entities, one component<br/>Arrays are almost sorted, std::sort | - | **0.0035s** |
| Sort 150k entities, one component<br/>Arrays are almost sorted, insertion sort | - | **0.0007s** |
If you are interested, you can compile the `benchmark` test in release mode (to
enable compiler optimizations, otherwise it would make little sense) by setting
the `BUILD_BENCHMARK` option of `CMake` to `ON`, then evaluate yourself whether
you're satisfied with the results or not.
Note: The default version of `EntityX` (`master` branch) wasn't added to the
comparison because it's already much slower than its compile-time counterpart.
Pretty interesting results, aren't them? In fact, these benchmarks are the ones
used by `EntityX` to show _how fast it is_. To be honest, they aren't so good
and these results shouldn't be taken too seriously (indeed they are completely
unrealistic).<br/>
The proposed entity-component system is incredibly fast to iterate entities,
this is a fact. The compiler can make a lot of optimizations because of how
`EnTT` works, even more when components aren't used at all. This is exactly the
case for these benchmarks. On the other hand, if we consider real world cases,
`EnTT` is somewhere between a bit and much faster than the other solutions
around when users also access the components and not just the entities, although
it isn't as fast as reported by these benchmarks.<br/>
This is why they are completely wrong and cannot be used to evaluate any of the
entity-component-system libraries out there.
Honestly I got tired of updating the README file whenever there is an
improvement.<br/>
There are already a lot of projects out there that use `EnTT` as a basis for
comparison (this should already tell you a lot). Many of these benchmarks are
completely wrong, many others are simply incomplete, good at omitting some
information and using the wrong function to compare a given feature. Certainly
there are also good ones but they age quickly if nobody updates them, especially
when the library they are dealing with is actively developed.
The choice to use `EnTT` should be based on its carefully designed API, its
set of features and the general performance, not because some single benchmark
shows it to be the fastest tool available.
set of features and the general performance, **not** because some single
benchmark shows it to be the fastest tool available.
In the future I'll likely try to get even better performance while still adding
new features, mainly for fun.<br/>
@@ -239,13 +221,11 @@ supports at least C++17.<br/>
The requirements below are mandatory to compile the tests and to extract the
documentation:
* `CMake` version 3.2 or later.
* `CMake` version 3.7 or later.
* `Doxygen` version 1.8 or later.
Alternatively, `Bazel` is also supported as a build system (credits to
[zaucy](https://github.com/zaucy) who introduced what's required with
[this](https://github.com/skypjack/entt/pull/291) pull request and offered to
maintain it).<br/>
Alternatively, [Bazel](https://bazel.build) is also supported as a build system
(credits to [zaucy](https://github.com/zaucy) who offered to maintain it).<br/>
In the documentation below I'll still refer to `CMake`, this being the official
build system of the library.
@@ -290,9 +270,12 @@ The API reference will be created in HTML format within the directory
<!--
@cond TURN_OFF_DOXYGEN
-->
It's also available [online](https://skypjack.github.io/entt/) for the latest
version.<br/>
Finally, there exists a [wiki](https://github.com/skypjack/entt/wiki) dedicated
The same version is also available [online](https://skypjack.github.io/entt/)
for the latest release, that is the last stable tag. If you are looking for
something more pleasing to the eye, consider reading the nice-looking version
available on [docsforge](https://entt.docsforge.com/): same documentation, much
more pleasant to read.<br/>
Moreover, there exists a [wiki](https://github.com/skypjack/entt/wiki) dedicated
to the project where users can find all related documentation pages.
<!--
@endcond TURN_OFF_DOXYGEN
@@ -317,16 +300,60 @@ Note that benchmarks are not part of this set.
`EnTT` is available for some of the most known packaging tools. In particular:
* [`Conan`](https://bintray.com/skypjack/conan/entt%3Askypjack/_latestVersion),
the C/C++ Package Manager for Developers.
* [`Conan`](https://github.com/conan-io/conan-center-index), the C/C++ Package
Manager for Developers.
* [`vcpkg`](https://github.com/Microsoft/vcpkg), Microsoft VC++ Packaging
Tool.<br/>
You can download and install `EnTT` in just a few simple steps:
```
$ git clone https://github.com/Microsoft/vcpkg.git
$ cd vcpkg
$ ./bootstrap-vcpkg.sh
$ ./vcpkg integrate install
$ vcpkg install entt
```
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
[create an issue or pull request](https://github.com/Microsoft/vcpkg) on the
`vcpkg` repository.
* [`Homebrew`](https://github.com/skypjack/homebrew-entt), the missing package
manager for macOS.<br/>
Available as a homebrew formula. Just type the following to install it:
```
brew install skypjack/entt/entt
```
* [`vcpkg`](https://github.com/Microsoft/vcpkg/tree/master/ports/entt),
Microsoft VC++ Packaging Tool.
* [`build2`](https://build2.org), build toolchain for developing and packaging C
and C++ code.<br/>
In order to use the [`entt`](https://cppget.org/entt) package in a `build2`
project, add the following line or a similar one to the `manifest` file:
```
depends: entt ^3.0.0
```
Also check that the configuration refers to a valid repository, so that the
package can be found by `build2`:
* [`cppget.org`](https://cppget.org), the open-source community central
repository, accessible as `https://pkg.cppget.org/1/stable`.
* [Package source repository](https://github.com/build2-packaging/entt):
accessible as either `https://github.com/build2-packaging/entt.git` or
`ssh://git@github.com/build2-packaging/entt.git`.
Feel free to [report issues](https://github.com/build2-packaging/entt) with
this package.
Both can be used with `bpkg add-repo` or added in a project
`repositories.manifest`. See the official
[documentation](https://build2.org/build2-toolchain/doc/build2-toolchain-intro.xhtml#guide-repositories)
for more details.
Consider this list a work in progress and help me to make it longer.
@@ -375,8 +402,8 @@ know who has participated so far.
# License
Code and documentation Copyright (c) 2017-2019 Michele Caini.<br/>
Logo Copyright (c) 2018-2019 Richard Caseres.
Code and documentation Copyright (c) 2017-2020 Michele Caini.<br/>
Logo Copyright (c) 2018-2020 Richard Caseres.
Code released under
[the MIT license](https://github.com/skypjack/entt/blob/master/LICENSE).
@@ -390,34 +417,10 @@ Logo released under
-->
# Support
## Patreon
Become a [patron](https://www.patreon.com/bePatron?c=1772573) and get access to
extra content, help me spend more time on the projects you love and create new
ones for you. Your support will help me to continue the work done so far and
make it more professional and feature-rich every day.<br/>
It takes very little to
[become a patron](https://www.patreon.com/bePatron?c=1772573) and thus help the
software you use every day. Don't miss the chance.
## Donation
Developing and maintaining `EnTT` takes some time and lots of coffee. I'd like
to add more and more functionalities in future and turn it in a full-featured
solution.<br/>
If you want to support this project, you can offer me an espresso. I'm from
Italy, we're used to turning the best coffee ever in code. If you find that
it's not enough, feel free to support me the way you prefer.<br/>
Take a look at the donation button at the top of the page for more details or
just click [here](https://www.paypal.me/skypjack).
## Hire me
If you start using `EnTT` and need help, if you want a new feature and want me
to give it the highest priority, if you have any other reason to contact me:
do not hesitate. I'm available for hiring.<br/>
Feel free to take a look at my [profile](https://github.com/skypjack) and
contact me by mail.
If you want to support this project, you can
[offer me](https://github.com/users/skypjack/sponsorship) an espresso.<br/>
If you find that it's not enough, feel free to
[help me](https://www.paypal.me/skypjack) the way you prefer.
<!--
@endcond TURN_OFF_DOXYGEN
-->

51
TODO
View File

@@ -1,32 +1,27 @@
* long term feature: templated generic vm
* long term feature: shared_ptr less locator
* long term feature: shared_ptr less resource cache
* 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
* runner proposal: https://en.wikipedia.org/wiki/Fork%E2%80%93join_model https://slide-rs.github.io/specs/03_dispatcher.html
* work stealing job system (see #100)
- mt scheduler based on const awareness for types
* meta: sort of meta view based on meta stuff to iterate entities, void * and meta info objects
* allow for built-in parallel each if possible
* work stealing job system (see #100) + mt scheduler based on const awareness for types
* meta: sort of meta view based on meta stuff to iterate entities, void * and meta info objects (remove runtime views, welcome reflection)
* allow to replace std:: with custom implementations
* remove runtime views, welcome reflection and what about snapshot?
* types defined at runtime that refer to the same compile-time type (but to different pools) are possible, the library is almost there
* add opaque input iterators to views and groups that return tuples <entity, T &...> (proxy), multi-pass guaranteed
* add fast lane for raw iterations, extend mt doc to describe allowed add/remove with pre-allocations on fast lanes
* registry.each<T...>(first, last) by iterators, entities/components guaranteed
* multi component registry::remove and some others?
* built-in support for dual (or N-) buffering
* allow for custom stomp functions
* deprecate/replace snapshot
* remove dependency
* remove prototype
* add examples (and credits) from @alanjfs :)
* static reflection, hint: template<> meta_type_t<Type>: meta_descriptor<name, func..., props..., etc...> (see #342)
* update documentation for meta, it contains less than half of the actual feature
TODO
* custom (decoupled) pools ==> double buffering, shared components, multi-model
* make meta work across boundaries
- inline variables are fine here, only the head represents a problem
- we should always resolve by looking into the list of types when working across boundaries, no direct resolve
* nested groups: AB/ABC/ABCD/... (hints: sort, check functions)
* use unordered_map for named pools and context variables:
* use direct access (pool-like) also for context variables
* improves multi-stomp
WIP:
* pagination doesn't work nicely across boundaries probably, give it a look. RO operations are fine, adding components maybe not.
* make it easier to hook into the type system and describe how to do that to eg auto-generate meta types on first use
* add observer functions aside observer class
* introduce the component iterators for non-contiguous collections of entities (multi component views, observers, user defined collections)
* snapshot: support for range-based archives
* update snapshot documentation to describe alternatives
* custom pools.
* the Perfect Model.
* page size 0 -> page less mode
* add ::reach and rev iterators to proxy objects for faster and unsafe iterations
* add example: 64 bit ids with 32 bits reserved for users' purposes
* composable views and "faster views", deprecate non-owning groups
* offset instead of pages in the sparse set? top level mask for sparse sets?
* add meta dynamic cast (search base for T in parent, we have the meta type already)
* make meta base/conv node work with storage/any
* deprecate/remove meta_base, meta_conv, ...

View File

@@ -1,41 +1 @@
workspace(name = "com_github_skypjack_entt")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "com_google_googletest",
strip_prefix = "googletest-release-1.8.1",
url = "https://github.com/google/googletest/archive/release-1.8.1.tar.gz",
sha256 = "9bf1fe5182a604b4135edc1a425ae356c9ad15e9b23f9f12a02e80184c3a249c",
)
http_archive(
name = "org_duktape",
url = "https://duktape.org/duktape-2.4.0.tar.xz",
strip_prefix = "duktape-2.4.0",
sha256 = "86a89307d1633b5cedb2c6e56dc86e92679fc34b05be551722d8cc69ab0771fc",
build_file_content = """
cc_library(
name = "duktape",
visibility = ["//visibility:public"],
strip_include_prefix = "src",
hdrs = ["src/duktape.h", "src/duk_config.h"],
srcs = ["src/duktape.c", "src/duktape.h", "src/duk_config.h"],
)
""",
)
http_archive(
name = "bazelregistry_cereal",
strip_prefix = "cereal-8629f40d932d57c5337d4557327f6f22436211b7",
url = "https://github.com/bazelregistry/cereal/archive/8629f40d932d57c5337d4557327f6f22436211b7.zip",
sha256 = "c983a7a2e16b153c3de022a0818d2f4836e510a3fc3bea9d3703de79f58a90a6",
)
# This is for bazelregistry_cereal
http_archive(
name = "bazelregistry_rapidjson",
strip_prefix = "rapidjson-6b980984dacf689be8f65be823203b967c69da04",
url = "https://github.com/bazelregistry/rapidjson/archive/6b980984dacf689be8f65be823203b967c69da04.zip",
sha256 = "82187ba8de53bab3b4fb9e56d0bae81a96fa27a115e5a6ce14c70f0ef9338965",
)

View File

@@ -1,44 +0,0 @@
# can use variables like {build} and {branch}
version: 1.0.{build}
skip_tags: true
image:
- Visual Studio 2019
- Visual Studio 2017
environment:
BUILD_DIR: "%APPVEYOR_BUILD_FOLDER%\\build"
CTEST_OUTPUT_ON_FAILURE: 1
configuration:
- Debug
matrix:
fast_finish: true
for:
-
matrix:
only:
- image: Visual Studio 2019
environment:
CMAKE_GENERATOR: "Visual Studio 16 2019"
-
matrix:
only:
- image: Visual Studio 2017
environment:
CMAKE_GENERATOR: "Visual Studio 15 2017"
before_build:
- cd %BUILD_DIR%
- cmake .. -DBUILD_TESTING=ON -DBUILD_LIB=ON -DCMAKE_CXX_FLAGS=/W1 -G"%CMAKE_GENERATOR%"
after_build:
- ctest --timeout 5 -C Debug -j4
build:
parallel: true
project: build/entt.sln
verbosity: minimal

View File

@@ -1 +0,0 @@
exports_files(["copts.bzl"], visibility = ["//:__subpackages__"])

View File

@@ -1,10 +0,0 @@
_msvc_copts = ["/std:c++17"]
_gcc_copts = ["-std=c++17"]
entt_copts = select({
# Windows
"@bazel_tools//src/conditions:windows": _msvc_copts,
"@bazel_tools//src/conditions:windows_msvc": _msvc_copts,
"@bazel_tools//src/conditions:windows_msys": _msvc_copts,
"//conditions:default": _gcc_copts,
})

View File

@@ -1,6 +0,0 @@
set(ENTT_VERSION "@PROJECT_VERSION@")
set(ENTT_INCLUDE_DIRS "@CMAKE_CURRENT_SOURCE_DIR@/src")
if(NOT CMAKE_VERSION VERSION_LESS "3.0")
include("${CMAKE_CURRENT_LIST_DIR}/EnTTTargets.cmake")
endif()

View File

@@ -1,11 +1,5 @@
set(ENTT_VERSION "@PROJECT_VERSION@")
@PACKAGE_INIT@
set_and_check(ENTT_INCLUDE_DIRS "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@")
if(NOT CMAKE_VERSION VERSION_LESS "3.0")
include("${CMAKE_CURRENT_LIST_DIR}/EnTTTargets.cmake")
endif()
set(EnTT_VERSION "@PROJECT_VERSION@")
include("${CMAKE_CURRENT_LIST_DIR}/EnTTTargets.cmake")
check_required_components("@PROJECT_NAME@")

View File

@@ -1,19 +0,0 @@
project(cereal-download NONE)
cmake_minimum_required(VERSION 3.2)
include(ExternalProject)
ExternalProject_Add(
cereal
GIT_REPOSITORY https://github.com/USCiLab/cereal.git
GIT_TAG v1.2.2
DOWNLOAD_DIR ${CEREAL_DEPS_DIR}
TMP_DIR ${CEREAL_DEPS_DIR}/tmp
STAMP_DIR ${CEREAL_DEPS_DIR}/stamp
SOURCE_DIR ${CEREAL_DEPS_DIR}/src
BINARY_DIR ${CEREAL_DEPS_DIR}/build
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)

View File

@@ -1,19 +0,0 @@
project(duktape-download NONE)
cmake_minimum_required(VERSION 3.2)
include(ExternalProject)
ExternalProject_Add(
duktape
GIT_REPOSITORY https://github.com/svaarala/duktape-releases.git
GIT_TAG v2.2.0
DOWNLOAD_DIR ${DUKTAPE_DEPS_DIR}
TMP_DIR ${DUKTAPE_DEPS_DIR}/tmp
STAMP_DIR ${DUKTAPE_DEPS_DIR}/stamp
SOURCE_DIR ${DUKTAPE_DEPS_DIR}/src
BINARY_DIR ${DUKTAPE_DEPS_DIR}/build
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)

View File

@@ -1,19 +0,0 @@
project(googletest-download NONE)
cmake_minimum_required(VERSION 3.2)
include(ExternalProject)
ExternalProject_Add(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG master
DOWNLOAD_DIR ${GOOGLETEST_DEPS_DIR}
TMP_DIR ${GOOGLETEST_DEPS_DIR}/tmp
STAMP_DIR ${GOOGLETEST_DEPS_DIR}/stamp
SOURCE_DIR ${GOOGLETEST_DEPS_DIR}/src
BINARY_DIR ${GOOGLETEST_DEPS_DIR}/build
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)

View File

@@ -1,11 +0,0 @@
#ifndef ENTT_CONFIG_VERSION_H
#define ENTT_CONFIG_VERSION_H
#define ENTT_VERSION "@PROJECT_VERSION@"
#define ENTT_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define ENTT_VERSION_MINOR @PROJECT_VERSION_MINOR@
#define ENTT_VERSION_PATCH @PROJECT_VERSION_PATCH@
#endif // ENTT_CONFIG_VERSION_H

View File

@@ -4,24 +4,19 @@ from cpt.packager import ConanMultiPackager
import os
if __name__ == "__main__":
username = os.getenv("GITHUB_ACTOR")
tag_version = os.getenv("GITHUB_REF")
tag_package = os.getenv("GITHUB_REPOSITORY")
login_username = os.getenv("CONAN_LOGIN_USERNAME")
username = os.getenv("CONAN_USERNAME")
tag_version = os.getenv("CONAN_PACKAGE_VERSION", os.getenv("TRAVIS_TAG"))
package_version = tag_version.replace("v", "")
package_name_unset = "SET-CONAN_PACKAGE_NAME-OR-CONAN_REFERENCE"
package_name = os.getenv("CONAN_PACKAGE_NAME", package_name_unset)
package_version = tag_version.replace("refs/tags/v", "")
package_name = tag_package.replace("skypjack/", "")
reference = "{}/{}".format(package_name, package_version)
channel = os.getenv("CONAN_CHANNEL", "stable")
upload = os.getenv("CONAN_UPLOAD")
stable_branch_pattern = os.getenv("CONAN_STABLE_BRANCH_PATTERN", r"v\d+\.\d+\.\d+.*")
test_folder = os.getenv("CPT_TEST_FOLDER", os.path.join("conan", "test_package"))
upload_only_when_stable = os.getenv("CONAN_UPLOAD_ONLY_WHEN_STABLE", True)
header_only = os.getenv("CONAN_HEADER_ONLY", False)
pure_c = os.getenv("CONAN_PURE_C", False)
disable_shared = os.getenv("CONAN_DISABLE_SHARED_BUILD", "False")
if disable_shared == "True" and package_name == package_name_unset:
raise Exception("CONAN_DISABLE_SHARED_BUILD: True is only supported when you define CONAN_PACKAGE_NAME")
builder = ConanMultiPackager(username=username,
reference=reference,
@@ -31,10 +26,7 @@ if __name__ == "__main__":
stable_branch_pattern=stable_branch_pattern,
upload_only_when_stable=upload_only_when_stable,
test_folder=test_folder)
if header_only == "False":
builder.add_common_builds(pure_c=pure_c)
else:
builder.add()
builder.add()
filtered_builds = []
for settings, options, env_vars, build_requires, reference in builder.items:

View File

@@ -43,8 +43,8 @@ int main() {
for(auto i = 0; i < 10; ++i) {
auto entity = registry.create();
registry.assign<position>(entity, i * 1.f, i * 1.f);
if(i % 2 == 0) { registry.assign<velocity>(entity, i * .1f, i * .1f); }
registry.emplace<position>(entity, i * 1.f, i * 1.f);
if(i % 2 == 0) { registry.emplace<velocity>(entity, i * .1f, i * .1f); }
}
update(dt, registry);

2
deps/.gitignore vendored
View File

@@ -1,2 +0,0 @@
*
!.gitignore

View File

@@ -2,6 +2,7 @@
# Doxygen configuration (documentation)
#
set(DOXY_DEPS_DIRECTORY ${EnTT_SOURCE_DIR}/deps)
set(DOXY_SOURCE_DIRECTORY ${EnTT_SOURCE_DIR}/src)
set(DOXY_DOCS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
set(DOXY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
@@ -13,18 +14,9 @@ add_custom_target(
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxy.cfg
WORKING_DIRECTORY ${EnTT_SOURCE_DIR}
VERBATIM
SOURCES doxy.in
)
install(
DIRECTORY ${DOXY_OUTPUT_DIRECTORY}/html
DESTINATION share/${PROJECT_NAME}-${PROJECT_VERSION}/
)
add_custom_target(
docs_aob
SOURCES
dox/extra.dox
md/config.md
md/core.md
md/entity.md
md/faq.md
@@ -33,6 +25,13 @@ add_custom_target(
md/locator.md
md/meta.md
md/process.md
md/references.md
md/resource.md
md/signal.md
doxy.in
)
install(
DIRECTORY ${DOXY_OUTPUT_DIRECTORY}/html
DESTINATION share/${PROJECT_NAME}-${PROJECT_VERSION}/
)

View File

@@ -1,4 +1,4 @@
# Doxyfile 1.8.13
# Doxyfile 1.8.16
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
@@ -17,11 +17,11 @@
# Project related configuration options
#---------------------------------------------------------------------------
# This tag specifies the encoding used for all characters in the config file
# that follow. The default is UTF-8 which is also the encoding used for all text
# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
# for the list of possible encodings.
# This tag specifies the encoding used for all characters in the configuration
# file that follow. The default is UTF-8 which is also the encoding used for all
# text before the first occurrence of this tag. Doxygen uses libiconv (or the
# iconv built into libc) for the transcoding. See
# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
# The default value is: UTF-8.
DOXYFILE_ENCODING = UTF-8
@@ -93,6 +93,14 @@ ALLOW_UNICODE_NAMES = NO
OUTPUT_LANGUAGE = English
# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all generated output in the proper direction.
# Possible values are: None, LTR, RTL and Context.
# The default value is: None.
OUTPUT_TEXT_DIRECTION = None
# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
# descriptions after the members that are listed in the file and class
# documentation (similar to Javadoc). Set to NO to disable this.
@@ -179,6 +187,16 @@ SHORT_NAMES = NO
JAVADOC_AUTOBRIEF = NO
# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line
# such as
# /***************
# as being the beginning of a Javadoc-style comment "banner". If set to NO, the
# Javadoc-style will behave just like regular comments and it will not be
# interpreted by doxygen.
# The default value is: NO.
JAVADOC_BANNER = NO
# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
# line (until the first dot) of a Qt-style comment as the brief description. If
# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
@@ -226,7 +244,12 @@ TAB_SIZE = 4
# will allow you to put the command \sideeffect (or @sideeffect) in the
# documentation, which will result in a user-defined paragraph with heading
# "Side Effects:". You can put \n's in the value part of an alias to insert
# newlines.
# newlines (in the resulting output). You can put ^^ in the value part of an
# alias to insert a newline as if a physical newline was in the original file.
# When you need a literal { or } or , in the value part of an alias you have to
# escape them by means of a backslash (\), this can lead to conflicts with the
# commands \{ and \} for these it is advised to use the version @{ and @} or use
# a double escape (\\{ and \\})
ALIASES =
@@ -264,17 +287,26 @@ OPTIMIZE_FOR_FORTRAN = NO
OPTIMIZE_OUTPUT_VHDL = NO
# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice
# sources only. Doxygen will then generate output that is more tailored for that
# language. For instance, namespaces will be presented as modules, types will be
# separated into more groups, etc.
# The default value is: NO.
OPTIMIZE_OUTPUT_SLICE = NO
# Doxygen selects the parser to use depending on the extension of the files it
# parses. With this tag you can assign which parser to use for a given
# extension. Doxygen has a built-in mapping, but you can override or extend it
# using this tag. The format is ext=language, where ext is a file extension, and
# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
# Fortran. In the later case the parser tries to guess whether the code is fixed
# or free formatted code, this is the default for Fortran type files), VHDL. For
# instance to make doxygen treat .inc files as Fortran files (default is PHP),
# and .f files as C (default is Fortran), use: inc=Fortran f=C.
# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice,
# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
# tries to guess whether the code is fixed or free formatted code, this is the
# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat
# .inc files as Fortran files (default is PHP), and .f files as C (default is
# Fortran), use: inc=Fortran f=C.
#
# Note: For files without extension you can use no_extension as a placeholder.
#
@@ -285,7 +317,7 @@ EXTENSION_MAPPING =
# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
# according to the Markdown format, which allows for more readable
# documentation. See http://daringfireball.net/projects/markdown/ for details.
# documentation. See https://daringfireball.net/projects/markdown/ for details.
# The output of markdown processing is further processed by doxygen, so you can
# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
# case of backward compatibilities issues.
@@ -297,10 +329,10 @@ MARKDOWN_SUPPORT = YES
# to that level are automatically included in the table of contents, even if
# they do not have an id attribute.
# Note: This feature currently applies only to Markdown headings.
# Minimum value: 0, maximum value: 99, default value: 0.
# Minimum value: 0, maximum value: 99, default value: 5.
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
TOC_INCLUDE_HEADINGS = 4
TOC_INCLUDE_HEADINGS = 5
# When enabled doxygen tries to link words that correspond to documented
# classes, or namespaces to their corresponding documentation. Such a link can
@@ -327,7 +359,7 @@ BUILTIN_STL_SUPPORT = NO
CPP_CLI_SUPPORT = NO
# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
# will parse them like normal C++ but will assume all classes use public instead
# of private inheritance when no explicit protection keyword is present.
# The default value is: NO.
@@ -433,6 +465,12 @@ EXTRACT_ALL = NO
EXTRACT_PRIVATE = NO
# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual
# methods of a class will be included in the documentation.
# The default value is: NO.
EXTRACT_PRIV_VIRTUAL = NO
# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
# scope will be included in the documentation.
# The default value is: NO.
@@ -511,7 +549,7 @@ INTERNAL_DOCS = NO
# 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
# and Mac users are advised to set this option to NO.
# (including Cygwin) ands Mac users are advised to set this option to NO.
# The default value is: system dependent.
CASE_SENSE_NAMES = YES
@@ -698,7 +736,7 @@ LAYOUT_FILE =
# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
# the reference definitions. This must be a list of .bib files. The .bib
# extension is automatically appended if omitted. This requires the bibtex tool
# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
# For LaTeX the style of the bibliography can be controlled using
# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
# search path. See also \cite for info how to create references.
@@ -743,7 +781,8 @@ WARN_IF_DOC_ERROR = YES
# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
# are documented, but have no documentation for their parameters or return
# value. If set to NO, doxygen will only warn about wrong or incomplete
# parameter documentation, but not about the absence of documentation.
# parameter documentation, but not about the absence of documentation. If
# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
# The default value is: NO.
WARN_NO_PARAMDOC = YES
@@ -781,13 +820,13 @@ WARN_LOGFILE =
# Note: If this tag is empty the current directory is searched.
INPUT = @DOXY_SOURCE_DIRECTORY@ \
@DOXY_DOCS_DIRECTORY@ \
@PROJECT_SOURCE_DIR@/README.md
@DOXY_DOCS_DIRECTORY@ \
@PROJECT_SOURCE_DIR@/README.md
# 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: http://www.gnu.org/software/libiconv) for the list of
# documentation (see: https://www.gnu.org/software/libiconv/) for the list of
# possible encodings.
# The default value is: UTF-8.
@@ -805,7 +844,7 @@ INPUT_ENCODING = UTF-8
# *.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, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
FILE_PATTERNS = *.h \
*.hpp \
@@ -825,7 +864,7 @@ RECURSIVE = YES
# Note that relative paths are relative to the directory from which doxygen is
# run.
EXCLUDE =
EXCLUDE = @DOXY_DEPS_DIRECTORY@
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded
@@ -963,7 +1002,7 @@ INLINE_SOURCES = NO
STRIP_CODE_COMMENTS = YES
# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
# function all documented functions referencing it will be listed.
# entity all documented functions referencing it will be listed.
# The default value is: NO.
REFERENCED_BY_RELATION = NO
@@ -995,12 +1034,12 @@ SOURCE_TOOLTIPS = YES
# If the USE_HTAGS tag is set to YES then the references to source code will
# point to the HTML generated by the htags(1) tool instead of doxygen built-in
# source browser. The htags tool is part of GNU's global source tagging system
# (see http://www.gnu.org/software/global/global.html). You will need version
# (see https://www.gnu.org/software/global/global.html). You will need version
# 4.8.6 or higher.
#
# To use it do the following:
# - Install the latest version of global
# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file
# - Make sure the INPUT points to the root of the source tree
# - Run doxygen as normal
#
@@ -1028,7 +1067,7 @@ VERBATIM_HEADERS = YES
# 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.
# generated with the -Duse_libclang=ON option for CMake.
# The default value is: NO.
CLANG_ASSISTED_PARSING = NO
@@ -1041,6 +1080,16 @@ CLANG_ASSISTED_PARSING = NO
CLANG_OPTIONS =
# If clang assisted parsing is enabled you can provide the clang parser with the
# path to the compilation database (see:
# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files
# were built. This is equivalent to specifying the "-p" option to a clang tool,
# such as clang-check. These options will then be passed to the parser.
# Note: The availability of this option depends on whether or not doxygen was
# generated with the -Duse_libclang=ON option for CMake.
CLANG_DATABASE_PATH =
#---------------------------------------------------------------------------
# Configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
@@ -1159,7 +1208,7 @@ HTML_EXTRA_FILES =
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
# will adjust the colors in the style sheet and background images according to
# this color. Hue is specified as an angle on a colorwheel, see
# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
# purple, and 360 is red again.
# Minimum value: 0, maximum value: 359, default value: 220.
@@ -1195,6 +1244,17 @@ HTML_COLORSTYLE_GAMMA = 80
HTML_TIMESTAMP = NO
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
# documentation will contain a main index with vertical navigation menus that
# are dynamically created via Javascript. If disabled, the navigation index will
# consists of multiple levels of tabs that are statically embedded in every HTML
# page. Disable this option to support browsers that do not have Javascript,
# like the Qt help browser.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_DYNAMIC_MENUS = YES
# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
# documentation will contain sections that can be hidden and shown after the
# page has loaded.
@@ -1218,13 +1278,13 @@ 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: http://developer.apple.com/tools/xcode/), introduced with
# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
# 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 http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
# for more information.
# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
# genXcode/_index.html for more information.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
@@ -1263,7 +1323,7 @@ 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: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
# (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
@@ -1339,7 +1399,7 @@ 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: http://qt-project.org/doc/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.
@@ -1347,7 +1407,7 @@ 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: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
# 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.
@@ -1356,7 +1416,7 @@ 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: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
# 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.
@@ -1364,7 +1424,7 @@ 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: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
# 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.
@@ -1372,7 +1432,7 @@ QHP_CUST_FILTER_ATTRS =
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
# project's filter section matches. Qt Help Project / Filter Attributes (see:
# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_SECT_FILTER_ATTRS =
@@ -1465,7 +1525,7 @@ EXT_LINKS_IN_WINDOW = NO
FORMULA_FONTSIZE = 10
# Use the FORMULA_TRANPARENT tag to determine whether or not the images
# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
# generated for formulas are transparent PNGs. Transparent PNGs are not
# supported properly for IE 6.0, but are supported on all modern browsers.
#
@@ -1477,7 +1537,7 @@ FORMULA_FONTSIZE = 10
FORMULA_TRANSPARENT = YES
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
# http://www.mathjax.org) which uses client side Javascript for the rendering
# https://www.mathjax.org) which uses client side Javascript for the rendering
# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
# installed or if you want to formulas look prettier in the HTML output. When
# enabled you may also need to install MathJax separately and configure the path
@@ -1504,11 +1564,11 @@ MATHJAX_FORMAT = HTML-CSS
# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
# Content Delivery Network so you can quickly see the result without installing
# MathJax. However, it is strongly recommended to install a local copy of
# MathJax from http://www.mathjax.org before deployment.
# The default value is: http://cdn.mathjax.org/mathjax/latest.
# MathJax from https://www.mathjax.org before deployment.
# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/
# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
# extension names that should be enabled during MathJax rendering. For example
@@ -1566,7 +1626,7 @@ 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: http://xapian.org/).
# Xapian (see: https://xapian.org/).
#
# See the section "External Indexing and Searching" for details.
# The default value is: NO.
@@ -1579,7 +1639,7 @@ 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: http://xapian.org/). See the section "External Indexing and
# 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.
@@ -1631,21 +1691,35 @@ LATEX_OUTPUT = latex
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
# invoked.
#
# Note that when enabling USE_PDFLATEX this option is only used for generating
# bitmaps for formulas in the HTML output, but not in the Makefile that is
# written to the output directory.
# The default file is: latex.
# Note that when not enabling USE_PDFLATEX the default is latex when enabling
# USE_PDFLATEX the default is pdflatex and when in the later case latex is
# chosen this is overwritten by pdflatex. For specific output languages the
# default can have been set differently, this depends on the implementation of
# the output language.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_CMD_NAME = latex
LATEX_CMD_NAME =
# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
# index for LaTeX.
# Note: This tag is used in the Makefile / make.bat.
# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file
# (.tex).
# The default file is: makeindex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
MAKEINDEX_CMD_NAME = makeindex
# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to
# generate index for LaTeX. In case there is no backslash (\) as first character
# it will be automatically added in the LaTeX code.
# Note: This tag is used in the generated output file (.tex).
# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.
# The default value is: makeindex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_MAKEINDEX_CMD = makeindex
# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
# documents. This may be useful for small projects and may help to save some
# trees in general.
@@ -1766,7 +1840,7 @@ LATEX_SOURCE_CODE = NO
# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
# bibliography, e.g. plainnat, or ieeetr. See
# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
# The default value is: plain.
# This tag requires that the tag GENERATE_LATEX is set to YES.
@@ -1780,6 +1854,14 @@ LATEX_BIB_STYLE = plain
LATEX_TIMESTAMP = NO
# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
# path from which the emoji images will be read. If a relative path is entered,
# it will be relative to the LATEX_OUTPUT directory. If left blank the
# LATEX_OUTPUT directory will be used.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_EMOJI_DIRECTORY =
#---------------------------------------------------------------------------
# Configuration options related to the RTF output
#---------------------------------------------------------------------------
@@ -1819,9 +1901,9 @@ COMPACT_RTF = NO
RTF_HYPERLINKS = NO
# Load stylesheet definitions from file. Syntax is similar to doxygen's config
# file, i.e. a series of assignments. You only have to provide replacements,
# missing definitions are set to their default value.
# Load stylesheet definitions from file. Syntax is similar to doxygen's
# configuration file, i.e. a series of assignments. You only have to provide
# replacements, missing definitions are set to their default value.
#
# See also section "Doxygen usage" for information on how to generate the
# default style sheet that doxygen normally uses.
@@ -1830,8 +1912,8 @@ RTF_HYPERLINKS = NO
RTF_STYLESHEET_FILE =
# Set optional variables used in the generation of an RTF document. Syntax is
# similar to doxygen's config file. A template extensions file can be generated
# using doxygen -e rtf extensionFile.
# similar to doxygen's configuration file. A template extensions file can be
# generated using doxygen -e rtf extensionFile.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_EXTENSIONS_FILE =
@@ -1917,6 +1999,13 @@ XML_OUTPUT = xml
XML_PROGRAMLISTING = YES
# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include
# namespace members in file scope as well, matching the HTML output.
# The default value is: NO.
# This tag requires that the tag GENERATE_XML is set to YES.
XML_NS_MEMB_FILE_SCOPE = NO
#---------------------------------------------------------------------------
# Configuration options related to the DOCBOOK output
#---------------------------------------------------------------------------
@@ -1949,9 +2038,9 @@ DOCBOOK_PROGRAMLISTING = NO
#---------------------------------------------------------------------------
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
# AutoGen Definitions (see http://autogen.sf.net) file that captures the
# structure of the code including all documentation. Note that this feature is
# still experimental and incomplete at the moment.
# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
# the structure of the code including all documentation. Note that this feature
# is still experimental and incomplete at the moment.
# The default value is: NO.
GENERATE_AUTOGEN_DEF = NO
@@ -2011,7 +2100,7 @@ ENABLE_PREPROCESSING = YES
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
MACRO_EXPANSION = NO
MACRO_EXPANSION = YES
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
# the macro expansion is limited to the macros specified with the PREDEFINED and
@@ -2118,12 +2207,6 @@ EXTERNAL_GROUPS = YES
EXTERNAL_PAGES = YES
# The PERL_PATH should be the absolute path and name of the perl script
# interpreter (i.e. the result of 'which perl').
# The default file (with absolute path) is: /usr/bin/perl.
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
@@ -2137,15 +2220,6 @@ PERL_PATH = /usr/bin/perl
CLASS_DIAGRAMS = YES
# You can define message sequence charts within doxygen comments using the \msc
# command. Doxygen will then run the mscgen tool (see:
# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
# documentation. The MSCGEN_PATH tag allows you to specify the directory where
# the mscgen tool resides. If left empty the tool is assumed to be found in the
# default search path.
MSCGEN_PATH =
# You can include diagrams made with dia in doxygen documentation. Doxygen will
# then run dia to produce the diagram and insert it in the documentation. The
# DIA_PATH tag allows you to specify the directory where the dia binary resides.

109
docs/md/config.md Normal file
View File

@@ -0,0 +1,109 @@
# Crash Course: configuration
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [Definitions](#definitions)
* [ENTT_STANDALONE](#entt_standalone)
* [ENTT_NOEXCEPT](#entt_noexcept)
* [ENTT_HS_SUFFIX and ENTT_HWS_SUFFIX](#entt_hs_suffix_and_entt_hws_suffix)
* [ENTT_USE_ATOMIC](#entt_use_atomic)
* [ENTT_ID_TYPE](#entt_id_type)
* [ENTT_PAGE_SIZE](#entt_page_size)
* [ENTT_ASSERT](#entt_assert)
* [ENTT_NO_ETO](#entt_no_eto)
* [ENTT_STANDARD_CPP](#entt_standard_cpp)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
`EnTT` doesn't offer many hooks for customization but it certainly offers
some.<br/>
In the vast majority of cases, users will have no interest in changing the
default parameters. For all other cases, the list of possible configurations
with which it's possible to adjust the behavior of the library at runtime can be
found below.
# Definitions
All options are intended as parameters to the compiler (or user-defined macros
within the compilation units, if preferred).<br/>
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_STANDALONE
`EnTT` is designed in such a way that it works (almost) everywhere out of the
box. However, this is the result of many refinements over time and a compromise
regarding some optimizations.<br/>
It's worth noting that users can get a small performance boost by passing this
definition to the compiler when the library is used in a standalone application.
## ENTT_NOEXCEPT
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.
## ENTT_HS_SUFFIX and ENTT_HWS_SUFFIX
The `hashed_string` class introduces the `_hs` and `_hws` suffixes to accompany
its user defined literals.<br/>
In the case of conflicts or even just to change these suffixes, it's possible to
do so by associating new ones with these definitions.
## ENTT_USE_ATOMIC
In general, `EnTT` doesn't offer primitives to support multi-threading. Many of
the features can be split over multiple threads without any explicit control and
the user is the only one who knows if and when a synchronization point is
required.<br/>
However, some features aren't easily accessible to users and can be made
thread-safe by means of this definition.
## ENTT_ID_TYPE
`entt::id_type` is directly controlled by this definition and widely used within
the library.<br/>
By default, its type is `std::uint32_t`. However, users can define a different
default type if necessary.
## ENTT_PAGE_SIZE
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 in some
corner cases.<br/>
The default size of a page is 32kB 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
structures. In fact, it offers many features that result in undefined behavior
if not used correctly.<br/>
To get around this, the library relies on a lot of `assert`s for the purpose of
detecting errors in debug builds. However, these assertions may in turn affect
performance to an extent.<br/>
This option is meant to disable all controls.
## ENTT_NO_ETO
In order to reduce memory consumption and increase performance, empty types are
never stored by the ECS module of `EnTT`.<br/>
Use this variable to treat these types like all others and therefore to create a
dedicated storage for them.
## ENTT_STANDARD_CPP
After many adventures, `EnTT` finally works fine across boundaries.<br/>
To do this, the library mixes some non-standard language features with others
that are perfectly compliant.<br/>
This definition will prevent the library from using non-standard techniques,
that is, functionalities that aren't fully compliant with the standard C++.

View File

@@ -6,12 +6,22 @@
# Table of Contents
* [Introduction](#introduction)
* [Compile-time identifiers](#compile-time-identifiers)
* [Runtime identifiers](#runtime-identifiers)
* [Unique sequential identifiers](#unique-sequential-identifiers)
* [Compile-time generator](#compile-time-generator)
* [Runtime generator](#runtime-generator)
* [Hashed strings](#hashed-strings)
* [Wide characters](wide-characters)
* [Conflicts](#conflicts)
* [Monostate](#monostate)
* [Type support](#type-support)
* [Type info](#type-info)
* [Almost unique identifiers](#almost-unique-identifiers)
* [Type index](#type-index)
* [Type traits](#type-traits)
* [Member class type](#member-class-type)
* [Integral constant](#integral-constant)
* [Tag](#tag)
* [Utilities](#utilities)
<!--
@endcond TURN_OFF_DOXYGEN
-->
@@ -23,19 +33,20 @@ of the library itself.<br/>
Hardly users will include these features in their code, but it's worth
describing what `EnTT` offers so as not to reinvent the wheel in case of need.
# Compile-time identifiers
# Unique sequential identifiers
Sometimes it's useful to be able to give unique identifiers to types at
compile-time.<br/>
There are plenty of different solutions out there and I could have used one of
them. However, I decided to spend my time to define a compact and versatile tool
Sometimes it's useful to be able to give unique, sequential numeric identifiers
to types either at compile-time or runtime.<br/>
There are plenty of different solutions for this out there and I could have used
one of them. However, I decided to spend my time to define a couple of tools
that fully embraces what the modern C++ has to offer.
The _result of my efforts_ is the `identifier` class template:
## Compile-time generator
To generate sequential numeric identifiers at compile-time, `EnTT` offers the
`identifier` class template:
```cpp
#include <ident.hpp>
// defines the identifiers for the given types
using id = entt::identifier<a_type, another_type>;
@@ -53,14 +64,14 @@ default:
}
```
This is all what the class template has to offer: a `type` inline variable that
contains a numerical identifier for the given type. It can be used in any
context where constant expressions are required.
This is all what this class template has to offer: a `type` inline variable that
contains a numeric identifier for the given type. It can be used in any context
where constant expressions are required.
As long as the list remains unchanged, identifiers are also guaranteed to be the
same for every run. In case they have been used in a production environment and
a type has to be removed, one can just use a placeholder to left the other
identifiers unchanged:
As long as the list remains unchanged, identifiers are also guaranteed to be
stable across different runs. In case they have been used in a production
environment and a type has to be removed, one can just use a placeholder to left
the other identifiers unchanged:
```cpp
template<typename> struct ignore_type {};
@@ -72,35 +83,30 @@ using id = entt::identifier<
>;
```
A bit ugly to see, but it works at least.
Perhaps a bit ugly to see in a codebase but it gets the job done at least.
# Runtime identifiers
## Runtime generator
Sometimes it's useful to be able to give unique identifiers to types at
runtime.<br/>
There are plenty of different solutions out there and I could have used one of
them. In fact, I adapted the most common one to my requirements and used it
extensively within the entire library.
It's the `family` class. Here is an example of use directly from the
entity-component system:
To generate sequential numeric identifiers at runtime, `EnTT` offers the
`family` class template:
```cpp
using component_family = entt::family<struct internal_registry_component_family>;
// defines a custom generator
using id = entt::family<struct my_tag>;
// ...
template<typename Component>
component_type component() const noexcept {
return component_family::type<Component>;
}
const auto a_type_id = id::type<a_type>;
const auto another_type_id = id::type<another_type>;
```
This is all what a _family_ has to offer: a `type` inline variable that contains
a numerical identifier for the given type.
a numeric identifier for the given type.<br/>
The generator is customizable, so as to get different _sequences_ for different
purposes if needed.
Please, note that identifiers aren't guaranteed to be the same for every run.
Indeed it mostly depends on the flow of execution.
Please, note that identifiers aren't guaranteed to be stable across different
runs. Indeed it mostly depends on the flow of execution.
# Hashed strings
@@ -182,3 +188,284 @@ entt::monostate<"mykey"_hs>{} = 42;
const bool b = entt::monostate<"mykey"_hs>{};
const int i = entt::monostate<entt::hashed_string{"mykey"}>{};
```
# Type support
`EnTT` provides some basic information about types of all kinds.<br/>
It also offers additional features that are not yet available in the standard
library or that will never be.
## Type info
This class template 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.
Currently, there are a couple of information available:
* The numeric identifier associated with a given type:
```cpp
auto id = entt::type_info<my_type>::id();
```
In general, the `id` function 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).
This function **can** use non-standard features of the language for its own
purposes. This makes it possible to provide compile-time identifiers that
remain stable across different runs.<br/>
In all cases, users can prevent the library from using these features by means
of the `ENTT_STANDARD_CPP` definition. In this case, there is no guarantee
that identifiers remain stable across executions. Moreover, they are generated
at runtime and are no longer a compile-time thing.
* The _name_ associated with a given type:
```cpp
auto name = entt::type_info<my_type>::name();
```
The name associated with a type is extracted from some information generally
made available by the compiler in use. Therefore, it may differ depending on
the compiler and may be empty in the event that this information isn't
available.<br/>
For example, given the following class:
```cpp
struct my_type { /* ... */ };
```
The name is `my_type` when compiled with GCC or CLang and `struct my_type`
when MSVC is in use.<br/>
Most of the time the name is also retrieved at compile-time and is therefore
always returned through an `std::string_view`. Users can easily access it and
modify it as needed, for example by removing the word `struct` to standardize
the result. `EnTT` won't do this for obvious reasons, since it requires
copying and creating a new string potentially at runtime.
This function **can** use non-standard features of the language for its own
purposes. As for the numeric identifier, users can prevent the library from
using non-standard features by means of the `ENTT_STANDARD_CPP` definition. In
this case, the name will be empty by default.
An external type system can also be used if needed. In fact, `type_info` 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_info<Type, std::enable_if_t<has_custom_data_v<Type>>> {
static constexpr entt::id_type id() ENTT_NOEXCEPT {
return Type::custom_id();
}
static constexpr std::string_view name() ENTT_NOEXCEPT {
return Type::custom_name();
}
};
```
Note that this class template and its specializations are widely used within
`EnTT`. It also plays a very important role in making `EnTT` work transparently
across boundaries in many cases.<br/>
Please refer to the dedicated section for more details.
### Almost unique identifiers
Since the default non-standard, compile-time implementation makes use of hashed
strings, it may happen that two types are assigned the same numeric
identifier.<br/>
In fact, although this is quite rare, it's not entirely excluded.
Another case where two types are assigned the same identifier is when classes
from different contexts (for example two or more libraries loaded at runtime)
have the same fully qualified name.<br/>
If the types have the same name and belong to the same namespace then their
identifiers _could_ be identical (they won't necessarily be the same though).
Fortunately, there are several easy ways to deal with this:
* The most trivial one is to define the `ENTT_STANDARD_CPP` macro. Runtime
identifiers don't suffer from the same problem in fact. However, this solution
doesn't work well with a plugin system, where the libraries aren't linked.
* Another possibility is to specialize the `type_info` class for one of the
conflicting types, in order to assign it a custom identifier. This is probably
the easiest solution that also preserves the feature of the tool.
* A fully customized identifier generation policy (based for example on enum
classes or preprocessing steps) may represent yet another option.
These are just some examples of possible approaches to the problem but there are
many others. As already mentioned above, since users have full control over
their types, this problem is in any case easy to solve and should not worry too
much.<br/>
In all likelihood, it will never happen to run into a conflict anyway.
## Type index
Types in `EnTT` are assigned also unique, sequential _indexes_ generated at
runtime:
```cpp
auto index = entt::type_index<my_type>::value();
```
This value may differ from the numeric identifier of a type and isn't guaranteed
to be stable across different runs. However, it can be very useful as index in
associative and unordered associative containers or for positional accesses in a
vector or an array.
So as not to conflict with the other tools available, the `family` class isn't
used to generate these indexes. Therefore, the numeric identifiers returned by
the two tools may differ.<br/>
On the other hand, this leaves users with full powers over the `family` class
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_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_index<Type, std::void_d<decltype(Type::index())>> {
static entt::id_type value() ENTT_NOEXCEPT {
return Type::index();
}
};
```
Note that indexes **must** still be generated sequentially in this case.<br/>
The tool is widely used within `EnTT`. Generating indices not sequentially would
break an assumption and would likely lead to undesired behaviors.
## Type traits
A handful of utilities and traits not present in the standard template library
but which can be useful in everyday life.<br/>
This list **is not** exhaustive and contains only some of the most useful
classes. Refer to the inline documentation for more information on the features
offered by this module.
### Member class type
The `auto` template parameter introduced with C++17 made it possible to simplify
many class templates and template functions but also made the class type opaque
when members are passed as template arguments.<br/>
The purpose of this utility is to extract the class type in a few lines of code:
```cpp
template<typename Member>
using clazz = entt::member_class_t<Member>;
```
### Integral constant
Since `std::integral_constant` may be annoying because of its form that requires
to specify both a type and a value of that type, there is a more user-friendly
shortcut for the creation of integral constants.<br/>
This shortcut is the alias template `entt::integral_constant`:
```cpp
constexpr auto constant = entt::integral_constant<42>;
```
Among the other uses, when combined with a hashed string it helps to define tags
as human-readable _names_ where actual types would be required otherwise:
```cpp
constexpr auto enemy_tag = entt::integral_constant<"enemy"_hs>;
registry.emplace<enemy_tag>(entity);
```
### Tag
Since `id_type` is very important and widely used in `EnTT`, there is a more
user-friendly shortcut for the creation of integral constants based on it.<br/>
This shortcut is the alias template `entt::tag`.
If used in combination with hashed strings, it helps to use human-readable names
where types would be required otherwise. As an example:
```cpp
registry.emplace<entt::tag<"enemy"_hs>>(entity);
```
However, this isn't the only permitted use. Literally any value convertible to
`id_type` is a good candidate, such as the named constants of an unscoped enum.
# Utilities
It's not possible to escape the temptation to add utilities of some kind to a
library. In fact, `EnTT` also provides a handful of tools to simplify the
life of developers:
* `entt::identity`: the identity function object that will be available with
C++20. It returns its argument unchanged and nothing more. It's useful as a
sort of _do nothing_ function in template programming.
* `entt::overload`: a tool to disambiguate different overloads from their
function type. It works with both free and member functions.<br/>
Consider the following definition:
```cpp
struct clazz {
void bar(int) {}
void bar() {}
};
```
This utility can be used to get the _right_ overload as:
```cpp
auto *member = entt::overload<void(int)>(&clazz::bar);
```
The line above is literally equivalent to:
```cpp
auto *member = static_cast<void(clazz:: *)(int)>(&clazz::bar);
```
Just easier to read and shorter to type.
* `entt::overloaded`: a small class template used to create a new type with an
overloaded `operator()` from a bunch of lambdas or functors.<br/>
As an example:
```cpp
entt::overloaded func{
[](int value) { /* ... */ },
[](char value) { /* ... */ }
};
func(42);
func('c');
```
Rather useful when doing metaprogramming and having to pass to a function a
callable object that supports multiple types at once.
* `entt::y_combinator`: this is a C++ implementation of **the** _y-combinator_.
If it's not clear what it is, there is probably no need for this utility.<br/>
Below is a small example to show its use:
```cpp
entt::y_combinator gauss([](const auto &self, auto value) -> unsigned int {
return value ? (value + self(value-1u)) : 0;
});
const auto result = gauss(3u);
```
Maybe convoluted at a first glance but certainly effective. Unfortunately,
the language doesn't make it possible to do much better.
This is a rundown of the (actually few) utilities made available by `EnTT`. The
list will probably grow over time but the size of each will remain rather small,
as has been the case so far.

File diff suppressed because it is too large Load Diff

View File

@@ -12,6 +12,8 @@
* [Custom entity identifiers: yay or nay?](#custom-entity-identifiers-yay-or-nay)
* [Warning C4307: integral constant overflow](#warning-C4307-integral-constant-overflow)
* [Warning C4003: the min, the max and the macro](#warning-C4003-the-min-the-max-and-the-macro)
* [The standard and the non-copyable types](#the-standard-and-the-non-copyable-types)
* [Which functions trigger which signals](#which-functions-trigger-which-signals)
<!--
@endcond TURN_OFF_DOXYGEN
-->
@@ -49,10 +51,15 @@ First of all, there are two things to do in a Windows project:
* Set the [`_ITERATOR_DEBUG_LEVEL`](https://docs.microsoft.com/cpp/standard-library/iterator-debug-level)
macro to 0. This will disable checked iterators and iterator debugging.
Moreover, the macro `ENTT_DISABLE_ASSERT` should be defined to disable internal
checks made by `EnTT` in debug. These are asserts introduced to help the users,
but require to access to the underlying containers and therefore risk ruining
the performance in some cases.
Moreover, the macro `ENTT_ASSERT` should be redefined to disable internal checks
made by `EnTT` in debug:
```cpp
#define ENTT_ASSERT(...) ((void)0)
```
These asserts are introduced to help the users but they require to access to the
underlying containers and therefore risk ruining the performance in some cases.
With these changes, debug performance should increase enough for most cases. If
you want something more, you can can also switch to an optimization level `O0`
@@ -94,25 +101,21 @@ performance from this component.
Custom entity identifiers are definitely a good idea in two cases at least:
* If `std::uint32_t` isn't large enough as an underlying type.
* 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 you want to avoid conflicts when using multiple registries.
These identifiers are nothing more than enum classes with some salt.<br/>
To simplify the creation of new identifiers, `EnTT` provides the macro
`ENTT_OPAQUE_TYPE` that accepts two arguments:
* The name you want to give to the new identifier (watch out for namespaces).
* The underlying type to use (either `std::uint16_t`, `std::uint32_t`
or `std::uint64_t`).
In fact, this is the definition of `entt::entity`:
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/>
In fact, this is a definition equivalent to that of `entt::entity`:
```cpp
ENTT_OPAQUE_TYPE(entity, std::uint32_t)
enum class entity: std::uint32_t {};
```
The use of this macro is highly recommended, so as not to run into problems if
the requirements for the identifiers should change in the future.
In theory, integral types can also be used as entity identifiers, even though
this may break in future and isn't recommended in general.
## Warning C4307: integral constant overflow
@@ -126,13 +129,9 @@ here is a workaround in the form of a macro:
```cpp
#if defined(_MSC_VER)
#define HS(str)\
__pragma(warning(push))\
__pragma(warning(disable:4307))\
entt::hashed_string{str}\
__pragma(warning(pop))
#define HS(str) __pragma(warning(suppress:4307)) entt::hashed_string{str}
#else
#define HS(str) entt::hashed_string{str}
#define HS(str) entt::hashed_string{str}
#endif
```
@@ -161,3 +160,59 @@ so as to get rid of the extra definitions:
Please refer to [this](https://github.com/skypjack/entt/issues/96) issue for
more details.
## The standard and the non-copyable types
`EnTT` uses internally the trait `std::is_copy_constructible_v` to check if a
component is actually copyable. This trait doesn't check if an object can
actually be copied but only verifies if there is a copy constructor
available.<br/>
This can lead to surprising results due to some idiosyncrasies of the standard
mainly related to the need to guarantee backward compatibility.
For example, `std::vector` defines a copy constructor no matter if its value
type is copyable or not. As a result, `std::is_copy_constructible_v` is true
for the following specialization:
```cpp
struct type {
std::vector<std::unique_ptr<action>> vec;
};
```
When trying to assign an instance of this type to an entity in the ECS part,
this may trigger a compilation error because we cannot really make a copy of
it.<br/>
As a workaround, users can mark the type explicitly as non-copyable:
```cpp
struct type {
type(const type &) = delete;
type & operator=(const type &) = delete;
std::vector<std::unique_ptr<action>> vec;
};
```
Unfortunately, this will also disable aggregate initialization.
## Which functions trigger which signals
The `registry` class offers three signals that are emitted following specific
operations. Maybe not everyone knows what these operations are, though.<br/>
If this isn't clear, below you can find a _vademecum_ for this purpose:
* `on_created` is invoked when a component is first added (neither modified nor
replaced) to an entity.
* `on_update` is called whenever an existing component is modified or replaced.
* `on_destroyed` is called when a component is explicitly or implicitly removed
from an entity.
Among the most controversial functions can be found `emplace_or_replace` and
`destroy`. However, following the above rules, it's quite simple to know what
will happen.<br/>
In the first case, `on_created` is invoked if the entity has not the component,
otherwise the latter is replaced and therefore `on_update` is triggered. As for
the second case, components are removed from their entities and thus freed when
they are recycled. It means that `on_destroyed` is triggered for every component
owned by the entity that is destroyed.

View File

@@ -5,151 +5,132 @@
-->
# Table of Contents
* [Introduction](#introduction)
* [Named types and traits class](#named-types-and-traits-class)
* [Do not mix types](#do-not-mix-types)
* [Macros, macros everywhere](#macros-macros-everywhere)
* [Conflicts](#conflicts)
* [Allocations: the dark side of the force](#allocations-the-dark-side-of-the-force)
* [Working across boundaries](#working-across-boundaries)
* [The EnTT way](#the-entt-way)
* [Meta context](#meta-context)
* [Memory management](#memory-management)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
# Working across boundaries
`EnTT` has historically had a limit when used across boundaries on Windows in
general and on GNU/Linux when default visibility was set to _hidden_. The
limitation is due mainly to a custom utility used to assign unique, sequential
identifiers to different types. Unfortunately, this tool is used by several core
classes (the `registry` among the others) that are thus almost unusable across
boundaries.<br/>
The reasons for that are beyond the purposes of this document. However, the good
news is that `EnTT` also offers now a way to overcome this limit and to push
things across boundaries without problems when needed.
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 straightforward. In
fact, everything just works transparently in almost all cases. There are only a
few exceptions, easy to deal with anyway.
# Named types and traits class
## The EnTT way
To allow a type to work properly across boundaries when used by a class that
requires to assign unique identifiers to types, users must specialize a class
template to literally give a compile-time name to the type itself.<br/>
The name of the class template is `name_type_traits` and the specialization must
be such that it exposes a static constexpr data member named `value` having type
either `ENTT_ID_TYPE` or `entt::hashed_string::hash_type`. Its value is the user
defined unique identifier assigned to the specific type.<br/>
Identifiers are not to be sequentially generated in this case.
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
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_info` class template is how identifiers are generated and thus made
available to the rest of the library. The `type_index` class template makes all
types _indexable_ instead, so as to speed up the lookup.
As an example:
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_index` confuses the other classes by giving potentially wrong
information to them.<br/>
To avoid problems, it's required to provide a custom generator or to suppress
the index generation as a whole:
```cpp
template<typename Type>
struct entt::type_index<Type> {};
```
All classes that use `type_index` perform also a check on the possibility of
creating indexes for types. If it's not a viable solution, they fallback on
the type id provided by `type_info`. The latter makes everything stable across
boundaries.<br/>
This is why suppressing the generation of the indexes solves the problem. In
case it's still necessary to associate sequential indexes with types, users
can refer to the `family` class, although knowing that these will not be
stable across boundaries.
For anyone who needs more details, the test suite contains multiple examples
covering the most common cases.<br/>
It goes without saying that it's impossible to cover all the possible cases.
However, what is offered should hopefully serve as a basis for all of them.
## Meta context
The runtime reflection system deserves a special mention when it comes to using
it across boundaries.<br/>
Since it's linked to a static context to which the visible components are
attached and different contexts don't relate to each other, they must be
_shared_ to allow the use of meta types across boundaries.
Sharing a context is trivial though. First of all, the local one must be
acquired in the main space:
```cpp
struct my_type { /* ... */ };
template<>
struct entt::named_type_traits<my_type> {
static constexpr auto value = "my_type"_hs;
};
entt::meta_ctx ctx{};
```
Because of the rules of the language, the specialization must reside in the
global namespace or in the `entt` namespace. There is no way to change this rule
unfortunately, because it doesn't depend on the library itself.
The good aspect of this approach is that it's not intrusive at all. The other
way around was in fact forcing users to inherit all their classes from a common
base. Something to avoid, at least from my point of view.<br/>
However, despite the fact that it's not intrusive, it would be great if it was
also easier to use and a bit less error-prone. This is why a bunch of macros
exist to ease defining named types.
## Do not mix types
Someone might think that this trick is valid only for the types to push across
boundaries. This isn't how things work. In fact, the problem is more complex
than that.<br/>
As a rule of thumb, users should never mix named and non-named types. Whenever
a type is given a name, all the types must be given a name. As an example,
consider the `registry` class template: in case it is pushed across boundaries,
all the types of components should be assigned a name to avoid subtle bugs.
Indeed, this constraint can be relaxed in many cases. However, it is difficult
to define a general rule to follow that is not the most stringent, unless users
know exactly what they are doing. Therefore, I won't elaborate on giving further
details on the topic.
# Macros, macros everywhere
The library comes with a set of predefined macros to use to declare named types
or export already existing ones. In particular:
* `ENTT_NAMED_TYPE` can be used to assign a name to already existing types. This
macro must be used in the global namespace even when the types to be named are
not.
```cpp
ENTT_NAMED_TYPE(my_type)
ENTT_NAMED_TYPE(ns::another_type)
```
* `ENTT_NAMED_STRUCT` can be used to define and export a struct at the same
time. It accepts also an optional namespace in which to define the given type.
This macro must be used in the global namespace.
```cpp
ENTT_NAMED_STRUCT(my_type, { /* struct definition */})
ENTT_NAMED_STRUCT(ns, another_type, { /* struct definition */})
```
* `ENTT_NAMED_CLASS` can be used to define and export a class at the same
time. It accepts also an optional namespace in which to define the given type.
This macro must be used in the global namespace.
```cpp
ENTT_NAMED_CLASS(my_type, { /* class definition */})
ENTT_NAMED_CLASS(ns, another_type, { /* class definition */})
```
Nested namespaces are supported out of the box as well in all cases. As an
example:
Then, it must passed to the receiving space that will set it as its global
context, thus releasing the local one that remains available but is no longer
referred to by the runtime reflection system:
```cpp
ENTT_NAMED_STRUCT(nested::ns, my_type, { /* struct definition */})
entt::meta_ctx::bind(ctx);
```
These macros can be used to avoid specializing the `named_type_traits` class
template. In all cases, the name of the class is used also as a seed to generate
the compile-time unique identifier.
From now on, both spaces will refer to the same context and on it will be
attached the new visible meta types, no matter where they are created.<br/>
A context can also be reset and then associated again locally as:
## Conflicts
```cpp
entt::meta_ctx::bind{entt::meta_ctx{});
```
When using macros, unique identifiers are 32/64 bit integers generated by
hashing strings during compilation. Therefore, conflicts are rare but still
possible. In case of conflicts, everything simply will get broken at runtime and
the strangest things will probably take place.<br/>
Unfortunately, there is no safe way to prevent it. If this happens, it will be
enough to give a different value to one of the conflicting types to solve the
problem. To do this, users can either assign a different name to the class or
directly define a specialization for the `named_type_traits` class template.
This is allowed because local and global contexts are separated. Therefore, it's
always possible to make the local context the current one again.
# Allocations: the dark side of the force
Before to release a context, all locally registered types should be reset to
avoid dangling references. Otherwise, if a type is accessed from another space
by name, there could be an attempt to address its parts that are no longer
available.
As long as `EnTT` won't support custom allocators, another problem with
allocations will remain alive instead. This is in fact easily solved, or at
least it is if one knows it.
## Memory Management
To allow users to add types dynamically, the library makes extensive use of type
erasure techniques and dynamic allocations for pools (whether they are for
components, events or anything else). The problem occurs when, for example, a
registry is created on one side of a boundary and a pool is dynamically created
on the other side. In the best case, everything will crash at the exit, while at
worst it will do so at runtime.<br/>
To avoid problems, the pools must be generated from the same side of the
boundary where the object that owns them is also created. As an example, when
the registry is created in the main executable and used across boundaries for a
given type of component, the pool for that type must be created before passing
around the registry itself. To do this is fortunately quite easy, since it is
sufficient to invoke any of the methods that involve the given type (continuing
the example with the registry, a call to `reserve` or `size` is more than
enough).
There is another subtle problem due to memory management that can lead to
headaches.<br/>
It can occur where there are pools of objects (such as components or events)
dynamically created on demand. This is usually not a problem when working with
linked libraries that rely on the same dynamic runtime. However, it can occur in
the case of plugins or statically linked runtimes.
Maybe one day some dedicated methods will be added that do nothing but create a
pool for a given type. Until now it has been preferred to keep the API cleaner
as they are not strictly necessary.
As an example, imagine creating an instance of `registry` in the main executable
and sharing it with a plugin. If the latter starts working with a component that
is unknown to the former, a dedicated pool is created within the registry on
first use.<br/>
As one can guess, this pool is instantiated on a different side of the boundary
from the `registry`. Therefore, the instance is now managing memory from
different spaces and this can quickly lead to crashes if not properly addressed.
To overcome the risk, it's recommended to use well-defined interfaces that make
fundamental types pass through the boundaries, isolating the instances of the
`EnTT` classes from time to time and as appropriate.<br/>
Refer to the test suite for some examples, read the documentation available
online about this type of issues or consult someone who has already had such
experiences to avoid problems.

View File

@@ -17,6 +17,10 @@ I hope this list can grow much more in the future:
* [Minecraft](https://minecraft.net/en-us/attribution/) by
[Mojang](https://mojang.com/): of course, **that** Minecraft, see the
open source attributions page for more details.
* [Land of the Rair](https://github.com/LandOfTheRair/core2): the new backend
of [a retro-style MUD](https://rair.land/) for the new age.
* [Openblack](https://github.com/openblack/openblack): open source
reimplementation of the game _Black & White_ (2001).
* [Face Smash](https://play.google.com/store/apps/details?id=com.gamee.facesmash):
a game to play with your face.
* [EnTT Pacman](https://github.com/Kerndog73/EnTT-Pacman): an example of how
@@ -40,8 +44,19 @@ I hope this list can grow much more in the future:
arcade puzzle game made in C++ using the `SDL2` and `EnTT` libraries.
* [Snake with EnTT](https://github.com/MasonRG/SnakeGame): simple snake game
made in C++ with the `SDL2` and `EnTT` libraries.
* [Mirrors lasers and robots](https://github.com/guillaume-haerinck/imac-tower-defense):
a small tower defense game based on mirror orientation.
* [PopHead](https://github.com/SPC-Some-Polish-Coders/PopHead/): 2D, Zombie,
RPG game made from scratch in C++.
* [Robotligan](https://github.com/Trisslotten/robotligan): multiplayer
football game.
* [DungeonSlayer](https://github.com/alohaeee/DungeonSlayer): 2D game made
from scratch in C++.
* [3DGame](https://github.com/kwarkGorny/3DGame): 2.5D top-down space shooter.
* Engines and the like:
* [Fling Engine](https://github.com/flingengine/FlingEngine): a Vulkan game
engine with a focus on data oriented design.
* [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
@@ -52,14 +67,25 @@ I hope this list can grow much more in the future:
[Qub3d](https://qub3d.org/): because blocks should be open source.
* [shiva](https://github.com/Milerius/shiva): modern C++ engine with
modularity.
* [NovusCore](https://github.com/novuscore/NovusCore): A modern take on World
* [NovusCore](https://github.com/novuscore/NovusCore): a modern take on World
of Warcraft emulation.
* [ImGui/EnTT editor](https://github.com/Green-Sky/imgui_entt_entity_editor):
A drop-in, single-file entity editor for `EnTT` that uses `ImGui` as
a drop-in, single-file entity editor for `EnTT` that uses `ImGui` as
graphical backend (with
[demo code](https://github.com/Green-Sky/imgui_entt_entity_editor_demo)).
* [SgOgl](https://github.com/stwe/SgOgl): a game engine library for OpenGL
developed for educational purposes.
* [Lumos](https://github.com/jmorton06/Lumos): game engine written in C++
using OpenGL and Vulkan.
* [Chrysalis](https://github.com/ivanhawkes/Chrysalis): action RPG SDK for
CRYENGINE games.
* [LM-Engine](https://github.com/Lawrencemm/LM-Engine): the Vim of game
engines.
* [TiltedOnline](https://github.com/tiltedphoques/TiltedOnline) by
[Tilted Phoques](https://github.com/tiltedphoques): Skyrim Together, a mod
intended to sync the player and the world state accross multiple clients.
* Articles and blog posts
* Articles, videos and blog posts:
* [Some posts](https://skypjack.github.io/tags/#entt) on my personal
[blog](https://skypjack.github.io/) are about `EnTT`, for those who want to
know **more** on this project.
@@ -73,23 +99,56 @@ I hope this list can grow much more in the future:
giant space battle.
* [Conan Adventures (SFML and EnTT in C++)](https://leinnan.github.io/blog/conan-adventuressfml-and-entt-in-c.html):
create projects in modern C++ using `SFML`, `EnTT`, `Conan` and `CMake`.
* [Adding EnTT ECS to Chrysalis](https://www.tauradius.com/post/adding-an-ecs-to-chrysalis/):
a blog entry (and its
[follow-up](https://www.tauradius.com/post/chrysalis-update-2020-08-02/))
about the integration of `EnTT` into `Chrysalis`, an action RPG SDK for
CRYENGINE games.
* [Creating Minecraft in One Week with C++ and Vulkan](https://vazgriz.com/189/creating-minecraft-in-one-week-with-c-and-vulkan/):
a crack at recreating Minecraft in one week using a custom C++ engine and
Vulkan ([code included](https://github.com/vazgriz/VoxelGame)).
* [Game Engine series](https://www.youtube.com/c/TheChernoProject/videos) by
[The Cherno](https://github.com/TheCherno):
- [Intro to EnTT](https://www.youtube.com/watch?v=D4hz0wEB978).
- [Entities and Components](https://www.youtube.com/watch?v=-B1iu4QJTUc).
- [The ENTITY Class](https://www.youtube.com/watch?v=GfSzeAcsBb0).
- [Camera Systems](https://www.youtube.com/watch?v=ubZn7BlrnTU).
- [Scene Camera](https://www.youtube.com/watch?v=UKVFRRufKzo).
- [Native Scripting](https://www.youtube.com/watch?v=iIUhg88MK5M).
- [Native Scripting (now with virtual functions!)](https://www.youtube.com/watch?v=1cHEcrIn8IQ).
- [Scene Hierarchy Panel](https://www.youtube.com/watch?v=wziDnE8guvI).
* [Ability Creator](https://www.erichildebrand.net/blog/ability-creator-project-retrospect):
project retrospect by [Eric Hildebrand](https://www.erichildebrand.net/).
* Any Other Business:
* The [ArcGIS Runtime SDKs](https://developers.arcgis.com/arcgis-runtime/)
by [Esri](https://www.esri.com/): they use `EnTT` for the internal ECS and
the cross platform C++ rendering engine. The SDKs are utilized by a lot of
* [ArcGIS Runtime SDKs](https://developers.arcgis.com/arcgis-runtime/) by
[Esri](https://www.esri.com/): they use `EnTT` for the internal ECS and the
cross platform C++ rendering engine. The SDKs are utilized by a lot of
enterprise custom apps, as well as by Esri for its own public applications
such as
[Explorer](https://play.google.com/store/apps/details?id=com.esri.explorer),
[Collector](https://play.google.com/store/apps/details?id=com.esri.arcgis.collector)
and
[Navigator](https://play.google.com/store/apps/details?id=com.esri.navigator).
* [FASTSUITE Edition 2](https://www.fastsuite.com/en_EN/fastsuite/fastsuite-edition-2.html)
by [Cenit](http://www.cenit.com/en_EN/about-us/overview.html): they use
`EnTT` to drive their simulation, that is, the communication between robot
controller emulator and renderer.
* [Apparently](https://www.linkedin.com/in/skypjack/)
[NIO](https://www.nio.io/): there was a collaboration to make some changes
to `EnTT`, at the time used for internal projects.
* [Apparently](https://www.linkedin.com/jobs/view/architekt-c%2B%2B-at-tieto-1219512333/)
[Tieto](https://www.tieto.com/): they published a job post where `EnTT` was
listed on their software stack.
* [Sequentity](https://github.com/alanjfs/sequentity): A MIDI-like
sequencer/tracker for C++ and `ImGui` (with `Magnum` and `EnTT`).
* [Godot meets EnTT](https://github.com/portaloffreedom/godot_entt_example/):
a simple example on how to use `EnTT` within
[`Godot`](https://godotengine.org/).
* [Godot and GameNetworkingSockets meet EnTT](https://github.com/portaloffreedom/godot_entt_net_example):
a simple example on how to use `EnTT` and
[`GameNetworkingSockets`](https://github.com/ValveSoftware/GameNetworkingSockets)
within [`Godot`](https://godotengine.org/).
* [MatchOneEntt](https://github.com/mhaemmerle/MatchOneEntt): port of
[Match One](https://github.com/sschmid/Match-One) for `Entitas-CSharp`.
* GitHub contains also

View File

@@ -10,6 +10,8 @@
* [Reflection in a nutshell](#reflection-in-a-nutshell)
* [Any as in any type](#any-as-in-any-type)
* [Enjoy the runtime](#enjoy-the-runtime)
* [Container support](#container-support)
* [Pointer-like types](#pointer-like-types)
* [Policies: the more, the less](#policies-the-more-the-less)
* [Named constants and enums](#named-constants-and-enums)
* [Properties and meta objects](#properties-and-meta-objects)
@@ -28,11 +30,11 @@ allocations. In one word: unsatisfactory.<br/>
I finally decided to write a built-in, non-intrusive and macro-free runtime
reflection system for `EnTT`. Maybe I didn't do better than others or maybe yes,
time will tell me, but at least I can model this tool around the library to
which it belongs and not vice versa.
which it belongs and not the opposite.
# Names and identifiers
The meta system doesn't force the user to use the tools provided by the library
The meta system doesn't force users to rely on the tools provided by the library
when it comes to working with names and identifiers. It does this by offering an
API that works with opaque identifiers that may or may not be generated by means
of a hashed string.<br/>
@@ -40,19 +42,19 @@ This means that users can assign any type of identifier to the meta objects, as
long as they are numeric. It doesn't matter if they are generated at runtime, at
compile-time or with custom functions.
However, the examples in the following sections are all based on the
That being said, the examples in the following sections are all based on the
`hashed_string` class as provided by this library. Therefore, where an
identifier is required, it's likely that a user defined literal is used as
follows:
```cpp
auto factory = entt::reflect<my_type>("reflected_type"_hs);
auto factory = entt::meta<my_type>().type("reflected_type"_hs);
```
For what it's worth, this is likely completely equivalent to:
```cpp
auto factory = entt::reflect<my_type>(42);
auto factory = entt::meta<my_type>().type(42);
```
Obviously, human-readable identifiers are more convenient to use and highly
@@ -63,23 +65,36 @@ recommended.
Reflection always starts from real types (users cannot reflect imaginary types
and it would not make much sense, we wouldn't be talking about reflection
anymore).<br/>
To _reflect_ a type, the library provides the `reflect` function:
To create a meta node, the library provides the `meta` function that accepts a
type to reflect as a template parameter:
```cpp
auto factory = entt::reflect<my_type>("reflected_type"_hs);
auto factory = entt::meta<my_type>();
```
It accepts the type to reflect as a template parameter and an optional
identifier as an argument. Identifiers are important because users can retrieve
meta types at runtime by searching for them by _name_. However, there are cases
in which users can be interested in adding features to a reflected type so that
the reflection system can use it correctly under the hood, but they don't want
to allow searching the type by _name_.<br/>
In both cases, the returned value is a factory object to use to continue
building the meta type.
This isn't enough to _export_ the given type and make it visible though.<br/>
The returned value is a factory object to use to continue building the meta
type. In order to make the type _visible_, users can assign it an identifier:
A factory is such that all its member functions returns the factory itself.
It can be used to extend the reflected type and add the following:
```cpp
auto factory = entt::meta<my_type>().type("reflected_type"_hs);
```
Or use the default one, that is, the built-in identifier for the given type:
```cpp
auto factory = entt::meta<my_type>().type();
```
Identifiers are important because users can retrieve meta types at runtime by
searching for them by _name_ other than by type.<br/>
On the other hand, there are cases in which users can be interested in adding
features to a reflected type so that the reflection system can use it correctly
under the hood, but they don't want to also make the type _searchable_. In this
case, it's sufficient not to invoke `type`.
A factory is such that all its member functions returns the factory itself or
a decorated version of it. This object can be used to add the following:
* _Constructors_. Actual constructors can be assigned to a reflected type by
specifying their list of arguments. Free functions (namely, factories) can be
@@ -89,7 +104,7 @@ It can be used to extend the reflected type and add the following:
Use the `ctor` member function for this purpose:
```cpp
entt::reflect<my_type>("reflected"_hs).ctor<int, char>().ctor<&factory>();
entt::meta<my_type>().ctor<int, char>().ctor<&factory>();
```
* _Destructors_. Free functions can be set as destructors of reflected types.
@@ -98,7 +113,7 @@ It can be used to extend the reflected type and add the following:
Use the `dtor` member function for this purpose:
```cpp
entt::reflect<my_type>("reflected"_hs).dtor<&destroy>();
entt::meta<my_type>().dtor<&destroy>();
```
A function should neither delete nor explicitly invoke the destructor of a
@@ -111,7 +126,7 @@ It can be used to extend the reflected type and add the following:
Use the `data` member function for this purpose:
```cpp
entt::reflect<my_type>("reflected"_hs)
entt::meta<my_type>()
.data<&my_type::static_variable>("static"_hs)
.data<&my_type::data_member>("member"_hs)
.data<&global_variable>("global"_hs);
@@ -120,9 +135,15 @@ It can be used to extend the reflected type and add the following:
This function requires as an argument the identifier to give to the meta data
once created. Users can then access meta data at runtime by searching for them
by _name_.<br/>
Data members can be set also by means of a couple of functions, namely a
setter and a getter. Setters and getters can be either free functions, member
functions or mixed ones, as long as they respect the required signatures.<br/>
Data members can also be defined by means of a _setter_ and _getter_. Setters
and getters can be either free functions, class members or a mix of them, as
long as they respect the required signatures. This approach is also convenient
to create a read-only variable from a non-const data member:
```cpp
entt::meta<my_type>().data<nullptr, &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
@@ -132,7 +153,7 @@ It can be used to extend the reflected type and add the following:
Use the `func` member function for this purpose:
```cpp
entt::reflect<my_type>("reflected"_hs)
entt::meta<my_type>()
.func<&my_type::static_function>("static"_hs)
.func<&my_type::member_function>("member"_hs)
.func<&free_function>("free"_hs);
@@ -148,7 +169,7 @@ It can be used to extend the reflected type and add the following:
Use the `base` member function for this purpose:
```cpp
entt::reflect<derived_type>("derived"_hs).base<base_type>();
entt::meta<derived_type>().base<base_type>();
```
From now on, wherever a `base_type` is required, an instance of `derived_type`
@@ -161,7 +182,7 @@ It can be used to extend the reflected type and add the following:
Use the `conv` member function for this purpose:
```cpp
entt::reflect<double>().conv<int>();
entt::meta<double>().conv<int>();
```
That's all, everything users need to create meta types and enjoy the reflection
@@ -173,7 +194,7 @@ definitely worth the price, at least for me.
## Any as in any type
The reflection system comes with its own meta any type. It may seem redundant
The reflection system comes with its own `meta_any` type. It may seem redundant
since C++17 introduced `std::any`, but it is not.<br/>
In fact, the _type_ returned by an `std::any` is a const reference to an
`std::type_info`, an implementation defined class that's not something everyone
@@ -181,39 +202,65 @@ wants to see in a software. Furthermore, the class `std::type_info` suffers from
some design flaws and there is even no way to _convert_ an `std::type_info` into
a meta type, thus linking the two worlds.
A meta any object provides an API similar to that of its most famous counterpart
and serves the same purpose of being an opaque container for any type of
value.<br/>
The class `meta_any` offers an API similar to that of its most famous
counterpart and serves the same purpose of being an opaque container for any
type of value.<br/>
It minimizes the allocations required, which are almost absent thanks to _SBO_
techniques. In fact, unless users deal with _fat types_ and create instances of
them though the reflection system, allocations are at zero.
them through the reflection system, allocations are at zero.
A meta any object can be created by any other object or as an empty container
to initialize later:
Creating instances of `meta_any`, whether empty or from existing objects, is
trivial:
```cpp
// a meta any object that contains an int
// a container for an int
entt::meta_any any{0};
// an empty meta any object
// an empty container
entt::meta_any empty{};
```
It takes the burden of destroying the contained instance when required.<br/>
Moreover, it can be used as an opaque container for unmanaged objects if needed:
The `meta_any` class takes also the burden of destroying the contained object
when required.<br/>
Furthermore, an instance of `meta_any` is not tied to a specific 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.
A particularly interesting feature of this class is that it can also be used as
an opaque container for non-const unmanaged objects:
```cpp
int value;
entt::meta_any any{entt::as_alias, value};
entt::meta_any any{std::ref(value)};
```
In this case, the contained instance is never destroyed and users must ensure
that the lifetime of the object exceeds that of the container.
In other words, whenever `meta_any` intercepts a `reference_wrapper`, it acts as
a reference to the original instance rather than making a copy of it. 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 `meta_any` from existing
ones:
A meta any object has a `type` member function that returns the meta type of the
contained value, if any. The member functions `try_cast`, `cast` and `convert`
are used to know if the underlying object has a given type as a base or if it
can be converted implicitly to it.
```cpp
// aliasing constructor
entt::meta_any ref = any.ref();
```
In this case, it doesn't matter if the starting container actually holds an
object or acts already as a reference for unmanaged elements, the new instance
thus created won't create copies and will only serve as a reference for the
original item.<br/>
It means that, starting from the example above, both `ref` and` any` will point
to the same object, whether it's initially contained in `any` or already an
unmanaged one. This is particularly useful for passing instances of `meta_any`
belonging to the external context by reference to a function or a constructor
rather than making copies of them.
The `meta_any` class also has a `type` member function that returns the meta
type of the contained value, if any. The member functions `try_cast`, `cast` and
`convert` are then used to know if the underlying object has a given type as a
base or if it can be converted implicitly to it.
## Enjoy the runtime
@@ -223,30 +270,35 @@ All this has the great merit that, unlike the vast majority of the things
present in this library and closely linked to the compile-time, the reflection
system stands in fact as a non-intrusive tool for the runtime.
To search for a reflected type there are two options: by type or by _name_. In
both cases, the search can be done by means of the `resolve` function:
To search for a reflected type there are a few options:
```cpp
// search for a reflected type by type
// direct access to a reflected type
auto by_type = entt::resolve<my_type>();
// search for a reflected type by name
auto by_name = entt::resolve("reflected_type"_hs);
// lookup of a reflected type by identifier
auto by_id = entt::resolve_id("reflected_type"_hs);
// lookup of a reflected type by type id
auto by_type_id = entt::resolve_type(entt::type_info<my_type>::id());
```
There exits also a third overload of the `resolve` function to use to iterate
all the reflected types at once:
There exits also an overload of the `resolve` function to use to iterate all the
reflected types at once as well as a `resolve_if` function to use to perform
more refined searches when needed:
```cpp
resolve([](auto type) {
// ...
});
auto by_lookup = resolve_if([](auto type) { return type.is_floating_point(); });
```
In all cases, the returned value is an instance of `meta_type`. This type of
objects offer an API to know the _runtime identifier_ of the type, to iterate
all the meta objects associated with them and even to build or destroy instances
of the underlying type.<br/>
In all cases, the returned value is an instance of `meta_type`. This kind of
objects offer an API to know their _runtime identifiers_, to iterate all the
meta objects associated with them and even to build instances of the underlying
type.<br/>
Refer to the inline documentation for all the details.
The meta objects that compose a meta type are accessed in the following ways:
@@ -260,20 +312,9 @@ The meta objects that compose a meta type are accessed in the following ways:
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 arguments, the expected
meta types and to invoke it, therefore to construct a new instance of the
underlying type.
* _Meta destructor_. It's returned by a dedicated function:
```cpp
auto dtor = entt::resolve<my_type>().dtor();
```
The returned type is `meta_dtor` and may be invalid if there is no custom
destructor set for the given meta type.<br/>
All what a meta destructor has to offer is a way to invoke it on a given
instance. Be aware that the result may not be what is expected.
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_:
@@ -283,9 +324,9 @@ The meta objects that compose a meta type are accessed in the following ways:
The returned type is `meta_data` and may be invalid if there is no meta data
object associated with the given identifier.<br/>
A meta data object offers an API to query the underlying type (ie to know if
it's a const or a static one), to get the meta type of the variable and to set
or get the contained value.
A meta data object offers an API to query the underlying type (for example, to
know if it's a const or a static one), to get the meta type of the variable
and to set or get the contained value.
* _Meta functions_. They are accessed by _name_:
@@ -295,11 +336,11 @@ The meta objects that compose a meta type are accessed in the following ways:
The returned type is `meta_func` and may be invalid if there is no meta
function object associated with the given identifier.<br/>
A meta function object offers an API to query the underlying type (ie to know
if it's a const or a static function), to know the number of arguments, the
meta return type and the meta types of the parameters. In addition, a meta
function object can be used to invoke the underlying function and then get the
return value in the form of meta any object.
A meta function object offers an API to query the underlying type (for
example, to know if it's a const or a static function), to know the number of
arguments, the meta return type and the meta types of the parameters. In
addition, a meta function object can be used to invoke the underlying function
and then get the return value in the form of a `meta_any` object.
* _Meta bases_. They are accessed through the _name_ of the base types:
@@ -323,23 +364,20 @@ The meta objects that compose a meta type are accessed in the following ways:
conversion function associated with the given type.<br/>
The meta conversion functions are as thin as the meta bases and with a very
similar interface. The sole difference is that they return a newly created
instance wrapped in a meta any object when they convert between different
instance wrapped in a `meta_any` object when they convert between different
types.
All the objects thus obtained as well as the meta types can be explicitly
converted to a boolean value to check if they are valid:
```cpp
auto func = entt::resolve<my_type>().func("member"_hs);
if(func) {
if(auto func = entt::resolve<my_type>().func("member"_hs); func) {
// ...
}
```
Furthermore, all meta objects with the exception of meta destructors can be
iterated through an overload that accepts a callback through which to return
them. As an example:
Furthermore, all meta objects can be iterated through an overload that accepts a
callback through which to return them. As an example:
```cpp
entt::resolve<my_type>().data([](auto data) {
@@ -347,23 +385,302 @@ entt::resolve<my_type>().data([](auto data) {
});
```
A meta type can also be used to `construct` or `destroy` actual instances of the
underlying type.<br/>
A meta type can be used to `construct` actual instances of the underlying
type.<br/>
In particular, the `construct` member function accepts a variable number of
arguments and searches for a match. It returns a `meta_any` object that may or
may not be initialized, depending on whether a suitable constructor has been
found or not. On the other side, the `destroy` member function accepts instances
of `meta_any` as well as actual objects by reference and invokes the registered
destructor if any.<br/>
Be aware that the result of a call to `destroy` may not be what is expected. The
purpose is to give users the ability to free up resources that require special
treatment and **not** to actually destroy instances.
arguments and searches for a match. It then returns a `meta_any` object that may
or may not be initialized, depending on whether a suitable constructor has been
found or not.
There is no object that wraps the destructor of a meta type nor a `destroy`
member function in its API. The reason is quickly explained: destructors are
invoked implicitly by `meta_any` behind the scenes and users have not to deal
with them explicitly. Furthermore, they have no name, cannot be searched and
wouldn't have member functions to expose anyway.<br/>
Therefore, exposing destructors would be pointless and would add nothing to the
library itself.
Meta types and meta objects in general contain much more than what is said: a
plethora of functions in addition to those listed whose purposes and uses go
unfortunately beyond the scope of this document.<br/>
I invite anyone interested in the subject to look at the code, experiment and
read the official documentation to get the best out of this powerful tool.
read the inline documentation to get the best out of this powerful tool.
## Container support
The meta module supports containers of all types out of the box.<br/>
Moreover, _containers_ doesn't necessarily mean those offered by the C++
standard library. In fact, user defined data structures can also work with the
meta system in many cases.
To make a container be recognized by the meta module, users are required to
provide specializations for either the `meta_sequence_container_traits` class or
the `meta_associative_container_traits` class, according with the actual _type_
of the container.<br/>
`EnTT` already exports the specializations for some common classes. In
particular:
* `std::vector` and `std::array` are exported as _sequence containers_.
* `std::map`, `std::set` and their unordered counterparts are exported as
_associative containers_.
It's important to include the header file `container.hpp` to make these
specializations available to the compiler when needed.<br/>
The same file also contains many examples for the users that are interested in
making their own containers available to the meta system.
When a specialization of the `meta_sequence_container_traits` class exists, the
meta system treats the wrapped type as a sequence container. In a similar way,
a type is treated as an associative container if a specialization of the
`meta_associative_container_traits` class is found for it.<br/>
Proxy objects are returned by dedicated members of the `meta_any` class. The
following is a deliberately verbose example of how users can access a proxy
object for a sequence container:
```cpp
std::vector<int> vec{1, 2, 3};
entt::meta_any any{std::ref(vec)};
if(any.type().is_sequence_container()) {
if(auto view = any.as_sequence_container(); view) {
// ...
}
}
```
The method to use to get a proxy object for associative containers is
`as_associative_container` instead.<br/>
It goes without saying that it's not necessary to perform a double check.
Instead, it's sufficient to query the meta type or verify that the proxy object
is valid. In fact, proxies are contextually convertible to bool to know if they
are valid. For example, invalid proxies are returned when the wrapped object
isn't a container.<br/>
In all cases, users aren't expected to _reflect_ containers explicitly. It's
sufficient to assign a container for which a specialization of the traits
classes exists to a `meta_any` object to be able to get its proxy object.
The interface of the `meta_sequence_container` proxy object is the same for all
types of sequence containers, although the available features differ from case
to case. In particular:
* The `value_type` member function returns the meta type of the elements.
* The `size` member function returns the number of elements in the container as
an unsigned integer value:
```cpp
const auto size = view.size();
```
* The `resize` member function allows to resize the wrapped container and
returns true in case of succes:
```cpp
const bool ok = view.resize(3u);
```
For example, it's not possible to resize fixed size containers.
* The `clear` member function allows to clear the wrapped container and returns
true in case of success:
```cpp
const bool ok = view.clear();
```
For example, it's not possible to clear fixed size containers.
* The `begin` and `end` member functions return opaque iterators that can be
used to iterate the container directly:
```cpp
for(entt::meta_any element: view) {
// ...
}
```
In all cases, given an underlying container of type `C`, the returned element
contains an object of type `C::value_type` which therefore depends on the
actual container.<br/>
All meta iterators are input iterators and don't offer an indirection operator
on purpose.
* The `insert` member function can be used to add elements to the container. It
accepts a meta iterator and the element to insert:
```cpp
auto last = view.end();
// appends an integer to the container
view.insert(last.handle(), 42);
```
This function returns a meta iterator pointing to the inserted element and a
boolean value to indicate whether the operation was successful or not. Note
that a call to `insert` may silently fail in case of fixed size containers or
whether the arguments aren't at least convertible to the required types.<br/>
Since the meta iterators are contextually convertible to bool, users can rely
on them to know if the operation has failed on the actual container or
upstream, for example for an argument conversion problem.
* The `erase` member function can be used to remove elements from the container.
It accepts a meta iterator to the element to remove:
```cpp
auto first = view.begin();
// removes the first element from the container
view.erase(first);
```
This function returns a meta iterator following the last removed element and a
boolean value to indicate whether the operation was successful or not. Note
that a call to `erase` may silently fail in case of fixed size containers.
* The `operator[]` can be used to access elements in a container. It accepts a
single argument, that is the position of the element to return:
```cpp
for(std::size_t pos{}, last = view.size(); pos < last; ++pos) {
entt::meta_any value = view[pos];
// ...
}
```
The function returns instances of `meta_any` that directly refer to the actual
elements. Modifying the returned object will then directly modify the element
inside the container.
Similarly, also the interface of the `meta_associative_container` proxy object
is the same for all types of associative containers. However, there are some
differences in behavior in the case of key-only containers. In particular:
* The `key_only` member function returns true if the wrapped container is a
key-only one.
* The `key_type` member function returns the meta type of the keys.
* The `mapped_type` member function returns an invalid meta type for key-only
containers and the meta type of the mapped values for all other types of
containers.
* The `value_type` member function returns the meta type of the elements.<br/>
For example, it returns the meta type of `int` for `std::set<int>` while it
returns the meta type of `std::pair<const int, char>` for
`std::map<int, char>`.
* The `size` member function returns the number of elements in the container as
an unsigned integer value:
```cpp
const auto size = view.size();
```
* The `clear` member function allows to clear the wrapped container and returns
true in case of success:
```cpp
const bool ok = view.clear();
```
* The `begin` and `end` member functions return opaque iterators that can be
used to iterate the container directly:
```cpp
for(std::pair<entt::meta_any, entt::meta_any> element: view) {
// ...
}
```
In all cases, given an underlying container of type `C`, the returned element
is a key-value pair where the key has type `C::key_type` and the value has
type `C::mapped_type`. Since key-only containers don't have a mapped type,
their _value_ is nothing more than an invalid `meta_any` object.<br/>
All meta iterators are input iterators and don't offer an indirection operator
on purpose.
While the accessed key is usually constant in the associative containers and
is therefore returned by copy, the value (if any) is wrapped by an instance of
`meta_any` that directly refers to the actual element. Modifying it will then
directly modify the element inside the container.
* The `insert` member function can be used to add elements to the container. It
accepts two arguments, respectively the key and the value to be inserted:
```cpp
auto last = view.end();
// appends an integer to the container
view.insert(last.handle(), 42, 'c');
```
This function returns a boolean value to indicate whether the operation was
successful or not. Note that a call to `insert` may fail when the arguments
aren't at least convertible to the required types.
* The `erase` member function can be used to remove elements from the container.
It accepts a single argument, that is the key to be removed:
```cpp
view.erase(42);
```
This function returns a boolean value to indicate whether the operation was
successful or not. Note that a call to `erase` may fail when the argument
isn't at least convertible to the required type.
* The `operator[]` can be used to access elements in a container. It accepts a
single argument, that is the key of the element to return:
```cpp
entt::meta_any value = view[42];
```
The function returns instances of `meta_any` that directly refer to the actual
elements. Modifying the returned object will then directly modify the element
inside the container.
Container support is deliberately minimal but theoretically sufficient to
satisfy all needs.
## Pointer-like types
As with containers, it's also possible to communicate to the meta system which
types to consider _pointers_. This will allow to dereference instances of
`meta_any`, obtaining light _references_ to the pointed objects that are also
correctly associated with their meta types.<br/>
To make the meta system recognize a type as _pointer-like_, users can specialize
the `is_meta_pointer_like` class. `EnTT` already exports the specializations for
some common classes. In particular:
* All types of raw pointers.
* `std::uniqe_ptr` and `std::shared_ptr`.
It's important to include the header file `pointer.hpp` to make these
specializations available to the compiler when needed.<br/>
The same file also contains many examples for the users that are interested in
making their own containers available to the meta system.
When a type is recognized as a pointer-like one by the meta system, it's
possible to dereference the instances of `meta_any` that contain these objects.
The following is a deliberately verbose example to show how to use this feature:
```cpp
int value = 42;
// meta type equivalent to that of int *
entt::meta_any any{&value};
if(any.type().is_meta_pointer_like()) {
// meta type equivalent to that of int
if(entt::meta_any ref = *any; ref) {
// ...
}
}
```
It goes without saying that it's not necessary to perform a double check.
Instead, it's sufficient to query the meta type or verify that the returned
object is valid. For example, invalid instances are returned when the wrapped
object hasn't a pointer-like type.<br/>
Note that dereferencing a pointer-like object returns an instance of `meta_any`
which refers to the pointed object and allows users to modify it directly.
## Policies: the more, the less
@@ -391,21 +708,19 @@ There are a few alternatives available at the moment:
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.
In the second case, instead, the property will not be accessible for
reading.
In the second case, instead, the property will not be accessible for reading.
As an example of use:
```cpp
entt::reflect<my_type>("reflected"_hs)
.func<&my_type::member_function, entt::as_void_t>("member"_hs);
entt::meta<my_type>().func<&my_type::member_function, entt::as_void_t>("member"_hs);
```
* The _as-alias_ policy, associated with the type `entt::as_alias_t`.<br/>
It allows to build wrappers that act as aliases for the objects used to
initialize them. Modifying the object contained in the wrapper for which the
_aliasing_ was requested will make it possible to directly modify the instance
used to initialize the wrapper itself.<br/>
* The _as-ref_ policy, associated with the type `entt::as_ref_t`.<br/>
It allows to build wrappers that act as references to unmanaged objects.
Modifying the object contained in the wrapper for which the _reference_ was
requested will make it possible to directly modify the instance used to
initialize the wrapper itself.<br/>
This policy works with constructors (for example, when objects are taken from
an external container rather than created on demand), data members and
functions in general (as long as their return types are lvalue references).
@@ -413,8 +728,7 @@ There are a few alternatives available at the moment:
As an example of use:
```cpp
entt::reflect<my_type>("reflected"_hs)
.data<&my_type::data_member, entt::as_alias_t>("member"_hs);
entt::meta<my_type>().data<&my_type::data_member, entt::as_ref_t>("member"_hs);
```
Some uses are rather trivial, but it's useful to note that there are some less
@@ -439,11 +753,11 @@ members of the reflected types.
Exporting constant values or elements from an enum is as simple as ever:
```cpp
entt::reflect<my_enum>()
entt::meta<my_enum>()
.data<my_enum::a_value>("a_value"_hs)
.data<my_enum::another_value>("another_value"_hs);
entt::reflect<int>().data<2048>("max_int"_hs);
entt::meta<int>().data<2048>("max_int"_hs);
```
It goes without saying that accessing them is trivial as well. It's a matter of
@@ -455,29 +769,90 @@ auto max = entt::resolve<int>().data("max_int"_hs).get({}).cast<int>();
```
As a side note, remember that all this happens behind the scenes without any
allocation because of the small object optimization performed by the meta any
allocation because of the small object optimization performed by the `meta_any`
class.
## Properties and meta objects
Sometimes (for example, when it comes to creating an editor) it might be useful
to be able to attach properties to the meta objects created. Fortunately, this
is possible for most of them.<br/>
To attach a property to a meta object, no matter what as long as it supports
properties, it is sufficient to provide an object at the time of construction
such that `std::get<0>` and `std::get<1>` are valid for it. In other terms, the
properties are nothing more than key/value pairs users can put in an
`std::pair`. As an example:
to attach properties to the meta objects created. Fortunately, this is possible
for most of them.<br/>
For the meta objects that support properties, the member functions of the
factory used for registering them will return a decorated version of the factory
itself. The latter can be used to attach properties to the last created meta
object.<br/>
Apparently, it's more difficult to say than to do:
```cpp
entt::reflect<my_type>("reflected"_hs, std::make_pair("tooltip"_hs, "message"));
entt::meta<my_type>().type("reflected_type"_hs).prop("tooltip"_hs, "message");
```
The meta objects that support properties offer then a couple of member functions
named `prop` to iterate them at once and to search a specific property by key:
Properties are always in the key/value form. There are no restrictions on the
type of the key or value, as long as they are copy constructible objects.<br/>
Multiple formats are supported when it comes to defining a property:
* Properties as key/value pairs:
```cpp
entt::meta<my_type>().type("reflected_type"_hs).prop("tooltip"_hs, "message");
```
* Properties as `std::pair`s:
```cpp
entt::meta<my_type>().type("reflected_type"_hs).prop(std::make_pair("tooltip"_hs, "message"));
```
* Key only properties:
```cpp
entt::meta<my_type>().type("reflected_type"_hs).prop(my_enum::key_only);
```
* Properties as `std::tuple`s:
```cpp
entt::meta<my_type>().type("reflected_type"_hs).prop(std::make_tuple(std::make_pair("tooltip"_hs, "message"), my_enum::key_only));
```
A tuple contains one or more properties. All of them are treated individually.
* Annotations:
```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
// iterate all the properties of a meta type
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
properties at once or to search a specific property by key:
```cpp
// iterate all properties of a meta type
entt::resolve<my_type>().prop([](auto prop) {
// ...
});
@@ -488,7 +863,7 @@ auto prop = entt::resolve<my_type>().prop("tooltip"_hs);
Meta properties are objects having a fairly poor interface, all in all. They
only provide the `key` and the `value` member functions to be used to retrieve
the key and the value contained in the form of meta any objects, respectively.
the key and the value contained in the form of `meta_any` objects, respectively.
## Unregister types
@@ -497,15 +872,13 @@ means unregistering all its data members, member functions, conversion functions
and so on. However, the base classes won't be unregistered, since they don't
necessarily depend on it. Similarly, implicitly generated types (as an example,
the meta types implicitly generated for function parameters when needed) won't
be unregistered.
To unregister a type, users can use the `unregister` function from the global
namespace:
be unregistered.<br/>
Roughly speaking, unregistering a type means disconnecting all associated meta
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::unregister<my_type>();
entt::resolve<my_type>().reset();
```
This function returns a boolean value that is true if the type is actually
registered with the reflection system, false otherwise.<br/>
The type can be re-registered later with a completely different name and form.

View File

@@ -74,6 +74,10 @@ Here is a minimal example for the sake of curiosity:
struct my_process: entt::process<my_process, std::uint32_t> {
using delta_type = std::uint32_t;
my_process(delta_type delay)
: remaining{delay}
{}
void update(delta_type delta, void *) {
remaining -= std::min(remaining, delta);
@@ -85,7 +89,7 @@ struct my_process: entt::process<my_process, std::uint32_t> {
}
private:
delta_type remaining{1000u};
delta_type remaining;
};
```
@@ -158,7 +162,7 @@ To attach a process to a scheduler there are mainly two ways:
the `attach` member function:
```cpp
scheduler.attach<my_process>("foobar");
scheduler.attach<my_process>(1000u);
```
* Otherwise, in case of a lambda or a functor, it's enough to provide an
@@ -182,7 +186,7 @@ scheduler.attach([](auto delta, void *, auto succeed, auto fail) {
// ...
})
// appends a child in the form of a process class
.then<my_process>();
.then<my_process>(1000u);
```
To update a scheduler and therefore all its processes, the `update` member

46
docs/md/references.md Normal file
View File

@@ -0,0 +1,46 @@
# Similar projects
There are many projects similar to `EnTT`, both open source and not.<br/>
Some even borrowed some ideas from this library and expressed them in different
languages.<br/>
Others developed different architectures from scratch and therefore offer
alternative solutions with their pros and cons.
Below an incomplete list of those that I've come across so far.<br/>
If some terms or designs aren't clear, I recommend referring to the
[_ECS Back and Forth_](https://skypjack.github.io/tags/#ecs) series for all the
details.
I hope this list can grow much more in the future:
* C:
* [Diana](https://github.com/discoloda/Diana): an ECS that uses sparse sets to
keep track of entities in systems.
* [Flecs](https://github.com/SanderMertens/flecs): a multithreaded archetype
ECS based on semi-contiguous arrays rather than chunks.
* [lent](https://github.com/nem0/lent): the Donald Trump of the ECS libraries.
* C++:
* [decs](https://github.com/vblanco20-1/decs): a chunk based archetype ECS.
* [ecst](https://github.com/SuperV1234/ecst): a multithreaded compile-time
ECS that uses sparse sets to keep track of entities in systems.
* [EntityX](https://github.com/alecthomas/entityx): a bitset based ECS that
uses a single large matrix of components indexed with entities.
* Go:
* [gecs](https://github.com/tutumagi/gecs): a sparse sets based ECS inspired
by `EnTT`.
* Javascript:
* [ecsy](https://github.com/MozillaReality/ecsy): I haven't had the time to
investigate the underlying design of `ecsy` but it looks cool anyway.
* Rust:
* [Legion](https://github.com/TomGillen/legion): a chunk based archetype ECS.
* [Shipyard](https://github.com/leudz/shipyard): it borrows some ideas from
`EnTT` and offers a sparse sets based ECS with grouping functionalities.
* [Specs](https://github.com/amethyst/specs): a parallel ECS based mainly on
hierarchical bitsets that allows different types of storage as needed.
If you know of other resources out there that can be of interest for the reader,
feel free to open an issue or a PR and I'll be glad to add them to this page.

View File

@@ -42,7 +42,7 @@ struct my_resource { const int value; };
```
A _loader_ is a class the aim of which is to load a specific resource. It has to
inherit directly from the dedicated base class as in the following example:
inherit directly from a dedicated base class as in the following example:
```cpp
struct my_loader final: entt::resource_loader<my_loader, my_resource> {
@@ -76,11 +76,11 @@ Finally, a cache is a specialization of a class template tailored to a specific
resource:
```cpp
using my_resource_cache = entt::resource_cache<my_resource>;
using my_cache = entt::resource_cache<my_resource>;
// ...
my_resource_cache cache{};
my_cache cache{};
```
The idea is to create different caches for different types of resources and to
@@ -105,21 +105,14 @@ cache.clear();
Besides these member functions, a cache contains what is needed to load, use and
discard resources of the given type.<br/>
Before to explore this part of the interface, it makes sense to mention how
resources are identified. The type of the identifiers to use is defined as:
Before exploring this part of the interface, it makes sense to mention how
resources are identified. They have type `id_type` and therefore they can be
created explicitly as in the following example:
```cpp
entt::resource_cache<resource>::resource_type
```
Where `resource_type` is an alias for `entt::hashed_string::hash_type`.
Therefore, resource identifiers are created explicitly as in the following
example:
```cpp
constexpr auto identifier = entt::resource_cache<resource>::resource_type{"my/resource/identifier"_hs};
constexpr auto identifier = "my/resource/identifier"_hs;
// this is equivalent to the following
constexpr auto hs = entt::hashed_string{"my/resource/identifier"};
constexpr entt::id_type hs = entt::hashed_string{"my/resource/identifier"};
```
The class `hashed_string` is described in a dedicated section, so I won't go in
@@ -142,7 +135,7 @@ loaded. In case the loader returns an invalid pointer, the handle is invalid as
well and therefore it can be easily used with an `if` statement:
```cpp
if(auto handle = cache.load<my_loader>("another/identifier"_hs, 42); handle) {
if(entt::resource_handle handle = cache.load<my_loader>("another/identifier"_hs, 42); handle) {
// ...
}
```
@@ -187,24 +180,24 @@ Users of a resource own a handle that guarantees that a resource isn't destroyed
until all the handles are destroyed, even if the resource itself is removed from
the cache.<br/>
Handles are tiny objects both movable and copyable. They return the contained
resource as a const reference on request:
resource as a (possibly const) reference on request:
* By means of the `get` member function:
```cpp
const auto &resource = handle.get();
auto &resource = handle.get();
```
* Using the proper cast operator:
```cpp
const auto &resource = handle;
auto &resource = handle;
```
* Through the dereference operator:
```cpp
const auto &resource = *handle;
auto &resource = *handle;
```
The resource can also be accessed directly using the arrow operator if required:

View File

@@ -7,6 +7,8 @@
* [Introduction](#introduction)
* [Delegate](#delegate)
* [Runtime arguments](#runtime-arguments)
* [Lambda support](#lambda-support)
* [Signals](#signals)
* [Event dispatcher](#event-dispatcher)
* [Event emitter](#event-emitter)
@@ -37,7 +39,7 @@ lightweight classes to solve the same and many other problems.
A delegate can be used as a general purpose invoker with no memory overhead for
free functions and members provided along with an instance on which to invoke
them.<br/>
It does not claim to be a drop-in replacement for an `std::function`, so do not
It doesn't claim to be a drop-in replacement for an `std::function`, so don't
expect to use it whenever an `std::function` fits well. That said, it's most
likely even a better fit than an `std::function` in a lot of cases, so expect to
use it quite a lot anyway.
@@ -63,7 +65,7 @@ As an example of use:
int f(int i) { return i; }
struct my_struct {
int f(const int &i) { return i }
int f(const int &i) const { return i; }
};
// bind a free function to the delegate
@@ -79,9 +81,9 @@ function type of the delegate is such that the parameter list is empty and the
value of the data member is at least convertible to the return type.
Free functions having type equivalent to `void(T &, args...)` are accepted as
well. In this case, `T &` is considered a payload and the function will receive
it back every time it's invoked. In other terms, this works just fine with the
above definition:
well. The first argument `T &` is considered a payload and the function will
receive it back every time it's invoked. In other terms, this works just fine
with the above definition:
```cpp
void g(const char &c, int i) { /* ... */ }
@@ -107,7 +109,7 @@ delegate(42);
```
Where the function type of the delegate is `void(int)` as above. It goes without
saying that the extra arguments are silently discarded internally.<br/>v
saying that the extra arguments are silently discarded internally.<br/>
This is a nice-to-have feature in a lot of cases, as an example when the
`delegate` class is used as a building block of a signal-slot system.
@@ -142,6 +144,97 @@ delegate. As long as a listener can be invoked with the given arguments to yield
a result that is convertible to the given result type, everything works just
fine.
As a side note, members of classes may or may not be associated with instances.
If they are not, the first argument of the function type must be that of the
class on which the members operate and an instance of this class must obviously
be passed when invoking the delegate:
```
entt::delegate<void(my_struct &, int)> delegate;
delegate.connect<&my_struct::f>();
my_struct instance;
delegate(instance, 42);
```
In this case, it's not possible to deduce the function type since the first
argument doesn't necessarily have to be a reference (for example, it can be a
pointer, as well as a const reference).<br/>
Therefore, the function type must be declared explicitly for unbound members.
## Runtime arguments
The `delegate` class is meant to be used primarily with template arguments.
However, as a consequence of its design, it can also offer minimal support for
runtime arguments.<br/>
When used in this modality, some feature aren't supported though. In particular:
* Curried functions aren't accepted.
* Functions with an argument list that differs from that of the delegate aren't
supported.
* Return type and types of arguments **must** coincide with those of the
delegate and _being at least convertible_ isn't enough anymore.
Moreover, for a given function type `Ret(Args...)`, the signature of the
functions connected at runtime must necessarily be `Ret(const void *, Args...)`.
Runtime arguments can be passed both to the constructor of a delegate and to the
`connect` member function. An optional parameter is also accepted in both cases.
This argument is used to pass arbitrary user data back and forth as a
`const void *` upon invocation.<br/>
To connect a function to a delegate _in the hard way_:
```cpp
int func(const void *ptr, int i) { return *static_cast<const int *>(ptr) * i; }
const int value = 42;
// use the constructor ...
entt::delegate delegate{&func, &value};
// ... or the connect member function
delegate.connect(&func, &value);
```
The type of the delegate is deduced from the function if possible. In this case,
since the first argument is an implementation detail, the deduced function type
is `int(int)`.<br/>
Invoking a delegate built in this way follows the same rules as previously
explained.
## Lambda support
In general, the `delegate` class doesn't fully support lambda functions in all
their nuances. The reason is pretty simple: a `delegate` isn't a drop-in
replacement for an `std::function`. Instead, it tries to overcome the problems
with the latter.<br/>
That being said, non-capturing lambda functions are supported, even though some
feature aren't available in this case.
This is a logical consequence of the support for connecting functions at
runtime. Therefore, lambda functions undergo the same rules and
limitations.<br/>
In fact, since non-capturing lambda functions decay to pointers to functions,
they can be used with a `delegate` as if they were _normal functions_ with
optional payload:
```cpp
my_struct instance;
// use the constructor ...
entt::delegate delegate{+[](const void *ptr, int value) {
return static_cast<const my_struct *>(ptr)->f(value);
}, &instance};
// ... or the connect member function
delegate.connect([](const void *ptr, int value) {
return static_cast<const my_struct *>(ptr)->f(value);
}, &instance);
```
As above, the first parameter (`const void *`) isn't part of the function type
of the delegate and is used to dispatch arbitrary user data back and forth. In
other terms, the function type of the delegate above is `int(int)`.
# Signals
Signal handlers work with references to classes, function pointers and pointers
@@ -158,11 +251,10 @@ _publish_ functionality to the clients of a class. The basic idea is to impose a
clear separation between the signal itself and the `sink` class, that is a tool
to be used to connect and disconnect listeners on the fly.
The API of a signal handler is straightforward. The most important thing is that
it comes in two forms: with and without a collector. In case a signal is
provided with a collector, all the values returned by the listeners can be
literally _collected_ and used later by the caller. Otherwise it works just like
a plain signal that emits events from time to time.<br/>
The API of a signal handler is straightforward. If a collector is supplied to
the signal when something is published, all the values returned by the listeners
can be literally _collected_ and used later by the caller. Otherwise, the class
works just like a plain signal that emits events from time to time.<br/>
To create instances of signal handlers it is sufficient to provide the type of
function to which they refer:
@@ -200,10 +292,10 @@ sink.disconnect<&foo>();
// disconnect a member function of an instance
sink.disconnect<&listener::bar>(instance);
// disconnect all the member functions of an instance, if any
// disconnect all member functions of an instance, if any
sink.disconnect(instance);
// discards all the listeners at once
// discards all listeners at once
sink.disconnect();
```
@@ -211,10 +303,20 @@ As shown above, the listeners don't have to strictly follow the signature of the
signal. As long as a listener can be invoked with the given arguments to yield a
result that is convertible to the given return type, everything works just
fine.<br/>
The `connect` member function returns by default a `connection` object to be
used as an alternative to break a connection by means of its `release` member
function. A `scoped_connection` can also be created from a connection. In this
case, the link is broken automatically as soon as the object goes out of scope.
It's also possible to connect a listener before other listeners already
contained by the signal. The `before` function returns a `sink` object correctly
initialized for the purpose that can be used to connect one or more listeners in
order and in the desired position:
```cpp
sink.before<&foo>().connect<&listener::bar>(instance);
```
In all cases, the `connect` member function returns by default a `connection`
object to be used as an alternative to break a connection by means of its
`release` member function. A `scoped_connection` can also be created from a
connection. In this case, the link is broken automatically as soon as the object
goes out of scope.
Once listeners are attached (or even if there are no listeners at all), events
and data in general can be published through a signal by means of the `publish`
@@ -285,9 +387,9 @@ entt::dispatcher dispatcher{};
```
In order to register an instance of a class to a dispatcher, its type must
expose one or more member functions the arguments of which are such that
`const E &` can be converted to them for each type of event `E`, no matter what
the return value is.<br/>
expose one or more member functions the arguments of which are such that `E &`
can be converted to them for each type of event `E`, no matter what the return
value is.<br/>
The name of the member function aimed to receive the event must be provided to
the `connect` member function of the sink in charge for the specific event:
@@ -345,7 +447,7 @@ the messages that are still pending are sent to the listeners at once:
```cpp
// emits all the events of the given type at once
dispatcher.update<my_event>();
dispatcher.update<an_event>();
// emits all the events queued so far at once
dispatcher.update();
@@ -375,9 +477,9 @@ The full list of accepted types of events isn't required. Handlers are created
internally on the fly and thus each type of event is accepted by default.
Whenever an event is published, an emitter provides the listeners with a
reference to itself along with a const reference to the event. Therefore
listeners have an handy way to work with it without incurring in the need of
capturing a reference to the emitter itself.<br/>
reference to itself along with a reference to the event. Therefore listeners
have an handy way to work with it without incurring in the need of capturing a
reference to the emitter itself.<br/>
In addition, an opaque object is returned each time a connection is established
between an emitter and a listener, allowing the caller to disconnect them at a
later time.<br/>
@@ -391,10 +493,10 @@ my_emitter emitter{};
```
Listeners must be movable and callable objects (free functions, lambdas,
functors, `std::function`s, whatever) whose function type is:
functors, `std::function`s, whatever) whose function type is compatible with:
```cpp
void(const Event &, my_emitter &)
void(Event &, my_emitter &)
```
Where `Event` is the type of event they want to listen.<br/>

View File

@@ -41,14 +41,14 @@ echo "Sedding..."
# change the url in the formula file
# the slashes in the URL must be escaped
ESCAPED_URL="$(sed -e 's/[\/&]/\\&/g' <<< "$URL")"
ESCAPED_URL="$(echo "$URL" | sed -e 's/[\/&]/\\&/g')"
sed -i -e '/url/s/".*"/"'$ESCAPED_URL'"/' $FORMULA
# change the hash in the formula file
sed -i -e '/sha256/s/".*"/"'$HASH'"/' $FORMULA
# delete temporary file created by sed
rm "$FORMULA-e"
rm -rf "$FORMULA-e"
# update remote repo
echo "Gitting..."

View File

@@ -1,3 +0,0 @@
#!/bin/sh
scripts/update_homebrew.sh $1

File diff suppressed because it is too large Load Diff

View File

@@ -3,45 +3,78 @@
#ifndef ENTT_NOEXCEPT
#define ENTT_NOEXCEPT noexcept
#endif // ENTT_NOEXCEPT
# define ENTT_NOEXCEPT noexcept
#endif
#ifndef ENTT_HS_SUFFIX
#define ENTT_HS_SUFFIX _hs
#endif // ENTT_HS_SUFFIX
# define ENTT_HS_SUFFIX _hs
#endif
#ifndef ENTT_HWS_SUFFIX
#define ENTT_HWS_SUFFIX _hws
#endif // ENTT_HWS_SUFFIX
# define ENTT_HWS_SUFFIX _hws
#endif
#ifndef ENTT_NO_ATOMIC
#include <atomic>
#define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
#else // ENTT_NO_ATOMIC
#define ENTT_MAYBE_ATOMIC(Type) Type
#endif // ENTT_NO_ATOMIC
#ifndef ENTT_USE_ATOMIC
# define ENTT_MAYBE_ATOMIC(Type) Type
#else
# include <atomic>
# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
#endif
#ifndef ENTT_ID_TYPE
#include <cstdint>
#define ENTT_ID_TYPE std::uint32_t
#endif // ENTT_ID_TYPE
# include <cstdint>
# define ENTT_ID_TYPE std::uint32_t
#endif
#ifndef ENTT_PAGE_SIZE
#define ENTT_PAGE_SIZE 32768
#endif // ENTT_PAGE_SIZE
# define ENTT_PAGE_SIZE 32768
#endif
#ifndef ENTT_DISABLE_ASSERT
#include <cassert>
#define ENTT_ASSERT(condition) assert(condition)
#else // ENTT_DISABLE_ASSERT
#define ENTT_ASSERT(...) ((void)0)
#endif // ENTT_DISABLE_ASSERT
#ifndef ENTT_ASSERT
# include <cassert>
# define ENTT_ASSERT(condition) assert(condition)
#endif
#endif // ENTT_CONFIG_CONFIG_H
#ifndef ENTT_NO_ETO
# include <type_traits>
# define ENTT_IS_EMPTY(Type) std::is_empty<Type>
#else
# include <type_traits>
# define ENTT_IS_EMPTY(Type) std::false_type
#endif
#ifndef ENTT_STANDARD_CPP
# if defined __clang__ || (defined __GNUC__ && __GNUC__ > 8)
# define ENTT_PRETTY_FUNCTION_CONSTEXPR
# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
# define ENTT_PRETTY_FUNCTION_PREFIX '='
# define ENTT_PRETTY_FUNCTION_SUFFIX ']'
# elif defined __GNUC__
# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
# define ENTT_PRETTY_FUNCTION_PREFIX '='
# define ENTT_PRETTY_FUNCTION_SUFFIX ']'
# elif defined _MSC_VER
# define ENTT_PRETTY_FUNCTION_CONSTEXPR
# define ENTT_PRETTY_FUNCTION __FUNCSIG__
# define ENTT_PRETTY_FUNCTION_PREFIX '<'
# define ENTT_PRETTY_FUNCTION_SUFFIX '>'
# endif
#endif
#ifndef ENTT_STANDALONE
# define ENTT_FAST_PATH(...) false
#else
# define ENTT_FAST_PATH(Cond) Cond
#endif
#endif

View File

@@ -2,10 +2,9 @@
#define ENTT_CONFIG_VERSION_H
#define ENTT_VERSION "3.1.0"
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 1
#define ENTT_VERSION_MINOR 5
#define ENTT_VERSION_PATCH 0
#endif // ENTT_CONFIG_VERSION_H
#endif

View File

@@ -2,6 +2,7 @@
#define ENTT_CORE_ALGORITHM_HPP
#include <vector>
#include <utility>
#include <iterator>
#include <algorithm>
@@ -79,7 +80,7 @@ struct insertion_sort {
*/
template<std::size_t Bit, std::size_t N>
struct radix_sort {
static_assert((N % Bit) == 0);
static_assert((N % Bit) == 0, "The maximum number of bits to sort must be a multiple of the number of bits processed per pass");
/**
* @brief Sorts the elements in a range.
@@ -140,4 +141,4 @@ struct radix_sort {
}
#endif // ENTT_CORE_ALGORITHM_HPP
#endif

33
src/entt/core/attribute.h Normal file
View File

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

View File

@@ -2,8 +2,8 @@
#define ENTT_CORE_FAMILY_HPP
#include <type_traits>
#include "../config/config.h"
#include "fwd.hpp"
namespace entt {
@@ -18,24 +18,20 @@ namespace entt {
*/
template<typename...>
class family {
inline static ENTT_MAYBE_ATOMIC(ENTT_ID_TYPE) identifier;
template<typename...>
// clang (since version 9) started to complain if auto is used instead of ENTT_ID_TYPE
inline static const ENTT_ID_TYPE inner = identifier++;
inline static ENTT_MAYBE_ATOMIC(id_type) identifier{};
public:
/*! @brief Unsigned integer type. */
using family_type = ENTT_ID_TYPE;
using family_type = id_type;
/*! @brief Statically generated unique identifier for the given type. */
template<typename... Type>
// at the time I'm writing, clang crashes during compilation if auto is used instead of family_type
inline static const family_type type = inner<std::decay_t<Type>...>;
inline static const family_type type = identifier++;
};
}
#endif // ENTT_CORE_FAMILY_HPP
#endif

18
src/entt/core/fwd.hpp Normal file
View File

@@ -0,0 +1,18 @@
#ifndef ENTT_CORE_FWD_HPP
#define ENTT_CORE_FWD_HPP
#include "../config/config.h"
namespace entt {
/*! @brief Alias declaration for type identifiers. */
using id_type = ENTT_ID_TYPE;
}
#endif

View File

@@ -3,7 +3,9 @@
#include <cstddef>
#include <cstdint>
#include "../config/config.h"
#include "fwd.hpp"
namespace entt {
@@ -24,6 +26,7 @@ struct fnv1a_traits;
template<>
struct fnv1a_traits<std::uint32_t> {
using type = std::uint32_t;
static constexpr std::uint32_t offset = 2166136261;
static constexpr std::uint32_t prime = 16777619;
};
@@ -31,6 +34,7 @@ struct fnv1a_traits<std::uint32_t> {
template<>
struct fnv1a_traits<std::uint64_t> {
using type = std::uint64_t;
static constexpr std::uint64_t offset = 14695981039346656037ull;
static constexpr std::uint64_t prime = 1099511628211ull;
};
@@ -41,7 +45,7 @@ struct fnv1a_traits<std::uint64_t> {
/**
* Internal details not to be documented.
* @endcond TURN_OFF_DOXYGEN
* @endcond
*/
@@ -58,7 +62,7 @@ struct fnv1a_traits<std::uint64_t> {
*/
template<typename Char>
class basic_hashed_string {
using traits_type = internal::fnv1a_traits<ENTT_ID_TYPE>;
using traits_type = internal::fnv1a_traits<id_type>;
struct const_wrapper {
// non-explicit constructor on purpose
@@ -67,15 +71,21 @@ class basic_hashed_string {
};
// FowlerNollVo hash function v. 1a - the good
static constexpr ENTT_ID_TYPE helper(ENTT_ID_TYPE partial, const Char *curr) ENTT_NOEXCEPT {
return curr[0] == 0 ? partial : helper((partial^curr[0])*traits_type::prime, curr+1);
[[nodiscard]] static constexpr id_type helper(const Char *curr) ENTT_NOEXCEPT {
auto value = traits_type::offset;
while(*curr != 0) {
value = (value ^ static_cast<traits_type::type>(*(curr++))) * traits_type::prime;
}
return value;
}
public:
/*! @brief Character type. */
using value_type = Char;
/*! @brief Unsigned integer type. */
using hash_type = ENTT_ID_TYPE;
using hash_type = id_type;
/**
* @brief Returns directly the numeric representation of a string.
@@ -93,8 +103,8 @@ public:
* @return The numeric representation of the string.
*/
template<std::size_t N>
static constexpr hash_type to_value(const value_type (&str)[N]) ENTT_NOEXCEPT {
return helper(traits_type::offset, str);
[[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT {
return helper(str);
}
/**
@@ -102,8 +112,8 @@ public:
* @param wrapper Helps achieving the purpose by relying on overloading.
* @return The numeric representation of the string.
*/
static hash_type to_value(const_wrapper wrapper) ENTT_NOEXCEPT {
return helper(traits_type::offset, wrapper.str);
[[nodiscard]] static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT {
return helper(wrapper.str);
}
/**
@@ -112,8 +122,8 @@ public:
* @param size Length of the string to hash.
* @return The numeric representation of the string.
*/
static hash_type to_value(const value_type *str, std::size_t size) ENTT_NOEXCEPT {
ENTT_ID_TYPE partial{traits_type::offset};
[[nodiscard]] static hash_type value(const value_type *str, std::size_t size) ENTT_NOEXCEPT {
id_type partial{traits_type::offset};
while(size--) { partial = (partial^(str++)[0])*traits_type::prime; }
return partial;
}
@@ -139,7 +149,7 @@ public:
*/
template<std::size_t N>
constexpr basic_hashed_string(const value_type (&curr)[N]) ENTT_NOEXCEPT
: str{curr}, hash{helper(traits_type::offset, curr)}
: str{curr}, hash{helper(curr)}
{}
/**
@@ -148,14 +158,14 @@ public:
* @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(traits_type::offset, 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.
*/
constexpr const value_type * data() const ENTT_NOEXCEPT {
[[nodiscard]] constexpr const value_type * data() const ENTT_NOEXCEPT {
return str;
}
@@ -163,25 +173,25 @@ public:
* @brief Returns the numeric representation of a hashed string.
* @return The numeric representation of the instance.
*/
constexpr hash_type value() const ENTT_NOEXCEPT {
[[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT {
return hash;
}
/**
* @brief Returns the human-readable representation of a hashed string.
* @return The string used to initialize the instance.
*/
constexpr operator const value_type *() const ENTT_NOEXCEPT { return str; }
/*! @copydoc data */
[[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { return data(); }
/*! @copydoc value */
constexpr operator hash_type() const ENTT_NOEXCEPT { return hash; }
/**
* @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.
*/
constexpr bool operator==(const basic_hashed_string &other) const ENTT_NOEXCEPT {
[[nodiscard]] constexpr bool operator==(const basic_hashed_string &other) const ENTT_NOEXCEPT {
return hash == other.hash;
}
@@ -214,7 +224,7 @@ basic_hashed_string(const Char (&str)[N]) ENTT_NOEXCEPT
* @return True if the two hashed strings are identical, false otherwise.
*/
template<typename Char>
constexpr bool operator!=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
[[nodiscard]] constexpr bool operator!=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
return !(lhs == rhs);
}
@@ -235,7 +245,7 @@ using hashed_wstring = basic_hashed_string<wchar_t>;
* @param str The literal without its suffix.
* @return A properly initialized hashed string.
*/
constexpr entt::hashed_string operator"" ENTT_HS_SUFFIX(const char *str, std::size_t) ENTT_NOEXCEPT {
[[nodiscard]] constexpr entt::hashed_string operator"" ENTT_HS_SUFFIX(const char *str, std::size_t) ENTT_NOEXCEPT {
return entt::hashed_string{str};
}
@@ -245,9 +255,9 @@ constexpr entt::hashed_string operator"" ENTT_HS_SUFFIX(const char *str, std::si
* @param str The literal without its suffix.
* @return A properly initialized hashed wstring.
*/
constexpr entt::hashed_wstring operator"" ENTT_HWS_SUFFIX(const wchar_t *str, std::size_t) ENTT_NOEXCEPT {
[[nodiscard]] constexpr entt::hashed_wstring operator"" ENTT_HWS_SUFFIX(const wchar_t *str, std::size_t) ENTT_NOEXCEPT {
return entt::hashed_wstring{str};
}
#endif // ENTT_CORE_HASHED_STRING_HPP
#endif

View File

@@ -3,9 +3,11 @@
#include <tuple>
#include <cstddef>
#include <utility>
#include <type_traits>
#include "../config/config.h"
#include "fwd.hpp"
namespace entt {
@@ -43,22 +45,22 @@ class identifier {
using tuple_type = std::tuple<std::decay_t<Types>...>;
template<typename Type, std::size_t... Indexes>
static constexpr ENTT_ID_TYPE get(std::index_sequence<Indexes...>) ENTT_NOEXCEPT {
static_assert(std::disjunction_v<std::is_same<Type, Types>...>);
return (0 + ... + (std::is_same_v<Type, std::tuple_element_t<Indexes, tuple_type>> ? ENTT_ID_TYPE(Indexes) : ENTT_ID_TYPE{}));
[[nodiscard]] static constexpr id_type get(std::index_sequence<Indexes...>) {
static_assert(std::disjunction_v<std::is_same<Type, Types>...>, "Invalid type");
return (0 + ... + (std::is_same_v<Type, std::tuple_element_t<Indexes, tuple_type>> ? id_type(Indexes) : id_type{}));
}
public:
/*! @brief Unsigned integer type. */
using identifier_type = ENTT_ID_TYPE;
using identifier_type = id_type;
/*! @brief Statically generated unique identifier for the given type. */
template<typename Type>
static constexpr identifier_type type = get<std::decay_t<Type>>(std::make_index_sequence<sizeof...(Types)>{});
static constexpr identifier_type type = get<std::decay_t<Type>>(std::index_sequence_for<Types...>{});
};
}
#endif // ENTT_CORE_IDENT_HPP
#endif

View File

@@ -2,8 +2,8 @@
#define ENTT_CORE_MONOSTATE_HPP
#include <cassert>
#include "../config/config.h"
#include "fwd.hpp"
namespace entt {
@@ -20,7 +20,7 @@ namespace entt {
* both during an assignment and when they try to read back their data.
* Otherwise, they can incur in unexpected results.
*/
template<ENTT_ID_TYPE>
template<id_type>
struct monostate {
/**
* @brief Assigns a value of a specific type to a given key.
@@ -52,11 +52,11 @@ private:
* @brief Helper variable template.
* @tparam Value Value used to differentiate between different variables.
*/
template<ENTT_ID_TYPE Value>
template<id_type Value>
inline monostate<Value> monostate_v = {};
}
#endif // ENTT_CORE_MONOSTATE_HPP
#endif

144
src/entt/core/type_info.hpp Normal file
View File

@@ -0,0 +1,144 @@
#ifndef ENTT_CORE_TYPE_INFO_HPP
#define ENTT_CORE_TYPE_INFO_HPP
#include <string_view>
#include "../config/config.h"
#include "../core/attribute.h"
#include "hashed_string.hpp"
#include "fwd.hpp"
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
struct ENTT_API type_index {
[[nodiscard]] static id_type next() ENTT_NOEXCEPT {
static ENTT_MAYBE_ATOMIC(id_type) value{};
return value++;
}
};
template<typename Type>
[[nodiscard]] constexpr auto 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 value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first);
return value;
#else
return std::string_view{};
#endif
}
}
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Type index.
* @tparam Type Type for which to generate a sequential identifier.
*/
template<typename Type, typename = void>
struct ENTT_API type_index {
/**
* @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_index::next();
return value;
}
};
/**
* @brief Provides the member constant `value` to true if a given type is
* indexable, false otherwise.
* @tparam Type Potentially indexable type.
*/
template<typename, typename = void>
struct has_type_index: std::false_type {};
/*! @brief has_type_index */
template<typename Type>
struct has_type_index<Type, std::void_t<decltype(type_index<Type>::value())>>: std::true_type {};
/**
* @brief Helper variable template.
* @tparam Type Potentially indexable type.
*/
template<typename Type>
inline constexpr bool has_type_index_v = has_type_index<Type>::value;
/**
* @brief Type info.
* @tparam Type Type for which to generate information.
*/
template<typename Type, typename = void>
struct type_info {
/**
* @brief Returns the numeric representation of a given type.
* @return The numeric representation of the given type.
*/
#if defined ENTT_PRETTY_FUNCTION_CONSTEXPR
[[nodiscard]] static constexpr id_type id() ENTT_NOEXCEPT {
constexpr auto value = hashed_string::value(ENTT_PRETTY_FUNCTION);
return value;
}
#elif defined ENTT_PRETTY_FUNCTION
[[nodiscard]] static id_type id() ENTT_NOEXCEPT {
static const auto value = hashed_string::value(ENTT_PRETTY_FUNCTION);
return value;
}
#else
[[nodiscard]] static id_type id() ENTT_NOEXCEPT {
return type_index<Type>::value();
}
#endif
/**
* @brief Returns the name of a given type.
* @return The name of the given type.
*/
#if defined ENTT_PRETTY_FUNCTION_CONSTEXPR
[[nodiscard]] static constexpr std::string_view name() ENTT_NOEXCEPT {
constexpr auto value = internal::type_name<Type>();
return value;
}
#elif defined ENTT_PRETTY_FUNCTION
[[nodiscard]] static std::string_view name() ENTT_NOEXCEPT {
static const auto value = internal::type_name<Type>();
return value;
}
#else
[[nodiscard]] static constexpr std::string_view name() ENTT_NOEXCEPT {
return internal::type_name<Type>();
}
#endif
};
}
#endif

View File

@@ -2,14 +2,76 @@
#define ENTT_CORE_TYPE_TRAITS_HPP
#include <cstddef>
#include <utility>
#include <type_traits>
#include "../config/config.h"
#include "../core/hashed_string.hpp"
#include "fwd.hpp"
namespace entt {
/**
* @brief Using declaration to be used to _repeat_ the same type a number of
* times equal to the size of a given parameter pack.
* @tparam Type A type to repeat.
*/
template<typename Type, typename>
using unpack_as_t = Type;
/**
* @brief Helper variable template to be used to _repeat_ the same value a
* number of times equal to the size of a given parameter pack.
* @tparam Value A value to repeat.
*/
template<auto Value, typename>
inline constexpr auto unpack_as_v = Value;
/**
* @brief Wraps a static constant.
* @tparam Value A static constant.
*/
template<auto Value>
using integral_constant = std::integral_constant<decltype(Value), Value>;
/**
* @brief Alias template to ease the creation of named values.
* @tparam Value A constant value at least convertible to `id_type`.
*/
template<id_type Value>
using tag = integral_constant<Value>;
/**
* @brief Utility class to disambiguate overloaded functions.
* @tparam N Number of choices available.
*/
template<std::size_t N>
struct choice_t
// Unfortunately, doxygen cannot parse such a construct.
/*! @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.
*/
template<std::size_t N>
inline constexpr choice_t<N> choice{};
/*! @brief A class to use to push around lists of types, nothing more. */
template<typename...>
struct type_list {};
@@ -35,7 +97,7 @@ struct type_list_size<type_list<Type...>>
* @tparam List Type list.
*/
template<class List>
constexpr auto type_list_size_v = type_list_size<List>::value;
inline constexpr auto type_list_size_v = type_list_size<List>::value;
/*! @brief Primary template isn't defined on purpose. */
@@ -120,158 +182,81 @@ template<typename Type>
using type_list_unique_t = typename type_list_unique<Type>::type;
/*! @brief Traits class used mainly to push things across boundaries. */
template<typename>
struct named_type_traits;
/**
* @brief Specialization used to get rid of constness.
* @tparam Type Named type.
* @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 = std::void_t<>>
struct is_equality_comparable: std::false_type {};
/*! @copydoc is_equality_comparable */
template<typename Type>
struct named_type_traits<const Type>
: named_type_traits<Type>
struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
: std::true_type
{};
/**
* @brief Helper type.
* @tparam Type Potentially named type.
* @brief Helper variable template.
* @tparam Type Potentially equality comparable type.
*/
template<typename Type>
using named_type_traits_t = typename named_type_traits<Type>::type;
template<class Type>
inline constexpr auto is_equality_comparable_v = is_equality_comparable<Type>::value;
/**
* @brief Provides the member constant `value` to true if a given type has a
* name. In all other cases, `value` is false.
* @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.
*/
template<typename, typename = std::void_t<>>
struct is_named_type: std::false_type {};
/**
* @brief Provides the member constant `value` to true if a given type has a
* name. In all other cases, `value` is false.
* @tparam Type Potentially named type.
*/
template<typename Type>
struct is_named_type<Type, std::void_t<named_type_traits_t<std::decay_t<Type>>>>: std::true_type {};
template<typename Type, typename = void>
struct is_eto_eligible
: ENTT_IS_EMPTY(Type)
{};
/**
* @brief Helper variable template.
* @tparam Type Potentially named type.
* @tparam Type Potential empty type.
*/
template<class Type>
constexpr auto is_named_type_v = is_named_type<Type>::value;
template<typename Type>
inline constexpr auto is_eto_eligible_v = is_eto_eligible<Type>::value;
/**
* @brief Defines an enum class to use for opaque identifiers and a dedicate
* `to_integer` function to convert the identifiers to their underlying type.
* @param clazz The name to use for the enum class.
* @param type The underlying type for the enum class.
* @brief Extracts the class of a non-static member object or function.
* @tparam Member A pointer to a non-static member object or function.
*/
#define ENTT_OPAQUE_TYPE(clazz, type)\
enum class clazz: type {};\
constexpr auto to_integer(const clazz id) ENTT_NOEXCEPT {\
return std::underlying_type_t<clazz>(id);\
}
template<typename Member>
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...));
template<typename Class, typename Ret, typename... Args>
static Class * clazz(Ret(Class:: *)(Args...) const);
template<typename Class, typename Type>
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.
*/
template<typename Member>
using member_class_t = typename member_class<Member>::type;
}
/**
* @brief Utility macro to deal with an issue of MSVC.
*
* See _msvc-doesnt-expand-va-args-correctly_ on SO for all the details.
*
* @param args Argument to expand.
*/
#define ENTT_EXPAND(args) args
/**
* @brief Makes an already existing type a named type.
*
* The current definition contains a workaround for Clang 6 because it fails to
* deduce correctly the type to use to specialize the class template.<br/>
* With a compiler that fully supports C++17 and works fine with deduction
* guides, the following should be fine instead:
*
* @code{.cpp}
* std::integral_constant<ENTT_ID_TYPE, entt::basic_hashed_string{#type}>
* @endcode
*
* In order to support even sligthly older compilers, I prefer to stick to the
* implementation below.
*
* @param type Type to assign a name to.
*/
#define ENTT_NAMED_TYPE(type)\
template<>\
struct entt::named_type_traits<type>\
: std::integral_constant<ENTT_ID_TYPE, entt::basic_hashed_string<std::remove_cv_t<std::remove_pointer_t<std::decay_t<decltype(#type)>>>>{#type}>\
{\
static_assert(std::is_same_v<std::decay_t<type>, type>);\
};
/**
* @brief Defines a named type (to use for structs).
* @param clazz Name of the type to define.
* @param body Body of the type to define.
*/
#define ENTT_NAMED_STRUCT_ONLY(clazz, body)\
struct clazz body;\
ENTT_NAMED_TYPE(clazz)
/**
* @brief Defines a named type (to use for structs).
* @param ns Namespace where to define the named type.
* @param clazz Name of the type to define.
* @param body Body of the type to define.
*/
#define ENTT_NAMED_STRUCT_WITH_NAMESPACE(ns, clazz, body)\
namespace ns { struct clazz body; }\
ENTT_NAMED_TYPE(ns::clazz)
/*! @brief Utility function to simulate macro overloading. */
#define ENTT_NAMED_STRUCT_OVERLOAD(_1, _2, _3, FUNC, ...) FUNC
/*! @brief Defines a named type (to use for structs). */
#define ENTT_NAMED_STRUCT(...) ENTT_EXPAND(ENTT_NAMED_STRUCT_OVERLOAD(__VA_ARGS__, ENTT_NAMED_STRUCT_WITH_NAMESPACE, ENTT_NAMED_STRUCT_ONLY,)(__VA_ARGS__))
/**
* @brief Defines a named type (to use for classes).
* @param clazz Name of the type to define.
* @param body Body of the type to define.
*/
#define ENTT_NAMED_CLASS_ONLY(clazz, body)\
class clazz body;\
ENTT_NAMED_TYPE(clazz)
/**
* @brief Defines a named type (to use for classes).
* @param ns Namespace where to define the named type.
* @param clazz Name of the type to define.
* @param body Body of the type to define.
*/
#define ENTT_NAMED_CLASS_WITH_NAMESPACE(ns, clazz, body)\
namespace ns { class clazz body; }\
ENTT_NAMED_TYPE(ns::clazz)
/*! @brief Utility function to simulate macro overloading. */
#define ENTT_NAMED_CLASS_MACRO(_1, _2, _3, FUNC, ...) FUNC
/*! @brief Defines a named type (to use for classes). */
#define ENTT_NAMED_CLASS(...) ENTT_EXPAND(ENTT_NAMED_CLASS_MACRO(__VA_ARGS__, ENTT_NAMED_CLASS_WITH_NAMESPACE, ENTT_NAMED_CLASS_ONLY,)(__VA_ARGS__))
#endif // ENTT_CORE_TYPE_TRAITS_HPP
#endif

View File

@@ -2,6 +2,7 @@
#define ENTT_CORE_UTILITY_HPP
#include <utility>
#include "../config/config.h"
@@ -17,34 +18,88 @@ struct identity {
* @return The submitted value as-is.
*/
template<class Type>
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 member functions.
* @tparam Type Function type of the desired overload.
* @tparam Class Type of class to which the member functions belong.
* @param member A valid pointer to a member function.
* @return Pointer to the member function.
* @brief Constant utility to disambiguate overloaded members of a class.
* @tparam Type Type of the desired overload.
* @tparam Class Type of class to which the member belongs.
* @param member A valid pointer to a member.
* @return Pointer to the member.
*/
template<typename Type, typename Class>
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.
* @tparam Type Function type of the desired overload.
* @tparam Func Function type of the desired overload.
* @param func A valid pointer to a function.
* @return Pointer to the function.
*/
template<typename Type>
constexpr auto overload(Type *func) ENTT_NOEXCEPT { return func; }
template<typename Func>
[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { return func; }
/**
* @brief Helper type for visitors.
* @tparam Func Types of function objects.
*/
template<class... Func>
struct overloaded: Func... {
using Func::operator()...;
};
/**
* @brief Deduction guide.
* @tparam Func Types of function objects.
*/
template<class... Func>
overloaded(Func...) -> overloaded<Func...>;
/**
* @brief Basic implementation of a y-combinator.
* @tparam Func Type of a potentially recursive function.
*/
template<class Func>
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)}
{}
/**
* @brief Invokes a y-combinator and therefore its underlying function.
* @tparam Args Types of arguments to use to invoke the underlying function.
* @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 {
return func(*this, std::forward<Args>(args)...);
}
/*! @copydoc operator()() */
template <class... Args>
decltype(auto) operator()(Args &&... args) {
return func(*this, std::forward<Args>(args)...);
}
private:
Func func;
};
}
#endif // ENTT_CORE_UTILITY_HPP
#endif

View File

@@ -2,7 +2,6 @@
#define ENTT_ENTITY_ACTOR_HPP
#include <cassert>
#include <utility>
#include <type_traits>
#include "../config/config.h"
@@ -25,16 +24,31 @@ namespace entt {
* @tparam Entity A valid entity type (see entt_traits for more details).
*/
template<typename Entity>
struct basic_actor {
struct [[deprecated("Consider using the handle class instead")]] basic_actor {
/*! @brief Type of registry used internally. */
using registry_type = basic_registry<Entity>;
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
basic_actor() ENTT_NOEXCEPT
: entt{entt::null}, reg{nullptr}
: entt{null}, reg{nullptr}
{}
/**
* @brief Move constructor.
*
* After actor move construction, instances that have been moved from are
* placed in a valid but unspecified state. It's highly discouraged to
* continue using them.
*
* @param other The instance to move from.
*/
basic_actor(basic_actor &&other) ENTT_NOEXCEPT
: entt{other.entt}, reg{other.reg}
{
other.entt = null;
}
/**
* @brief Constructs an actor from a given registry.
* @param ref An instance of the registry class.
@@ -48,7 +62,7 @@ struct basic_actor {
* @param entity A valid entity identifier.
* @param ref An instance of the registry class.
*/
explicit basic_actor(entity_type entity, registry_type &ref)
explicit basic_actor(entity_type entity, registry_type &ref) ENTT_NOEXCEPT
: entt{entity}, reg{&ref}
{
ENTT_ASSERT(ref.valid(entity));
@@ -61,21 +75,6 @@ struct basic_actor {
}
}
/**
* @brief Move constructor.
*
* After actor move construction, instances that have been moved from are
* placed in a valid but unspecified state. It's highly discouraged to
* continue using them.
*
* @param other The instance to move from.
*/
basic_actor(basic_actor &&other)
: entt{other.entt}, reg{other.reg}
{
other.entt = null;
}
/**
* @brief Move assignment operator.
*
@@ -86,7 +85,7 @@ struct basic_actor {
* @param other The instance to move from.
* @return This actor.
*/
basic_actor & operator=(basic_actor &&other) {
basic_actor & operator=(basic_actor &&other) ENTT_NOEXCEPT {
if(this != &other) {
auto tmp{std::move(other)};
std::swap(reg, tmp.reg);
@@ -112,7 +111,7 @@ struct basic_actor {
*/
template<typename Component, typename... Args>
decltype(auto) assign(Args &&... args) {
return reg->template assign_or_replace<Component>(entt, std::forward<Args>(args)...);
return reg->template emplace_or_replace<Component>(entt, std::forward<Args>(args)...);
}
/**
@@ -130,8 +129,8 @@ struct basic_actor {
* @return True if the actor has all the components, false otherwise.
*/
template<typename... Component>
bool has() const ENTT_NOEXCEPT {
return (reg->template has<Component>(entt) && ...);
[[nodiscard]] bool has() const {
return reg->template has<Component...>(entt);
}
/**
@@ -140,13 +139,13 @@ struct basic_actor {
* @return References to the components owned by the actor.
*/
template<typename... Component>
decltype(auto) get() const ENTT_NOEXCEPT {
[[nodiscard]] decltype(auto) get() const {
return std::as_const(*reg).template get<Component...>(entt);
}
/*! @copydoc get */
template<typename... Component>
decltype(auto) get() ENTT_NOEXCEPT {
[[nodiscard]] decltype(auto) get() {
return reg->template get<Component...>(entt);
}
@@ -156,13 +155,13 @@ struct basic_actor {
* @return Pointers to the components owned by the actor.
*/
template<typename... Component>
auto try_get() const ENTT_NOEXCEPT {
[[nodiscard]] auto try_get() const {
return std::as_const(*reg).template try_get<Component...>(entt);
}
/*! @copydoc try_get */
template<typename... Component>
auto try_get() ENTT_NOEXCEPT {
[[nodiscard]] auto try_get() {
return reg->template try_get<Component...>(entt);
}
@@ -170,12 +169,12 @@ struct basic_actor {
* @brief Returns a reference to the underlying registry.
* @return A reference to the underlying registry.
*/
const registry_type & backend() const ENTT_NOEXCEPT {
[[nodiscard]] const registry_type & backend() const ENTT_NOEXCEPT {
return *reg;
}
/*! @copydoc backend */
registry_type & backend() ENTT_NOEXCEPT {
[[nodiscard]] registry_type & backend() ENTT_NOEXCEPT {
return const_cast<registry_type &>(std::as_const(*this).backend());
}
@@ -183,7 +182,7 @@ struct basic_actor {
* @brief Returns the entity associated with an actor.
* @return The entity associated with the actor.
*/
entity_type entity() const ENTT_NOEXCEPT {
[[nodiscard]] entity_type entity() const ENTT_NOEXCEPT {
return entt;
}
@@ -191,7 +190,7 @@ struct basic_actor {
* @brief Checks if an actor refers to a valid entity or not.
* @return True if the actor refers to a valid entity, false otherwise.
*/
explicit operator bool() const ENTT_NOEXCEPT {
[[nodiscard]] explicit operator bool() const {
return reg && reg->valid(entt);
}
@@ -204,4 +203,4 @@ private:
}
#endif // ENTT_ENTITY_ACTOR_HPP
#endif

View File

@@ -2,6 +2,7 @@
#define ENTT_ENTITY_ENTITY_HPP
#include <cstddef>
#include <cstdint>
#include <type_traits>
#include "../config/config.h"
@@ -16,10 +17,20 @@ namespace entt {
* Primary template isn't defined on purpose. All the specializations give a
* compile-time error unless the template parameter is an accepted entity type.
*/
template<typename>
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>>
{};
/**
* @brief Entity traits for a 16 bits entity identifier.
*
@@ -35,14 +46,14 @@ struct entt_traits<std::uint16_t> {
/*! @brief Underlying version type. */
using version_type = std::uint8_t;
/*! @brief Difference type. */
using difference_type = std::int32_t;
using difference_type = std::int16_t;
/*! @brief Mask to use to get the entity number out of an identifier. */
static constexpr std::uint16_t entity_mask = 0xFFF;
static constexpr entity_type entity_mask = 0xFFF;
/*! @brief Mask to use to get the version out of an identifier. */
static constexpr std::uint16_t version_mask = 0xF;
static constexpr entity_type version_mask = 0xF;
/*! @brief Extent of the entity number within an identifier. */
static constexpr auto entity_shift = 12;
static constexpr std::size_t entity_shift = 12u;
};
@@ -61,14 +72,14 @@ struct entt_traits<std::uint32_t> {
/*! @brief Underlying version type. */
using version_type = std::uint16_t;
/*! @brief Difference type. */
using difference_type = std::int64_t;
using difference_type = std::int32_t;
/*! @brief Mask to use to get the entity number out of an identifier. */
static constexpr std::uint32_t entity_mask = 0xFFFFF;
static constexpr entity_type entity_mask = 0xFFFFF;
/*! @brief Mask to use to get the version out of an identifier. */
static constexpr std::uint32_t version_mask = 0xFFF;
static constexpr entity_type version_mask = 0xFFF;
/*! @brief Extent of the entity number within an identifier. */
static constexpr auto entity_shift = 20;
static constexpr std::size_t entity_shift = 20u;
};
@@ -90,71 +101,107 @@ struct entt_traits<std::uint64_t> {
using difference_type = std::int64_t;
/*! @brief Mask to use to get the entity number out of an identifier. */
static constexpr std::uint64_t entity_mask = 0xFFFFFFFF;
static constexpr entity_type entity_mask = 0xFFFFFFFF;
/*! @brief Mask to use to get the version out of an identifier. */
static constexpr std::uint64_t version_mask = 0xFFFFFFFF;
static constexpr entity_type version_mask = 0xFFFFFFFF;
/*! @brief Extent of the entity number within an identifier. */
static constexpr auto entity_shift = 32;
static constexpr std::size_t entity_shift = 32u;
};
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
* @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);
}
namespace internal {
class null {
/*! @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>
using traits_type = entt_traits<std::underlying_type_t<Entity>>;
public:
template<typename Entity>
constexpr operator Entity() const ENTT_NOEXCEPT {
return Entity{traits_type<Entity>::entity_mask};
[[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT {
return Entity{entt_traits<Entity>::entity_mask};
}
constexpr bool operator==(null) const ENTT_NOEXCEPT {
/**
* @brief Compares two null objects.
* @return True in all cases.
*/
[[nodiscard]] constexpr bool operator==(null_t) const ENTT_NOEXCEPT {
return true;
}
constexpr bool operator!=(null) const ENTT_NOEXCEPT {
/**
* @brief Compares two null objects.
* @return False in all cases.
*/
[[nodiscard]] constexpr bool operator!=(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>
constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT {
return (to_integer(entity) & traits_type<Entity>::entity_mask) == to_integer(static_cast<Entity>(*this));
[[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>
constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT {
[[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>
constexpr bool operator==(const Entity entity, null other) ENTT_NOEXCEPT {
return other == entity;
[[nodiscard]] constexpr bool operator==(const Entity entity, 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>
constexpr bool operator!=(const Entity entity, null other) ENTT_NOEXCEPT {
return other != entity;
}
[[nodiscard]] constexpr bool operator!=(const Entity entity, null_t other) ENTT_NOEXCEPT {
return !(other == entity);
}
/**
* Internal details not to be documented.
* @endcond TURN_OFF_DOXYGEN
* @endcond
*/
@@ -165,10 +212,10 @@ constexpr bool operator!=(const Entity entity, null other) ENTT_NOEXCEPT {
* any allowed type. Similarly, there exist comparision operators between the
* null entity and any other entity identifier.
*/
constexpr auto null = internal::null{};
inline constexpr null_t null{};
}
#endif // ENTT_ENTITY_ENTITY_HPP
#endif

View File

@@ -2,90 +2,100 @@
#define ENTT_ENTITY_FWD_HPP
#include <cstdint>
#include "../config/config.h"
#include "../core/type_traits.hpp"
#include "../core/fwd.hpp"
namespace entt {
/*! @class basic_registry */
template <typename>
class basic_registry;
/*! @class basic_view */
template<typename, typename...>
template<typename...>
class basic_view;
/*! @class basic_runtime_view */
template<typename>
class basic_runtime_view;
/*! @class basic_group */
template<typename...>
class basic_group;
/*! @class basic_observer */
template<typename>
class basic_observer;
/*! @class basic_actor */
template <typename>
struct basic_actor;
/*! @class basic_prototype */
template<typename>
class basic_prototype;
/*! @class basic_snapshot */
template<typename>
struct basic_handle;
template<typename>
class basic_snapshot;
/*! @class basic_snapshot_loader */
template<typename>
class basic_snapshot_loader;
/*! @class basic_continuous_loader */
template<typename>
class basic_continuous_loader;
/*! @brief Alias declaration for the most common use case. */
ENTT_OPAQUE_TYPE(entity, ENTT_ID_TYPE)
/*! @brief Alias declaration for the most common use case. */
ENTT_OPAQUE_TYPE(component, ENTT_ID_TYPE)
/*! @brief Default entity identifier. */
enum class entity: id_type {};
/*! @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 actor = basic_actor<entity>;
/*! @brief Alias declaration for the most common use case. */
using prototype = basic_prototype<entity>;
using actor [[deprecated("Consider using the handle class instead")]] = basic_actor<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. */
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 Component Types of components iterated by the view.
* @tparam Types Types of components iterated by the view.
*/
template<typename... Types>
using view = basic_view<entity, Types...>;
/*! @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 Types Types of components iterated by the group.
@@ -97,4 +107,4 @@ using group = basic_group<entity, Types...>;
}
#endif // ENTT_ENTITY_FWD_HPP
#endif

File diff suppressed because it is too large Load Diff

280
src/entt/entity/handle.hpp Normal file
View File

@@ -0,0 +1,280 @@
#ifndef ENTT_ENTITY_HANDLE_HPP
#define ENTT_ENTITY_HANDLE_HPP
#include "registry.hpp"
namespace entt {
/**
* @brief Non-owning handle to an entity.
*
* Tiny wrapper around a registry and an entity.
*
* @tparam Entity A valid entity type (see entt_traits for more details).
*/
template<typename Entity>
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 = std::conditional_t<
std::is_const_v<Entity>,
const basic_registry<entity_type>,
basic_registry<entity_type>
>;
/**
* @brief Constructs a handle from a given registry and entity.
* @param ref An instance of the registry class.
* @param value An entity identifier.
*/
basic_handle(registry_type &ref, entity_type value = null) ENTT_NOEXCEPT
: reg{&ref}, entt{value}
{}
/**
* @brief Assigns an entity to a handle.
* @param value An entity identifier.
* @return This handle.
*/
basic_handle & operator=(const entity_type value) ENTT_NOEXCEPT {
entt = value;
return *this;
}
/**
* @brief Assigns the null object to a handle.
* @return This handle.
*/
basic_handle & operator=(null_t) ENTT_NOEXCEPT {
return (*this = static_cast<entity_type>(null));
}
/**
* @brief Constructs a const handle from a non-const one.
* @return A const handle referring to the same entity.
*/
[[nodiscard]] operator basic_handle<const entity_type>() const ENTT_NOEXCEPT {
return {*reg, entt};
}
/**
* @brief Converts a handle to its underlying entity.
* @return An entity identifier.
*/
[[nodiscard]] operator entity_type() const ENTT_NOEXCEPT {
return entity();
}
/**
* @brief Checks if a handle refers to a valid entity or not.
* @return True if the handle refers to a valid entity, false otherwise.
*/
[[nodiscard]] explicit operator bool() const {
return reg->valid(entt);
}
/**
* @brief Returns a reference to the underlying registry.
* @return A reference to the underlying registry.
*/
[[nodiscard]] registry_type & registry() const ENTT_NOEXCEPT {
return *reg;
}
/**
* @brief Returns the entity associated with a handle.
* @return The entity associated with the handle.
*/
[[nodiscard]] entity_type entity() const ENTT_NOEXCEPT {
return entt;
}
/**
* @brief Assigns the given component to a handle.
* @sa basic_registry::emplace
* @tparam Component Type of component to create.
* @tparam Args Types of arguments to use to construct the component.
* @param args Parameters to use to initialize the component.
* @return A reference to the newly created component.
*/
template<typename Component, typename... Args>
decltype(auto) emplace(Args &&... args) const {
return reg->template emplace<Component>(entt, std::forward<Args>(args)...);
}
/**
* @brief Assigns or replaces the given component for a handle.
* @sa basic_registry::emplace_or_replace
* @tparam Component Type of component to assign or replace.
* @tparam Args Types of arguments to use to construct the component.
* @param args Parameters to use to initialize the component.
* @return A reference to the newly created component.
*/
template<typename Component, typename... Args>
decltype(auto) emplace_or_replace(Args &&... args) const {
return reg->template emplace_or_replace<Component>(entt, std::forward<Args>(args)...);
}
/**
* @brief Patches the given component for a handle.
* @sa basic_registry::patch
* @tparam Component Type of component to patch.
* @tparam Func Types of the function objects to invoke.
* @param func Valid function objects.
* @return A reference to the patched component.
*/
template<typename Component, typename... Func>
decltype(auto) patch(Func &&... func) const {
return reg->template patch<Component>(entt, std::forward<Func>(func)...);
}
/**
* @brief Replaces the given component for a handle.
* @sa basic_registry::replace
* @tparam Component Type of component to replace.
* @tparam Args Types of arguments to use to construct the component.
* @param args Parameters to use to initialize the component.
* @return A reference to the component being replaced.
*/
template<typename Component, typename... Args>
decltype(auto) replace(Args &&... args) const {
return reg->template replace<Component>(entt, std::forward<Args>(args)...);
}
/**
* @brief Removes the given components from a handle.
* @sa basic_registry::remove
* @tparam Component Types of components to remove.
*/
template<typename... Components>
void remove() const {
reg->template remove<Components...>(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... Components>
decltype(auto) remove_if_exists() const {
return reg->template remove_if_exists<Components...>(entt);
}
/**
* @brief Removes all the components from a handle and makes it orphaned.
* @sa basic_registry::remove_all
*/
void remove_all() const {
reg->remove_all(entt);
}
/**
* @brief Checks if a handle has all the given components.
* @sa basic_registry::has
* @tparam Component Components for which to perform the check.
* @return True if the handle has all the components, false otherwise.
*/
template<typename... Components>
[[nodiscard]] decltype(auto) has() const {
return reg->template has<Components...>(entt);
}
/**
* @brief Checks if a handle has at least one of the given components.
* @sa basic_registry::any
* @tparam Component Components for which to perform the check.
* @return True if the handle has at least one of the given components,
* false otherwise.
*/
template<typename... Components>
[[nodiscard]] decltype(auto) any() const {
return reg->template any<Components...>(entt);
}
/**
* @brief Returns references to the given components for a handle.
* @sa basic_registry::get
* @tparam Component Types of components to get.
* @return References to the components owned by the handle.
*/
template<typename... Components>
[[nodiscard]] decltype(auto) get() const {
return reg->template get<Components...>(entt);
}
/**
* @brief Returns a reference to the given component for a handle.
* @sa basic_registry::get_or_emplace
* @tparam Component Type of component to get.
* @tparam Args Types of arguments to use to construct the component.
* @param args Parameters to use to initialize the component.
* @return Reference to the component owned by the handle.
*/
template<typename Component, typename... Args>
[[nodiscard]] decltype(auto) get_or_emplace(Args &&... args) const {
return reg->template get_or_emplace<Component>(entt, std::forward<Args>(args)...);
}
/**
* @brief Returns pointers to the given components for a handle.
* @sa basic_registry::try_get
* @tparam Component Types of components to get.
* @return Pointers to the components owned by the handle.
*/
template<typename... Components>
[[nodiscard]] decltype(auto) try_get() const {
return reg->template try_get<Components...>(entt);
}
/**
* @brief Checks if a handle has components assigned.
* @return True if the handle has no components assigned, false otherwise.
*/
[[nodiscard]] bool orphan() const {
return reg->orphan(entt);
}
/**
* @brief Visits a handle and returns the types for its components.
* @sa basic_registry::visit
* @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));
}
private:
registry_type *reg;
entity_type entt;
};
/**
* @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>;
}
#endif

View File

@@ -4,8 +4,10 @@
#include <type_traits>
#include "../config/config.h"
#include "../signal/sigh.hpp"
#include "../core/type_traits.hpp"
#include "../signal/delegate.hpp"
#include "registry.hpp"
#include "fwd.hpp"
namespace entt {
@@ -19,7 +21,7 @@ namespace entt {
template<bool Const, typename Entity>
struct as_view {
/*! @brief Type of registry to convert. */
using registry_type = std::conditional_t<Const, const entt::basic_registry<Entity>, entt::basic_registry<Entity>>;
using registry_type = std::conditional_t<Const, const basic_registry<Entity>, basic_registry<Entity>>;
/**
* @brief Constructs a converter for a given registry.
@@ -29,12 +31,13 @@ struct as_view {
/**
* @brief Conversion function from a registry to a view.
* @tparam Exclude Types of components used to filter the view.
* @tparam Component Type of components used to construct the view.
* @return A newly created view.
*/
template<typename... Component>
operator entt::basic_view<Entity, Component...>() const {
return reg.template view<Component...>();
template<typename Exclude, typename... Component>
operator basic_view<Entity, Exclude, Component...>() const {
return reg.template view<Component...>(Exclude{});
}
private:
@@ -67,7 +70,7 @@ as_view(const basic_registry<Entity> &) ENTT_NOEXCEPT -> as_view<true, Entity>;
template<bool Const, typename Entity>
struct as_group {
/*! @brief Type of registry to convert. */
using registry_type = std::conditional_t<Const, const entt::basic_registry<Entity>, entt::basic_registry<Entity>>;
using registry_type = std::conditional_t<Const, const basic_registry<Entity>, basic_registry<Entity>>;
/**
* @brief Constructs a converter for a given registry.
@@ -83,7 +86,7 @@ struct as_group {
* @return A newly created group.
*/
template<typename Exclude, typename Get, typename... Owned>
operator entt::basic_group<Entity, Exclude, Get, Owned...>() const {
operator basic_group<Entity, Exclude, Get, Owned...>() const {
return reg.template group<Owned...>(Get{}, Exclude{});
}
@@ -109,30 +112,38 @@ template<typename Entity>
as_group(const basic_registry<Entity> &) ENTT_NOEXCEPT -> as_group<true, Entity>;
/**
* @brief Alias template to ease the assignment of tags to entities.
*
* If used in combination with hashed strings, it simplifies the assignment of
* tags to entities and the use of tags in general where a type would be
* required otherwise.<br/>
* As an example and where the user defined literal for hashed strings hasn't
* been changed:
* @code{.cpp}
* entt::registry registry;
* registry.assign<entt::tag<"enemy"_hs>>(entity);
* @endcode
*
* @note
* Tags are empty components and therefore candidates for the empty component
* optimization.
*
* @tparam Value The numeric representation of an instance of hashed string.
* @brief Helper to create a listener that directly invokes a member function.
* @tparam Member Member function to invoke on a component of the given type.
* @tparam Entity A valid entity type (see entt_traits for more details).
* @param reg A registry that contains the given entity and its components.
* @param entt Entity from which to get the component.
*/
template<ENTT_ID_TYPE Value>
using tag = std::integral_constant<ENTT_ID_TYPE, Value>;
template<auto Member, typename Entity = entity>
void invoke(basic_registry<Entity> &reg, const Entity entt) {
static_assert(std::is_member_function_pointer_v<decltype(Member)>, "Invalid pointer to non-static member function");
delegate<void(basic_registry<Entity> &, const Entity)> func;
func.template connect<Member>(reg.template get<member_class_t<decltype(Member)>>(entt));
func(reg, entt);
}
/**
* @brief Returns the entity associated with a given component.
* @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.
* @return The entity associated with the given component.
*/
template<typename Entity, typename Component>
Entity to_entity(const basic_registry<Entity> &reg, const Component &component) {
return *(reg.template data<Component>() + (&component - reg.template raw<Component>()));
}
}
#endif // ENTT_ENTITY_HELPER_HPP
#endif

View File

@@ -6,12 +6,12 @@
#include <cstddef>
#include <cstdint>
#include <utility>
#include <algorithm>
#include <type_traits>
#include "../config/config.h"
#include "../core/type_traits.hpp"
#include "registry.hpp"
#include "storage.hpp"
#include "utility.hpp"
#include "entity.hpp"
#include "fwd.hpp"
@@ -52,7 +52,7 @@ struct basic_collector<> {
*/
template<typename... AllOf, typename... NoneOf>
static constexpr auto group(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT {
return basic_collector<matcher<matcher<type_list<>, type_list<>>, type_list<NoneOf...>, type_list<AllOf...>>>{};
return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>>{};
}
/**
@@ -61,19 +61,24 @@ struct basic_collector<> {
* @return The updated collector.
*/
template<typename AnyOf>
static constexpr auto replace() ENTT_NOEXCEPT {
return basic_collector<matcher<matcher<type_list<>, type_list<>>, AnyOf>>{};
static constexpr auto update() ENTT_NOEXCEPT {
return basic_collector<matcher<type_list<>, type_list<>, AnyOf>>{};
}
};
/**
* @brief Collector.
* @copydetails basic_collector<>
* @tparam AnyOf Types of components for which changes should be detected.
* @tparam Matcher Types of grouping matchers.
* @tparam Reject Untracked types used to filter out entities.
* @tparam Require Untracked types required by the matcher.
* @tparam Rule Specific details of the current matcher.
* @tparam Other Other matchers.
*/
template<typename... Reject, typename... Require, typename... Rule, typename... Other>
struct basic_collector<matcher<matcher<type_list<Reject...>, type_list<Require...>>, Rule...>, Other...> {
struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule...>, Other...> {
/*! @brief Current matcher. */
using current_type = matcher<type_list<Reject...>, type_list<Require...>, Rule...>;
/**
* @brief Adds a grouping matcher to the collector.
* @tparam AllOf Types of components tracked by the matcher.
@@ -82,8 +87,7 @@ struct basic_collector<matcher<matcher<type_list<Reject...>, type_list<Require..
*/
template<typename... AllOf, typename... NoneOf>
static constexpr auto group(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT {
using first = matcher<matcher<type_list<Reject...>, type_list<Require...>>, Rule...>;
return basic_collector<first, Other..., matcher<matcher<type_list<>, type_list<>>, type_list<NoneOf...>, type_list<AllOf...>>>{};
return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>, current_type, Other...>{};
}
/**
@@ -92,9 +96,8 @@ struct basic_collector<matcher<matcher<type_list<Reject...>, type_list<Require..
* @return The updated collector.
*/
template<typename AnyOf>
static constexpr auto replace() ENTT_NOEXCEPT {
using first = matcher<matcher<type_list<Reject...>, type_list<Require...>>, Rule...>;
return basic_collector<first, Other..., matcher<matcher<type_list<>, type_list<>>, AnyOf>>{};
static constexpr auto update() ENTT_NOEXCEPT {
return basic_collector<matcher<type_list<>, type_list<>, AnyOf>, current_type, Other...>{};
}
/**
@@ -105,13 +108,14 @@ struct basic_collector<matcher<matcher<type_list<Reject...>, type_list<Require..
*/
template<typename... AllOf, typename... NoneOf>
static constexpr auto where(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT {
return basic_collector<matcher<matcher<type_list<Reject..., NoneOf...>, type_list<Require..., AllOf...>>, Rule...>, Other...>{};
using extended_type = matcher<type_list<Reject..., NoneOf...>, type_list<Require..., AllOf...>, Rule...>;
return basic_collector<extended_type, Other...>{};
}
};
/*! @brief Variable template used to ease the definition of collectors. */
constexpr basic_collector<> collector{};
inline constexpr basic_collector<> collector{};
/**
@@ -126,8 +130,8 @@ constexpr basic_collector<> collector{};
* collector:
*
* * Observing matcher: an observer will return at least all the living entities
* for which one or more of the given components have been explicitly
* replaced and not yet destroyed.
* for which one or more of the given components have been updated and not yet
* destroyed.
* * Grouping matcher: an observer will return at least all the living entities
* that would have entered the given group if it existed and that would have
* not yet left it.
@@ -156,7 +160,7 @@ constexpr basic_collector<> collector{};
* behavior.
*
* @warning
* Lifetime of an observer doesn't necessarily have to overcome the one of the
* Lifetime of an observer doesn't necessarily have to overcome that of the
* registry to which it is connected. However, the observer must be disconnected
* from the registry before being destroyed to avoid crashes due to dangling
* pointers.
@@ -171,19 +175,22 @@ class basic_observer {
struct matcher_handler;
template<typename... Reject, typename... Require, typename AnyOf>
struct matcher_handler<matcher<matcher<type_list<Reject...>, type_list<Require...>>, AnyOf>> {
struct matcher_handler<matcher<type_list<Reject...>, type_list<Require...>, AnyOf>> {
template<std::size_t Index>
static void maybe_valid_if(basic_observer &obs, const Entity entt, const basic_registry<Entity> &reg) {
if(reg.template has<Require...>(entt) && !(reg.template has<Reject>(entt) || ...)) {
auto *comp = obs.view.try_get(entt);
(comp ? *comp : obs.view.construct(entt)) |= (1 << Index);
static void maybe_valid_if(basic_observer &obs, const basic_registry<Entity> &reg, const Entity entt) {
if(reg.template has<Require...>(entt) && !reg.template any<Reject...>(entt)) {
if(auto *comp = obs.view.try_get(entt); !comp) {
obs.view.emplace(entt);
}
obs.view.get(entt) |= (1 << Index);
}
}
template<std::size_t Index>
static void discard_if(basic_observer &obs, const Entity entt) {
static void discard_if(basic_observer &obs, const basic_registry<Entity> &, const Entity entt) {
if(auto *value = obs.view.try_get(entt); value && !(*value &= (~(1 << Index)))) {
obs.view.destroy(entt);
obs.view.erase(entt);
}
}
@@ -191,34 +198,35 @@ class basic_observer {
static void connect(basic_observer &obs, basic_registry<Entity> &reg) {
(reg.template on_destroy<Require>().template connect<&discard_if<Index>>(obs), ...);
(reg.template on_construct<Reject>().template connect<&discard_if<Index>>(obs), ...);
reg.template on_replace<AnyOf>().template connect<&maybe_valid_if<Index>>(obs);
reg.template on_update<AnyOf>().template connect<&maybe_valid_if<Index>>(obs);
reg.template on_destroy<AnyOf>().template connect<&discard_if<Index>>(obs);
}
static void disconnect(basic_observer &obs, basic_registry<Entity> &reg) {
(reg.template on_destroy<Require>().disconnect(obs), ...);
(reg.template on_construct<Reject>().disconnect(obs), ...);
reg.template on_replace<AnyOf>().disconnect(obs);
reg.template on_update<AnyOf>().disconnect(obs);
reg.template on_destroy<AnyOf>().disconnect(obs);
}
};
template<typename... Reject, typename... Require, typename... NoneOf, typename... AllOf>
struct matcher_handler<matcher<matcher<type_list<Reject...>, type_list<Require...>>, type_list<NoneOf...>, type_list<AllOf...>>> {
struct matcher_handler<matcher<type_list<Reject...>, type_list<Require...>, type_list<NoneOf...>, AllOf...>> {
template<std::size_t Index>
static void maybe_valid_if(basic_observer &obs, const Entity entt, const basic_registry<Entity> &reg) {
if(reg.template has<AllOf...>(entt) && !(reg.template has<NoneOf>(entt) || ...)
&& reg.template has<Require...>(entt) && !(reg.template has<Reject>(entt) || ...))
{
auto *comp = obs.view.try_get(entt);
(comp ? *comp : obs.view.construct(entt)) |= (1 << Index);
static void maybe_valid_if(basic_observer &obs, const basic_registry<Entity> &reg, const Entity entt) {
if(reg.template has<AllOf..., Require...>(entt) && !reg.template any<NoneOf..., Reject...>(entt)) {
if(auto *comp = obs.view.try_get(entt); !comp) {
obs.view.emplace(entt);
}
obs.view.get(entt) |= (1 << Index);
}
}
template<std::size_t Index>
static void discard_if(basic_observer &obs, const Entity entt) {
static void discard_if(basic_observer &obs, const basic_registry<Entity> &, const Entity entt) {
if(auto *value = obs.view.try_get(entt); value && !(*value &= (~(1 << Index)))) {
obs.view.destroy(entt);
obs.view.erase(entt);
}
}
@@ -249,7 +257,7 @@ class basic_observer {
template<typename... Matcher, std::size_t... Index>
void connect(basic_registry<Entity> &reg, std::index_sequence<Index...>) {
static_assert(sizeof...(Matcher) < std::numeric_limits<payload_type>::digits);
static_assert(sizeof...(Matcher) < std::numeric_limits<payload_type>::digits, "Too many matchers");
(matcher_handler<Matcher>::template connect<Index>(*this, reg), ...);
release = &basic_observer::disconnect<Matcher...>;
}
@@ -258,12 +266,12 @@ public:
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
/*! @brief Unsigned integer type. */
using size_type = typename sparse_set<Entity>::size_type;
/*! @brief Input iterator type. */
using iterator_type = typename sparse_set<Entity>::iterator_type;
using size_type = std::size_t;
/*! @brief Random access iterator type. */
using iterator = typename sparse_set<Entity>::iterator;
/*! @brief Default constructor. */
basic_observer() ENTT_NOEXCEPT
basic_observer()
: target{}, release{}, view{}
{}
@@ -278,12 +286,12 @@ public:
* @param reg A valid reference to a registry.
*/
template<typename... Matcher>
basic_observer(basic_registry<entity_type> &reg, basic_collector<Matcher...>) ENTT_NOEXCEPT
basic_observer(basic_registry<entity_type> &reg, basic_collector<Matcher...>)
: target{&reg},
release{},
view{}
{
connect<Matcher...>(reg, std::make_index_sequence<sizeof...(Matcher)>{});
connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{});
}
/*! @brief Default destructor. */
@@ -309,9 +317,9 @@ public:
template<typename... Matcher>
void connect(basic_registry<entity_type> &reg, basic_collector<Matcher...>) {
disconnect();
connect<Matcher...>(reg, std::make_index_sequence<sizeof...(Matcher)>{});
connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{});
target = &reg;
view.reset();
view.clear();
}
/*! @brief Disconnects an observer from the registry it keeps track of. */
@@ -326,7 +334,7 @@ public:
* @brief Returns the number of elements in an observer.
* @return Number of elements.
*/
size_type size() const ENTT_NOEXCEPT {
[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
return view.size();
}
@@ -334,7 +342,7 @@ public:
* @brief Checks whether an observer is empty.
* @return True if the observer is empty, false otherwise.
*/
bool empty() const ENTT_NOEXCEPT {
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
return view.empty();
}
@@ -345,12 +353,12 @@ public:
* always a valid range, even if the container is empty.
*
* @note
* There are no guarantees on the order of the entities. Use `begin` and
* `end` if you want to iterate the observer in the expected order.
* Entities are in the reverse order as returned by the `begin`/`end`
* iterators.
*
* @return A pointer to the array of entities.
*/
const entity_type * data() const ENTT_NOEXCEPT {
[[nodiscard]] const entity_type * data() const ENTT_NOEXCEPT {
return view.data();
}
@@ -362,7 +370,7 @@ public:
*
* @return An iterator to the first entity of the observer.
*/
iterator_type begin() const ENTT_NOEXCEPT {
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
return view.sparse_set<entity_type>::begin();
}
@@ -376,18 +384,17 @@ public:
* @return An iterator to the entity following the last entity of the
* observer.
*/
iterator_type end() const ENTT_NOEXCEPT {
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
return view.sparse_set<entity_type>::end();
}
/*! @brief Resets the underlying container. */
void clear() {
view.reset();
/*! @brief Clears the underlying container. */
void clear() ENTT_NOEXCEPT {
view.clear();
}
/**
* @brief Iterates entities and applies the given function object to them,
* then clears the observer.
* @brief Iterates entities and applies the given function object to them.
*
* The function object is invoked for each entity.<br/>
* The signature of the function must be equivalent to the following form:
@@ -401,20 +408,16 @@ public:
*/
template<typename Func>
void each(Func func) const {
static_assert(std::is_invocable_v<Func, entity_type>);
std::for_each(begin(), end(), std::move(func));
for(const auto entity: *this) {
func(entity);
}
}
/**
* @brief Iterates entities and applies the given function object to them,
* then clears the observer.
*
* The function object is invoked for each entity.<br/>
* The signature of the function must be equivalent to the following form:
*
* @code{.cpp}
* void(const entity_type);
* @endcode
* @sa each
*
* @tparam Func Type of the function object to invoke.
* @param func A valid function object.
@@ -435,4 +438,4 @@ private:
}
#endif // ENTT_ENTITY_OBSERVER_HPP
#endif

53
src/entt/entity/pool.hpp Normal file
View File

@@ -0,0 +1,53 @@
#ifndef ENTT_ENTITY_POOL_HPP
#define ENTT_ENTITY_POOL_HPP
#include <type_traits>
#include "storage.hpp"
namespace entt {
/**
* @brief Applies component-to-pool conversion and defines the resulting type as
* the member typedef type.
*
* Formally:
*
* * If the component type is a non-const one, the member typedef type is the
* declared storage type.
* * If the component type is a const one, the member typedef type is the
* declared storage type, except it has a const-qualifier added.
*
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Type Type of objects assigned to the entities.
*/
template<typename Entity, typename Type, typename = void>
struct pool {
/*! @brief Resulting type after component-to-pool conversion. */
using type = storage<Entity, Type>;
};
/*! @copydoc pool */
template<typename Entity, typename Type>
struct pool<Entity, const Type> {
/*! @brief Resulting type after component-to-pool conversion. */
using type = std::add_const_t<typename pool<Entity, std::remove_const_t<Type>>::type>;
};
/**
* @brief Alias declaration to use to make component-to-pool conversions.
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Type Type of objects assigned to the entities.
*/
template<typename Entity, typename Type>
using pool_t = typename pool<Entity, Type>::type;
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -3,14 +3,12 @@
#include <iterator>
#include <cassert>
#include <vector>
#include <utility>
#include <algorithm>
#include <type_traits>
#include "../config/config.h"
#include "sparse_set.hpp"
#include "entity.hpp"
#include "fwd.hpp"
@@ -50,7 +48,7 @@ namespace entt {
* have a valid reference and won't be updated accordingly).
*
* @warning
* Lifetime of a view must overcome the one of the registry that generated it.
* Lifetime of a view must not overcome that of the registry that generated it.
* In any other case, attempting to use a view results in undefined behavior.
*
* @tparam Entity A valid entity type (see entt_traits for more details).
@@ -60,72 +58,80 @@ class basic_runtime_view {
/*! @brief A registry is allowed to create views. */
friend class basic_registry<Entity>;
using underlying_iterator_type = typename sparse_set<Entity>::iterator_type;
using traits_type = entt_traits<std::underlying_type_t<Entity>>;
using underlying_iterator = typename sparse_set<Entity>::iterator;
class iterator {
class view_iterator final {
friend class basic_runtime_view<Entity>;
iterator(underlying_iterator_type first, underlying_iterator_type last, const sparse_set<Entity> * const *others, const sparse_set<Entity> * const *length) ENTT_NOEXCEPT
: begin{first},
end{last},
from{others},
to{length}
view_iterator(const std::vector<const sparse_set<Entity> *> &cpools, const std::vector<const sparse_set<Entity> *> &ignore, underlying_iterator curr) ENTT_NOEXCEPT
: pools{&cpools},
filter{&ignore},
it{curr}
{
if(begin != end && !valid()) {
if(it != (*pools)[0]->end() && !valid()) {
++(*this);
}
}
bool valid() const ENTT_NOEXCEPT {
return std::all_of(from, to, [entt = *begin](const auto *view) {
return view->has(entt);
});
[[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_type::difference_type;
using value_type = typename underlying_iterator_type::value_type;
using pointer = typename underlying_iterator_type::pointer;
using reference = typename underlying_iterator_type::reference;
using iterator_category = std::forward_iterator_tag;
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;
iterator() ENTT_NOEXCEPT = default;
view_iterator() ENTT_NOEXCEPT = default;
iterator & operator++() ENTT_NOEXCEPT {
return (++begin != end && !valid()) ? ++(*this) : *this;
view_iterator & operator++() {
while(++it != (*pools)[0]->end() && !valid());
return *this;
}
iterator operator++(int) ENTT_NOEXCEPT {
iterator orig = *this;
view_iterator operator++(int) {
view_iterator orig = *this;
return ++(*this), orig;
}
bool operator==(const iterator &other) const ENTT_NOEXCEPT {
return other.begin == begin;
view_iterator & operator--() ENTT_NOEXCEPT {
while(--it != (*pools)[0]->begin() && !valid());
return *this;
}
bool operator!=(const iterator &other) const ENTT_NOEXCEPT {
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);
}
pointer operator->() const ENTT_NOEXCEPT {
return begin.operator->();
[[nodiscard]] pointer operator->() const {
return it.operator->();
}
reference operator*() const ENTT_NOEXCEPT {
[[nodiscard]] reference operator*() const {
return *operator->();
}
private:
underlying_iterator_type begin;
underlying_iterator_type end;
const sparse_set<Entity> * const *from;
const sparse_set<Entity> * const *to;
const std::vector<const sparse_set<Entity> *> *pools;
const std::vector<const sparse_set<Entity> *> *filter;
underlying_iterator it;
};
basic_runtime_view(std::vector<const sparse_set<Entity> *> others) ENTT_NOEXCEPT
: pools{std::move(others)}
basic_runtime_view(std::vector<const sparse_set<Entity> *> cpools, std::vector<const sparse_set<Entity> *> 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) {
return (!lhs && rhs) || (lhs && rhs && lhs->size() < rhs->size());
@@ -135,23 +141,23 @@ class basic_runtime_view {
std::rotate(pools.begin(), it, pools.end());
}
bool valid() const ENTT_NOEXCEPT {
[[nodiscard]] bool valid() const {
return !pools.empty() && pools.front();
}
public:
/*! @brief Underlying entity identifier. */
using entity_type = typename sparse_set<Entity>::entity_type;
using entity_type = Entity;
/*! @brief Unsigned integer type. */
using size_type = typename sparse_set<Entity>::size_type;
/*! @brief Input iterator type. */
using iterator_type = iterator;
using size_type = std::size_t;
/*! @brief Bidirectional iterator type. */
using iterator = view_iterator;
/**
* @brief Estimates the number of entities that have the given components.
* @return Estimated number of entities that have the given components.
*/
size_type size() const ENTT_NOEXCEPT {
[[nodiscard]] size_type size() const {
return valid() ? pools.front()->size() : size_type{};
}
@@ -159,7 +165,7 @@ public:
* @brief Checks if the view is definitely empty.
* @return True if the view is definitely empty, false otherwise.
*/
bool empty() const ENTT_NOEXCEPT {
[[nodiscard]] bool empty() const {
return !valid() || pools.front()->empty();
}
@@ -172,21 +178,13 @@ public:
* `end()`.
*
* @note
* Input iterators stay true to the order imposed to the underlying data
* Iterators stay true to the order imposed to the underlying data
* structures.
*
* @return An iterator to the first entity that has the given components.
*/
iterator_type begin() const ENTT_NOEXCEPT {
iterator_type it{};
if(valid()) {
const auto &pool = *pools.front();
const auto * const *data = pools.data();
it = { pool.begin(), pool.end(), data + 1, data + pools.size() };
}
return it;
[[nodiscard]] iterator begin() const {
return valid() ? iterator{pools, filter, pools[0]->begin()} : iterator{};
}
/**
@@ -198,21 +196,14 @@ public:
* results in undefined behavior.
*
* @note
* Input iterators stay true to the order imposed to the underlying data
* Iterators stay true to the order imposed to the underlying data
* structures.
*
* @return An iterator to the entity following the last entity that has the
* given components.
*/
iterator_type end() const ENTT_NOEXCEPT {
iterator_type it{};
if(valid()) {
const auto &pool = *pools.front();
it = { pool.end(), pool.end(), nullptr, nullptr };
}
return it;
[[nodiscard]] iterator end() const {
return valid() ? iterator{pools, filter, pools[0]->end()} : iterator{};
}
/**
@@ -220,10 +211,9 @@ public:
* @param entt A valid entity identifier.
* @return True if the view contains the given entity, false otherwise.
*/
bool contains(const entity_type entt) const ENTT_NOEXCEPT {
return valid() && std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *view) {
return view->find(entt) != view->end();
});
[[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); });
}
/**
@@ -243,15 +233,18 @@ public:
*/
template<typename Func>
void each(Func func) const {
std::for_each(begin(), end(), func);
for(const auto entity: *this) {
func(entity);
}
}
private:
std::vector<const sparse_set<Entity> *> pools;
std::vector<const sparse_set<Entity> *> filter;
};
}
#endif // ENTT_ENTITY_RUNTIME_VIEW_HPP
#endif

View File

@@ -3,6 +3,7 @@
#include <array>
#include <vector>
#include <cstddef>
#include <utility>
#include <iterator>
@@ -31,14 +32,7 @@ class basic_snapshot {
/*! @brief A registry is allowed to create snapshots. */
friend class basic_registry<Entity>;
using follow_fn_type = Entity(const basic_registry<Entity> &, const Entity);
using traits_type = entt_traits<std::underlying_type_t<Entity>>;
basic_snapshot(const basic_registry<Entity> *source, Entity init, follow_fn_type *fn) ENTT_NOEXCEPT
: reg{source},
seed{init},
follow{fn}
{}
using traits_type = entt_traits<Entity>;
template<typename Component, typename Archive, typename It>
void get(Archive &archive, std::size_t sz, It first, It last) const {
@@ -71,6 +65,17 @@ class basic_snapshot {
}
public:
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
/**
* @brief Constructs an instance that is bound to a given registry.
* @param source A valid reference to a registry.
*/
basic_snapshot(const basic_registry<entity_type> &source) ENTT_NOEXCEPT
: reg{&source}
{}
/*! @brief Default move constructor. */
basic_snapshot(basic_snapshot &&) = default;
@@ -78,10 +83,10 @@ public:
basic_snapshot & operator=(basic_snapshot &&) = default;
/**
* @brief Puts aside all the entities that are still in use.
* @brief Puts aside all the entities from the underlying registry.
*
* Entities are serialized along with their versions. Destroyed entities are
* not taken in consideration by this function.
* taken in consideration as well by this function.
*
* @tparam Archive Type of output archive.
* @param archive A valid reference to an output archive.
@@ -89,34 +94,14 @@ public:
*/
template<typename Archive>
const basic_snapshot & entities(Archive &archive) const {
archive(typename traits_type::entity_type(reg->alive()));
reg->each([&archive](const auto entt) { archive(entt); });
return *this;
}
const auto sz = reg->size();
auto first = reg->data();
const auto last = first + sz;
/**
* @brief Puts aside destroyed entities.
*
* Entities are serialized along with their versions. Entities that are
* still in use are not taken in consideration by this function.
*
* @tparam Archive Type of output archive.
* @param archive A valid reference to an output archive.
* @return An object of this type to continue creating the snapshot.
*/
template<typename Archive>
const basic_snapshot & destroyed(Archive &archive) const {
auto size = reg->size() - reg->alive();
archive(typename traits_type::entity_type(size));
archive(typename traits_type::entity_type(sz));
if(size) {
auto curr = seed;
archive(curr);
for(--size; size; --size) {
curr = follow(*reg, curr);
archive(curr);
}
while(first != last) {
archive(*(first++));
}
return *this;
@@ -135,25 +120,7 @@ public:
*/
template<typename... Component, typename Archive>
const basic_snapshot & component(Archive &archive) const {
if constexpr(sizeof...(Component) == 1) {
const auto sz = reg->template size<Component...>();
const auto *entities = reg->template data<Component...>();
archive(typename traits_type::entity_type(sz));
for(std::remove_const_t<decltype(sz)> pos{}; pos < sz; ++pos) {
const auto entt = entities[pos];
if constexpr(std::is_empty_v<Component...>) {
archive(entt);
} else {
archive(entt, reg->template get<Component...>(entt));
}
};
} else {
(component<Component>(archive), ...);
}
(component<Component>(archive, reg->template data<Component>(), reg->template data<Component>() + reg->template size<Component>()), ...);
return *this;
}
@@ -173,14 +140,12 @@ public:
*/
template<typename... Component, typename Archive, typename It>
const basic_snapshot & component(Archive &archive, It first, It last) const {
component<Component...>(archive, first, last, std::make_index_sequence<sizeof...(Component)>{});
component<Component...>(archive, first, last, std::index_sequence_for<Component...>{});
return *this;
}
private:
const basic_registry<Entity> *reg;
const Entity seed;
follow_fn_type *follow;
const basic_registry<entity_type> *reg;
};
@@ -199,52 +164,49 @@ class basic_snapshot_loader {
/*! @brief A registry is allowed to create snapshot loaders. */
friend class basic_registry<Entity>;
using force_fn_type = void(basic_registry<Entity> &, const Entity, const bool);
using traits_type = entt_traits<std::underlying_type_t<Entity>>;
using traits_type = entt_traits<Entity>;
basic_snapshot_loader(basic_registry<Entity> *source, force_fn_type *fn) ENTT_NOEXCEPT
: reg{source},
force{fn}
{
// to restore a snapshot as a whole requires a clean registry
ENTT_ASSERT(reg->empty());
}
template<typename Archive>
void assure(Archive &archive, bool discard) const {
template<typename Type, typename Archive>
void assign(Archive &archive) const {
typename traits_type::entity_type length{};
archive(length);
while(length--) {
Entity entt{};
archive(entt);
force(*reg, entt, discard);
}
}
entity_type entt{};
template<typename Type, typename Archive, typename... Args>
void assign(Archive &archive, Args... args) const {
typename traits_type::entity_type length{};
archive(length);
while(length--) {
static constexpr auto discard = false;
Entity entt{};
if constexpr(std::is_empty_v<Type>) {
if constexpr(std::is_empty_v<Type>) {
while(length--) {
archive(entt);
force(*reg, entt, discard);
reg->template assign<Type>(args..., entt);
} else {
Type instance{};
const auto entity = reg->valid(entt) ? entt : reg->create(entt);
ENTT_ASSERT(entity == entt);
reg->template emplace<Type>(entity);
}
} else {
Type instance{};
while(length--) {
archive(entt, instance);
force(*reg, entt, discard);
reg->template assign<Type>(args..., entt, std::as_const(instance));
const auto entity = reg->valid(entt) ? entt : reg->create(entt);
ENTT_ASSERT(entity == entt);
reg->template emplace<Type>(entity, std::move(instance));
}
}
}
public:
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
/**
* @brief Constructs an instance that is bound to a given registry.
* @param source A valid reference to a registry.
*/
basic_snapshot_loader(basic_registry<entity_type> &source) ENTT_NOEXCEPT
: reg{&source}
{
// restoring a snapshot as a whole requires a clean registry
ENTT_ASSERT(reg->empty());
}
/*! @brief Default move constructor. */
basic_snapshot_loader(basic_snapshot_loader &&) = default;
@@ -263,25 +225,17 @@ public:
*/
template<typename Archive>
const basic_snapshot_loader & entities(Archive &archive) const {
static constexpr auto discard = false;
assure(archive, discard);
return *this;
}
typename traits_type::entity_type length{};
archive(length);
std::vector<entity_type> all(length);
for(decltype(length) pos{}; pos < length; ++pos) {
archive(all[pos]);
}
reg->assign(all.cbegin(), all.cend());
/**
* @brief Restores entities that were destroyed during serialization.
*
* This function restores the entities that were destroyed during
* serialization and gives them the versions they originally had.
*
* @tparam Archive Type of input archive.
* @param archive A valid reference to an input archive.
* @return A valid loader to continue restoring data.
*/
template<typename Archive>
const basic_snapshot_loader & destroyed(Archive &archive) const {
static constexpr auto discard = true;
assure(archive, discard);
return *this;
}
@@ -323,8 +277,7 @@ public:
}
private:
basic_registry<Entity> *reg;
force_fn_type *force;
basic_registry<entity_type> *reg;
};
@@ -332,7 +285,7 @@ private:
* @brief Utility class for _continuous loading_.
*
* A _continuous loader_ is designed to load data from a source registry to a
* (possibly) non-empty destination. The loader can accomodate in a registry
* (possibly) non-empty destination. The loader can accommodate in a registry
* more than one snapshot in a sort of _continuous loading_ that updates the
* destination one step at a time.<br/>
* Identifiers that entities originally had are not transferred to the target.
@@ -346,7 +299,7 @@ private:
*/
template<typename Entity>
class basic_continuous_loader {
using traits_type = entt_traits<std::underlying_type_t<Entity>>;
using traits_type = entt_traits<Entity>;
void destroy(Entity entt) {
const auto it = remloc.find(entt);
@@ -371,39 +324,59 @@ class basic_continuous_loader {
}
}
template<typename Other, typename Type, typename Member>
void update(Other &instance, Member Type:: *member) {
if constexpr(!std::is_same_v<Other, Type>) {
return;
} else if constexpr(std::is_same_v<Member, Entity>) {
instance.*member = map(instance.*member);
} else {
// maybe a container? let's try...
for(auto &entt: instance.*member) {
entt = map(entt);
template<typename Container>
auto update(int, Container &container)
-> decltype(typename Container::mapped_type{}, void()) {
// map like container
Container other;
for(auto &&pair: container) {
using first_type = std::remove_const_t<typename std::decay_t<decltype(pair)>::first_type>;
using second_type = typename std::decay_t<decltype(pair)>::second_type;
if constexpr(std::is_same_v<first_type, entity_type> && std::is_same_v<second_type, entity_type>) {
other.emplace(map(pair.first), map(pair.second));
} else if constexpr(std::is_same_v<first_type, entity_type>) {
other.emplace(map(pair.first), std::move(pair.second));
} else {
static_assert(std::is_same_v<second_type, entity_type>, "Neither the key nor the value are of entity type");
other.emplace(std::move(pair.first), map(pair.second));
}
}
std::swap(container, other);
}
template<typename Container>
auto update(char, Container &container)
-> decltype(typename Container::value_type{}, void()) {
// vector like container
static_assert(std::is_same_v<typename Container::value_type, entity_type>, "Invalid value type");
for(auto &&entt: container) {
entt = map(entt);
}
}
template<typename Archive>
void assure(Archive &archive, void(basic_continuous_loader:: *member)(Entity)) {
typename traits_type::entity_type length{};
archive(length);
while(length--) {
Entity entt{};
archive(entt);
(this->*member)(entt);
template<typename Other, typename Type, typename 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>) {
instance.*member = map(instance.*member);
} else {
// maybe a container? let's try...
update(0, instance.*member);
}
}
template<typename Component>
void reset() {
void remove_if_exists() {
for(auto &&ref: remloc) {
const auto local = ref.second.first;
if(reg->valid(local)) {
reg->template reset<Component>(local);
reg->template remove_if_exists<Component>(local);
}
}
}
@@ -413,19 +386,22 @@ class basic_continuous_loader {
typename traits_type::entity_type length{};
archive(length);
while(length--) {
Entity entt{};
entity_type entt{};
if constexpr(std::is_empty_v<Other>) {
if constexpr(std::is_empty_v<Other>) {
while(length--) {
archive(entt);
restore(entt);
reg->template assign_or_replace<Other>(map(entt));
} else {
Other instance{};
reg->template emplace_or_replace<Other>(map(entt));
}
} else {
Other instance{};
while(length--) {
archive(entt, instance);
(update(instance, member), ...);
restore(entt);
reg->template assign_or_replace<Other>(map(entt), std::as_const(instance));
reg->template emplace_or_replace<Other>(map(entt), std::move(instance));
}
}
}
@@ -435,7 +411,7 @@ public:
using entity_type = Entity;
/**
* @brief Constructs a loader that is bound to a given registry.
* @brief Constructs an instance that is bound to a given registry.
* @param source A valid reference to a registry.
*/
basic_continuous_loader(basic_registry<entity_type> &source) ENTT_NOEXCEPT
@@ -460,23 +436,21 @@ public:
*/
template<typename Archive>
basic_continuous_loader & entities(Archive &archive) {
assure(archive, &basic_continuous_loader::restore);
return *this;
}
typename traits_type::entity_type length{};
entity_type entt{};
archive(length);
for(decltype(length) pos{}; pos < length; ++pos) {
archive(entt);
if(const auto entity = (to_integral(entt) & traits_type::entity_mask); entity == pos) {
restore(entt);
} else {
destroy(entt);
}
}
/**
* @brief Restores entities that were destroyed during serialization.
*
* This function restores the entities that were destroyed during
* serialization and creates local counterparts for them if required.
*
* @tparam Archive Type of input archive.
* @param archive A valid reference to an input archive.
* @return A non-const reference to this loader.
*/
template<typename Archive>
basic_continuous_loader & destroyed(Archive &archive) {
assure(archive, &basic_continuous_loader::destroy);
return *this;
}
@@ -501,7 +475,7 @@ public:
*/
template<typename... Component, typename Archive, typename... Type, typename... Member>
basic_continuous_loader & component(Archive &archive, Member Type:: *... member) {
(reset<Component>(), ...);
(remove_if_exists<Component>(), ...);
(assign<Component>(archive, member...), ...);
return *this;
}
@@ -559,7 +533,7 @@ public:
* @param entt An entity identifier.
* @return True if `entity` is managed by the loader, false otherwise.
*/
bool has(entity_type entt) const ENTT_NOEXCEPT {
[[nodiscard]] bool contains(entity_type entt) const ENTT_NOEXCEPT {
return (remloc.find(entt) != remloc.cend());
}
@@ -568,7 +542,7 @@ public:
* @param entt An entity identifier.
* @return The local identifier if any, the null entity otherwise.
*/
entity_type map(entity_type entt) const ENTT_NOEXCEPT {
[[nodiscard]] entity_type map(entity_type entt) const ENTT_NOEXCEPT {
const auto it = remloc.find(entt);
entity_type other = null;
@@ -588,4 +562,4 @@ private:
}
#endif // ENTT_ENTITY_SNAPSHOT_HPP
#endif

View File

@@ -2,13 +2,11 @@
#define ENTT_ENTITY_SPARSE_SET_HPP
#include <algorithm>
#include <iterator>
#include <utility>
#include <vector>
#include <memory>
#include <cstddef>
#include <numeric>
#include <type_traits>
#include "../config/config.h"
#include "../core/algorithm.hpp"
@@ -35,32 +33,28 @@ namespace entt {
* purpose of the framework.
*
* @note
* There are no guarantees that entities are returned in the insertion order
* when iterate a sparse set. Do not make assumption on the order in any case.
*
* @note
* Internal data structures arrange elements to maximize performance. Because of
* that, there are no guarantees that elements have the expected order when
* iterate directly the internal packed array (see `data` and `size` member
* functions for that). Use `begin` and `end` instead.
* Internal data structures arrange elements to maximize performance. There are
* no guarantees that entities are returned in the insertion order when iterate
* a sparse set. Do not make assumption on the order in any case.
*
* @tparam Entity A valid entity type (see entt_traits for more details).
*/
template<typename Entity>
class sparse_set {
using traits_type = entt_traits<std::underlying_type_t<Entity>>;
static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0), "ENTT_PAGE_SIZE must be a power of two");
static constexpr auto entt_per_page = ENTT_PAGE_SIZE / sizeof(Entity);
static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0));
static constexpr auto entt_per_page = ENTT_PAGE_SIZE / sizeof(typename traits_type::entity_type);
using traits_type = entt_traits<Entity>;
using page_type = std::unique_ptr<Entity[]>;
class iterator {
class sparse_set_iterator final {
friend class sparse_set<Entity>;
using direct_type = const std::vector<Entity>;
using packed_type = std::vector<Entity>;
using index_type = typename traits_type::difference_type;
iterator(direct_type *ref, const index_type idx) ENTT_NOEXCEPT
: direct{ref}, index{idx}
sparse_set_iterator(const packed_type &ref, const index_type idx) ENTT_NOEXCEPT
: packed{&ref}, index{idx}
{}
public:
@@ -70,107 +64,113 @@ class sparse_set {
using reference = const value_type &;
using iterator_category = std::random_access_iterator_tag;
iterator() ENTT_NOEXCEPT = default;
sparse_set_iterator() ENTT_NOEXCEPT = default;
iterator & operator++() ENTT_NOEXCEPT {
sparse_set_iterator & operator++() ENTT_NOEXCEPT {
return --index, *this;
}
iterator operator++(int) ENTT_NOEXCEPT {
sparse_set_iterator operator++(int) ENTT_NOEXCEPT {
iterator orig = *this;
return ++(*this), orig;
}
iterator & operator--() ENTT_NOEXCEPT {
sparse_set_iterator & operator--() ENTT_NOEXCEPT {
return ++index, *this;
}
iterator operator--(int) ENTT_NOEXCEPT {
iterator orig = *this;
return --(*this), orig;
sparse_set_iterator operator--(int) ENTT_NOEXCEPT {
sparse_set_iterator orig = *this;
return operator--(), orig;
}
iterator & operator+=(const difference_type value) ENTT_NOEXCEPT {
sparse_set_iterator & operator+=(const difference_type value) ENTT_NOEXCEPT {
index -= value;
return *this;
}
iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
return iterator{direct, index-value};
sparse_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
sparse_set_iterator copy = *this;
return (copy += value);
}
iterator & operator-=(const difference_type value) ENTT_NOEXCEPT {
sparse_set_iterator & operator-=(const difference_type value) ENTT_NOEXCEPT {
return (*this += -value);
}
iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
sparse_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
return (*this + -value);
}
difference_type operator-(const iterator &other) const ENTT_NOEXCEPT {
difference_type operator-(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
return other.index - index;
}
reference operator[](const difference_type value) const ENTT_NOEXCEPT {
const auto pos = size_type(index-value-1);
return (*direct)[pos];
[[nodiscard]] reference operator[](const difference_type value) const {
const auto pos = size_type(index-value-1u);
return (*packed)[pos];
}
bool operator==(const iterator &other) const ENTT_NOEXCEPT {
[[nodiscard]] bool operator==(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
return other.index == index;
}
bool operator!=(const iterator &other) const ENTT_NOEXCEPT {
[[nodiscard]] bool operator!=(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
bool operator<(const iterator &other) const ENTT_NOEXCEPT {
[[nodiscard]] bool operator<(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
return index > other.index;
}
bool operator>(const iterator &other) const ENTT_NOEXCEPT {
[[nodiscard]] bool operator>(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
return index < other.index;
}
bool operator<=(const iterator &other) const ENTT_NOEXCEPT {
[[nodiscard]] bool operator<=(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
return !(*this > other);
}
bool operator>=(const iterator &other) const ENTT_NOEXCEPT {
[[nodiscard]] bool operator>=(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
return !(*this < other);
}
pointer operator->() const ENTT_NOEXCEPT {
const auto pos = size_type(index-1);
return &(*direct)[pos];
[[nodiscard]] pointer operator->() const {
const auto pos = size_type(index-1u);
return &(*packed)[pos];
}
reference operator*() const ENTT_NOEXCEPT {
[[nodiscard]] reference operator*() const {
return *operator->();
}
private:
direct_type *direct;
const packed_type *packed;
index_type index;
};
void assure(const std::size_t page) {
if(!(page < reverse.size())) {
reverse.resize(page+1);
}
if(!reverse[page]) {
reverse[page] = std::make_unique<entity_type[]>(entt_per_page);
// null is safe in all cases for our purposes
std::fill_n(reverse[page].get(), entt_per_page, null);
}
[[nodiscard]] auto page(const Entity entt) const ENTT_NOEXCEPT {
return size_type{(to_integral(entt) & traits_type::entity_mask) / entt_per_page};
}
auto map(const Entity entt) const ENTT_NOEXCEPT {
const auto identifier = to_integer(entt) & traits_type::entity_mask;
const auto page = size_type(identifier / entt_per_page);
const auto offset = size_type(identifier & (entt_per_page - 1));
return std::make_pair(page, offset);
[[nodiscard]] auto offset(const Entity entt) const ENTT_NOEXCEPT {
return size_type{to_integral(entt) & (entt_per_page - 1)};
}
[[nodiscard]] page_type & assure(const std::size_t pos) {
if(!(pos < sparse.size())) {
sparse.resize(pos+1);
}
if(!sparse[pos]) {
sparse[pos].reset(new entity_type[entt_per_page]);
// null is safe in all cases for our purposes
for(auto *first = sparse[pos].get(), *last = first + entt_per_page; first != last; ++first) {
*first = null;
}
}
return sparse[pos];
}
public:
@@ -179,46 +179,18 @@ public:
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Random access iterator type. */
using iterator_type = iterator;
using iterator = sparse_set_iterator;
/*! @brief Reverse iterator type. */
using reverse_iterator = const entity_type *;
/*! @brief Default constructor. */
sparse_set() = default;
/**
* @brief Copy constructor.
* @param other The instance to copy from.
*/
sparse_set(const sparse_set &other)
: reverse{},
direct{other.direct}
{
for(size_type pos{}, last = other.reverse.size(); pos < last; ++pos) {
if(other.reverse[pos]) {
assure(pos);
std::copy_n(other.reverse[pos].get(), entt_per_page, reverse[pos].get());
}
}
}
/*! @brief Default move constructor. */
sparse_set(sparse_set &&) = default;
/*! @brief Default destructor. */
virtual ~sparse_set() ENTT_NOEXCEPT = default;
/**
* @brief Copy assignment operator.
* @param other The instance to copy from.
* @return This sparse set.
*/
sparse_set & operator=(const sparse_set &other) {
if(&other != this) {
auto tmp{other};
*this = std::move(tmp);
}
return *this;
}
virtual ~sparse_set() = default;
/*! @brief Default move assignment operator. @return This sparse set. */
sparse_set & operator=(sparse_set &&) = default;
@@ -232,7 +204,7 @@ public:
* @param cap Desired capacity.
*/
void reserve(const size_type cap) {
direct.reserve(cap);
packed.reserve(cap);
}
/**
@@ -240,19 +212,19 @@ public:
* allocated space for.
* @return Capacity of the sparse set.
*/
size_type capacity() const ENTT_NOEXCEPT {
return direct.capacity();
[[nodiscard]] size_type capacity() const ENTT_NOEXCEPT {
return packed.capacity();
}
/*! @brief Requests the removal of unused capacity. */
void shrink_to_fit() {
// conservative approach
if(direct.empty()) {
reverse.clear();
if(packed.empty()) {
sparse.clear();
}
reverse.shrink_to_fit();
direct.shrink_to_fit();
sparse.shrink_to_fit();
packed.shrink_to_fit();
}
/**
@@ -265,8 +237,8 @@ public:
*
* @return Extent of the sparse set.
*/
size_type extent() const ENTT_NOEXCEPT {
return reverse.size() * entt_per_page;
[[nodiscard]] size_type extent() const ENTT_NOEXCEPT {
return sparse.size() * entt_per_page;
}
/**
@@ -279,16 +251,16 @@ public:
*
* @return Number of elements.
*/
size_type size() const ENTT_NOEXCEPT {
return direct.size();
[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
return packed.size();
}
/**
* @brief Checks whether a sparse set is empty.
* @return True if the sparse set is empty, false otherwise.
*/
bool empty() const ENTT_NOEXCEPT {
return direct.empty();
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
return packed.empty();
}
/**
@@ -298,16 +270,13 @@ public:
* always a valid range, even if the container is empty.
*
* @note
* There are no guarantees on the order, even though `respect` has been
* previously invoked. Internal data structures arrange elements to maximize
* performance. Accessing them directly gives a performance boost but less
* guarantees. Use `begin` and `end` if you want to iterate the sparse set
* in the expected order.
* Entities are in the reverse order as returned by the `begin`/`end`
* iterators.
*
* @return A pointer to the internal packed array.
*/
const entity_type * data() const ENTT_NOEXCEPT {
return direct.data();
[[nodiscard]] const entity_type * data() const ENTT_NOEXCEPT {
return packed.data();
}
/**
@@ -317,15 +286,11 @@ public:
* array. If the sparse set is empty, the returned iterator will be equal to
* `end()`.
*
* @note
* Random access iterators stay true to the order imposed by a call to
* `respect`.
*
* @return An iterator to the first entity of the internal packed array.
*/
iterator_type begin() const ENTT_NOEXCEPT {
const typename traits_type::difference_type pos = direct.size();
return iterator_type{&direct, pos};
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
const typename traits_type::difference_type pos = packed.size();
return iterator{packed, pos};
}
/**
@@ -335,15 +300,39 @@ public:
* the internal packed array. Attempting to dereference the returned
* iterator results in undefined behavior.
*
* @note
* Random access iterators stay true to the order imposed by a call to
* `respect`.
*
* @return An iterator to the element following the last entity of the
* internal packed array.
*/
iterator_type end() const ENTT_NOEXCEPT {
return iterator_type{&direct, {}};
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
return iterator{packed, {}};
}
/**
* @brief Returns a reverse iterator to the beginning.
*
* The returned iterator points to the first entity of the reversed internal
* packed array. If the sparse set is empty, the returned iterator will be
* equal to `rend()`.
*
* @return An iterator to the first entity of the reversed internal packed
* array.
*/
[[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT {
return packed.data();
}
/**
* @brief Returns a reverse iterator to the end.
*
* The returned iterator points to the element following the last entity in
* the reversed internal packed array. Attempting to dereference the
* returned iterator results in undefined behavior.
*
* @return An iterator to the element following the last entity of the
* reversed internal packed array.
*/
[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
return rbegin() + packed.size();
}
/**
@@ -352,8 +341,8 @@ public:
* @return An iterator to the given entity if it's found, past the end
* iterator otherwise.
*/
iterator_type find(const entity_type entt) const ENTT_NOEXCEPT {
return has(entt) ? --(end() - index(entt)) : end();
[[nodiscard]] iterator find(const entity_type entt) const {
return contains(entt) ? --(end() - index(entt)) : end();
}
/**
@@ -361,10 +350,10 @@ public:
* @param entt A valid entity identifier.
* @return True if the sparse set contains the entity, false otherwise.
*/
bool has(const entity_type entt) const ENTT_NOEXCEPT {
auto [page, offset] = map(entt);
// testing against null permits to avoid accessing the direct vector
return (page < reverse.size() && reverse[page] && reverse[page][offset] != null);
[[nodiscard]] bool contains(const entity_type entt) const {
const auto curr = page(entt);
// testing against null permits to avoid accessing the packed array
return (curr < sparse.size() && sparse[curr] && sparse[curr][offset(entt)] != null);
}
/**
@@ -379,10 +368,9 @@ public:
* @param entt A valid entity identifier.
* @return The position of the entity in the sparse set.
*/
size_type index(const entity_type entt) const ENTT_NOEXCEPT {
ENTT_ASSERT(has(entt));
auto [page, offset] = map(entt);
return size_type(reverse[page][offset]);
[[nodiscard]] size_type index(const entity_type entt) const {
ENTT_ASSERT(contains(entt));
return size_type{to_integral(sparse[page(entt)][offset(entt)])};
}
/**
@@ -396,12 +384,10 @@ public:
*
* @param entt A valid entity identifier.
*/
void construct(const entity_type entt) {
ENTT_ASSERT(!has(entt));
auto [page, offset] = map(entt);
assure(page);
reverse[page][offset] = entity_type(direct.size());
direct.push_back(entt);
void emplace(const entity_type entt) {
ENTT_ASSERT(!contains(entt));
assure(page(entt))[offset(entt)] = entity_type(static_cast<typename traits_type::entity_type>(packed.size()));
packed.push_back(entt);
}
/**
@@ -413,20 +399,20 @@ public:
* An assertion will abort the execution at runtime in debug mode if the
* sparse set already contains the given entity.
*
* @tparam It Type of forward iterator.
* @tparam It Type of input iterator.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
*/
template<typename It>
void batch(It first, It last) {
std::for_each(std::make_reverse_iterator(last), std::make_reverse_iterator(first), [this, next = direct.size()](const auto entt) mutable {
ENTT_ASSERT(!has(entt));
auto [page, offset] = map(entt);
assure(page);
reverse[page][offset] = entity_type(next++);
});
void insert(It first, It last) {
auto next = static_cast<typename traits_type::entity_type>(packed.size());
packed.insert(packed.end(), first, last);
direct.insert(direct.end(), first, last);
while(first != last) {
const auto entt = *(first++);
ENTT_ASSERT(!contains(entt));
assure(page(entt))[offset(entt)] = entity_type(next++);
}
}
/**
@@ -440,14 +426,14 @@ public:
*
* @param entt A valid entity identifier.
*/
void destroy(const entity_type entt) {
ENTT_ASSERT(has(entt));
auto [from_page, from_offset] = map(entt);
auto [to_page, to_offset] = map(direct.back());
direct[size_type(reverse[from_page][from_offset])] = entity_type(direct.back());
reverse[to_page][to_offset] = reverse[from_page][from_offset];
reverse[from_page][from_offset] = null;
direct.pop_back();
void erase(const entity_type entt) {
ENTT_ASSERT(contains(entt));
const auto curr = page(entt);
const auto pos = offset(entt);
packed[size_type{to_integral(sparse[curr][pos])}] = packed.back();
sparse[page(packed.back())][offset(packed.back())] = sparse[curr][pos];
sparse[curr][pos] = null;
packed.pop_back();
}
/**
@@ -462,16 +448,14 @@ public:
* An assertion will abort the execution at runtime in debug mode if the
* sparse set doesn't contain the given entities.
*
* @param lhs A valid position within the sparse set.
* @param rhs A valid position within the sparse set.
* @param lhs A valid entity identifier.
* @param rhs A valid entity identifier.
*/
virtual void swap(const size_type lhs, const size_type rhs) ENTT_NOEXCEPT {
ENTT_ASSERT(lhs < direct.size());
ENTT_ASSERT(rhs < direct.size());
auto [src_page, src_offset] = map(direct[lhs]);
auto [dst_page, dst_offset] = map(direct[rhs]);
std::swap(reverse[src_page][src_offset], reverse[dst_page][dst_offset]);
std::swap(direct[lhs], direct[rhs]);
virtual void swap(const entity_type lhs, const entity_type rhs) {
auto &from = sparse[page(lhs)][offset(lhs)];
auto &to = sparse[page(rhs)][offset(rhs)];
std::swap(packed[size_type{to_integral(from)}], packed[size_type{to_integral(to)}]);
std::swap(from, to);
}
/**
@@ -499,15 +483,6 @@ public:
* * An iterator past the last element of the range to sort.
* * A comparison function to use to compare the elements.
*
* The comparison function object received by the sort function object
* hasn't necessarily the type of the one passed along with the other
* parameters to this member function.
*
* @note
* Attempting to iterate elements using a raw pointer returned by a call to
* either `data` or `raw` gives no guarantees on the order, even though
* `sort` has been invoked.
*
* @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.
@@ -518,26 +493,71 @@ public:
* @param args Arguments to forward to the sort function object, if any.
*/
template<typename Compare, typename Sort = std_sort, typename... Args>
void sort(iterator_type first, iterator_type last, Compare compare, Sort algo = Sort{}, Args &&... args) {
ENTT_ASSERT(!(first > last));
void sort(iterator first, iterator last, Compare compare, Sort algo = Sort{}, Args &&... args) {
ENTT_ASSERT(!(last < first));
ENTT_ASSERT(!(last > end()));
std::vector<size_type> copy(last - first);
const auto offset = std::distance(last, end());
std::iota(copy.begin(), copy.end(), size_type{});
const auto length = std::distance(first, last);
const auto skip = std::distance(last, end());
const auto to = packed.rend() - skip;
const auto from = to - length;
algo(copy.rbegin(), copy.rend(), [this, offset, compare = std::move(compare)](const auto lhs, const auto rhs) {
return compare(std::as_const(direct[lhs+offset]), std::as_const(direct[rhs+offset]));
}, std::forward<Args>(args)...);
algo(from, to, std::move(compare), std::forward<Args>(args)...);
for(size_type pos{}, length = copy.size(); pos < length; ++pos) {
for(size_type pos = skip, end = skip+length; pos < end; ++pos) {
sparse[page(packed[pos])][offset(packed[pos])] = entity_type(static_cast<typename traits_type::entity_type>(pos));
}
}
/**
* @brief Sort elements according to the given comparison function.
*
* @sa sort
*
* This function is a slightly slower version of `sort` that invokes the
* caller to indicate which entities are swapped.<br/>
* It's recommended when the caller wants to sort its own data structures to
* align them with the order induced in the sparse set.
*
* The signature of the callback should be equivalent to the following:
*
* @code{.cpp}
* bool(const Entity, const Entity);
* @endcode
*
* @tparam Apply Type of function object to invoke to notify the caller.
* @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.
* @param first An iterator to the first element of the range to sort.
* @param last An iterator past the last element of the range to sort.
* @param apply A valid function object to use as a callback.
* @param compare A valid comparison function object.
* @param algo A valid sort function object.
* @param args Arguments to forward to the sort function object, if any.
*/
template<typename Apply, typename Compare, typename Sort = std_sort, typename... Args>
void arrange(iterator first, iterator last, Apply apply, Compare compare, Sort algo = Sort{}, Args &&... args) {
ENTT_ASSERT(!(last < first));
ENTT_ASSERT(!(last > end()));
const auto length = std::distance(first, last);
const auto skip = std::distance(last, end());
const auto to = packed.rend() - skip;
const auto from = to - length;
algo(from, to, std::move(compare), std::forward<Args>(args)...);
for(size_type pos = skip, end = skip+length; pos < end; ++pos) {
auto curr = pos;
auto next = copy[curr];
auto next = index(packed[curr]);
while(curr != next) {
swap(copy[curr] + offset, copy[next] + offset);
copy[curr] = curr;
apply(packed[curr], packed[next]);
sparse[page(packed[curr])][offset(packed[curr])] = entity_type(static_cast<typename traits_type::entity_type>(curr));
curr = next;
next = copy[curr];
next = index(packed[curr]);
}
}
}
@@ -555,22 +575,18 @@ public:
* the expected order after a call to `respect`. See `begin` and `end` for
* more details.
*
* @note
* Attempting to iterate elements using the raw pointer returned by `data`
* gives no guarantees on the order, even though `respect` has been invoked.
*
* @param other The sparse sets that imposes the order of the entities.
*/
void respect(const sparse_set &other) ENTT_NOEXCEPT {
void respect(const sparse_set &other) {
const auto to = other.end();
auto from = other.begin();
size_type pos = direct.size() - 1;
size_type pos = packed.size() - 1;
while(pos && from != to) {
if(has(*from)) {
if(*from != direct[pos]) {
swap(pos, index(*from));
if(contains(*from)) {
if(*from != packed[pos]) {
swap(packed[pos], *from);
}
--pos;
@@ -581,20 +597,20 @@ public:
}
/**
* @brief Resets a sparse set.
* @brief Clears a sparse set.
*/
void reset() {
reverse.clear();
direct.clear();
void clear() ENTT_NOEXCEPT {
sparse.clear();
packed.clear();
}
private:
std::vector<std::unique_ptr<entity_type[]>> reverse;
std::vector<entity_type> direct;
std::vector<page_type> sparse;
std::vector<entity_type> packed;
};
}
#endif // ENTT_ENTITY_SPARSE_SET_HPP
#endif

View File

@@ -4,15 +4,15 @@
#include <algorithm>
#include <iterator>
#include <numeric>
#include <utility>
#include <vector>
#include <cstddef>
#include <type_traits>
#include "../config/config.h"
#include "../core/algorithm.hpp"
#include "sparse_set.hpp"
#include "../core/type_traits.hpp"
#include "entity.hpp"
#include "sparse_set.hpp"
namespace entt {
@@ -32,15 +32,13 @@ namespace entt {
* iterators.
*
* @note
* Internal data structures arrange elements to maximize performance. Because of
* that, there are no guarantees that elements have the expected order when
* iterate directly the internal packed array (see `raw` and `size` member
* functions for that). Use `begin` and `end` instead.
* Internal data structures arrange elements to maximize performance. There are
* no guarantees that objects are returned in the insertion order when iterate
* a storage. Do not make assumption on the order in any case.
*
* @warning
* Empty types aren't explicitly instantiated. Temporary objects are returned in
* place of the instances of the components and raw access isn't available for
* them.
* Empty types aren't explicitly instantiated. Therefore, many of the functions
* normally available for non-empty types will not be available for empty ones.
*
* @sa sparse_set<Entity>
*
@@ -48,19 +46,21 @@ namespace entt {
* @tparam Type Type of objects assigned to the entities.
*/
template<typename Entity, typename Type, typename = std::void_t<>>
class basic_storage: public sparse_set<Entity> {
class storage: public sparse_set<Entity> {
static_assert(std::is_move_constructible_v<Type> && std::is_move_assignable_v<Type>, "The managed type must be at least move constructible and assignable");
using underlying_type = sparse_set<Entity>;
using traits_type = entt_traits<std::underlying_type_t<Entity>>;
using traits_type = entt_traits<Entity>;
template<bool Const>
class iterator {
friend class basic_storage<Entity, Type>;
class storage_iterator final {
friend class storage<Entity, Type>;
using instance_type = std::conditional_t<Const, const std::vector<Type>, std::vector<Type>>;
using index_type = typename traits_type::difference_type;
iterator(instance_type *ref, const index_type idx) ENTT_NOEXCEPT
: instances{ref}, index{idx}
storage_iterator(instance_type &ref, const index_type idx) ENTT_NOEXCEPT
: instances{&ref}, index{idx}
{}
public:
@@ -70,82 +70,83 @@ class basic_storage: public sparse_set<Entity> {
using reference = std::conditional_t<Const, const value_type &, value_type &>;
using iterator_category = std::random_access_iterator_tag;
iterator() ENTT_NOEXCEPT = default;
storage_iterator() ENTT_NOEXCEPT = default;
iterator & operator++() ENTT_NOEXCEPT {
storage_iterator & operator++() ENTT_NOEXCEPT {
return --index, *this;
}
iterator operator++(int) ENTT_NOEXCEPT {
iterator orig = *this;
storage_iterator operator++(int) ENTT_NOEXCEPT {
storage_iterator orig = *this;
return ++(*this), orig;
}
iterator & operator--() ENTT_NOEXCEPT {
storage_iterator & operator--() ENTT_NOEXCEPT {
return ++index, *this;
}
iterator operator--(int) ENTT_NOEXCEPT {
iterator orig = *this;
return --(*this), orig;
storage_iterator operator--(int) ENTT_NOEXCEPT {
storage_iterator orig = *this;
return operator--(), orig;
}
iterator & operator+=(const difference_type value) ENTT_NOEXCEPT {
storage_iterator & operator+=(const difference_type value) ENTT_NOEXCEPT {
index -= value;
return *this;
}
iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
return iterator{instances, index-value};
storage_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
storage_iterator copy = *this;
return (copy += value);
}
iterator & operator-=(const difference_type value) ENTT_NOEXCEPT {
storage_iterator & operator-=(const difference_type value) ENTT_NOEXCEPT {
return (*this += -value);
}
iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
storage_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
return (*this + -value);
}
difference_type operator-(const iterator &other) const ENTT_NOEXCEPT {
difference_type operator-(const storage_iterator &other) const ENTT_NOEXCEPT {
return other.index - index;
}
reference operator[](const difference_type value) const ENTT_NOEXCEPT {
[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT {
const auto pos = size_type(index-value-1);
return (*instances)[pos];
}
bool operator==(const iterator &other) const ENTT_NOEXCEPT {
[[nodiscard]] bool operator==(const storage_iterator &other) const ENTT_NOEXCEPT {
return other.index == index;
}
bool operator!=(const iterator &other) const ENTT_NOEXCEPT {
[[nodiscard]] bool operator!=(const storage_iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
bool operator<(const iterator &other) const ENTT_NOEXCEPT {
[[nodiscard]] bool operator<(const storage_iterator &other) const ENTT_NOEXCEPT {
return index > other.index;
}
bool operator>(const iterator &other) const ENTT_NOEXCEPT {
[[nodiscard]] bool operator>(const storage_iterator &other) const ENTT_NOEXCEPT {
return index < other.index;
}
bool operator<=(const iterator &other) const ENTT_NOEXCEPT {
[[nodiscard]] bool operator<=(const storage_iterator &other) const ENTT_NOEXCEPT {
return !(*this > other);
}
bool operator>=(const iterator &other) const ENTT_NOEXCEPT {
[[nodiscard]] bool operator>=(const storage_iterator &other) const ENTT_NOEXCEPT {
return !(*this < other);
}
pointer operator->() const ENTT_NOEXCEPT {
const auto pos = size_type(index-1);
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
const auto pos = size_type(index-1u);
return &(*instances)[pos];
}
reference operator*() const ENTT_NOEXCEPT {
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
return *operator->();
}
@@ -158,13 +159,18 @@ public:
/*! @brief Type of the objects associated with the entities. */
using object_type = Type;
/*! @brief Underlying entity identifier. */
using entity_type = typename underlying_type::entity_type;
using entity_type = Entity;
/*! @brief Unsigned integer type. */
using size_type = typename underlying_type::size_type;
using size_type = std::size_t;
/*! @brief Random access iterator type. */
using iterator_type = iterator<false>;
using iterator = storage_iterator<false>;
/*! @brief Constant random access iterator type. */
using const_iterator_type = iterator<true>;
using const_iterator = storage_iterator<true>;
/*! @brief Reverse iterator type. */
using reverse_iterator = Type *;
/*! @brief Constant reverse iterator type. */
using const_reverse_iterator = const Type *;
/**
* @brief Increases the capacity of a storage.
@@ -192,77 +198,113 @@ public:
* always a valid range, even if the container is empty.
*
* @note
* There are no guarantees on the order, even though either `sort` or
* `respect` has been previously invoked. Internal data structures arrange
* elements to maximize performance. Accessing them directly gives a
* performance boost but less guarantees. Use `begin` and `end` if you want
* to iterate the storage in the expected order.
* Objects are in the reverse order as returned by the `begin`/`end`
* iterators.
*
* @return A pointer to the array of objects.
*/
const object_type * raw() const ENTT_NOEXCEPT {
[[nodiscard]] const object_type * raw() const ENTT_NOEXCEPT {
return instances.data();
}
/*! @copydoc raw */
object_type * raw() ENTT_NOEXCEPT {
[[nodiscard]] object_type * raw() ENTT_NOEXCEPT {
return const_cast<object_type *>(std::as_const(*this).raw());
}
/**
* @brief Returns an iterator to the beginning.
*
* The returned iterator points to the first instance of the given type. If
* the storage is empty, the returned iterator will be equal to `end()`.
* The returned iterator points to the first instance of the internal array.
* If the storage is empty, the returned iterator will be equal to `end()`.
*
* @note
* Random access iterators stay true to the order imposed by a call to
* either `sort` or `respect`.
*
* @return An iterator to the first instance of the given type.
* @return An iterator to the first instance of the internal array.
*/
const_iterator_type cbegin() const ENTT_NOEXCEPT {
[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
const typename traits_type::difference_type pos = underlying_type::size();
return const_iterator_type{&instances, pos};
return const_iterator{instances, pos};
}
/*! @copydoc cbegin */
const_iterator_type begin() const ENTT_NOEXCEPT {
[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
return cbegin();
}
/*! @copydoc begin */
iterator_type begin() ENTT_NOEXCEPT {
[[nodiscard]] iterator begin() ENTT_NOEXCEPT {
const typename traits_type::difference_type pos = underlying_type::size();
return iterator_type{&instances, pos};
return iterator{instances, pos};
}
/**
* @brief Returns an iterator to the end.
*
* The returned iterator points to the element following the last instance
* of the given type. Attempting to dereference the returned iterator
* of the internal array. Attempting to dereference the returned iterator
* results in undefined behavior.
*
* @note
* Random access iterators stay true to the order imposed by a call to
* either `sort` or `respect`.
*
* @return An iterator to the element following the last instance of the
* given type.
* internal array.
*/
const_iterator_type cend() const ENTT_NOEXCEPT {
return const_iterator_type{&instances, {}};
[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
return const_iterator{instances, {}};
}
/*! @copydoc cend */
const_iterator_type end() const ENTT_NOEXCEPT {
[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
return cend();
}
/*! @copydoc end */
iterator_type end() ENTT_NOEXCEPT {
return iterator_type{&instances, {}};
[[nodiscard]] iterator end() ENTT_NOEXCEPT {
return iterator{instances, {}};
}
/**
* @brief Returns a reverse iterator to the beginning.
*
* The returned iterator points to the first instance of the reversed
* internal array. If the storage is empty, the returned iterator will be
* equal to `rend()`.
*
* @return An iterator to the first instance of the reversed internal array.
*/
[[nodiscard]] const_reverse_iterator crbegin() const ENTT_NOEXCEPT {
return instances.data();
}
/*! @copydoc crbegin */
[[nodiscard]] const_reverse_iterator rbegin() const ENTT_NOEXCEPT {
return crbegin();
}
/*! @copydoc rbegin */
[[nodiscard]] reverse_iterator rbegin() ENTT_NOEXCEPT {
return instances.data();
}
/**
* @brief Returns a reverse iterator to the end.
*
* The returned iterator points to the element following the last instance
* of the reversed internal array. Attempting to dereference the returned
* iterator results in undefined behavior.
*
* @return An iterator to the element following the last instance of the
* reversed internal array.
*/
[[nodiscard]] const_reverse_iterator crend() const ENTT_NOEXCEPT {
return crbegin() + instances.size();
}
/*! @copydoc crend */
[[nodiscard]] const_reverse_iterator rend() const ENTT_NOEXCEPT {
return crend();
}
/*! @copydoc rend */
[[nodiscard]] reverse_iterator rend() ENTT_NOEXCEPT {
return rbegin() + instances.size();
}
/**
@@ -277,12 +319,12 @@ public:
* @param entt A valid entity identifier.
* @return The object associated with the entity.
*/
const object_type & get(const entity_type entt) const ENTT_NOEXCEPT {
[[nodiscard]] const object_type & get(const entity_type entt) const {
return instances[underlying_type::index(entt)];
}
/*! @copydoc get */
object_type & get(const entity_type entt) ENTT_NOEXCEPT {
[[nodiscard]] object_type & get(const entity_type entt) {
return const_cast<object_type &>(std::as_const(*this).get(entt));
}
@@ -291,12 +333,12 @@ public:
* @param entt A valid entity identifier.
* @return The object associated with the entity, if any.
*/
const object_type * try_get(const entity_type entt) const ENTT_NOEXCEPT {
return underlying_type::has(entt) ? (instances.data() + underlying_type::index(entt)) : nullptr;
[[nodiscard]] const object_type * try_get(const entity_type entt) const {
return underlying_type::contains(entt) ? (instances.data() + underlying_type::index(entt)) : nullptr;
}
/*! @copydoc try_get */
object_type * try_get(const entity_type entt) ENTT_NOEXCEPT {
[[nodiscard]] object_type * try_get(const entity_type entt) {
return const_cast<object_type *>(std::as_const(*this).try_get(entt));
}
@@ -316,26 +358,22 @@ public:
* @tparam Args Types of arguments to use to construct the object.
* @param entt A valid entity identifier.
* @param args Parameters to use to construct an object for the entity.
* @return The object associated with the entity.
*/
template<typename... Args>
object_type & construct(const entity_type entt, Args &&... args) {
void emplace(const entity_type entt, Args &&... args) {
if constexpr(std::is_aggregate_v<object_type>) {
instances.emplace_back(Type{std::forward<Args>(args)...});
instances.push_back(Type{std::forward<Args>(args)...});
} else {
instances.emplace_back(std::forward<Args>(args)...);
}
// entity goes after component in case constructor throws
underlying_type::construct(entt);
return instances.back();
underlying_type::emplace(entt);
}
/**
* @brief Assigns one or more entities to a storage and default constructs
* their objects.
*
* The object type must be at least move and default insertable.
* @brief Assigns one or more entities to a storage and constructs their
* objects from a given instance.
*
* @warning
* Attempting to assign an entity that already belongs to the storage
@@ -343,41 +381,36 @@ public:
* An assertion will abort the execution at runtime in debug mode if the
* storage already contains the given entity.
*
* @tparam It Type of forward iterator.
* @tparam It Type of input iterator.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @return An iterator to the list of instances just created and sorted the
* same of the entities.
* @param value An instance of the object to construct.
*/
template<typename It>
iterator_type batch(It first, It last) {
instances.resize(instances.size() + std::distance(first, last));
// entity goes after component in case constructor throws
underlying_type::batch(first, last);
return begin();
void insert(It first, It last, const object_type &value = {}) {
instances.insert(instances.end(), std::distance(first, last), value);
// entities go after components in case constructors throw
underlying_type::insert(first, last);
}
/**
* @brief Assigns one or more entities to a storage and copy constructs
* their objects.
* @brief Assigns one or more entities to a storage and constructs their
* objects from a given range.
*
* The object type must be at least move and copy insertable.
* @sa construct
*
* @sa batch
*
* @tparam It Type of forward iterator.
* @tparam EIt Type of input iterator.
* @tparam CIt Type of input iterator.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @param value The value to initialize the new objects with.
* @return An iterator to the list of instances just created and sorted the
* same of the entities.
* @param from An iterator to the first element of the range of objects.
* @param to An iterator past the last element of the range of objects.
*/
template<typename It>
iterator_type batch(It first, It last, const object_type &value) {
instances.resize(instances.size() + std::distance(first, last), value);
// entity goes after component in case constructor throws
underlying_type::batch(first, last);
return begin();
template<typename EIt, typename CIt>
void insert(EIt first, EIt last, CIt from, CIt to) {
instances.insert(instances.end(), from, to);
// entities go after components in case constructors throw
underlying_type::insert(first, last);
}
/**
@@ -391,11 +424,11 @@ public:
*
* @param entt A valid entity identifier.
*/
void destroy(const entity_type entt) {
void erase(const entity_type entt) {
auto other = std::move(instances.back());
instances[underlying_type::index(entt)] = std::move(other);
instances.pop_back();
underlying_type::destroy(entt);
underlying_type::erase(entt);
}
/**
@@ -407,13 +440,11 @@ public:
* An assertion will abort the execution at runtime in debug mode if the
* sparse set doesn't contain the given entities.
*
* @param lhs A valid position within the sparse set.
* @param rhs A valid position within the sparse set.
* @param lhs A valid entity identifier.
* @param rhs A valid entity identifier.
*/
void swap(const size_type lhs, const size_type rhs) ENTT_NOEXCEPT override {
ENTT_ASSERT(lhs < instances.size());
ENTT_ASSERT(rhs < instances.size());
std::swap(instances[lhs], instances[rhs]);
void swap(const entity_type lhs, const entity_type rhs) override {
std::swap(instances[underlying_type::index(lhs)], instances[underlying_type::index(rhs)]);
underlying_type::swap(lhs, rhs);
}
@@ -443,14 +474,10 @@ public:
* * An iterator past the last element of the range to sort.
* * A comparison function to use to compare the elements.
*
* The comparison function object received by the sort function object
* hasn't necessarily the type of the one passed along with the other
* parameters to this member function.
*
* @note
* Attempting to iterate elements using a raw pointer returned by a call to
* either `data` or `raw` gives no guarantees on the order, even though
* `sort` has been invoked.
* @warning
* Empty types are never instantiated. Therefore, only comparison function
* objects that require to return entities rather than components are
* accepted.
*
* @tparam Compare Type of comparison function object.
* @tparam Sort Type of sort function object.
@@ -462,26 +489,29 @@ public:
* @param args Arguments to forward to the sort function object, if any.
*/
template<typename Compare, typename Sort = std_sort, typename... Args>
void sort(iterator_type first, iterator_type last, Compare compare, Sort algo = Sort{}, Args &&... args) {
ENTT_ASSERT(!(first > last));
void sort(iterator first, iterator last, Compare compare, Sort algo = Sort{}, Args &&... args) {
ENTT_ASSERT(!(last < first));
ENTT_ASSERT(!(last > end()));
const auto from = underlying_type::begin() + std::distance(begin(), first);
const auto to = from + std::distance(first, last);
if constexpr(std::is_invocable_v<Compare, const object_type &, const object_type &>) {
static_assert(!std::is_empty_v<object_type>);
const auto apply = [this](const auto lhs, const auto rhs) {
std::swap(instances[underlying_type::index(lhs)], instances[underlying_type::index(rhs)]);
};
underlying_type::sort(from, to, [this, compare = std::move(compare)](const auto lhs, const auto rhs) {
if constexpr(std::is_invocable_v<Compare, const object_type &, const object_type &>) {
underlying_type::arrange(from, to, std::move(apply), [this, compare = std::move(compare)](const auto lhs, const auto rhs) {
return compare(std::as_const(instances[underlying_type::index(lhs)]), std::as_const(instances[underlying_type::index(rhs)]));
}, std::move(algo), std::forward<Args>(args)...);
} else {
underlying_type::sort(from, to, std::move(compare), std::move(algo), std::forward<Args>(args)...);
underlying_type::arrange(from, to, std::move(apply), std::move(compare), std::move(algo), std::forward<Args>(args)...);
}
}
/*! @brief Resets a storage. */
void reset() {
underlying_type::reset();
/*! @brief Clears a storage. */
void clear() {
underlying_type::clear();
instances.clear();
}
@@ -490,215 +520,59 @@ private:
};
/*! @copydoc basic_storage */
/*! @copydoc storage */
template<typename Entity, typename Type>
class basic_storage<Entity, Type, std::enable_if_t<std::is_empty_v<Type>>>: public sparse_set<Entity> {
using traits_type = entt_traits<std::underlying_type_t<Entity>>;
class storage<Entity, Type, std::enable_if_t<is_eto_eligible_v<Type>>>: public sparse_set<Entity> {
using underlying_type = sparse_set<Entity>;
class iterator {
friend class basic_storage<Entity, Type>;
using index_type = typename traits_type::difference_type;
iterator(const index_type idx) ENTT_NOEXCEPT
: index{idx}
{}
public:
using difference_type = index_type;
using value_type = Type;
using pointer = const value_type *;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
iterator() ENTT_NOEXCEPT = default;
iterator & operator++() ENTT_NOEXCEPT {
return --index, *this;
}
iterator operator++(int) ENTT_NOEXCEPT {
iterator orig = *this;
return ++(*this), orig;
}
iterator & operator--() ENTT_NOEXCEPT {
return ++index, *this;
}
iterator operator--(int) ENTT_NOEXCEPT {
iterator orig = *this;
return --(*this), orig;
}
iterator & operator+=(const difference_type value) ENTT_NOEXCEPT {
index -= value;
return *this;
}
iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
return iterator{index-value};
}
iterator & operator-=(const difference_type value) ENTT_NOEXCEPT {
return (*this += -value);
}
iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
return (*this + -value);
}
difference_type operator-(const iterator &other) const ENTT_NOEXCEPT {
return other.index - index;
}
reference operator[](const difference_type) const ENTT_NOEXCEPT {
return {};
}
bool operator==(const iterator &other) const ENTT_NOEXCEPT {
return other.index == index;
}
bool operator!=(const iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
bool operator<(const iterator &other) const ENTT_NOEXCEPT {
return index > other.index;
}
bool operator>(const iterator &other) const ENTT_NOEXCEPT {
return index < other.index;
}
bool operator<=(const iterator &other) const ENTT_NOEXCEPT {
return !(*this > other);
}
bool operator>=(const iterator &other) const ENTT_NOEXCEPT {
return !(*this < other);
}
pointer operator->() const ENTT_NOEXCEPT {
return nullptr;
}
reference operator*() const ENTT_NOEXCEPT {
return {};
}
private:
index_type index;
};
public:
/*! @brief Type of the objects associated with the entities. */
using object_type = Type;
/*! @brief Underlying entity identifier. */
using entity_type = typename underlying_type::entity_type;
using entity_type = Entity;
/*! @brief Unsigned integer type. */
using size_type = typename underlying_type::size_type;
/*! @brief Random access iterator type. */
using iterator_type = iterator;
using size_type = std::size_t;
/**
* @brief Returns an iterator to the beginning.
*
* The returned iterator points to the first instance of the given type. If
* the storage is empty, the returned iterator will be equal to `end()`.
*
* @note
* Input iterators stay true to the order imposed by a call to either `sort`
* or `respect`.
*
* @return An iterator to the first instance of the given type.
*/
iterator_type cbegin() const ENTT_NOEXCEPT {
const typename traits_type::difference_type pos = underlying_type::size();
return iterator_type{pos};
}
/*! @copydoc cbegin */
iterator_type begin() const ENTT_NOEXCEPT {
return cbegin();
}
/**
* @brief Returns an iterator to the end.
*
* The returned iterator points to the element following the last instance
* of the given type. Attempting to dereference the returned iterator
* results in undefined behavior.
*
* @note
* Input iterators stay true to the order imposed by a call to either `sort`
* or `respect`.
*
* @return An iterator to the element following the last instance of the
* given type.
*/
iterator_type cend() const ENTT_NOEXCEPT {
return iterator_type{};
}
/*! @copydoc cend */
iterator_type end() const ENTT_NOEXCEPT {
return cend();
}
/**
* @brief Returns the object associated with an entity.
*
* @note
* Empty types aren't explicitly instantiated. Therefore, this function
* always returns a temporary object.
* @brief Assigns an entity to a storage and constructs its object.
*
* @warning
* Attempting to use an entity that doesn't belong to the storage results in
* undefined behavior.<br/>
* Attempting to use an entity that already belongs to the storage results
* in undefined behavior.<br/>
* An assertion will abort the execution at runtime in debug mode if the
* storage doesn't contain the given entity.
* storage already contains the given entity.
*
* @tparam Args Types of arguments to use to construct the object.
* @param entt A valid entity identifier.
* @return The object associated with the entity.
* @param args Parameters to use to construct an object for the entity.
*/
object_type get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT {
ENTT_ASSERT(underlying_type::has(entt));
return {};
template<typename... Args>
void emplace(const entity_type entt, Args &&... args) {
[[maybe_unused]] object_type instance{std::forward<Args>(args)...};
underlying_type::emplace(entt);
}
/**
* @brief Assigns one or more entities to a storage.
*
* The object type must be at least default constructible.
*
* @warning
* Attempting to assign an entity that already belongs to the storage
* results in undefined behavior.<br/>
* An assertion will abort the execution at runtime in debug mode if the
* storage already contains the given entity.
*
* @tparam It Type of forward iterator.
* @tparam It Type of input iterator.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @return An iterator to the list of instances just created and sorted the
* same of the entities.
*/
template<typename It>
iterator_type batch(It first, It last, const object_type & = {}) {
underlying_type::batch(first, last);
return begin();
void insert(It first, It last, const object_type & = {}) {
underlying_type::insert(first, last);
}
};
/*! @copydoc basic_storage */
template<typename Entity, typename Type>
struct storage: basic_storage<Entity, Type> {};
}
#endif // ENTT_ENTITY_STORAGE_HPP
#endif

View File

@@ -21,7 +21,7 @@ struct exclude_t: type_list<Type...> {};
* @tparam Type List of types.
*/
template<typename... Type>
constexpr exclude_t<Type...> exclude{};
inline constexpr exclude_t<Type...> exclude{};
/**
@@ -37,10 +37,10 @@ struct get_t: type_list<Type...>{};
* @tparam Type List of types.
*/
template<typename... Type>
constexpr get_t<Type...> get{};
inline constexpr get_t<Type...> get{};
}
#endif // ENTT_ENTITY_UTILITY_HPP
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,15 +1,19 @@
#include "core/algorithm.hpp"
#include "core/attribute.h"
#include "core/family.hpp"
#include "core/hashed_string.hpp"
#include "core/ident.hpp"
#include "core/monostate.hpp"
#include "core/type_info.hpp"
#include "core/type_traits.hpp"
#include "core/utility.hpp"
#include "entity/actor.hpp"
#include "entity/entity.hpp"
#include "entity/group.hpp"
#include "entity/handle.hpp"
#include "entity/helper.hpp"
#include "entity/observer.hpp"
#include "entity/pool.hpp"
#include "entity/registry.hpp"
#include "entity/runtime_view.hpp"
#include "entity/snapshot.hpp"
@@ -18,9 +22,17 @@
#include "entity/utility.hpp"
#include "entity/view.hpp"
#include "locator/locator.hpp"
#include "meta/container.hpp"
#include "meta/ctx.hpp"
#include "meta/factory.hpp"
#include "meta/internal.hpp"
#include "meta/meta.hpp"
#include "meta/pointer.hpp"
#include "meta/policy.hpp"
#include "meta/range.hpp"
#include "meta/resolve.hpp"
#include "meta/type_traits.hpp"
#include "platform/android-ndk-r17.hpp"
#include "process/process.hpp"
#include "process/scheduler.hpp"
#include "resource/cache.hpp"

View File

@@ -1,3 +1,4 @@
#include "core/fwd.hpp"
#include "entity/fwd.hpp"
#include "resource/fwd.hpp"
#include "signal/fwd.hpp"

View File

@@ -35,7 +35,7 @@ struct service_locator {
* @brief Tests if a valid service implementation is set.
* @return True if the service is set, false otherwise.
*/
static bool empty() ENTT_NOEXCEPT {
[[nodiscard]] static bool empty() ENTT_NOEXCEPT {
return !static_cast<bool>(service);
}
@@ -49,7 +49,7 @@ struct service_locator {
*
* @return A reference to the service implementation currently set, if any.
*/
static std::weak_ptr<Service> get() ENTT_NOEXCEPT {
[[nodiscard]] static std::weak_ptr<Service> get() ENTT_NOEXCEPT {
return service;
}
@@ -67,7 +67,7 @@ struct service_locator {
*
* @return A reference to the service implementation currently set, if any.
*/
static Service & ref() ENTT_NOEXCEPT {
[[nodiscard]] static Service & ref() ENTT_NOEXCEPT {
return *service;
}
@@ -108,4 +108,4 @@ private:
}
#endif // ENTT_LOCATOR_LOCATOR_HPP
#endif

387
src/entt/meta/container.hpp Normal file
View File

@@ -0,0 +1,387 @@
#ifndef ENTT_META_CONTAINER_HPP
#define ENTT_META_CONTAINER_HPP
#include <array>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include "../config/config.h"
#include "type_traits.hpp"
namespace entt {
namespace internal {
template<typename Container, template<typename> class... Trait>
struct container_traits: public Trait<Container>... {};
/**
* @brief Basic STL-compatible container traits
* @tparam Container The type of the container.
*/
template<typename Container>
struct basic_container {
/*! @brief Iterator type of the container. */
using iterator = typename Container::iterator;
/*! @brief Unsigned integer type. */
using size_type = typename Container::size_type;
/*! @brief Value type of the container. */
using value_type = typename Container::value_type;
/**
* @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 size_type size(const Container &cont) ENTT_NOEXCEPT {
return cont.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 iterator begin(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 iterator end(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 Key type of the sequence container. */
using key_type = typename Container::key_type;
/**
* @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 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(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(Container &cont, 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::value_type & get(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(Container &cont, 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(Container &cont, const typename Container::key_type &key, 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(Container &cont, 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(Container &cont, typename Container::iterator it, 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(Container &cont, typename Container::iterator it) {
return { cont.erase(it), true };
}
};
/**
* @brief STL-compatible fixed sequence container traits
* @tparam Container The type of the container.
*/
template<typename Container>
struct fixed_sequence_container {
/**
* @brief Does nothing.
* @return False to indicate failure in all cases.
*/
[[nodiscard]] static bool resize(const Container &, typename Container::size_type) {
return false;
}
/**
* @brief Does nothing.
* @return False to indicate failure in all cases.
*/
[[nodiscard]] static bool clear(const Container &) {
return false;
}
/**
* @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::iterator, const typename Container::value_type &) {
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> erase(const Container &, typename Container::iterator) {
return { {}, false };
}
};
}
/**
* @brief Meta sequence container traits for `std::vector`s of any type.
* @tparam Type The type of elements.
* @tparam Args Other arguments.
*/
template<typename Type, typename... Args>
struct meta_sequence_container_traits<std::vector<Type, Args...>>
: internal::container_traits<
std::vector<Type, Args...>,
internal::basic_container,
internal::basic_dynamic_container,
internal::basic_sequence_container,
internal::dynamic_sequence_container
>
{};
/**
* @brief Meta sequence container traits for `std::array`s of any type.
* @tparam Type The type of elements.
* @tparam N The number of elements.
*/
template<typename Type, auto N>
struct meta_sequence_container_traits<std::array<Type, N>>
: internal::container_traits<
std::array<Type, N>,
internal::basic_container,
internal::basic_sequence_container,
internal::fixed_sequence_container
>
{};
/**
* @brief Meta associative container traits for `std::map`s of any type.
* @tparam Key The key type of elements.
* @tparam Value The value type of elements.
* @tparam Args Other arguments.
*/
template<typename Key, typename Value, typename... Args>
struct meta_associative_container_traits<std::map<Key, Value, Args...>>
: internal::container_traits<
std::map<Key, Value, Args...>,
internal::basic_container,
internal::basic_associative_container,
internal::basic_dynamic_container,
internal::basic_dynamic_associative_container,
internal::dynamic_associative_key_value_container
>
{
/*! @brief Mapped type of the sequence container. */
using mapped_type = typename std::map<Key, Value, Args...>::mapped_type;
};
/**
* @brief Meta associative container traits for `std::unordered_map`s of any
* type.
* @tparam Key The key type of elements.
* @tparam Value The value type of elements.
* @tparam Args Other arguments.
*/
template<typename Key, typename Value, typename... Args>
struct meta_associative_container_traits<std::unordered_map<Key, Value, Args...>>
: internal::container_traits<
std::unordered_map<Key, Value, Args...>,
internal::basic_container,
internal::basic_associative_container,
internal::basic_dynamic_container,
internal::basic_dynamic_associative_container,
internal::dynamic_associative_key_value_container
>
{
/*! @brief Mapped type of the sequence container. */
using mapped_type = typename std::unordered_map<Key, Value, Args...>::mapped_type;
};
/**
* @brief Meta associative container traits for `std::set`s of any type.
* @tparam Key The type of elements.
* @tparam Args Other arguments.
*/
template<typename Key, typename... Args>
struct meta_associative_container_traits<std::set<Key, Args...>>
: internal::container_traits<
std::set<Key, Args...>,
internal::basic_container,
internal::basic_associative_container,
internal::basic_dynamic_container,
internal::basic_dynamic_associative_container,
internal::dynamic_associative_key_only_container
>
{};
/**
* @brief Meta associative container traits for `std::unordered_set`s of any
* type.
* @tparam Key The type of elements.
* @tparam Args Other arguments.
*/
template<typename Key, typename... Args>
struct meta_associative_container_traits<std::unordered_set<Key, Args...>>
: internal::container_traits<
std::unordered_set<Key, Args...>,
internal::basic_container,
internal::basic_associative_container,
internal::basic_dynamic_container,
internal::basic_dynamic_associative_container,
internal::dynamic_associative_key_only_container
>
{};
}
#endif

68
src/entt/meta/ctx.hpp Normal file
View File

@@ -0,0 +1,68 @@
#ifndef ENTT_META_CTX_HPP
#define ENTT_META_CTX_HPP
#include "../core/attribute.h"
#include "../config/config.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 {
static meta_type_node *chain = nullptr;
return chain;
}
[[nodiscard]] static meta_type_node ** & global() ENTT_NOEXCEPT {
static meta_type_node **chain = &local();
return chain;
}
};
}
/**
* Internal details not to be documented.
* @endcond
*/
/*! @brief Opaque container for a meta context. */
struct meta_ctx {
/**
* @brief Binds the meta system to a given context.
* @param other A valid context to which to bind.
*/
static void bind(meta_ctx other) ENTT_NOEXCEPT {
internal::meta_context::global() = other.ctx;
}
private:
internal::meta_type_node **ctx{&internal::meta_context::local()};
};
}
#endif

File diff suppressed because it is too large Load Diff

455
src/entt/meta/internal.hpp Normal file
View File

@@ -0,0 +1,455 @@
#ifndef ENTT_META_INTERNAL_HPP
#define ENTT_META_INTERNAL_HPP
#include <cstddef>
#include <functional>
#include <iterator>
#include <type_traits>
#include <utility>
#include "../core/attribute.h"
#include "../config/config.h"
#include "../core/fwd.hpp"
#include "../core/type_info.hpp"
#include "../core/type_traits.hpp"
#include "type_traits.hpp"
namespace entt {
class meta_any;
struct meta_handle;
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
class meta_storage {
using storage_type = std::aligned_storage_t<sizeof(void *), alignof(void *)>;
using copy_fn_type = void(meta_storage &, const meta_storage &);
using steal_fn_type = void(meta_storage &, meta_storage &);
using destroy_fn_type = void(meta_storage &);
template<typename Type, typename = std::void_t<>>
struct type_traits {
template<typename... Args>
static void instance(meta_storage &buffer, Args &&... args) {
buffer.instance = new Type{std::forward<Args>(args)...};
new (&buffer.storage) Type *{static_cast<Type *>(buffer.instance)};
}
static void destroy(meta_storage &buffer) {
delete static_cast<Type *>(buffer.instance);
}
static void copy(meta_storage &to, const meta_storage &from) {
to.instance = new Type{*static_cast<const Type *>(from.instance)};
new (&to.storage) Type *{static_cast<Type *>(to.instance)};
}
static void steal(meta_storage &to, meta_storage &from) {
new (&to.storage) Type *{static_cast<Type *>(from.instance)};
to.instance = from.instance;
}
};
template<typename Type>
struct type_traits<Type, std::enable_if_t<sizeof(Type) <= sizeof(void *) && std::is_nothrow_move_constructible_v<Type>>> {
template<typename... Args>
static void instance(meta_storage &buffer, Args &&... args) {
buffer.instance = new (&buffer.storage) Type{std::forward<Args>(args)...};
}
static void destroy(meta_storage &buffer) {
static_cast<Type *>(buffer.instance)->~Type();
}
static void copy(meta_storage &to, const meta_storage &from) {
to.instance = new (&to.storage) Type{*static_cast<const Type *>(from.instance)};
}
static void steal(meta_storage &to, meta_storage &from) {
to.instance = new (&to.storage) Type{std::move(*static_cast<Type *>(from.instance))};
destroy(from);
}
};
public:
/*! @brief Default constructor. */
meta_storage() ENTT_NOEXCEPT
: storage{},
instance{},
destroy_fn{},
copy_fn{},
steal_fn{}
{}
template<typename Type, typename... Args>
explicit meta_storage(std::in_place_type_t<Type>, [[maybe_unused]] Args &&... args)
: meta_storage{}
{
if constexpr(!std::is_void_v<Type>) {
type_traits<Type>::instance(*this, std::forward<Args>(args)...);
destroy_fn = &type_traits<Type>::destroy;
copy_fn = &type_traits<Type>::copy;
steal_fn = &type_traits<Type>::steal;
}
}
template<typename Type>
meta_storage(std::reference_wrapper<Type> value)
: meta_storage{}
{
instance = &value.get();
}
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, meta_storage>>>
meta_storage(Type &&value)
: meta_storage{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)}
{}
meta_storage(const meta_storage &other)
: meta_storage{}
{
(other.copy_fn ? other.copy_fn : [](auto &to, const auto &from) { to.instance = from.instance; })(*this, other);
destroy_fn = other.destroy_fn;
copy_fn = other.copy_fn;
steal_fn = other.steal_fn;
}
meta_storage(meta_storage &&other)
: meta_storage{}
{
swap(*this, other);
}
~meta_storage() {
if(destroy_fn) {
destroy_fn(*this);
}
}
meta_storage & operator=(meta_storage other) {
swap(other, *this);
return *this;
}
[[nodiscard]] const void * data() const ENTT_NOEXCEPT {
return instance;
}
[[nodiscard]] void * data() ENTT_NOEXCEPT {
return const_cast<void *>(std::as_const(*this).data());
}
template<typename Type, typename... Args>
void emplace(Args &&... args) {
*this = meta_storage{std::in_place_type<Type>, std::forward<Args>(args)...};
}
[[nodiscard]] meta_storage ref() const ENTT_NOEXCEPT {
meta_storage other{};
other.instance = instance;
return other;
}
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
return !(instance == nullptr);
}
friend void swap(meta_storage &lhs, meta_storage &rhs) {
using std::swap;
if(lhs.steal_fn && rhs.steal_fn) {
meta_storage buffer{};
lhs.steal_fn(buffer, lhs);
rhs.steal_fn(lhs, rhs);
lhs.steal_fn(rhs, buffer);
} else if(lhs.steal_fn) {
lhs.steal_fn(rhs, lhs);
} else if(rhs.steal_fn) {
rhs.steal_fn(lhs, rhs);
} else {
swap(lhs.instance, rhs.instance);
}
swap(lhs.destroy_fn, rhs.destroy_fn);
swap(lhs.copy_fn, rhs.copy_fn);
swap(lhs.steal_fn, rhs.steal_fn);
}
private:
storage_type storage;
void *instance;
destroy_fn_type *destroy_fn;
copy_fn_type *copy_fn;
steal_fn_type *steal_fn;
};
struct meta_type_node;
struct meta_prop_node {
meta_prop_node * next;
meta_any(* const key)();
meta_any(* const 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;
};
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 *);
};
struct meta_ctor_node {
using size_type = std::size_t;
meta_type_node * const parent;
meta_ctor_node * next;
meta_prop_node * prop;
const size_type size;
meta_type_node *(* const arg)(size_type) ENTT_NOEXCEPT;
meta_any(* const invoke)(meta_any * const);
};
struct meta_data_node {
id_type id;
meta_type_node * const parent;
meta_data_node * next;
meta_prop_node * prop;
const bool is_static;
meta_type_node *(* const type)() ENTT_NOEXCEPT;
bool(* const set)(meta_handle, meta_any);
meta_any(* const get)(meta_handle);
};
struct meta_func_node {
using size_type = std::size_t;
id_type id;
meta_type_node * const parent;
meta_func_node * next;
meta_prop_node * prop;
const size_type size;
const bool is_const;
const bool is_static;
meta_type_node *(* const ret)() ENTT_NOEXCEPT;
meta_type_node *(* const arg)(size_type) ENTT_NOEXCEPT;
meta_any(* const invoke)(meta_handle, meta_any *);
};
struct meta_type_node {
using size_type = std::size_t;
const id_type type_id;
id_type id;
meta_type_node * next;
meta_prop_node * prop;
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 size_type rank;
size_type(* const extent)(size_type);
bool(* const compare)(const void *, const void *);
meta_type_node *(* const remove_pointer)() ENTT_NOEXCEPT;
meta_type_node *(* const remove_extent)() ENTT_NOEXCEPT;
meta_base_node *base{nullptr};
meta_conv_node *conv{nullptr};
meta_ctor_node *ctor{nullptr};
meta_data_node *data{nullptr};
meta_func_node *func{nullptr};
void(* dtor)(void *){nullptr};
};
template<typename Node>
class meta_range {
struct range_iterator {
using difference_type = std::ptrdiff_t;
using value_type = Node;
using pointer = value_type *;
using reference = value_type &;
using iterator_category = std::forward_iterator_tag;
range_iterator() ENTT_NOEXCEPT = default;
range_iterator(Node *head) ENTT_NOEXCEPT
: node{head}
{}
range_iterator & operator++() ENTT_NOEXCEPT {
return node = node->next, *this;
}
range_iterator operator++(int) ENTT_NOEXCEPT {
range_iterator orig = *this;
return ++(*this), orig;
}
[[nodiscard]] bool operator==(const range_iterator &other) const ENTT_NOEXCEPT {
return other.node == node;
}
[[nodiscard]] bool operator!=(const range_iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
return node;
}
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
return *operator->();
}
private:
Node *node{nullptr};
};
public:
using iterator = range_iterator;
meta_range() ENTT_NOEXCEPT = default;
meta_range(Node *head)
: node{head}
{}
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
return iterator{node};
}
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
return iterator{};
}
private:
Node *node{nullptr};
};
template<auto Member, typename Op>
auto find_if(const Op &op, const meta_type_node *node)
-> std::decay_t<decltype(node->*Member)> {
std::decay_t<decltype(node->*Member)> ret = nullptr;
for(auto &&curr: meta_range{node->*Member}) {
if(op(&curr)) {
ret = &curr;
break;
}
}
if(!ret) {
for(auto &&curr: meta_range{node->base}) {
if(ret = find_if<Member>(op, curr.type()); ret) {
break;
}
}
}
return ret;
}
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");
[[nodiscard]] static bool compare(const void *lhs, const void *rhs) {
if constexpr(!std::is_function_v<Type> && is_equality_comparable_v<Type>) {
return *static_cast<const Type *>(lhs) == *static_cast<const Type *>(rhs);
} else {
return lhs == rhs;
}
}
template<std::size_t... Index>
[[nodiscard]] static auto extent(meta_type_node::size_type dim, std::index_sequence<Index...>) {
meta_type_node::size_type ext{};
((ext = (dim == Index ? std::extent_v<Type, Index> : ext)), ...);
return ext;
}
public:
[[nodiscard]] static meta_type_node * resolve() ENTT_NOEXCEPT {
static meta_type_node node{
type_info<Type>::id(),
{},
nullptr,
nullptr,
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>,
has_meta_sequence_container_traits_v<Type>,
has_meta_associative_container_traits_v<Type>,
std::rank_v<Type>,
[](meta_type_node::size_type dim) {
return extent(dim, std::make_index_sequence<std::rank_v<Type>>{});
},
&compare, // workaround for an issue with VS2017
&meta_node<std::remove_const_t<std::remove_pointer_t<Type>>>::resolve,
&meta_node<std::remove_const_t<std::remove_extent_t<Type>>>::resolve
};
return &node;
}
};
template<typename... Type>
struct meta_info: meta_node<std::remove_cv_t<std::remove_reference_t<Type>>...> {};
}
/**
* Internal details not to be documented.
* @endcond
*/
}
#endif

File diff suppressed because it is too large Load Diff

48
src/entt/meta/pointer.hpp Normal file
View File

@@ -0,0 +1,48 @@
#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
{};
/**
* @brief Makes `std::shared_ptr`s of any type pointer-like types for the meta
* system.
* @tparam Type Element type.
*/
template<typename Type>
struct is_meta_pointer_like<std::shared_ptr<Type>>
: std::true_type
{};
/**
* @brief Makes `std::unique_ptr`s of any type pointer-like types for the meta
* system.
* @tparam Type Element type.
* @tparam Args Other arguments.
*/
template<typename Type, typename... Args>
struct is_meta_pointer_like<std::unique_ptr<Type, Args...>>
: std::true_type
{};
}
#endif

View File

@@ -5,12 +5,12 @@
namespace entt {
/*! @brief Empty class type used to request the _as alias_ policy. */
struct as_alias_t {};
/*! @brief Empty class type used to request the _as ref_ policy. */
struct as_ref_t {};
/*! @brief Disambiguation tag. */
constexpr as_alias_t as_alias;
inline constexpr as_ref_t as_ref;
/*! @brief Empty class type used to request the _as-is_ policy. */
@@ -24,4 +24,4 @@ struct as_void_t {};
}
#endif // ENTT_META_POLICY_HPP
#endif

98
src/entt/meta/range.hpp Normal file
View File

@@ -0,0 +1,98 @@
#ifndef ENTT_META_RANGE_HPP
#define ENTT_META_RANGE_HPP
#include "internal.hpp"
namespace entt {
/**
* @brief Iterable range to use to iterate all types of meta objects.
* @tparam Type Type of meta objects iterated.
*/
template<typename 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 = typename Type::node_type;
range_iterator() ENTT_NOEXCEPT = default;
range_iterator(node_type *head) ENTT_NOEXCEPT
: it{head}
{}
range_iterator & operator++() ENTT_NOEXCEPT {
return ++it, *this;
}
range_iterator operator++(int) ENTT_NOEXCEPT {
range_iterator orig = *this;
return ++(*this), orig;
}
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
return it.operator->();
}
[[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:
typename internal::meta_range<node_type>::iterator it{};
};
public:
/*! @brief Node type. */
using node_type = typename Type::node_type;
/*! @brief Input iterator type. */
using iterator = range_iterator;
/*! @brief Default constructor. */
meta_range() ENTT_NOEXCEPT = default;
/**
* @brief Constructs a meta range from a given node.
* @param head The underlying node with which to construct the range.
*/
meta_range(node_type *head)
: node{head}
{}
/**
* @brief Returns an iterator to the beginning.
* @return An iterator to the first meta object of the range.
*/
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
return iterator{node};
}
/**
* @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 {
return iterator{};
}
private:
node_type *node{nullptr};
};
}
#endif

59
src/entt/meta/resolve.hpp Normal file
View File

@@ -0,0 +1,59 @@
#ifndef ENTT_META_RESOLVE_HPP
#define ENTT_META_RESOLVE_HPP
#include <algorithm>
#include "ctx.hpp"
#include "meta.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.
* @return The meta type associated with the given type, if any.
*/
template<typename Type>
[[nodiscard]] meta_type resolve() ENTT_NOEXCEPT {
return internal::meta_info<Type>::resolve();
}
/**
* @brief Returns a range to use to visit all meta types.
* @return An iterable range to use to visit all meta types.
*/
[[nodiscard]] inline meta_range<meta_type> resolve() {
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_id(const id_type id) ENTT_NOEXCEPT {
internal::meta_range range{*internal::meta_context::global()};
return std::find_if(range.begin(), range.end(), [id](const auto &curr) { return curr.id == id; }).operator->();
}
/**
* @brief Returns the meta type associated with a given type id, if any.
* @param id Unique identifier.
* @return The meta type associated with the given type id, if any.
*/
[[nodiscard]] inline meta_type resolve_type(const id_type id) ENTT_NOEXCEPT {
internal::meta_range range{*internal::meta_context::global()};
return std::find_if(range.begin(), range.end(), [id](const auto &curr) { return curr.type_id == id; }).operator->();
}
}
#endif

View File

@@ -0,0 +1,119 @@
#ifndef ENTT_META_TYPE_TRAITS_HPP
#define ENTT_META_TYPE_TRAITS_HPP
#include <type_traits>
namespace entt {
/**
* @brief Traits class template to be specialized to enable support for meta
* sequence containers.
*/
template<typename>
struct meta_sequence_container_traits;
/**
* @brief Traits class template to be specialized to enable support for meta
* associative containers.
*/
template<typename>
struct meta_associative_container_traits;
/**
* @brief Provides the member constant `value` to true if support for meta
* sequence containers is enabled for the given type, false otherwise.
* @tparam Type Potentially sequence container type.
*/
template<typename Type, typename = void>
struct has_meta_sequence_container_traits: std::false_type {};
/*! @copydoc has_meta_sequence_container_traits */
template<typename Type>
struct has_meta_sequence_container_traits<Type, std::void_t<typename meta_sequence_container_traits<Type>::value_type>>
: std::true_type
{};
/**
* @brief Helper variable template.
* @tparam Type Potentially sequence container type.
*/
template<typename Type>
inline constexpr auto has_meta_sequence_container_traits_v = has_meta_sequence_container_traits<Type>::value;
/**
* @brief Provides the member constant `value` to true if support for meta
* associative containers is enabled for the given type, false otherwise.
* @tparam Type Potentially associative container type.
*/
template<typename, typename = void>
struct has_meta_associative_container_traits: std::false_type {};
/*! @copydoc has_meta_associative_container_traits */
template<typename Type>
struct has_meta_associative_container_traits<Type, std::void_t<typename meta_associative_container_traits<Type>::key_type>>
: std::true_type
{};
/**
* @brief Helper variable template.
* @tparam Type Potentially associative container type.
*/
template<typename Type>
inline constexpr auto has_meta_associative_container_traits_v = has_meta_associative_container_traits<Type>::value;
/**
* @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>::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.
* @tparam Type Potentially pointer-like type.
*/
template<typename>
struct is_meta_pointer_like: std::false_type {};
/**
* @brief Helper variable template.
* @tparam Type Potentially pointer-like type.
*/
template<typename Type>
inline constexpr auto is_meta_pointer_like_v = is_meta_pointer_like<Type>::value;
}
#endif

View File

@@ -0,0 +1,86 @@
#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>
namespace std {
namespace internal {
template<typename Func, typename... Args>
constexpr auto is_invocable(int) -> decltype(std::invoke(std::declval<Func>(), std::declval<Args>()...), std::true_type{});
template<typename, typename...>
constexpr std::false_type is_invocable(...);
template<typename Ret, typename Func, typename... Args>
constexpr auto is_invocable_r(int)
-> std::enable_if_t<decltype(std::is_convertible_v<decltype(std::invoke(std::declval<Func>(), std::declval<Args>()...)), Ret>, std::true_type>;
template<typename, typename, typename...>
constexpr std::false_type is_invocable_r(...);
}
template<typename Func, typename... Args>
struct is_invocable: decltype(internal::is_invocable<Func, Args...>(0)) {};
template<typename Func, typename... Argsv>
inline constexpr bool is_invocable_v = std::is_invocable<Func, Args...>::value;
template<typename Ret, typename Func, typename... Args>
struct is_invocable_r: decltype(internal::is_invocable_r<Ret, Func, Args...>(0)) {};
template<typename Ret, typename Func, typename... Args>
inline constexpr bool is_invocable_r_v = std::is_invocable_r<Ret, Func, Args...>::value;
template<typename Func, typename...Args>
struct invoke_result {
using type = decltype(std::invoke(std::declval<Func>(), std::declval<Args>()...));
};
template<typename Func, typename... Args>
using invoke_result_t = typename std::invoke_result<Func, Args...>::type;
}
#endif
#endif
/**
* Internal details not to be documented.
* @endcond
*/
#endif

View File

@@ -5,6 +5,7 @@
#include <utility>
#include <type_traits>
#include "../config/config.h"
#include "../core/type_traits.hpp"
namespace entt {
@@ -81,41 +82,37 @@ class process {
FINISHED
};
template<state value>
using state_value_t = std::integral_constant<state, value>;
template<typename Target = Derived>
auto tick(int, state_value_t<state::UNINITIALIZED>)
-> decltype(std::declval<Target>().init()) {
auto next(integral_constant<state::UNINITIALIZED>)
-> decltype(std::declval<Target>().init(), void()) {
static_cast<Target *>(this)->init();
}
template<typename Target = Derived>
auto tick(int, state_value_t<state::RUNNING>, Delta delta, void *data)
-> decltype(std::declval<Target>().update(delta, data)) {
auto next(integral_constant<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 tick(int, state_value_t<state::SUCCEEDED>)
-> decltype(std::declval<Target>().succeeded()) {
auto next(integral_constant<state::SUCCEEDED>)
-> decltype(std::declval<Target>().succeeded(), void()) {
static_cast<Target *>(this)->succeeded();
}
template<typename Target = Derived>
auto tick(int, state_value_t<state::FAILED>)
-> decltype(std::declval<Target>().failed()) {
auto next(integral_constant<state::FAILED>)
-> decltype(std::declval<Target>().failed(), void()) {
static_cast<Target *>(this)->failed();
}
template<typename Target = Derived>
auto tick(int, state_value_t<state::ABORTED>)
-> decltype(std::declval<Target>().aborted()) {
auto next(integral_constant<state::ABORTED>)
-> decltype(std::declval<Target>().aborted(), void()) {
static_cast<Target *>(this)->aborted();
}
template<state value, typename... Args>
void tick(char, state_value_t<value>, Args &&...) const ENTT_NOEXCEPT {}
void next(...) const ENTT_NOEXCEPT {}
protected:
/**
@@ -171,8 +168,8 @@ public:
using delta_type = Delta;
/*! @brief Default destructor. */
virtual ~process() ENTT_NOEXCEPT {
static_assert(std::is_base_of_v<process, Derived>);
virtual ~process() {
static_assert(std::is_base_of_v<process, Derived>, "Incorrect use of the class template");
}
/**
@@ -183,12 +180,12 @@ public:
*
* @param immediately Requests an immediate operation.
*/
void abort(const bool immediately = false) ENTT_NOEXCEPT {
void abort(const bool immediately = false) {
if(alive()) {
current = state::ABORTED;
if(immediately) {
tick(0);
tick({});
}
}
}
@@ -197,7 +194,7 @@ public:
* @brief Returns true if a process is either running or paused.
* @return True if the process is still alive, false otherwise.
*/
bool alive() const ENTT_NOEXCEPT {
[[nodiscard]] bool alive() const ENTT_NOEXCEPT {
return current == state::RUNNING || current == state::PAUSED;
}
@@ -205,7 +202,7 @@ public:
* @brief Returns true if a process is already terminated.
* @return True if the process is terminated, false otherwise.
*/
bool dead() const ENTT_NOEXCEPT {
[[nodiscard]] bool dead() const ENTT_NOEXCEPT {
return current == state::FINISHED;
}
@@ -213,7 +210,7 @@ public:
* @brief Returns true if a process is currently paused.
* @return True if the process is paused, false otherwise.
*/
bool paused() const ENTT_NOEXCEPT {
[[nodiscard]] bool paused() const ENTT_NOEXCEPT {
return current == state::PAUSED;
}
@@ -221,7 +218,7 @@ public:
* @brief Returns true if a process terminated with errors.
* @return True if the process terminated with errors, false otherwise.
*/
bool rejected() const ENTT_NOEXCEPT {
[[nodiscard]] bool rejected() const ENTT_NOEXCEPT {
return stopped;
}
@@ -233,11 +230,11 @@ public:
void tick(const Delta delta, void *data = nullptr) {
switch (current) {
case state::UNINITIALIZED:
tick(0, state_value_t<state::UNINITIALIZED>{});
next(integral_constant<state::UNINITIALIZED>{});
current = state::RUNNING;
break;
case state::RUNNING:
tick(0, state_value_t<state::RUNNING>{}, delta, data);
next(integral_constant<state::RUNNING>{}, delta, data);
break;
default:
// suppress warnings
@@ -247,16 +244,16 @@ public:
// if it's dead, it must be notified and removed immediately
switch(current) {
case state::SUCCEEDED:
tick(0, state_value_t<state::SUCCEEDED>{});
next(integral_constant<state::SUCCEEDED>{});
current = state::FINISHED;
break;
case state::FAILED:
tick(0, state_value_t<state::FAILED>{});
next(integral_constant<state::FAILED>{});
current = state::FINISHED;
stopped = true;
break;
case state::ABORTED:
tick(0, state_value_t<state::ABORTED>{});
next(integral_constant<state::ABORTED>{});
current = state::FINISHED;
stopped = true;
break;
@@ -337,4 +334,4 @@ struct process_adaptor: process<process_adaptor<Func, Delta>, Delta>, private Fu
}
#endif // ENTT_PROCESS_PROCESS_HPP
#endif

View File

@@ -63,7 +63,7 @@ class scheduler {
template<typename Proc, typename... Args>
continuation then(Args &&... args) {
static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>);
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});
handler = handler->next.get();
@@ -80,7 +80,7 @@ class scheduler {
};
template<typename Proc>
static bool update(process_handler &handler, const Delta delta, void *data) {
[[nodiscard]] static bool update(process_handler &handler, const Delta delta, void *data) {
auto *process = static_cast<Proc *>(handler.instance.get());
process->tick(delta, data);
@@ -111,10 +111,10 @@ class scheduler {
public:
/*! @brief Unsigned integer type. */
using size_type = typename std::vector<process_handler>::size_type;
using size_type = std::size_t;
/*! @brief Default constructor. */
scheduler() ENTT_NOEXCEPT = default;
scheduler() = default;
/*! @brief Default move constructor. */
scheduler(scheduler &&) = default;
@@ -126,7 +126,7 @@ public:
* @brief Number of processes currently scheduled.
* @return Number of processes currently scheduled.
*/
size_type size() const ENTT_NOEXCEPT {
[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
return handlers.size();
}
@@ -134,7 +134,7 @@ public:
* @brief Returns true if at least a process is currently scheduled.
* @return True if there are scheduled processes, false otherwise.
*/
bool empty() const ENTT_NOEXCEPT {
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
return handlers.empty();
}
@@ -175,7 +175,7 @@ public:
*/
template<typename Proc, typename... Args>
auto attach(Args &&... args) {
static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>);
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};
// forces the process to exit the uninitialized state
@@ -281,9 +281,9 @@ public:
decltype(handlers) exec;
exec.swap(handlers);
std::for_each(exec.begin(), exec.end(), [immediately](auto &handler) {
for(auto &&handler: exec) {
handler.abort(handler, immediately);
});
}
std::move(handlers.begin(), handlers.end(), std::back_inserter(exec));
handlers.swap(exec);
@@ -297,4 +297,4 @@ private:
}
#endif // ENTT_PROCESS_SCHEDULER_HPP
#endif

View File

@@ -3,10 +3,11 @@
#include <memory>
#include <utility>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include "../config/config.h"
#include "../core/fwd.hpp"
#include "handle.hpp"
#include "loader.hpp"
#include "fwd.hpp"
@@ -26,14 +27,11 @@ namespace entt {
* @tparam Resource Type of resources managed by a cache.
*/
template<typename Resource>
class resource_cache {
using container_type = std::unordered_map<ENTT_ID_TYPE, std::shared_ptr<Resource>>;
public:
struct resource_cache {
/*! @brief Unsigned integer type. */
using size_type = typename container_type::size_type;
using size_type = std::size_t;
/*! @brief Type of resources managed by a cache. */
using resource_type = ENTT_ID_TYPE;
using resource_type = Resource;
/*! @brief Default constructor. */
resource_cache() = default;
@@ -48,7 +46,7 @@ public:
* @brief Number of resources managed by a cache.
* @return Number of resources currently stored.
*/
size_type size() const ENTT_NOEXCEPT {
[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
return resources.size();
}
@@ -56,7 +54,7 @@ public:
* @brief Returns true if a cache contains no resources, false otherwise.
* @return True if the cache contains no resources, false otherwise.
*/
bool empty() const ENTT_NOEXCEPT {
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
return resources.empty();
}
@@ -93,20 +91,20 @@ public:
* @return A handle for the given resource.
*/
template<typename Loader, typename... Args>
resource_handle<Resource> load(const resource_type id, Args &&... args) {
static_assert(std::is_base_of_v<resource_loader<Loader, Resource>, Loader>);
resource_handle<Resource> handle{};
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> resource{};
if(auto it = resources.find(id); it == resources.cend()) {
if(auto resource = Loader{}.get(std::forward<Args>(args)...); resource) {
resources[id] = resource;
handle = std::move(resource);
if(auto instance = Loader{}.get(std::forward<Args>(args)...); instance) {
resources[id] = instance;
resource = std::move(instance);
}
} else {
handle = it->second;
resource = it->second;
}
return handle;
return resource;
}
/**
@@ -133,7 +131,7 @@ public:
* @return A handle for the given resource.
*/
template<typename Loader, typename... Args>
resource_handle<Resource> reload(const resource_type id, Args &&... args) {
resource_handle<Resource> reload(const id_type id, Args &&... args) {
return (discard(id), load<Loader>(id, std::forward<Args>(args)...));
}
@@ -150,7 +148,7 @@ public:
* @return A handle for the given resource.
*/
template<typename Loader, typename... Args>
resource_handle<Resource> temp(Args &&... args) const {
[[nodiscard]] resource_handle<Resource> temp(Args &&... args) const {
return { Loader{}.get(std::forward<Args>(args)...) };
}
@@ -167,7 +165,7 @@ public:
* @param id Unique resource identifier.
* @return A handle for the given resource.
*/
resource_handle<Resource> handle(const resource_type id) const {
[[nodiscard]] resource_handle<Resource> handle(const id_type id) const {
auto it = resources.find(id);
return { it == resources.end() ? nullptr : it->second };
}
@@ -177,7 +175,7 @@ public:
* @param id Unique resource identifier.
* @return True if the cache contains the resource, false otherwise.
*/
bool contains(const resource_type id) const ENTT_NOEXCEPT {
[[nodiscard]] bool contains(const id_type id) const {
return (resources.find(id) != resources.cend());
}
@@ -189,7 +187,7 @@ public:
*
* @param id Unique resource identifier.
*/
void discard(const resource_type id) ENTT_NOEXCEPT {
void discard(const id_type id) {
if(auto it = resources.find(id); it != resources.end()) {
resources.erase(it);
}
@@ -204,9 +202,9 @@ public:
* forms:
*
* @code{.cpp}
* void(const resource_type);
* void(resource_handle<Resource>);
* void(const resource_type, resource_handle<Resource>);
* void(const entt::id_type);
* void(entt::resource_handle<Resource>);
* void(const entt::id_type, entt::resource_handle<Resource>);
* @endcode
*
* @tparam Func Type of the function object to invoke.
@@ -220,7 +218,7 @@ public:
while(begin != end) {
auto curr = begin++;
if constexpr(std::is_invocable_v<Func, resource_type>) {
if constexpr(std::is_invocable_v<Func, id_type>) {
func(curr->first);
} else if constexpr(std::is_invocable_v<Func, resource_handle<Resource>>) {
func(resource_handle{ curr->second });
@@ -231,11 +229,11 @@ public:
}
private:
container_type resources;
std::unordered_map<id_type, std::shared_ptr<Resource>> resources;
};
}
#endif // ENTT_RESOURCE_CACHE_HPP
#endif

View File

@@ -2,21 +2,17 @@
#define ENTT_RESOURCE_FWD_HPP
#include "../config/config.h"
namespace entt {
/*! @class resource_cache */
template<typename>
class resource_cache;
struct resource_cache;
/*! @class resource_handle */
template<typename>
class resource_handle;
/*! @class resource_loader */
template<typename, typename>
class resource_loader;
@@ -24,4 +20,4 @@ class resource_loader;
}
#endif // ENTT_RESOURCE_FWD_HPP
#endif

View File

@@ -26,7 +26,7 @@ namespace entt {
template<typename Resource>
class resource_handle {
/*! @brief Resource handles are friends of their caches. */
friend class resource_cache<Resource>;
friend struct resource_cache<Resource>;
resource_handle(std::shared_ptr<Resource> res) ENTT_NOEXCEPT
: resource{std::move(res)}
@@ -46,27 +46,35 @@ public:
*
* @return A reference to the managed resource.
*/
const Resource & get() const ENTT_NOEXCEPT {
[[nodiscard]] const Resource & get() const ENTT_NOEXCEPT {
ENTT_ASSERT(static_cast<bool>(resource));
return *resource;
}
/*! @copydoc get */
Resource & get() ENTT_NOEXCEPT {
[[nodiscard]] Resource & get() ENTT_NOEXCEPT {
return const_cast<Resource &>(std::as_const(*this).get());
}
/*! @copydoc get */
operator const Resource & () const ENTT_NOEXCEPT { return get(); }
[[nodiscard]] operator const Resource & () const ENTT_NOEXCEPT {
return get();
}
/*! @copydoc get */
operator Resource & () ENTT_NOEXCEPT { return get(); }
[[nodiscard]] operator Resource & () ENTT_NOEXCEPT {
return get();
}
/*! @copydoc get */
const Resource & operator *() const ENTT_NOEXCEPT { return get(); }
[[nodiscard]] const Resource & operator *() const ENTT_NOEXCEPT {
return get();
}
/*! @copydoc get */
Resource & operator *() ENTT_NOEXCEPT { return get(); }
[[nodiscard]] Resource & operator *() ENTT_NOEXCEPT {
return get();
}
/**
* @brief Gets a pointer to the managed resource.
@@ -79,13 +87,13 @@ public:
* @return A pointer to the managed resource or `nullptr` if the handle
* contains no resource at all.
*/
const Resource * operator->() const ENTT_NOEXCEPT {
[[nodiscard]] const Resource * operator->() const ENTT_NOEXCEPT {
ENTT_ASSERT(static_cast<bool>(resource));
return resource.get();
}
/*! @copydoc operator-> */
Resource * operator->() ENTT_NOEXCEPT {
[[nodiscard]] Resource * operator->() ENTT_NOEXCEPT {
return const_cast<Resource *>(std::as_const(*this).operator->());
}
@@ -93,7 +101,9 @@ public:
* @brief Returns true if a handle contains a resource, false otherwise.
* @return True if the handle contains a resource, false otherwise.
*/
explicit operator bool() const { return static_cast<bool>(resource); }
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
return static_cast<bool>(resource);
}
private:
std::shared_ptr<Resource> resource;
@@ -103,4 +113,4 @@ private:
}
#endif // ENTT_RESOURCE_HANDLE_HPP
#endif

View File

@@ -44,7 +44,7 @@ namespace entt {
template<typename Loader, typename Resource>
class resource_loader {
/*! @brief Resource loaders are friends of their caches. */
friend class resource_cache<Resource>;
friend struct resource_cache<Resource>;
/**
* @brief Loads the resource and returns it.
@@ -53,7 +53,7 @@ class resource_loader {
* @return The resource just loaded or an empty pointer in case of errors.
*/
template<typename... Args>
std::shared_ptr<Resource> get(Args &&... args) const {
[[nodiscard]] std::shared_ptr<Resource> get(Args &&... args) const {
return static_cast<const Loader *>(this)->load(std::forward<Args>(args)...);
}
};
@@ -62,4 +62,4 @@ class resource_loader {
}
#endif // ENTT_RESOURCE_LOADER_HPP
#endif

View File

@@ -3,9 +3,8 @@
#include <tuple>
#include <cstring>
#include <cstddef>
#include <utility>
#include <algorithm>
#include <functional>
#include <type_traits>
#include "../config/config.h"
@@ -24,37 +23,33 @@ namespace internal {
template<typename Ret, typename... Args>
auto to_function_pointer(Ret(*)(Args...)) -> Ret(*)(Args...);
auto function_pointer(Ret(*)(Args...)) -> Ret(*)(Args...);
template<typename Ret, typename... Args, typename Type, typename Payload, typename = std::enable_if_t<std::is_convertible_v<Payload &, Type &>>>
auto to_function_pointer(Ret(*)(Type &, Args...), Payload &) -> Ret(*)(Args...);
template<typename Ret, typename Type, typename... Args, typename Other>
auto function_pointer(Ret(*)(Type, Args...), Other &&) -> Ret(*)(Args...);
template<typename Class, typename Ret, typename... Args>
auto to_function_pointer(Ret(Class:: *)(Args...), const Class &) -> Ret(*)(Args...);
template<typename Class, typename Ret, typename... Args, typename... Other>
auto function_pointer(Ret(Class:: *)(Args...), Other &&...) -> Ret(*)(Args...);
template<typename Class, typename Ret, typename... Args>
auto to_function_pointer(Ret(Class:: *)(Args...) const, const Class &) -> Ret(*)(Args...);
template<typename Class, typename Ret, typename... Args, typename... Other>
auto function_pointer(Ret(Class:: *)(Args...) const, Other &&...) -> Ret(*)(Args...);
template<typename Class, typename Type>
auto to_function_pointer(Type Class:: *, const Class &) -> Type(*)();
template<typename Class, typename Type, typename... Other>
auto function_pointer(Type Class:: *, Other &&...) -> Type(*)();
template<typename>
struct function_extent;
template<typename... Type>
using function_pointer_t = decltype(internal::function_pointer(std::declval<Type>()...));
template<typename Ret, typename... Args>
struct function_extent<Ret(*)(Args...)> {
static constexpr auto value = sizeof...(Args);
};
template<typename Func>
constexpr auto function_extent_v = function_extent<Func>::value;
template<typename... Class, typename Ret, typename... Args>
[[nodiscard]] constexpr auto index_sequence_for(Ret(*)(Args...)) {
return std::index_sequence_for<Class..., Args...>{};
}
}
@@ -62,7 +57,7 @@ constexpr auto function_extent_v = function_extent<Func>::value;
/**
* Internal details not to be documented.
* @endcond TURN_OFF_DOXYGEN
* @endcond
*/
@@ -73,7 +68,7 @@ struct connect_arg_t {};
/*! @brief Constant of type connect_arg_t used to disambiguate calls. */
template<auto Func>
constexpr connect_arg_t<Func> connect_arg{};
inline constexpr connect_arg_t<Func> connect_arg{};
/**
@@ -92,50 +87,47 @@ class delegate;
* Unmanaged delegate for function pointers and members. Users of this class are
* in charge of disconnecting instances before deleting them.
*
* A delegate can be used as general purpose invoker with no memory overhead for
* free functions (with or without payload) and members provided along with an
* instance on which to invoke them.
* A delegate can be used as a general purpose invoker without memory overhead
* for free functions possibly with payloads and bound or unbound members.
*
* @tparam Ret Return type of a function type.
* @tparam Args Types of arguments of a function type.
*/
template<typename Ret, typename... Args>
class delegate<Ret(Args...)> {
using proto_fn_type = Ret(const void *, std::tuple<Args &&...>);
template<auto Function, std::size_t... Index>
void connect(std::index_sequence<Index...>) ENTT_NOEXCEPT {
static_assert(std::is_invocable_r_v<Ret, decltype(Function), std::tuple_element_t<Index, std::tuple<Args...>>...>);
data = nullptr;
fn = [](const void *, std::tuple<Args &&...> args) -> Ret {
// Ret(...) makes void(...) eat the return values to avoid errors
return Ret(std::invoke(Function, std::forward<std::tuple_element_t<Index, std::tuple<Args...>>>(std::get<Index>(args))...));
template<auto Candidate, std::size_t... Index>
[[nodiscard]] auto wrap(std::index_sequence<Index...>) ENTT_NOEXCEPT {
return [](const void *, Args... args) -> Ret {
[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
return Ret(std::invoke(Candidate, std::forward<std::tuple_element_t<Index, std::tuple<Args...>>>(std::get<Index>(arguments))...));
};
}
template<auto Candidate, typename Type, std::size_t... Index>
void connect(Type &value_or_instance, std::index_sequence<Index...>) ENTT_NOEXCEPT {
static_assert(std::is_invocable_r_v<Ret, decltype(Candidate), Type &, std::tuple_element_t<Index, std::tuple<Args...>>...>);
data = &value_or_instance;
[[nodiscard]] auto wrap(Type &, std::index_sequence<Index...>) ENTT_NOEXCEPT {
return [](const void *payload, Args... args) -> Ret {
[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
Type *curr = static_cast<Type *>(const_cast<std::conditional_t<std::is_const_v<Type>, const void *, void *>>(payload));
return Ret(std::invoke(Candidate, *curr, std::forward<std::tuple_element_t<Index, std::tuple<Args...>>>(std::get<Index>(arguments))...));
};
}
fn = [](const void *payload, std::tuple<Args &&...> args) -> Ret {
Type *curr = nullptr;
if constexpr(std::is_const_v<Type>) {
curr = static_cast<Type *>(payload);
} else {
curr = static_cast<Type *>(const_cast<void *>(payload));
}
// Ret(...) makes void(...) eat the return values to avoid errors
return Ret(std::invoke(Candidate, *curr, std::forward<std::tuple_element_t<Index, std::tuple<Args...>>>(std::get<Index>(args))...));
template<auto Candidate, typename Type, std::size_t... Index>
[[nodiscard]] auto wrap(Type *, std::index_sequence<Index...>) ENTT_NOEXCEPT {
return [](const void *payload, Args... args) -> Ret {
[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
Type *curr = static_cast<Type *>(const_cast<std::conditional_t<std::is_const_v<Type>, const void *, void *>>(payload));
return Ret(std::invoke(Candidate, curr, std::forward<std::tuple_element_t<Index, std::tuple<Args...>>>(std::get<Index>(arguments))...));
};
}
public:
/*! @brief Function type of the contained target. */
using function_type = Ret(const void *, Args...);
/*! @brief Function type of the delegate. */
using function_type = Ret(Args...);
using type = Ret(Args...);
/*! @brief Return type of the delegate. */
using result_type = Ret;
/*! @brief Default constructor. */
delegate() ENTT_NOEXCEPT
@@ -143,59 +135,125 @@ public:
{}
/**
* @brief Constructs a delegate and connects a free function to it.
* @tparam Function A valid free function pointer.
* @brief Constructs a delegate and connects a free function or an unbound
* member.
* @tparam Candidate Function or member to connect to the delegate.
*/
template<auto Function>
delegate(connect_arg_t<Function>) ENTT_NOEXCEPT
: delegate{}
{
connect<Function>();
template<auto Candidate>
delegate(connect_arg_t<Candidate>) ENTT_NOEXCEPT {
connect<Candidate>();
}
/**
* @brief Constructs a delegate and connects a member for a given instance
* or a free function with payload.
* @tparam Candidate Member or free function to connect to the delegate.
* @brief Constructs a delegate and connects a free function with payload or
* a bound member.
* @tparam Candidate Function or member to connect to the delegate.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid reference that fits the purpose.
* @param value_or_instance A valid object that fits the purpose.
*/
template<auto Candidate, typename Type>
delegate(connect_arg_t<Candidate>, Type &value_or_instance) ENTT_NOEXCEPT
: delegate{}
{
connect<Candidate>(value_or_instance);
delegate(connect_arg_t<Candidate>, Type &&value_or_instance) ENTT_NOEXCEPT {
connect<Candidate>(std::forward<Type>(value_or_instance));
}
/**
* @brief Connects a free function to a delegate.
* @tparam Function A valid free function pointer.
* @brief Constructs a delegate and connects an user defined function with
* optional payload.
* @param function Function to connect to the delegate.
* @param payload User defined arbitrary data.
*/
template<auto Function>
void connect() ENTT_NOEXCEPT {
constexpr auto extent = internal::function_extent_v<decltype(internal::to_function_pointer(std::declval<decltype(Function)>()))>;
connect<Function>(std::make_index_sequence<extent>{});
delegate(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT {
connect(function, payload);
}
/**
* @brief Connects a member function for a given instance or a free function
* with payload to a delegate.
* @brief Connects a free function or an unbound member to a delegate.
* @tparam Candidate Function or member to connect to the delegate.
*/
template<auto Candidate>
void connect() ENTT_NOEXCEPT {
data = nullptr;
if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Args...>) {
fn = [](const void *, Args... args) -> Ret {
return Ret(std::invoke(Candidate, std::forward<Args>(args)...));
};
} else if constexpr(std::is_member_pointer_v<decltype(Candidate)>) {
fn = wrap<Candidate>(internal::index_sequence_for<std::tuple_element_t<0, std::tuple<Args...>>>(internal::function_pointer_t<decltype(Candidate)>{}));
} else {
fn = wrap<Candidate>(internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate)>{}));
}
}
/**
* @brief Connects a free function with payload or a bound member to a
* delegate.
*
* The delegate isn't responsible for the connected object or the payload.
* Users must always guarantee that the lifetime of the instance overcomes
* the one of the delegate.<br/>
* the one of the delegate.<br/>
* When used to connect a free function with payload, its signature must be
* such that the instance is the first argument before the ones used to
* define the delegate itself.
*
* @tparam Candidate Member or free function to connect to the delegate.
* @tparam Candidate Function or member to connect to the delegate.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid reference that fits the purpose.
*/
template<auto Candidate, typename Type>
void connect(Type &value_or_instance) ENTT_NOEXCEPT {
constexpr auto extent = internal::function_extent_v<decltype(internal::to_function_pointer(std::declval<decltype(Candidate)>(), value_or_instance))>;
connect<Candidate>(value_or_instance, std::make_index_sequence<extent>{});
data = &value_or_instance;
if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type &, Args...>) {
fn = [](const void *payload, Args... args) -> Ret {
Type *curr = static_cast<Type *>(const_cast<std::conditional_t<std::is_const_v<Type>, const void *, void *>>(payload));
return Ret(std::invoke(Candidate, *curr, std::forward<Args>(args)...));
};
} else {
fn = wrap<Candidate>(value_or_instance, internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate), Type>{}));
}
}
/**
* @brief Connects a free function with payload or a bound member to a
* delegate.
*
* @sa connect(Type &)
*
* @tparam Candidate Function or member to connect to the delegate.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid pointer that fits the purpose.
*/
template<auto Candidate, typename Type>
void connect(Type *value_or_instance) ENTT_NOEXCEPT {
data = value_or_instance;
if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type *, Args...>) {
fn = [](const void *payload, Args... args) -> Ret {
Type *curr = static_cast<Type *>(const_cast<std::conditional_t<std::is_const_v<Type>, const void *, void *>>(payload));
return Ret(std::invoke(Candidate, curr, std::forward<Args>(args)...));
};
} else {
fn = wrap<Candidate>(value_or_instance, internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate), Type>{}));
}
}
/**
* @brief Connects an user defined function with optional payload to a
* delegate.
*
* The delegate isn't responsible for the connected object or the payload.
* Users must always guarantee that the lifetime of an instance overcomes
* the one of the delegate.<br/>
* The payload is returned as the first argument to the target function in
* all cases.
*
* @param function Function to connect to the delegate.
* @param payload User defined arbitrary data.
*/
void connect(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT {
fn = function;
data = payload;
}
/**
@@ -212,7 +270,7 @@ public:
* @brief Returns the instance or the payload linked to a delegate, if any.
* @return An opaque pointer to the underlying data.
*/
const void * instance() const ENTT_NOEXCEPT {
[[nodiscard]] const void * instance() const ENTT_NOEXCEPT {
return data;
}
@@ -232,16 +290,16 @@ public:
*/
Ret operator()(Args... args) const {
ENTT_ASSERT(fn);
return fn(data, std::forward_as_tuple(std::forward<Args>(args)...));
return fn(data, std::forward<Args>(args)...);
}
/**
* @brief Checks whether a delegate actually stores a listener.
* @return False if the delegate is empty, true otherwise.
*/
explicit operator bool() const ENTT_NOEXCEPT {
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
// no need to test also data
return fn;
return !(fn == nullptr);
}
/**
@@ -249,12 +307,12 @@ public:
* @param other Delegate with which to compare.
* @return False if the two contents differ, true otherwise.
*/
bool operator==(const delegate<Ret(Args...)> &other) const ENTT_NOEXCEPT {
[[nodiscard]] bool operator==(const delegate<Ret(Args...)> &other) const ENTT_NOEXCEPT {
return fn == other.fn && data == other.data;
}
private:
proto_fn_type *fn;
function_type *fn;
const void *data;
};
@@ -268,40 +326,37 @@ private:
* @return True if the two contents differ, false otherwise.
*/
template<typename Ret, typename... Args>
bool operator!=(const delegate<Ret(Args...)> &lhs, const delegate<Ret(Args...)> &rhs) ENTT_NOEXCEPT {
[[nodiscard]] bool operator!=(const delegate<Ret(Args...)> &lhs, const delegate<Ret(Args...)> &rhs) ENTT_NOEXCEPT {
return !(lhs == rhs);
}
/**
* @brief Deduction guide.
*
* It allows to deduce the function type of the delegate directly from a
* function provided to the constructor.
*
* @tparam Function A valid free function pointer.
* @tparam Candidate Function or member to connect to the delegate.
*/
template<auto Function>
delegate(connect_arg_t<Function>) ENTT_NOEXCEPT
-> delegate<std::remove_pointer_t<decltype(internal::to_function_pointer(Function))>>;
template<auto Candidate>
delegate(connect_arg_t<Candidate>) ENTT_NOEXCEPT
-> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate)>>>;
/**
* @brief Deduction guide.
*
* It allows to deduce the function type of the delegate directly from a member
* or a free function with payload provided to the constructor.
*
* @param value_or_instance A valid reference that fits the purpose.
* @tparam Candidate Member or free function to connect to the delegate.
* @tparam Candidate Function or member to connect to the delegate.
* @tparam Type Type of class or type of payload.
*/
template<auto Candidate, typename Type>
delegate(connect_arg_t<Candidate>, Type &value_or_instance) ENTT_NOEXCEPT
-> delegate<std::remove_pointer_t<decltype(internal::to_function_pointer(Candidate, value_or_instance))>>;
delegate(connect_arg_t<Candidate>, Type &&) ENTT_NOEXCEPT
-> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate), Type>>>;
/*! @brief Deduction guide. */
template<typename Ret, typename... Args>
delegate(Ret(*)(const void *, Args...), const void * = nullptr) ENTT_NOEXCEPT
-> delegate<Ret(Args...)>;
}
#endif // ENTT_SIGNAL_DELEGATE_HPP
#endif

View File

@@ -2,13 +2,14 @@
#define ENTT_SIGNAL_DISPATCHER_HPP
#include <vector>
#include <cstddef>
#include <memory>
#include <utility>
#include <type_traits>
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../core/family.hpp"
#include "../core/type_traits.hpp"
#include "../core/fwd.hpp"
#include "../core/type_info.hpp"
#include "sigh.hpp"
@@ -22,117 +23,102 @@ namespace entt {
* events to be published all together once per tick.<br/>
* Listeners are provided in the form of member functions. For each event of
* type `Event`, listeners are such that they can be invoked with an argument of
* type `const Event &`, no matter what the return type is.
* type `Event &`, no matter what the return type is.
*
* The types of the instances are `Class &`. Users must guarantee that the
* lifetimes of the objects overcome the one of the dispatcher itself to avoid
* crashes.
* The dispatcher creates instances of the `sigh` class internally. Refer to the
* documentation of the latter for more details.
*/
class dispatcher {
using event_family = family<struct internal_dispatcher_event_family>;
template<typename Class, typename Event>
using instance_type = typename sigh<void(const Event &)>::template instance_type<Class>;
struct base_wrapper {
virtual ~base_wrapper() = default;
struct basic_pool {
virtual ~basic_pool() = default;
virtual void publish() = 0;
virtual void disconnect(void *) = 0;
virtual void clear() ENTT_NOEXCEPT = 0;
[[nodiscard]] virtual id_type type_id() const ENTT_NOEXCEPT = 0;
};
template<typename Event>
struct signal_wrapper: base_wrapper {
using signal_type = sigh<void(const Event &)>;
struct pool_handler final: basic_pool {
using signal_type = sigh<void(Event &)>;
using sink_type = typename signal_type::sink_type;
void publish() override {
for(const auto &event: events[current]) {
signal.publish(event);
const auto length = events.size();
for(std::size_t pos{}; pos < length; ++pos) {
signal.publish(events[pos]);
}
events[current++].clear();
current %= std::extent<decltype(events)>::value;
events.erase(events.cbegin(), events.cbegin()+length);
}
sink_type sink() ENTT_NOEXCEPT {
void disconnect(void *instance) override {
sink().disconnect(instance);
}
void clear() ENTT_NOEXCEPT override {
events.clear();
}
[[nodiscard]] sink_type sink() ENTT_NOEXCEPT {
return entt::sink{signal};
}
template<typename... Args>
void trigger(Args &&... args) {
signal.publish({ std::forward<Args>(args)... });
Event instance{std::forward<Args>(args)...};
signal.publish(instance);
}
template<typename... Args>
void enqueue(Args &&... args) {
events[current].emplace_back(std::forward<Args>(args)...);
if constexpr(std::is_aggregate_v<Event>) {
events.push_back(Event{std::forward<Args>(args)...});
} else {
events.emplace_back(std::forward<Args>(args)...);
}
}
[[nodiscard]] id_type type_id() const ENTT_NOEXCEPT override {
return type_info<Event>::id();
}
private:
signal_type signal{};
std::vector<Event> events[2];
int current{};
};
struct wrapper_data {
std::unique_ptr<base_wrapper> wrapper;
ENTT_ID_TYPE runtime_type;
std::vector<Event> events;
};
template<typename Event>
static auto type() ENTT_NOEXCEPT {
if constexpr(is_named_type_v<Event>) {
return named_type_traits<Event>::value;
} else {
return event_family::type<Event>;
}
}
[[nodiscard]] pool_handler<Event> & assure() {
static_assert(std::is_same_v<Event, std::decay_t<Event>>, "Invalid event type");
template<typename Event>
signal_wrapper<Event> & assure() {
const auto wtype = type<Event>();
wrapper_data *wdata = nullptr;
if constexpr(ENTT_FAST_PATH(has_type_index_v<Event>)) {
const auto index = type_index<Event>::value();
if constexpr(is_named_type_v<Event>) {
const auto it = std::find_if(wrappers.begin(), wrappers.end(), [wtype](const auto &candidate) {
return candidate.wrapper && candidate.runtime_type == wtype;
});
wdata = (it == wrappers.cend() ? &wrappers.emplace_back() : &(*it));
} else {
if(!(wtype < wrappers.size())) {
wrappers.resize(wtype+1);
if(!(index < pools.size())) {
pools.resize(index+1u);
}
wdata = &wrappers[wtype];
if(wdata->wrapper && wdata->runtime_type != wtype) {
wrappers.emplace_back();
std::swap(wrappers[wtype], wrappers.back());
wdata = &wrappers[wtype];
if(!pools[index]) {
pools[index].reset(new pool_handler<Event>{});
}
}
if(!wdata->wrapper) {
wdata->wrapper = std::make_unique<signal_wrapper<Event>>();
wdata->runtime_type = wtype;
return static_cast<pool_handler<Event> &>(*pools[index]);
} else {
auto it = std::find_if(pools.begin(), pools.end(), [id = type_info<Event>::id()](const auto &cpool) { return id == cpool->type_id(); });
return static_cast<pool_handler<Event> &>(it == pools.cend() ? *pools.emplace_back(new pool_handler<Event>{}) : **it);
}
return static_cast<signal_wrapper<Event> &>(*wdata->wrapper);
}
public:
/*! @brief Type of sink for the given event. */
template<typename Event>
using sink_type = typename signal_wrapper<Event>::sink_type;
/**
* @brief Returns a sink object for the given event.
*
* A sink is an opaque object used to connect listeners to events.
*
* The function type for a listener is:
* The function type for a listener is _compatible_ with:
* @code{.cpp}
* void(const Event &);
* void(Event &);
* @endcode
*
* The order of invocation of the listeners isn't guaranteed.
@@ -143,7 +129,7 @@ public:
* @return A temporary sink object.
*/
template<typename Event>
sink_type<Event> sink() ENTT_NOEXCEPT {
[[nodiscard]] auto sink() {
return assure<Event>().sink();
}
@@ -205,6 +191,53 @@ public:
assure<std::decay_t<Event>>().enqueue(std::forward<Event>(event));
}
/**
* @brief Utility function to disconnect everything related to a given value
* or instance from a dispatcher.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid object that fits the purpose.
*/
template<typename Type>
void disconnect(Type &value_or_instance) {
disconnect(&value_or_instance);
}
/**
* @brief Utility function to disconnect everything related to a given value
* or instance from a dispatcher.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid object that fits the purpose.
*/
template<typename Type>
void disconnect(Type *value_or_instance) {
for(auto &&cpool: pools) {
if(cpool) {
cpool->disconnect(value_or_instance);
}
}
}
/**
* @brief Discards all the events queued so far.
*
* If no types are provided, the dispatcher will clear all the existing
* pools.
*
* @tparam Event Type of events to discard.
*/
template<typename... Event>
void clear() {
if constexpr(sizeof...(Event) == 0) {
for(auto &&cpool: pools) {
if(cpool) {
cpool->clear();
}
}
} else {
(assure<Event>().clear(), ...);
}
}
/**
* @brief Delivers all the pending events of the given type.
*
@@ -227,21 +260,19 @@ public:
* to reduce at a minimum the time spent in the bodies of the listeners.
*/
void update() const {
for(auto pos = wrappers.size(); pos; --pos) {
auto &wdata = wrappers[pos-1];
if(wdata.wrapper) {
wdata.wrapper->publish();
for(auto pos = pools.size(); pos; --pos) {
if(auto &&cpool = pools[pos-1]; cpool) {
cpool->publish();
}
}
}
private:
std::vector<wrapper_data> wrappers;
std::vector<std::unique_ptr<basic_pool>> pools;
};
}
#endif // ENTT_SIGNAL_DISPATCHER_HPP
#endif

View File

@@ -2,16 +2,17 @@
#define ENTT_SIGNAL_EMITTER_HPP
#include <type_traits>
#include <functional>
#include <algorithm>
#include <utility>
#include <memory>
#include <vector>
#include <functional>
#include <iterator>
#include <list>
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../core/family.hpp"
#include "../core/type_traits.hpp"
#include "../core/fwd.hpp"
#include "../core/type_info.hpp"
namespace entt {
@@ -29,33 +30,32 @@ namespace entt {
* }
* @endcode
*
* Handlers for the type of events are created internally on the fly. It's not
* Pools for the type of events are created internally on the fly. It's not
* required to specify in advance the full list of accepted types.<br/>
* Moreover, whenever an event is published, an emitter provides the listeners
* with a reference to itself along with a const reference to the event.
* Therefore listeners have an handy way to work with it without incurring in
* the need of capturing a reference to the emitter.
* with a reference to itself along with a reference to the event. Therefore
* listeners have an handy way to work with it without incurring in the need of
* capturing a reference to the emitter.
*
* @tparam Derived Actual type of emitter that extends the class template.
*/
template<typename Derived>
class emitter {
using handler_family = family<struct internal_emitter_handler_family>;
struct base_handler {
virtual ~base_handler() = default;
struct basic_pool {
virtual ~basic_pool() = default;
virtual bool empty() const ENTT_NOEXCEPT = 0;
virtual void clear() ENTT_NOEXCEPT = 0;
virtual id_type type_id() const ENTT_NOEXCEPT = 0;
};
template<typename Event>
struct event_handler: base_handler {
using listener_type = std::function<void(const Event &, Derived &)>;
struct pool_handler final: basic_pool {
using listener_type = std::function<void(Event &, Derived &)>;
using element_type = std::pair<bool, listener_type>;
using container_type = std::list<element_type>;
using connection_type = typename container_type::iterator;
bool empty() const ENTT_NOEXCEPT override {
[[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) &&
@@ -64,9 +64,13 @@ class emitter {
void clear() ENTT_NOEXCEPT override {
if(publishing) {
auto func = [](auto &&element) { element.first = true; };
std::for_each(once_list.begin(), once_list.end(), func);
std::for_each(on_list.begin(), on_list.end(), func);
for(auto &&element: once_list) {
element.first = true;
}
for(auto &&element: on_list) {
element.first = true;
}
} else {
once_list.clear();
on_list.clear();
@@ -81,7 +85,7 @@ class emitter {
return on_list.emplace(on_list.cend(), false, std::move(listener));
}
void erase(connection_type conn) ENTT_NOEXCEPT {
void erase(connection_type conn) {
conn->first = true;
if(!publishing) {
@@ -91,81 +95,66 @@ class emitter {
}
}
void publish(const Event &event, Derived &ref) {
void publish(Event &event, Derived &ref) {
container_type swap_list;
once_list.swap(swap_list);
auto func = [&event, &ref](auto &&element) {
return element.first ? void() : element.second(event, ref);
};
publishing = true;
std::for_each(on_list.rbegin(), on_list.rend(), func);
std::for_each(swap_list.rbegin(), swap_list.rend(), func);
for(auto &&element: on_list) {
element.first ? void() : element.second(event, ref);
}
for(auto &&element: swap_list) {
element.first ? void() : element.second(event, ref);
}
publishing = false;
on_list.remove_if([](auto &&element) { return element.first; });
}
[[nodiscard]] id_type type_id() const ENTT_NOEXCEPT override {
return type_info<Event>::id();
}
private:
bool publishing{false};
container_type once_list{};
container_type on_list{};
};
struct handler_data {
std::unique_ptr<base_handler> handler;
ENTT_ID_TYPE runtime_type;
};
template<typename Event>
static auto type() ENTT_NOEXCEPT {
if constexpr(is_named_type_v<Event>) {
return named_type_traits<Event>::value;
[[nodiscard]] const pool_handler<Event> & assure() const {
static_assert(std::is_same_v<Event, std::decay_t<Event>>, "Invalid event type");
if constexpr(ENTT_FAST_PATH(has_type_index_v<Event>)) {
const auto index = type_index<Event>::value();
if(!(index < pools.size())) {
pools.resize(index+1u);
}
if(!pools[index]) {
pools[index].reset(new pool_handler<Event>{});
}
return static_cast<pool_handler<Event> &>(*pools[index]);
} else {
return handler_family::type<Event>;
auto it = std::find_if(pools.begin(), pools.end(), [id = type_info<Event>::id()](const auto &cpool) { return id == cpool->type_id(); });
return static_cast<pool_handler<Event> &>(it == pools.cend() ? *pools.emplace_back(new pool_handler<Event>{}) : **it);
}
}
template<typename Event>
event_handler<Event> * assure() const ENTT_NOEXCEPT {
const auto htype = type<Event>();
handler_data *hdata = nullptr;
if constexpr(is_named_type_v<Event>) {
const auto it = std::find_if(handlers.begin(), handlers.end(), [htype](const auto &candidate) {
return candidate.handler && candidate.runtime_type == htype;
});
hdata = (it == handlers.cend() ? &handlers.emplace_back() : &(*it));
} else {
if(!(htype < handlers.size())) {
handlers.resize(htype+1);
}
hdata = &handlers[htype];
if(hdata->handler && hdata->runtime_type != htype) {
handlers.emplace_back();
std::swap(handlers[htype], handlers.back());
hdata = &handlers[htype];
}
}
if(!hdata->handler) {
hdata->handler = std::make_unique<event_handler<Event>>();
hdata->runtime_type = htype;
}
return static_cast<event_handler<Event> *>(hdata->handler.get());
[[nodiscard]] pool_handler<Event> & assure() {
return const_cast<pool_handler<Event> &>(std::as_const(*this).template assure<Event>());
}
public:
/** @brief Type of listeners accepted for the given event. */
template<typename Event>
using listener = typename event_handler<Event>::listener_type;
using listener = typename pool_handler<Event>::listener_type;
/**
* @brief Generic connection type for events.
@@ -177,28 +166,28 @@ public:
* @tparam Event Type of event for which the connection is created.
*/
template<typename Event>
struct connection: private event_handler<Event>::connection_type {
struct connection: private pool_handler<Event>::connection_type {
/** @brief Event emitters are friend classes of connections. */
friend class emitter;
/*! @brief Default constructor. */
connection() ENTT_NOEXCEPT = default;
connection() = default;
/**
* @brief Creates a connection that wraps its underlying instance.
* @param conn A connection object to wrap.
*/
connection(typename event_handler<Event>::connection_type conn)
: event_handler<Event>::connection_type{std::move(conn)}
connection(typename pool_handler<Event>::connection_type conn)
: pool_handler<Event>::connection_type{std::move(conn)}
{}
};
/*! @brief Default constructor. */
emitter() ENTT_NOEXCEPT = default;
emitter() = default;
/*! @brief Default destructor. */
virtual ~emitter() ENTT_NOEXCEPT {
static_assert(std::is_base_of_v<emitter<Derived>, Derived>);
virtual ~emitter() {
static_assert(std::is_base_of_v<emitter<Derived>, Derived>, "Incorrect use of the class template");
}
/*! @brief Default move constructor. */
@@ -220,7 +209,8 @@ public:
*/
template<typename Event, typename... Args>
void publish(Args &&... args) {
assure<Event>()->publish({ std::forward<Args>(args)... }, *static_cast<Derived *>(this));
Event instance{std::forward<Args>(args)...};
assure<Event>().publish(instance, *static_cast<Derived *>(this));
}
/**
@@ -232,7 +222,7 @@ public:
* to be used later to disconnect the listener if required.
*
* The listener is as a callable object that can be moved and the type of
* which is `void(const Event &, Derived &)`.
* which is _compatible_ with `void(Event &, Derived &)`.
*
* @note
* Whenever an event is emitted, the emitter provides the listener with a
@@ -245,7 +235,7 @@ public:
*/
template<typename Event>
connection<Event> on(listener<Event> instance) {
return assure<Event>()->on(std::move(instance));
return assure<Event>().on(std::move(instance));
}
/**
@@ -257,7 +247,7 @@ public:
* to be used later to disconnect the listener if required.
*
* The listener is as a callable object that can be moved and the type of
* which is `void(const Event &, Derived &)`.
* which is _compatible_ with `void(Event &, Derived &)`.
*
* @note
* Whenever an event is emitted, the emitter provides the listener with a
@@ -270,7 +260,7 @@ public:
*/
template<typename Event>
connection<Event> once(listener<Event> instance) {
return assure<Event>()->once(std::move(instance));
return assure<Event>().once(std::move(instance));
}
/**
@@ -283,8 +273,8 @@ public:
* @param conn A valid connection.
*/
template<typename Event>
void erase(connection<Event> conn) ENTT_NOEXCEPT {
assure<Event>()->erase(std::move(conn));
void erase(connection<Event> conn) {
assure<Event>().erase(std::move(conn));
}
/**
@@ -296,8 +286,8 @@ public:
* @tparam Event Type of event to reset.
*/
template<typename Event>
void clear() ENTT_NOEXCEPT {
assure<Event>()->clear();
void clear() {
assure<Event>().clear();
}
/**
@@ -307,9 +297,11 @@ public:
* results in undefined behavior.
*/
void clear() ENTT_NOEXCEPT {
std::for_each(handlers.begin(), handlers.end(), [](auto &&hdata) {
return hdata.handler ? hdata.handler->clear() : void();
});
for(auto &&cpool: pools) {
if(cpool) {
cpool->clear();
}
}
}
/**
@@ -318,26 +310,26 @@ public:
* @return True if there are no listeners registered, false otherwise.
*/
template<typename Event>
bool empty() const ENTT_NOEXCEPT {
return assure<Event>()->empty();
[[nodiscard]] bool empty() const {
return assure<Event>().empty();
}
/**
* @brief Checks if there are listeners registered with the event emitter.
* @return True if there are no listeners registered, false otherwise.
*/
bool empty() const ENTT_NOEXCEPT {
return std::all_of(handlers.cbegin(), handlers.cend(), [](auto &&hdata) {
return !hdata.handler || hdata.handler->empty();
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
return std::all_of(pools.cbegin(), pools.cend(), [](auto &&cpool) {
return !cpool || cpool->empty();
});
}
private:
mutable std::vector<handler_data> handlers{};
mutable std::vector<std::unique_ptr<basic_pool>> pools{};
};
}
#endif // ENTT_SIGNAL_EMITTER_HPP
#endif

View File

@@ -2,21 +2,30 @@
#define ENTT_SIGNAL_FWD_HPP
#include "../config/config.h"
namespace entt {
/*! @class delegate */
template<typename>
class delegate;
/*! @class sink */
class dispatcher;
template<typename>
class emitter;
class connection;
struct scoped_connection;
template<typename>
class sink;
/*! @class sigh */
template<typename>
class sigh;
@@ -24,4 +33,4 @@ class sigh;
}
#endif // ENTT_SIGNAL_FWD_HPP
#endif

View File

@@ -2,9 +2,10 @@
#define ENTT_SIGNAL_SIGH_HPP
#include <algorithm>
#include <utility>
#include <vector>
#include <utility>
#include <iterator>
#include <algorithm>
#include <functional>
#include <type_traits>
#include "../config/config.h"
@@ -61,9 +62,9 @@ class sigh<Ret(Args...)> {
public:
/*! @brief Unsigned integer type. */
using size_type = typename std::vector<delegate<Ret(Args...)>>::size_type;
using size_type = std::size_t;
/*! @brief Sink type. */
using sink_type = entt::sink<Ret(Args...)>;
using sink_type = sink<Ret(Args...)>;
/**
* @brief Instance type when it comes to connecting member functions.
@@ -76,7 +77,7 @@ public:
* @brief Number of listeners connected to the signal.
* @return Number of listeners currently connected.
*/
size_type size() const ENTT_NOEXCEPT {
[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
return calls.size();
}
@@ -84,7 +85,7 @@ public:
* @brief Returns false if at least a listener is connected to the signal.
* @return True if the signal has no listeners connected, false otherwise.
*/
bool empty() const ENTT_NOEXCEPT {
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
return calls.empty();
}
@@ -96,8 +97,8 @@ public:
* @param args Arguments to use to invoke listeners.
*/
void publish(Args... args) const {
for(auto pos = calls.size(); pos; --pos) {
calls[pos-1](args...);
for(auto &&call: std::as_const(calls)) {
call(args...);
}
}
@@ -117,22 +118,20 @@ public:
*/
template<typename Func>
void collect(Func func, Args... args) const {
bool stop = false;
for(auto pos = calls.size(); pos && !stop; --pos) {
for(auto &&call: calls) {
if constexpr(std::is_void_v<Ret>) {
if constexpr(std::is_invocable_r_v<bool, Func>) {
calls[pos-1](args...);
stop = func();
call(args...);
if(func()) { break; }
} else {
calls[pos-1](args...);
call(args...);
func();
}
} else {
if constexpr(std::is_invocable_r_v<bool, Func, Ret>) {
stop = func(calls[pos-1](args...));
if(func(call(args...))) { break; }
} else {
func(calls[pos-1](args...));
func(call(args...));
}
}
}
@@ -160,46 +159,14 @@ class connection {
{}
public:
/*! Default constructor. */
/*! @brief Default constructor. */
connection() = default;
/*! @brief Default copy constructor. */
connection(const connection &) = default;
/**
* @brief Default move constructor.
* @param other The instance to move from.
*/
connection(connection &&other)
: connection{}
{
std::swap(disconnect, other.disconnect);
std::swap(signal, other.signal);
}
/*! @brief Default copy assignment operator. @return This connection. */
connection & operator=(const connection &) = default;
/**
* @brief Default move assignment operator.
* @param other The instance to move from.
* @return This connection.
*/
connection & operator=(connection &&other) {
if(this != &other) {
auto tmp{std::move(other)};
disconnect = tmp.disconnect;
signal = tmp.signal;
}
return *this;
}
/**
* @brief Checks whether a connection is properly initialized.
* @return True if the connection is properly initialized, false otherwise.
*/
explicit operator bool() const ENTT_NOEXCEPT {
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
return static_cast<bool>(disconnect);
}
@@ -226,27 +193,24 @@ private:
* A scoped connection automatically breaks the link between the two objects
* when it goes out of scope.
*/
struct scoped_connection: private connection {
/*! Default constructor. */
struct scoped_connection {
/*! @brief Default constructor. */
scoped_connection() = default;
/**
* @brief Constructs a scoped connection from a basic connection.
* @param conn A valid connection object.
* @param other A valid connection object.
*/
scoped_connection(const connection &conn)
: connection{conn}
scoped_connection(const connection &other)
: conn{other}
{}
/*! @brief Default copy constructor, deleted on purpose. */
scoped_connection(const scoped_connection &) = delete;
/*! @brief Default move constructor. */
scoped_connection(scoped_connection &&) = default;
/*! @brief Automatically breaks the link on destruction. */
~scoped_connection() {
connection::release();
conn.release();
}
/**
@@ -256,33 +220,30 @@ struct scoped_connection: private connection {
scoped_connection & operator=(const scoped_connection &) = delete;
/**
* @brief Default move assignment operator.
* @brief Acquires a connection.
* @param other The connection object to acquire.
* @return This scoped connection.
*/
scoped_connection & operator=(scoped_connection &&) = default;
/**
* @brief Copies a connection.
* @param other The connection object to copy.
* @return This scoped connection.
*/
scoped_connection & operator=(const connection &other) {
static_cast<connection &>(*this) = other;
scoped_connection & operator=(connection other) {
conn = std::move(other);
return *this;
}
/**
* @brief Moves a connection.
* @param other The connection object to move.
* @return This scoped connection.
* @brief Checks whether a scoped connection is properly initialized.
* @return True if the connection is properly initialized, false otherwise.
*/
scoped_connection & operator=(connection &&other) {
static_cast<connection &>(*this) = std::move(other);
return *this;
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
return static_cast<bool>(conn);
}
using connection::operator bool;
using connection::release;
/*! @brief Breaks the connection. */
void release() {
conn.release();
}
private:
connection conn;
};
@@ -296,6 +257,10 @@ struct scoped_connection: private connection {
* The clear separation between a signal and a sink permits to store the former
* as private data member without exposing the publish functionality to the
* users of the class.
*
* @warning
* Lifetime of a sink must not overcome that of the signal to which it refers.
* In any other case, attempting to use a sink results in undefined behavior.
*
* @tparam Ret Return type of a function type.
* @tparam Args Types of arguments of a function type.
@@ -303,15 +268,16 @@ struct scoped_connection: private connection {
template<typename Ret, typename... Args>
class sink<Ret(Args...)> {
using signal_type = sigh<Ret(Args...)>;
using difference_type = typename std::iterator_traits<typename decltype(signal_type::calls)::iterator>::difference_type;
template<auto Candidate, typename Type>
static void release(Type &value_or_instance, void *signal) {
static void release(Type value_or_instance, void *signal) {
sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>(value_or_instance);
}
template<auto Function>
template<auto Candidate>
static void release(void *signal) {
sink{*static_cast<signal_type *>(signal)}.disconnect<Function>();
sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>();
}
public:
@@ -320,100 +286,207 @@ public:
* @param ref A valid reference to a signal object.
*/
sink(sigh<Ret(Args...)> &ref) ENTT_NOEXCEPT
: signal{&ref}
: offset{},
signal{&ref}
{}
/**
* @brief Returns false if at least a listener is connected to the sink.
* @return True if the sink has no listeners connected, false otherwise.
*/
bool empty() const ENTT_NOEXCEPT {
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
return signal->calls.empty();
}
/**
* @brief Connects a free function to a signal.
*
* The signal handler performs checks to avoid multiple connections for free
* functions.
*
* @brief Returns a sink that connects before a given free function or an
* unbound member.
* @tparam Function A valid free function pointer.
* @return A properly initialized connection object.
* @return A properly initialized sink object.
*/
template<auto Function>
[[nodiscard]] sink before() {
delegate<Ret(Args...)> call{};
call.template connect<Function>();
const auto &calls = signal->calls;
const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call));
sink other{*this};
other.offset = std::distance(it, calls.cend());
return other;
}
/**
* @brief Returns a sink that connects before a free function with payload
* or a bound member.
* @tparam Candidate Member or free function to look for.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid object that fits the purpose.
* @return A properly initialized sink object.
*/
template<auto Candidate, typename Type>
[[nodiscard]] sink before(Type &&value_or_instance) {
delegate<Ret(Args...)> call{};
call.template connect<Candidate>(value_or_instance);
const auto &calls = signal->calls;
const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call));
sink other{*this};
other.offset = std::distance(it, calls.cend());
return other;
}
/**
* @brief Returns a sink that connects before a given instance or specific
* payload.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid object that fits the purpose.
* @return A properly initialized sink object.
*/
template<typename Type>
[[nodiscard]] sink before(Type &value_or_instance) {
return before(&value_or_instance);
}
/**
* @brief Returns a sink that connects before a given instance or specific
* payload.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid pointer that fits the purpose.
* @return A properly initialized sink object.
*/
template<typename Type>
[[nodiscard]] sink before(Type *value_or_instance) {
sink other{*this};
if(value_or_instance) {
const auto &calls = signal->calls;
const auto it = std::find_if(calls.cbegin(), calls.cend(), [value_or_instance](const auto &delegate) {
return delegate.instance() == value_or_instance;
});
other.offset = std::distance(it, calls.cend());
}
return other;
}
/**
* @brief Returns a sink that connects before anything else.
* @return A properly initialized sink object.
*/
[[nodiscard]] sink before() {
sink other{*this};
other.offset = signal->calls.size();
return other;
}
/**
* @brief Connects a free function or an unbound member to a signal.
*
* The signal handler performs checks to avoid multiple connections for the
* same function.
*
* @tparam Candidate Function or member to connect to the signal.
* @return A properly initialized connection object.
*/
template<auto Candidate>
connection connect() {
disconnect<Function>();
disconnect<Candidate>();
delegate<Ret(Args...)> call{};
call.template connect<Candidate>();
signal->calls.insert(signal->calls.end() - offset, std::move(call));
delegate<void(void *)> conn{};
conn.template connect<&release<Function>>();
signal->calls.emplace_back(delegate<Ret(Args...)>{connect_arg<Function>});
conn.template connect<&release<Candidate>>();
return { std::move(conn), signal };
}
/**
* @brief Connects a member function or a free function with payload to a
* @brief Connects a free function with payload or a bound member to a
* signal.
*
* The signal isn't responsible for the connected object or the payload.
* Users must always guarantee that the lifetime of the instance overcomes
* the one of the delegate. On the other side, the signal handler performs
* the one of the signal. On the other side, the signal handler performs
* checks to avoid multiple connections for the same function.<br/>
* When used to connect a free function with payload, its signature must be
* such that the instance is the first argument before the ones used to
* define the delegate itself.
* define the signal itself.
*
* @tparam Candidate Member or free function to connect to the signal.
* @tparam Candidate Function or member to connect to the signal.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid reference that fits the purpose.
* @param value_or_instance A valid object that fits the purpose.
* @return A properly initialized connection object.
*/
template<auto Candidate, typename Type>
connection connect(Type &value_or_instance) {
connection connect(Type &&value_or_instance) {
disconnect<Candidate>(value_or_instance);
delegate<Ret(Args...)> call{};
call.template connect<Candidate>(value_or_instance);
signal->calls.insert(signal->calls.end() - offset, std::move(call));
delegate<void(void *)> conn{};
conn.template connect<&sink::release<Candidate, Type>>(value_or_instance);
signal->calls.emplace_back(delegate<Ret(Args...)>{connect_arg<Candidate>, value_or_instance});
conn.template connect<&release<Candidate, Type>>(value_or_instance);
return { std::move(conn), signal };
}
/**
* @brief Disconnects a free function from a signal.
* @tparam Function A valid free function pointer.
* @brief Disconnects a free function or an unbound member from a signal.
* @tparam Candidate Function or member to disconnect from the signal.
*/
template<auto Function>
template<auto Candidate>
void disconnect() {
auto &calls = signal->calls;
delegate<Ret(Args...)> delegate{};
delegate.template connect<Function>();
calls.erase(std::remove(calls.begin(), calls.end(), delegate), calls.end());
delegate<Ret(Args...)> call{};
call.template connect<Candidate>();
calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end());
}
/**
* @brief Disconnects a member function or a free function with payload from
* a signal.
* @tparam Candidate Member or free function to disconnect from the signal.
* @brief Disconnects a free function with payload or a bound member from a
* signal.
* @tparam Candidate Function or member to disconnect from the signal.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid reference that fits the purpose.
* @param value_or_instance A valid object that fits the purpose.
*/
template<auto Candidate, typename Type>
void disconnect(Type &value_or_instance) {
void disconnect(Type &&value_or_instance) {
auto &calls = signal->calls;
delegate<Ret(Args...)> delegate{};
delegate.template connect<Candidate>(value_or_instance);
calls.erase(std::remove(calls.begin(), calls.end(), delegate), calls.end());
delegate<Ret(Args...)> call{};
call.template connect<Candidate>(value_or_instance);
calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end());
}
/**
* @brief Disconnects member functions or free functions based on an
* instance or specific payload.
* @brief Disconnects free functions with payload or bound members from a
* signal.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid reference that fits the purpose.
* @param value_or_instance A valid object that fits the purpose.
*/
template<typename Type>
void disconnect(const Type &value_or_instance) {
auto &calls = signal->calls;
calls.erase(std::remove_if(calls.begin(), calls.end(), [&value_or_instance](const auto &delegate) {
return delegate.instance() == &value_or_instance;
}), calls.end());
void disconnect(Type &value_or_instance) {
disconnect(&value_or_instance);
}
/**
* @brief Disconnects free functions with payload or bound members from a
* signal.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid object that fits the purpose.
*/
template<typename Type>
void disconnect(Type *value_or_instance) {
if(value_or_instance) {
auto &calls = signal->calls;
calls.erase(std::remove_if(calls.begin(), calls.end(), [value_or_instance](const auto &delegate) {
return delegate.instance() == value_or_instance;
}), calls.end());
}
}
/*! @brief Disconnects all the listeners from a signal. */
@@ -422,6 +495,7 @@ public:
}
private:
difference_type offset;
signal_type *signal;
};
@@ -442,4 +516,4 @@ sink(sigh<Ret(Args...)> &) ENTT_NOEXCEPT -> sink<Ret(Args...)>;
}
#endif // ENTT_SIGNAL_SIGH_HPP
#endif

View File

View File

@@ -2,128 +2,209 @@
# Tests configuration
#
include(FetchContent)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
if(FIND_GTEST_PACKAGE)
find_package(GTest REQUIRED)
else()
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG master
GIT_SHALLOW 1
)
FetchContent_GetProperties(googletest)
if(NOT googletest_POPULATED)
FetchContent_Populate(googletest)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
endif()
add_library(GTest::Main ALIAS gtest_main)
target_compile_features(gtest PUBLIC cxx_std_17)
target_compile_features(gtest_main PUBLIC cxx_std_17)
target_compile_features(gmock PUBLIC cxx_std_17)
target_compile_features(gmock_main PUBLIC cxx_std_17)
endif()
include_directories($<TARGET_PROPERTY:EnTT,INTERFACE_INCLUDE_DIRECTORIES>)
add_compile_options($<TARGET_PROPERTY:EnTT,INTERFACE_COMPILE_OPTIONS>)
macro(SETUP_LIBRARY_TARGET LIB_TARGET)
set_target_properties(${LIB_TARGET} PROPERTIES CXX_EXTENSIONS OFF)
target_compile_definitions(${LIB_TARGET} PRIVATE $<TARGET_PROPERTY:EnTT,INTERFACE_COMPILE_DEFINITIONS>)
target_compile_features(${LIB_TARGET} PRIVATE $<TARGET_PROPERTY:EnTT,INTERFACE_COMPILE_FEATURES>)
target_compile_options(${LIB_TARGET} PRIVATE $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-pedantic -Wall>)
target_compile_options(${LIB_TARGET} PRIVATE $<$<CXX_COMPILER_ID:MSVC>:/EHsc>)
endmacro()
function(SETUP_TARGET TARGET_NAME)
set_target_properties(${TARGET_NAME} PROPERTIES CXX_EXTENSIONS OFF)
target_link_libraries(${TARGET_NAME} PRIVATE EnTT)
target_compile_options(
${TARGET_NAME}
PRIVATE
$<$<NOT:$<PLATFORM_ID:Windows>>:-pedantic -fvisibility=hidden -Wall -Wshadow -Wno-deprecated-declarations>
$<$<PLATFORM_ID:Windows>:/EHsc /W1 /wd4996 /w14800>
)
target_compile_options(
${TARGET_NAME}
PRIVATE
$<$<AND:$<CONFIG:Debug>,$<NOT:$<PLATFORM_ID:Windows>>>:-O0 -g>
$<$<AND:$<CONFIG:Release>,$<NOT:$<PLATFORM_ID:Windows>>>:-O2>
$<$<AND:$<CONFIG:Debug>,$<PLATFORM_ID:Windows>>:/Od>
$<$<AND:$<CONFIG:Release>,$<PLATFORM_ID:Windows>>:/O2>
)
target_compile_definitions(${TARGET_NAME} PRIVATE ENTT_STANDALONE ${ARGN})
endfunction()
add_library(odr OBJECT odr.cpp)
SETUP_LIBRARY_TARGET(odr)
SETUP_TARGET(odr)
macro(SETUP_AND_ADD_TEST TEST_NAME TEST_SOURCE)
add_executable(${TEST_NAME} $<TARGET_OBJECTS:odr> ${TEST_SOURCE})
set_target_properties(${TEST_NAME} PROPERTIES CXX_EXTENSIONS OFF)
target_link_libraries(${TEST_NAME} PRIVATE EnTT GTest::Main Threads::Threads)
target_compile_definitions(${TEST_NAME} PRIVATE $<TARGET_PROPERTY:EnTT,INTERFACE_COMPILE_DEFINITIONS>)
target_compile_features(${TEST_NAME} PRIVATE $<TARGET_PROPERTY:EnTT,INTERFACE_COMPILE_FEATURES>)
target_compile_options(${TEST_NAME} PRIVATE $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-pedantic -Wall>)
target_compile_options(${TEST_NAME} PRIVATE $<$<CXX_COMPILER_ID:MSVC>:/EHsc>)
function(SETUP_BASIC_TEST TEST_NAME TEST_SOURCES)
add_executable(${TEST_NAME} $<TARGET_OBJECTS:odr> ${TEST_SOURCES})
target_link_libraries(${TEST_NAME} PRIVATE GTest::Main Threads::Threads)
SETUP_TARGET(${TEST_NAME} ${ARGN})
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME})
endmacro()
endfunction()
function(SETUP_LIB_TEST TEST_NAME)
add_library(_${TEST_NAME} SHARED $<TARGET_OBJECTS:odr> lib/${TEST_NAME}/lib.cpp)
SETUP_TARGET(_${TEST_NAME} ENTT_API_EXPORT)
SETUP_BASIC_TEST(lib_${TEST_NAME} lib/${TEST_NAME}/main.cpp ENTT_API_IMPORT)
target_link_libraries(lib_${TEST_NAME} PRIVATE _${TEST_NAME})
endfunction()
function(SETUP_PLUGIN_TEST TEST_NAME)
add_library(_${TEST_NAME} MODULE $<TARGET_OBJECTS:odr> lib/${TEST_NAME}/plugin.cpp)
SETUP_TARGET(_${TEST_NAME} NOMINMAX ${ARGVN})
SETUP_BASIC_TEST(lib_${TEST_NAME} lib/${TEST_NAME}/main.cpp NOMINMAX PLUGIN="$<TARGET_FILE:_${TEST_NAME}>" ${ARGVN})
target_include_directories(_${TEST_NAME} PRIVATE ${cr_INCLUDE_DIR})
target_include_directories(lib_${TEST_NAME} PRIVATE ${cr_INCLUDE_DIR})
target_link_libraries(lib_${TEST_NAME} PRIVATE ${CMAKE_DL_LIBS})
endfunction()
# Test benchmark
if(BUILD_BENCHMARK)
SETUP_AND_ADD_TEST(benchmark benchmark/benchmark.cpp)
SETUP_BASIC_TEST(benchmark benchmark/benchmark.cpp)
endif()
# Test example
if(BUILD_EXAMPLE)
SETUP_BASIC_TEST(custom_identifier example/custom_identifier.cpp)
endif()
# Test lib
if(BUILD_LIB)
add_library(a_module_shared SHARED lib/a_module.cpp)
add_library(a_module_static STATIC lib/a_module.cpp)
add_library(another_module_shared SHARED lib/another_module.cpp)
add_library(another_module_static STATIC lib/another_module.cpp)
FetchContent_Declare(
cr
GIT_REPOSITORY https://github.com/fungos/cr.git
GIT_TAG master
GIT_SHALLOW 1
)
SETUP_LIBRARY_TARGET(a_module_shared)
SETUP_LIBRARY_TARGET(a_module_static)
SETUP_LIBRARY_TARGET(another_module_shared)
SETUP_LIBRARY_TARGET(another_module_static)
FetchContent_GetProperties(cr)
SETUP_AND_ADD_TEST(lib_shared lib/lib.cpp)
target_link_libraries(lib_shared PRIVATE a_module_shared another_module_shared)
if(NOT cr_POPULATED)
FetchContent_Populate(cr)
set(cr_INCLUDE_DIR ${cr_SOURCE_DIR})
endif()
SETUP_AND_ADD_TEST(lib_static lib/lib.cpp)
target_link_libraries(lib_static PRIVATE a_module_static another_module_static)
endif()
SETUP_LIB_TEST(dispatcher)
SETUP_LIB_TEST(emitter)
SETUP_LIB_TEST(meta)
SETUP_LIB_TEST(registry)
# Test mod
SETUP_PLUGIN_TEST(dispatcher_plugin)
SETUP_PLUGIN_TEST(emitter_plugin)
SETUP_PLUGIN_TEST(meta_plugin)
SETUP_PLUGIN_TEST(registry_plugin)
if(BUILD_MOD)
set(DUKTAPE_DEPS_DIR ${EnTT_SOURCE_DIR}/deps/duktape)
configure_file(${EnTT_SOURCE_DIR}/cmake/in/duktape.in ${DUKTAPE_DEPS_DIR}/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . WORKING_DIRECTORY ${DUKTAPE_DEPS_DIR})
execute_process(COMMAND ${CMAKE_COMMAND} --build . WORKING_DIRECTORY ${DUKTAPE_DEPS_DIR})
set(DUKTAPE_SRC_DIR ${DUKTAPE_DEPS_DIR}/src/src)
set(MOD_TEST_SOURCE ${DUKTAPE_SRC_DIR}/duktape.c mod/mod.cpp)
SETUP_AND_ADD_TEST(mod "${MOD_TEST_SOURCE}")
target_include_directories(mod PRIVATE ${DUKTAPE_SRC_DIR})
SETUP_PLUGIN_TEST(meta_plugin_std ENTT_STANDARD_CPP)
endif()
# Test snapshot
if(BUILD_SNAPSHOT)
set(CEREAL_DEPS_DIR ${EnTT_SOURCE_DIR}/deps/cereal)
configure_file(${EnTT_SOURCE_DIR}/cmake/in/cereal.in ${CEREAL_DEPS_DIR}/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . WORKING_DIRECTORY ${CEREAL_DEPS_DIR})
execute_process(COMMAND ${CMAKE_COMMAND} --build . WORKING_DIRECTORY ${CEREAL_DEPS_DIR})
set(CEREAL_SRC_DIR ${CEREAL_DEPS_DIR}/src/include)
FetchContent_Declare(
cereal
GIT_REPOSITORY https://github.com/USCiLab/cereal.git
GIT_TAG v1.2.2
GIT_SHALLOW 1
)
SETUP_AND_ADD_TEST(cereal snapshot/snapshot.cpp)
target_include_directories(cereal PRIVATE ${CEREAL_SRC_DIR})
FetchContent_GetProperties(cereal)
if(NOT cereal_POPULATED)
FetchContent_Populate(cereal)
set(cereal_INCLUDE_DIR ${cereal_SOURCE_DIR}/include)
endif()
SETUP_BASIC_TEST(cereal snapshot/snapshot.cpp)
target_include_directories(cereal PRIVATE ${cereal_INCLUDE_DIR})
endif()
# Test core
SETUP_AND_ADD_TEST(algorithm entt/core/algorithm.cpp)
SETUP_AND_ADD_TEST(family entt/core/family.cpp)
SETUP_AND_ADD_TEST(hashed_string entt/core/hashed_string.cpp)
SETUP_AND_ADD_TEST(ident entt/core/ident.cpp)
SETUP_AND_ADD_TEST(monostate entt/core/monostate.cpp)
SETUP_AND_ADD_TEST(type_traits entt/core/type_traits.cpp)
SETUP_AND_ADD_TEST(utility entt/core/utility.cpp)
SETUP_BASIC_TEST(algorithm entt/core/algorithm.cpp)
SETUP_BASIC_TEST(family entt/core/family.cpp)
SETUP_BASIC_TEST(hashed_string entt/core/hashed_string.cpp)
SETUP_BASIC_TEST(ident entt/core/ident.cpp)
SETUP_BASIC_TEST(monostate entt/core/monostate.cpp)
SETUP_BASIC_TEST(type_info entt/core/type_info.cpp)
SETUP_BASIC_TEST(type_traits entt/core/type_traits.cpp)
SETUP_BASIC_TEST(utility entt/core/utility.cpp)
# Test entity
SETUP_AND_ADD_TEST(actor entt/entity/actor.cpp)
SETUP_AND_ADD_TEST(entity entt/entity/entity.cpp)
SETUP_AND_ADD_TEST(group entt/entity/group.cpp)
SETUP_AND_ADD_TEST(helper entt/entity/helper.cpp)
SETUP_AND_ADD_TEST(observer entt/entity/observer.cpp)
SETUP_AND_ADD_TEST(registry entt/entity/registry.cpp)
SETUP_AND_ADD_TEST(runtime_view entt/entity/runtime_view.cpp)
SETUP_AND_ADD_TEST(snapshot entt/entity/snapshot.cpp)
SETUP_AND_ADD_TEST(sparse_set entt/entity/sparse_set.cpp)
SETUP_AND_ADD_TEST(storage entt/entity/storage.cpp)
SETUP_AND_ADD_TEST(view entt/entity/view.cpp)
SETUP_BASIC_TEST(actor entt/entity/actor.cpp)
SETUP_BASIC_TEST(entity entt/entity/entity.cpp)
SETUP_BASIC_TEST(group entt/entity/group.cpp)
SETUP_BASIC_TEST(handle entt/entity/handle.cpp)
SETUP_BASIC_TEST(helper entt/entity/helper.cpp)
SETUP_BASIC_TEST(observer entt/entity/observer.cpp)
SETUP_BASIC_TEST(registry entt/entity/registry.cpp)
SETUP_BASIC_TEST(registry_no_eto entt/entity/registry_no_eto.cpp ENTT_NO_ETO)
SETUP_BASIC_TEST(runtime_view entt/entity/runtime_view.cpp)
SETUP_BASIC_TEST(snapshot entt/entity/snapshot.cpp)
SETUP_BASIC_TEST(sparse_set entt/entity/sparse_set.cpp)
SETUP_BASIC_TEST(storage entt/entity/storage.cpp)
SETUP_BASIC_TEST(view entt/entity/view.cpp)
# Test locator
SETUP_AND_ADD_TEST(locator entt/locator/locator.cpp)
SETUP_BASIC_TEST(locator entt/locator/locator.cpp)
# Test meta
SETUP_AND_ADD_TEST(meta entt/meta/meta.cpp)
SETUP_BASIC_TEST(meta_any entt/meta/meta_any.cpp)
SETUP_BASIC_TEST(meta_base entt/meta/meta_base.cpp)
SETUP_BASIC_TEST(meta_container entt/meta/meta_container.cpp)
SETUP_BASIC_TEST(meta_conv entt/meta/meta_conv.cpp)
SETUP_BASIC_TEST(meta_ctor entt/meta/meta_ctor.cpp)
SETUP_BASIC_TEST(meta_data entt/meta/meta_data.cpp)
SETUP_BASIC_TEST(meta_func entt/meta/meta_func.cpp)
SETUP_BASIC_TEST(meta_pointer entt/meta/meta_pointer.cpp)
SETUP_BASIC_TEST(meta_prop entt/meta/meta_prop.cpp)
SETUP_BASIC_TEST(meta_range entt/meta/meta_range.cpp)
SETUP_BASIC_TEST(meta_type entt/meta/meta_type.cpp)
# Test process
SETUP_AND_ADD_TEST(process entt/process/process.cpp)
SETUP_AND_ADD_TEST(scheduler entt/process/scheduler.cpp)
SETUP_BASIC_TEST(process entt/process/process.cpp)
SETUP_BASIC_TEST(scheduler entt/process/scheduler.cpp)
# Test resource
SETUP_AND_ADD_TEST(resource entt/resource/resource.cpp)
SETUP_BASIC_TEST(resource entt/resource/resource.cpp)
# Test signal
SETUP_AND_ADD_TEST(delegate entt/signal/delegate.cpp)
SETUP_AND_ADD_TEST(dispatcher entt/signal/dispatcher.cpp)
SETUP_AND_ADD_TEST(emitter entt/signal/emitter.cpp)
SETUP_AND_ADD_TEST(sigh entt/signal/sigh.cpp)
SETUP_BASIC_TEST(delegate entt/signal/delegate.cpp)
SETUP_BASIC_TEST(dispatcher entt/signal/dispatcher.cpp)
SETUP_BASIC_TEST(emitter entt/signal/emitter.cpp)
SETUP_BASIC_TEST(sigh entt/signal/sigh.cpp)

View File

@@ -1,11 +0,0 @@
load("//bazel:copts.bzl", "entt_copts")
cc_binary(
name = "benchmark",
srcs = ["benchmark.cpp"],
copts = entt_copts,
deps = [
"//:entt",
"@com_google_googletest//:gtest_main",
],
)

View File

@@ -4,6 +4,7 @@
#include <chrono>
#include <iterator>
#include <gtest/gtest.h>
#include <entt/core/type_info.hpp>
#include <entt/entity/registry.hpp>
struct position {
@@ -37,24 +38,24 @@ void pathological(Func func) {
for(std::uint64_t i = 0; i < 500000L; i++) {
const auto entity = registry.create();
registry.assign<position>(entity);
registry.assign<velocity>(entity);
registry.assign<comp<0>>(entity);
registry.emplace<position>(entity);
registry.emplace<velocity>(entity);
registry.emplace<comp<0>>(entity);
}
for(auto i = 0; i < 10; ++i) {
registry.each([i = 0, &registry](const auto entity) mutable {
if(!(++i % 7)) { registry.reset<position>(entity); }
if(!(++i % 11)) { registry.reset<velocity>(entity); }
if(!(++i % 13)) { registry.reset<comp<0>>(entity); }
if(!(++i % 7)) { registry.remove_if_exists<position>(entity); }
if(!(++i % 11)) { registry.remove_if_exists<velocity>(entity); }
if(!(++i % 13)) { registry.remove_if_exists<comp<0>>(entity); }
if(!(++i % 17)) { registry.destroy(entity); }
});
for(std::uint64_t j = 0; j < 50000L; j++) {
const auto entity = registry.create();
registry.assign<position>(entity);
registry.assign<velocity>(entity);
registry.assign<comp<0>>(entity);
registry.emplace<position>(entity);
registry.emplace<velocity>(entity);
registry.emplace<comp<0>>(entity);
}
}
@@ -63,10 +64,10 @@ void pathological(Func func) {
});
}
TEST(Benchmark, Construct) {
TEST(Benchmark, Create) {
entt::registry registry;
std::cout << "Constructing 1000000 entities" << std::endl;
std::cout << "Creating 1000000 entities" << std::endl;
timer timer;
@@ -77,43 +78,45 @@ TEST(Benchmark, Construct) {
timer.elapsed();
}
TEST(Benchmark, ConstructMany) {
TEST(Benchmark, CreateMany) {
entt::registry registry;
std::vector<entt::entity> entities(1000000);
std::cout << "Constructing 1000000 entities at once" << std::endl;
std::cout << "Creating 1000000 entities at once" << std::endl;
timer timer;
registry.create(entities.begin(), entities.end());
timer.elapsed();
}
TEST(Benchmark, ConstructManyAndAssignComponents) {
TEST(Benchmark, CreateManyAndEmplaceComponents) {
entt::registry registry;
std::vector<entt::entity> entities(1000000);
std::cout << "Constructing 1000000 entities at once and assign components" << std::endl;
std::cout << "Creating 1000000 entities at once and emplace components" << std::endl;
timer timer;
registry.create(entities.begin(), entities.end());
for(const auto entity: entities) {
registry.assign<position>(entity);
registry.assign<velocity>(entity);
registry.emplace<position>(entity);
registry.emplace<velocity>(entity);
}
timer.elapsed();
}
TEST(Benchmark, ConstructManyWithComponents) {
TEST(Benchmark, CreateManyWithComponents) {
entt::registry registry;
std::vector<entt::entity> entities(1000000);
std::cout << "Constructing 1000000 entities at once with components" << std::endl;
std::cout << "Creating 1000000 entities at once with components" << std::endl;
timer timer;
registry.create<position, velocity>(entities.begin(), entities.end());
registry.create(entities.begin(), entities.end());
registry.insert<position>(entities.begin(), entities.end());
registry.insert<velocity>(entities.begin(), entities.end());
timer.elapsed();
}
@@ -142,10 +145,10 @@ TEST(Benchmark, IterateSingleComponent1M) {
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<position>(entity);
registry.emplace<position>(entity);
}
auto test = [&registry](auto func) {
auto test = [&](auto func) {
timer timer;
registry.view<position>().each(func);
timer.elapsed();
@@ -163,11 +166,11 @@ TEST(Benchmark, IterateSingleComponentRuntime1M) {
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<position>(entity);
registry.emplace<position>(entity);
}
auto test = [&registry](auto func) {
entt::component types[] = { registry.type<position>() };
auto test = [&](auto func) {
entt::id_type types[] = { entt::type_info<position>::id() };
timer timer;
registry.runtime_view(std::begin(types), std::end(types)).each(func);
@@ -186,11 +189,11 @@ TEST(Benchmark, IterateTwoComponents1M) {
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<position>(entity);
registry.assign<velocity>(entity);
registry.emplace<position>(entity);
registry.emplace<velocity>(entity);
}
auto test = [&registry](auto func) {
auto test = [&](auto func) {
timer timer;
registry.view<position, velocity>().each(func);
timer.elapsed();
@@ -208,14 +211,14 @@ TEST(Benchmark, IterateTwoComponents1MHalf) {
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<velocity>(entity);
registry.emplace<velocity>(entity);
if(i % 2) {
registry.assign<position>(entity);
registry.emplace<position>(entity);
}
}
auto test = [&registry](auto func) {
auto test = [&](auto func) {
timer timer;
registry.view<position, velocity>().each(func);
timer.elapsed();
@@ -233,14 +236,14 @@ TEST(Benchmark, IterateTwoComponents1MOne) {
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<velocity>(entity);
registry.emplace<velocity>(entity);
if(i == 500000L) {
registry.assign<position>(entity);
registry.emplace<position>(entity);
}
}
auto test = [&registry](auto func) {
auto test = [&](auto func) {
timer timer;
registry.view<position, velocity>().each(func);
timer.elapsed();
@@ -253,19 +256,19 @@ TEST(Benchmark, IterateTwoComponents1MOne) {
TEST(Benchmark, IterateTwoComponentsNonOwningGroup1M) {
entt::registry registry;
registry.group<>(entt::get<position, velocity>);
const auto group = registry.group<>(entt::get<position, velocity>);
std::cout << "Iterating over 1000000 entities, two components, non owning group" << std::endl;
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<position>(entity);
registry.assign<velocity>(entity);
registry.emplace<position>(entity);
registry.emplace<velocity>(entity);
}
auto test = [&registry](auto func) {
auto test = [&](auto func) {
timer timer;
registry.group<>(entt::get<position, velocity>).each(func);
group.each(func);
timer.elapsed();
};
@@ -276,19 +279,19 @@ TEST(Benchmark, IterateTwoComponentsNonOwningGroup1M) {
TEST(Benchmark, IterateTwoComponentsFullOwningGroup1M) {
entt::registry registry;
registry.group<position, velocity>();
const auto group = registry.group<position, velocity>();
std::cout << "Iterating over 1000000 entities, two components, full owning group" << std::endl;
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<position>(entity);
registry.assign<velocity>(entity);
registry.emplace<position>(entity);
registry.emplace<velocity>(entity);
}
auto test = [&registry](auto func) {
auto test = [&](auto func) {
timer timer;
registry.group<position, velocity>().each(func);
group.each(func);
timer.elapsed();
};
@@ -299,19 +302,19 @@ TEST(Benchmark, IterateTwoComponentsFullOwningGroup1M) {
TEST(Benchmark, IterateTwoComponentsPartialOwningGroup1M) {
entt::registry registry;
registry.group<position>(entt::get<velocity>);
const auto group = registry.group<position>(entt::get<velocity>);
std::cout << "Iterating over 1000000 entities, two components, partial owning group" << std::endl;
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<position>(entity);
registry.assign<velocity>(entity);
registry.emplace<position>(entity);
registry.emplace<velocity>(entity);
}
auto test = [&registry](auto func) {
auto test = [&](auto func) {
timer timer;
registry.group<position>(entt::get<velocity>).each(func);
group.each(func);
timer.elapsed();
};
@@ -327,12 +330,15 @@ TEST(Benchmark, IterateTwoComponentsRuntime1M) {
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<position>(entity);
registry.assign<velocity>(entity);
registry.emplace<position>(entity);
registry.emplace<velocity>(entity);
}
auto test = [&registry](auto func) {
entt::component types[] = { registry.type<position>(), registry.type<velocity>() };
auto test = [&](auto func) {
entt::id_type types[] = {
entt::type_info<position>::id(),
entt::type_info<velocity>::id()
};
timer timer;
registry.runtime_view(std::begin(types), std::end(types)).each(func);
@@ -352,15 +358,18 @@ TEST(Benchmark, IterateTwoComponentsRuntime1MHalf) {
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<velocity>(entity);
registry.emplace<velocity>(entity);
if(i % 2) {
registry.assign<position>(entity);
registry.emplace<position>(entity);
}
}
auto test = [&registry](auto func) {
entt::component types[] = { registry.type<position>(), registry.type<velocity>() };
auto test = [&](auto func) {
entt::id_type types[] = {
entt::type_info<position>::id(),
entt::type_info<velocity>::id()
};
timer timer;
registry.runtime_view(std::begin(types), std::end(types)).each(func);
@@ -380,15 +389,18 @@ TEST(Benchmark, IterateTwoComponentsRuntime1MOne) {
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<velocity>(entity);
registry.emplace<velocity>(entity);
if(i == 500000L) {
registry.assign<position>(entity);
registry.emplace<position>(entity);
}
}
auto test = [&registry](auto func) {
entt::component types[] = { registry.type<position>(), registry.type<velocity>() };
auto test = [&](auto func) {
entt::id_type types[] = {
entt::type_info<position>::id(),
entt::type_info<velocity>::id()
};
timer timer;
registry.runtime_view(std::begin(types), std::end(types)).each(func);
@@ -408,12 +420,12 @@ TEST(Benchmark, IterateThreeComponents1M) {
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<position>(entity);
registry.assign<velocity>(entity);
registry.assign<comp<0>>(entity);
registry.emplace<position>(entity);
registry.emplace<velocity>(entity);
registry.emplace<comp<0>>(entity);
}
auto test = [&registry](auto func) {
auto test = [&](auto func) {
timer timer;
registry.view<position, velocity, comp<0>>().each(func);
timer.elapsed();
@@ -431,15 +443,15 @@ TEST(Benchmark, IterateThreeComponents1MHalf) {
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<velocity>(entity);
registry.assign<comp<0>>(entity);
registry.emplace<velocity>(entity);
registry.emplace<comp<0>>(entity);
if(i % 2) {
registry.assign<position>(entity);
registry.emplace<position>(entity);
}
}
auto test = [&registry](auto func) {
auto test = [&](auto func) {
timer timer;
registry.view<position, velocity, comp<0>>().each(func);
timer.elapsed();
@@ -457,15 +469,15 @@ TEST(Benchmark, IterateThreeComponents1MOne) {
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<velocity>(entity);
registry.assign<comp<0>>(entity);
registry.emplace<velocity>(entity);
registry.emplace<comp<0>>(entity);
if(i == 500000L) {
registry.assign<position>(entity);
registry.emplace<position>(entity);
}
}
auto test = [&registry](auto func) {
auto test = [&](auto func) {
timer timer;
registry.view<position, velocity, comp<0>>().each(func);
timer.elapsed();
@@ -478,20 +490,20 @@ TEST(Benchmark, IterateThreeComponents1MOne) {
TEST(Benchmark, IterateThreeComponentsNonOwningGroup1M) {
entt::registry registry;
registry.group<>(entt::get<position, velocity, comp<0>>);
const auto group = registry.group<>(entt::get<position, velocity, comp<0>>);
std::cout << "Iterating over 1000000 entities, three components, non owning group" << std::endl;
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<position>(entity);
registry.assign<velocity>(entity);
registry.assign<comp<0>>(entity);
registry.emplace<position>(entity);
registry.emplace<velocity>(entity);
registry.emplace<comp<0>>(entity);
}
auto test = [&registry](auto func) {
auto test = [&](auto func) {
timer timer;
registry.group<>(entt::get<position, velocity, comp<0>>).each(func);
group.each(func);
timer.elapsed();
};
@@ -502,20 +514,20 @@ TEST(Benchmark, IterateThreeComponentsNonOwningGroup1M) {
TEST(Benchmark, IterateThreeComponentsFullOwningGroup1M) {
entt::registry registry;
registry.group<position, velocity, comp<0>>();
const auto group = registry.group<position, velocity, comp<0>>();
std::cout << "Iterating over 1000000 entities, three components, full owning group" << std::endl;
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<position>(entity);
registry.assign<velocity>(entity);
registry.assign<comp<0>>(entity);
registry.emplace<position>(entity);
registry.emplace<velocity>(entity);
registry.emplace<comp<0>>(entity);
}
auto test = [&registry](auto func) {
auto test = [&](auto func) {
timer timer;
registry.group<position, velocity, comp<0>>().each(func);
group.each(func);
timer.elapsed();
};
@@ -526,20 +538,20 @@ TEST(Benchmark, IterateThreeComponentsFullOwningGroup1M) {
TEST(Benchmark, IterateThreeComponentsPartialOwningGroup1M) {
entt::registry registry;
registry.group<position, velocity>(entt::get<comp<0>>);
const auto group = registry.group<position, velocity>(entt::get<comp<0>>);
std::cout << "Iterating over 1000000 entities, three components, partial owning group" << std::endl;
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<position>(entity);
registry.assign<velocity>(entity);
registry.assign<comp<0>>(entity);
registry.emplace<position>(entity);
registry.emplace<velocity>(entity);
registry.emplace<comp<0>>(entity);
}
auto test = [&registry](auto func) {
auto test = [&](auto func) {
timer timer;
registry.group<position, velocity>(entt::get<comp<0>>).each(func);
group.each(func);
timer.elapsed();
};
@@ -555,13 +567,17 @@ TEST(Benchmark, IterateThreeComponentsRuntime1M) {
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<position>(entity);
registry.assign<velocity>(entity);
registry.assign<comp<0>>(entity);
registry.emplace<position>(entity);
registry.emplace<velocity>(entity);
registry.emplace<comp<0>>(entity);
}
auto test = [&registry](auto func) {
entt::component types[] = { registry.type<position>(), registry.type<velocity>(), registry.type<comp<0>>() };
auto test = [&](auto func) {
entt::id_type types[] = {
entt::type_info<position>::id(),
entt::type_info<velocity>::id(),
entt::type_info<comp<0>>::id()
};
timer timer;
registry.runtime_view(std::begin(types), std::end(types)).each(func);
@@ -582,16 +598,20 @@ TEST(Benchmark, IterateThreeComponentsRuntime1MHalf) {
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<velocity>(entity);
registry.assign<comp<0>>(entity);
registry.emplace<velocity>(entity);
registry.emplace<comp<0>>(entity);
if(i % 2) {
registry.assign<position>(entity);
registry.emplace<position>(entity);
}
}
auto test = [&registry](auto func) {
entt::component types[] = { registry.type<position>(), registry.type<velocity>(), registry.type<comp<0>>() };
auto test = [&](auto func) {
entt::id_type types[] = {
entt::type_info<position>::id(),
entt::type_info<velocity>::id(),
entt::type_info<comp<0>>::id()
};
timer timer;
registry.runtime_view(std::begin(types), std::end(types)).each(func);
@@ -612,16 +632,20 @@ TEST(Benchmark, IterateThreeComponentsRuntime1MOne) {
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<velocity>(entity);
registry.assign<comp<0>>(entity);
registry.emplace<velocity>(entity);
registry.emplace<comp<0>>(entity);
if(i == 500000L) {
registry.assign<position>(entity);
registry.emplace<position>(entity);
}
}
auto test = [&registry](auto func) {
entt::component types[] = { registry.type<position>(), registry.type<velocity>(), registry.type<comp<0>>() };
auto test = [&](auto func) {
entt::id_type types[] = {
entt::type_info<position>::id(),
entt::type_info<velocity>::id(),
entt::type_info<comp<0>>::id()
};
timer timer;
registry.runtime_view(std::begin(types), std::end(types)).each(func);
@@ -642,14 +666,14 @@ TEST(Benchmark, IterateFiveComponents1M) {
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<position>(entity);
registry.assign<velocity>(entity);
registry.assign<comp<0>>(entity);
registry.assign<comp<1>>(entity);
registry.assign<comp<2>>(entity);
registry.emplace<position>(entity);
registry.emplace<velocity>(entity);
registry.emplace<comp<0>>(entity);
registry.emplace<comp<1>>(entity);
registry.emplace<comp<2>>(entity);
}
auto test = [&registry](auto func) {
auto test = [&](auto func) {
timer timer;
registry.view<position, velocity, comp<0>, comp<1>, comp<2>>().each(func);
timer.elapsed();
@@ -667,17 +691,17 @@ TEST(Benchmark, IterateFiveComponents1MHalf) {
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<velocity>(entity);
registry.assign<comp<0>>(entity);
registry.assign<comp<1>>(entity);
registry.assign<comp<2>>(entity);
registry.emplace<velocity>(entity);
registry.emplace<comp<0>>(entity);
registry.emplace<comp<1>>(entity);
registry.emplace<comp<2>>(entity);
if(i % 2) {
registry.assign<position>(entity);
registry.emplace<position>(entity);
}
}
auto test = [&registry](auto func) {
auto test = [&](auto func) {
timer timer;
registry.view<position, velocity, comp<0>, comp<1>, comp<2>>().each(func);
timer.elapsed();
@@ -695,17 +719,17 @@ TEST(Benchmark, IterateFiveComponents1MOne) {
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<velocity>(entity);
registry.assign<comp<0>>(entity);
registry.assign<comp<1>>(entity);
registry.assign<comp<2>>(entity);
registry.emplace<velocity>(entity);
registry.emplace<comp<0>>(entity);
registry.emplace<comp<1>>(entity);
registry.emplace<comp<2>>(entity);
if(i == 500000L) {
registry.assign<position>(entity);
registry.emplace<position>(entity);
}
}
auto test = [&registry](auto func) {
auto test = [&](auto func) {
timer timer;
registry.view<position, velocity, comp<0>, comp<1>, comp<2>>().each(func);
timer.elapsed();
@@ -718,22 +742,22 @@ TEST(Benchmark, IterateFiveComponents1MOne) {
TEST(Benchmark, IterateFiveComponentsNonOwningGroup1M) {
entt::registry registry;
registry.group<>(entt::get<position, velocity, comp<0>, comp<1>, comp<2>>);
const auto group = registry.group<>(entt::get<position, velocity, comp<0>, comp<1>, comp<2>>);
std::cout << "Iterating over 1000000 entities, five components, non owning group" << std::endl;
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<position>(entity);
registry.assign<velocity>(entity);
registry.assign<comp<0>>(entity);
registry.assign<comp<1>>(entity);
registry.assign<comp<2>>(entity);
registry.emplace<position>(entity);
registry.emplace<velocity>(entity);
registry.emplace<comp<0>>(entity);
registry.emplace<comp<1>>(entity);
registry.emplace<comp<2>>(entity);
}
auto test = [&registry](auto func) {
auto test = [&](auto func) {
timer timer;
registry.group<>(entt::get<position, velocity, comp<0>, comp<1>, comp<2>>).each(func);
group.each(func);
timer.elapsed();
};
@@ -744,22 +768,22 @@ TEST(Benchmark, IterateFiveComponentsNonOwningGroup1M) {
TEST(Benchmark, IterateFiveComponentsFullOwningGroup1M) {
entt::registry registry;
registry.group<position, velocity, comp<0>, comp<1>, comp<2>>();
const auto group = registry.group<position, velocity, comp<0>, comp<1>, comp<2>>();
std::cout << "Iterating over 1000000 entities, five components, full owning group" << std::endl;
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<position>(entity);
registry.assign<velocity>(entity);
registry.assign<comp<0>>(entity);
registry.assign<comp<1>>(entity);
registry.assign<comp<2>>(entity);
registry.emplace<position>(entity);
registry.emplace<velocity>(entity);
registry.emplace<comp<0>>(entity);
registry.emplace<comp<1>>(entity);
registry.emplace<comp<2>>(entity);
}
auto test = [&registry](auto func) {
auto test = [&](auto func) {
timer timer;
registry.group<position, velocity, comp<0>, comp<1>, comp<2>>().each(func);
group.each(func);
timer.elapsed();
};
@@ -770,22 +794,22 @@ TEST(Benchmark, IterateFiveComponentsFullOwningGroup1M) {
TEST(Benchmark, IterateFiveComponentsPartialFourOfFiveOwningGroup1M) {
entt::registry registry;
registry.group<position, velocity, comp<0>, comp<1>>(entt::get<comp<2>>);
const auto group = registry.group<position, velocity, comp<0>, comp<1>>(entt::get<comp<2>>);
std::cout << "Iterating over 1000000 entities, five components, partial (4 of 5) owning group" << std::endl;
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<position>(entity);
registry.assign<velocity>(entity);
registry.assign<comp<0>>(entity);
registry.assign<comp<1>>(entity);
registry.assign<comp<2>>(entity);
registry.emplace<position>(entity);
registry.emplace<velocity>(entity);
registry.emplace<comp<0>>(entity);
registry.emplace<comp<1>>(entity);
registry.emplace<comp<2>>(entity);
}
auto test = [&registry](auto func) {
auto test = [&](auto func) {
timer timer;
registry.group<position, velocity, comp<0>, comp<1>>(entt::get<comp<2>>).each(func);
group.each(func);
timer.elapsed();
};
@@ -796,22 +820,22 @@ TEST(Benchmark, IterateFiveComponentsPartialFourOfFiveOwningGroup1M) {
TEST(Benchmark, IterateFiveComponentsPartialThreeOfFiveOwningGroup1M) {
entt::registry registry;
registry.group<position, velocity, comp<0>>(entt::get<comp<1>, comp<2>>);
const auto group = registry.group<position, velocity, comp<0>>(entt::get<comp<1>, comp<2>>);
std::cout << "Iterating over 1000000 entities, five components, partial (3 of 5) owning group" << std::endl;
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<position>(entity);
registry.assign<velocity>(entity);
registry.assign<comp<0>>(entity);
registry.assign<comp<1>>(entity);
registry.assign<comp<2>>(entity);
registry.emplace<position>(entity);
registry.emplace<velocity>(entity);
registry.emplace<comp<0>>(entity);
registry.emplace<comp<1>>(entity);
registry.emplace<comp<2>>(entity);
}
auto test = [&registry](auto func) {
auto test = [&](auto func) {
timer timer;
registry.group<position, velocity, comp<0>>(entt::get<comp<1>, comp<2>>).each(func);
group.each(func);
timer.elapsed();
};
@@ -827,20 +851,20 @@ TEST(Benchmark, IterateFiveComponentsRuntime1M) {
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<position>(entity);
registry.assign<velocity>(entity);
registry.assign<comp<0>>(entity);
registry.assign<comp<1>>(entity);
registry.assign<comp<2>>(entity);
registry.emplace<position>(entity);
registry.emplace<velocity>(entity);
registry.emplace<comp<0>>(entity);
registry.emplace<comp<1>>(entity);
registry.emplace<comp<2>>(entity);
}
auto test = [&registry](auto func) {
entt::component types[] = {
registry.type<position>(),
registry.type<velocity>(),
registry.type<comp<0>>(),
registry.type<comp<1>>(),
registry.type<comp<2>>()
auto test = [&](auto func) {
entt::id_type types[] = {
entt::type_info<position>::id(),
entt::type_info<velocity>::id(),
entt::type_info<comp<0>>::id(),
entt::type_info<comp<1>>::id(),
entt::type_info<comp<2>>::id()
};
timer timer;
@@ -864,23 +888,23 @@ TEST(Benchmark, IterateFiveComponentsRuntime1MHalf) {
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<velocity>(entity);
registry.assign<comp<0>>(entity);
registry.assign<comp<1>>(entity);
registry.assign<comp<2>>(entity);
registry.emplace<velocity>(entity);
registry.emplace<comp<0>>(entity);
registry.emplace<comp<1>>(entity);
registry.emplace<comp<2>>(entity);
if(i % 2) {
registry.assign<position>(entity);
registry.emplace<position>(entity);
}
}
auto test = [&registry](auto func) {
entt::component types[] = {
registry.type<position>(),
registry.type<velocity>(),
registry.type<comp<0>>(),
registry.type<comp<1>>(),
registry.type<comp<2>>()
auto test = [&](auto func) {
entt::id_type types[] = {
entt::type_info<position>::id(),
entt::type_info<velocity>::id(),
entt::type_info<comp<0>>::id(),
entt::type_info<comp<1>>::id(),
entt::type_info<comp<2>>::id()
};
timer timer;
@@ -904,23 +928,23 @@ TEST(Benchmark, IterateFiveComponentsRuntime1MOne) {
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.assign<velocity>(entity);
registry.assign<comp<0>>(entity);
registry.assign<comp<1>>(entity);
registry.assign<comp<2>>(entity);
registry.emplace<velocity>(entity);
registry.emplace<comp<0>>(entity);
registry.emplace<comp<1>>(entity);
registry.emplace<comp<2>>(entity);
if(i == 500000L) {
registry.assign<position>(entity);
registry.emplace<position>(entity);
}
}
auto test = [&registry](auto func) {
entt::component types[] = {
registry.type<position>(),
registry.type<velocity>(),
registry.type<comp<0>>(),
registry.type<comp<1>>(),
registry.type<comp<2>>()
auto test = [&](auto func) {
entt::id_type types[] = {
entt::type_info<position>::id(),
entt::type_info<velocity>::id(),
entt::type_info<comp<0>>::id(),
entt::type_info<comp<1>>::id(),
entt::type_info<comp<2>>::id()
};
timer timer;
@@ -990,7 +1014,7 @@ TEST(Benchmark, SortSingle) {
for(std::uint64_t i = 0; i < 150000L; i++) {
const auto entity = registry.create();
registry.assign<position>(entity, i, i);
registry.emplace<position>(entity, i, i);
}
timer timer;
@@ -1009,8 +1033,8 @@ TEST(Benchmark, SortMulti) {
for(std::uint64_t i = 0; i < 150000L; i++) {
const auto entity = registry.create();
registry.assign<position>(entity, i, i);
registry.assign<velocity>(entity, i, i);
registry.emplace<position>(entity, i, i);
registry.emplace<velocity>(entity, i, i);
}
registry.sort<position>([](const auto &lhs, const auto &rhs) {
@@ -1026,13 +1050,13 @@ TEST(Benchmark, SortMulti) {
TEST(Benchmark, AlmostSortedStdSort) {
entt::registry registry;
entt::entity entities[3];
entt::entity entities[3]{};
std::cout << "Sort 150000 entities, almost sorted, std::sort" << std::endl;
for(std::uint64_t i = 0; i < 150000L; i++) {
const auto entity = registry.create();
registry.assign<position>(entity, i, i);
registry.emplace<position>(entity, i, i);
if(!(i % 50000)) {
entities[i / 50000] = entity;
@@ -1042,7 +1066,7 @@ TEST(Benchmark, AlmostSortedStdSort) {
for(std::uint64_t i = 0; i < 3; ++i) {
registry.destroy(entities[i]);
const auto entity = registry.create();
registry.assign<position>(entity, 50000 * i, 50000 * i);
registry.emplace<position>(entity, 50000 * i, 50000 * i);
}
timer timer;
@@ -1056,13 +1080,13 @@ TEST(Benchmark, AlmostSortedStdSort) {
TEST(Benchmark, AlmostSortedInsertionSort) {
entt::registry registry;
entt::entity entities[3];
entt::entity entities[3]{};
std::cout << "Sort 150000 entities, almost sorted, insertion sort" << std::endl;
for(std::uint64_t i = 0; i < 150000L; i++) {
const auto entity = registry.create();
registry.assign<position>(entity, i, i);
registry.emplace<position>(entity, i, i);
if(!(i % 50000)) {
entities[i / 50000] = entity;
@@ -1072,7 +1096,7 @@ TEST(Benchmark, AlmostSortedInsertionSort) {
for(std::uint64_t i = 0; i < 3; ++i) {
registry.destroy(entities[i]);
const auto entity = registry.create();
registry.assign<position>(entity, 50000 * i, 50000 * i);
registry.emplace<position>(entity, 50000 * i, 50000 * i);
}
timer timer;

View File

View File

@@ -1,71 +0,0 @@
load("//bazel:copts.bzl", "entt_copts")
cc_test(
name = "algorithm",
srcs = ["algorithm.cpp"],
copts = entt_copts,
deps = [
"//:entt",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "family",
srcs = ["family.cpp"],
copts = entt_copts,
deps = [
"//:entt",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "hashed_string",
srcs = ["hashed_string.cpp"],
copts = entt_copts,
deps = [
"//:entt",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "ident",
srcs = ["ident.cpp"],
copts = entt_copts,
deps = [
"//:entt",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "monostate",
srcs = ["monostate.cpp"],
copts = entt_copts,
deps = [
"//:entt",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "type_traits",
srcs = ["type_traits.cpp"],
copts = entt_copts,
deps = [
"//:entt",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "utility",
srcs = ["utility.cpp"],
copts = entt_copts,
deps = [
"//:entt",
"@com_google_googletest//:gtest_main",
],
)

View File

@@ -16,7 +16,7 @@ TEST(Family, Functionalities) {
}
TEST(Family, Uniqueness) {
ASSERT_EQ(a_family::type<int>, a_family::type<int &>);
ASSERT_EQ(a_family::type<int>, a_family::type<int &&>);
ASSERT_EQ(a_family::type<int>, a_family::type<const int &>);
ASSERT_NE(a_family::type<int>, a_family::type<int &>);
ASSERT_NE(a_family::type<int>, a_family::type<int &&>);
ASSERT_NE(a_family::type<int>, a_family::type<const int &>);
}

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