Compare commits

..

257 Commits

Author SHA1 Message Date
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
158 changed files with 5514 additions and 5688 deletions

View File

@@ -5,14 +5,13 @@ on: [push, pull_request]
jobs:
linux:
timeout-minutes: 5
timeout-minutes: 10
strategy:
matrix:
os: [ubuntu-18.04]
compiler: [g++, clang++]
runs-on: ${{ matrix.os }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
@@ -30,16 +29,18 @@ jobs:
run: ctest --timeout 5 -C Debug -j4
windows:
timeout-minutes: 5
timeout-minutes: 10
strategy:
matrix:
os: [windows-2019, windows-2016]
os: [windows-latest, windows-2016]
toolset: [clang-cl, default]
include:
- os: windows-2019
generator: Visual Studio 16 2019
- toolset: clang-cl
toolset_option: -T"ClangCl"
exclude:
- os: windows-2016
generator: Visual Studio 15 2017
toolset: clang-cl
runs-on: ${{ matrix.os }}
@@ -48,7 +49,7 @@ jobs:
- name: Compile tests
working-directory: build
run: |
cmake -DBUILD_TESTING=ON -DBUILD_LIB=ON -DCMAKE_CXX_FLAGS=/W1 -G"${{ matrix.generator }}" ..
cmake -DBUILD_TESTING=ON -DBUILD_LIB=ON ${{ matrix.toolset_option }} ..
cmake --build . -j 4
- name: Run tests
working-directory: build
@@ -57,8 +58,8 @@ jobs:
run: ctest --timeout 5 -C Debug -j4
macos:
timeout-minutes: 5
runs-on: macOS-10.14
timeout-minutes: 10
runs-on: macOS-latest
steps:
- uses: actions/checkout@v1

View File

@@ -5,7 +5,7 @@ on: [push, pull_request]
jobs:
codecov:
timeout-minutes: 30
timeout-minutes: 10
runs-on: ubuntu-latest
steps:

View File

@@ -1,33 +0,0 @@
name: deploy
on:
push:
tags:
- v*
jobs:
conan:
timeout-minutes: 5
runs-on: ubuntu-latest
steps:
- uses: docker://conanio/gcc8
- uses: actions/checkout@v1
- name: Setup Python
uses: actions/setup-python@master
with:
version: 3.6
- name: Install
run: |
chmod +x conan/ci/install.sh
./conan/ci/install.sh
- name: Deploy
env:
CONAN_LOGIN_USERNAME: ${{ secrets.CONAN_LOGIN_USERNAME }}
CONAN_PASSWORD: ${{ secrets.CONAN_PASSWORD }}
CONAN_UPLOAD: ${{ secrets.CONAN_UPLOAD }}
CONAN_GCC_VERSIONS: 8
run: |
chmod +x conan/ci/build.sh
./conan/ci/build.sh

View File

@@ -1,9 +0,0 @@
load("//bazel:copts.bzl", "entt_copts")
cc_library(
name = "entt",
visibility = ["//visibility:public"],
strip_include_prefix = "src",
hdrs = glob(["src/**/*.h", "src/**/*.hpp"]),
copts = entt_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,31 +12,38 @@ 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.2.2 LANGUAGES CXX)
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("* Copyright (c) 2017-2020 Michele Caini <michele.caini@gmail.com>")
message("*")
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
@@ -66,37 +73,28 @@ endif()
# Add EnTT target
#
include(GNUInstallDirs)
add_library(EnTT INTERFACE)
add_library(EnTT::EnTT ALIAS EnTT)
configure_file(${EnTT_SOURCE_DIR}/cmake/in/version.h.in ${EnTT_SOURCE_DIR}/src/entt/config/version.h @ONLY)
target_include_directories(
EnTT INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
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
$<$<AND:$<CONFIG:Debug>,$<NOT:$<CXX_COMPILER_ID:MSVC>>>:DEBUG>
$<$<AND:$<CONFIG:Release>,$<NOT:$<CXX_COMPILER_ID:MSVC>>>:RELEASE>
)
if(USE_ASAN)
target_compile_options(EnTT INTERFACE $<$<AND:$<CONFIG:Debug>,$<NOT:$<PLATFORM_ID:Windows>>>:-fsanitize=address -fno-omit-frame-pointer>)
target_link_libraries(EnTT INTERFACE $<$<AND:$<CONFIG:Debug>,$<NOT:$<PLATFORM_ID:Windows>>>:-fsanitize=address -fno-omit-frame-pointer>)
endif()
if(USE_COMPILE_OPTIONS)
target_compile_options(
EnTT
INTERFACE $<$<AND:$<CONFIG:Debug>,$<NOT:$<PLATFORM_ID:Windows>>>:-O0 -g>
# it seems that -O3 ruins a bit the performance when using clang ...
INTERFACE $<$<AND:$<CONFIG:Release>,$<CXX_COMPILER_ID:Clang>,$<OR:$<PLATFORM_ID:Darwin>,$<PLATFORM_ID:Linux>>>:-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)
@@ -168,35 +166,11 @@ export(PACKAGE EnTT)
option(BUILD_TESTING "Enable testing with ctest." OFF)
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)
if(BUILD_MOD)
enable_language(C)
endif()
enable_testing()
add_subdirectory(test)
endif()
@@ -219,16 +193,16 @@ endif()
# AOB
#
FILE(GLOB GH_WORKFLOWS .github/workflows/*.yml)
add_custom_target(
entt_aob
SOURCES
${GH_WORKFLOWS}
.github/FUNDING.yml
AUTHORS
CONTRIBUTING.md
LICENSE
README.md
TODO
set(
AOB_SOURCES
.github/workflows/build.yml
.github/workflows/coverage.yml
.github/FUNDING.yml
AUTHORS
CONTRIBUTING.md
LICENSE
README.md
TODO
)
add_custom_target(aob SOURCES ${AOB_SOURCES})

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

View File

@@ -17,7 +17,9 @@ 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:
---
@@ -217,16 +219,9 @@ 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/>
In the documentation below I'll still refer to `CMake`, this being the official
build system of the library.
If you are looking for a C++14 version of `EnTT`, check out the git tag `cpp14`.
## Library
@@ -269,8 +264,8 @@ 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
version, that is the last stable tag.<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
@@ -295,11 +290,11 @@ 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/tree/master/ports/entt),
Microsoft VC++ Packaging Tool.<br/>
* [`vcpkg`](https://github.com/Microsoft/vcpkg), Microsoft VC++ Packaging
Tool.<br/>
You can download and install `EnTT` in just a few simple steps:
```
@@ -371,8 +366,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).

60
TODO
View File

@@ -1,40 +1,28 @@
* 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
* 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
* 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)
* 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
* built-in support for dual (or N-) buffering
* allow for custom stomp functions
* deprecate/replace snapshot
* hibitset, views and non-owning groups
* custom (decoupled) pools ==> double buffering, shared components, multi-model
* snapshot rework/deprecation
- create(hint: entity) -> force-create
- assign<T...>(first, last)
* use unordered_map for named pools and context variables:
- use direct access (pool-like) also for context variables
- allow for key/value variables where the key is an ENTT_ID_TYPE
- improves multi-stomp
* multi component registry::remove and some others?
- auto foo(It first, It last = entity_type{null})
* range based registry::remove and some others?
* allow to replace std:: with custom implementations
* custom (decoupled) pools ==> N-buffering, shared components, multi-model, hibitsets, and so on
* add examples (and credits) from @alanjfs :)
* explore the possibility to wrap other backend with a XHandler component
* use [[nodiscard]] consistently for safety purposes
* static reflection, hint: template<> meta_type_t<Type>: meta_descriptor<name, func..., props..., etc...>
* null support for entt::component
* add ENTT_CUSTOM_NAMED_TYPE for custom names
* Make another attempt to overcome named types
* meta: members+class as fake functions, is it possible?
* named types: almost-stable index optimization for direct access to pools, no more linear searches
* static reflection, hint: template<> meta_type_t<Type>: meta_descriptor<name, func..., props..., etc...> (see #342)
* observer: user defined filters (eg .replace<T, &function> or .group<T, U, &func>)
* use underlying_type as entity type within pools and registry? it would make different registries work together flawlessy
* can we write a bool conv func for entt::entity that silently compares it to null?
* reset... reset everywhere...
* is it possible to make 0 the entity null?
* document undocumented parts (entt::overload and a few others)
* any-of rule for views/groups (eg entity has A and any of B/C/D)
- get -> all, exclude -> none
* review prepare after clone and the others have been removed
* remove copy-ctor/op from sparse set and storage (it doesn't make much sense)
* WIP:
- deprecate snapshot, loader, ...
- provide documentation to describe alternatives
* WIP: snapshot rework/deprecation
- remove snapshot/loader from registry, make them external (faster) tools
- deprecate snapshot classes, update documentation to describe alternatives

View File

@@ -1,41 +0,0 @@
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 +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,20 +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
GIT_SHALLOW 1
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,20 +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
GIT_SHALLOW 1
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,20 +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
GIT_SHALLOW 1
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

2
deps/.gitignore vendored
View File

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

View File

@@ -2,30 +2,38 @@
# 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})
configure_file(doxy.in doxy.cfg @ONLY)
set(
DOC_SOURCES
dox/extra.dox
md/core.md
md/entity.md
md/faq.md
md/lib.md
md/links.md
md/locator.md
md/meta.md
md/process.md
md/resource.md
md/signal.md
doxy.in
)
add_custom_target(
docs ALL
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxy.cfg
WORKING_DIRECTORY ${EnTT_SOURCE_DIR}
VERBATIM
SOURCES doxy.in
SOURCES ${DOC_SOURCES}
)
install(
DIRECTORY ${DOXY_OUTPUT_DIRECTORY}/html
DESTINATION share/${PROJECT_NAME}-${PROJECT_VERSION}/
)
FILE(GLOB MD_FILES md/*.md)
add_custom_target(
docs_aob
SOURCES
dox/extra.dox
${MD_FILES}
)

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

View File

@@ -12,6 +12,11 @@
* [Wide characters](wide-characters)
* [Conflicts](#conflicts)
* [Monostate](#monostate)
* [Type info](#type-info)
* [Almost unique identifiers](#almost-unique-identifiers)
* [Traits](#traits)
* [Member class type](#member-class-type)
* [Tags](#tags)
<!--
@endcond TURN_OFF_DOXYGEN
-->
@@ -182,3 +187,110 @@ entt::monostate<"mykey"_hs>{} = 42;
const bool b = entt::monostate<"mykey"_hs>{};
const int i = entt::monostate<entt::hashed_string{"mykey"}>{};
```
# Type info
The `type_info` class template is meant to provide some basic information about
types of all kinds.<br/>
Currently, the only information available is 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 compilers).<br/>
This function **can** use non-standard features of the language for its own
purposes. This allows it to provide compile-time identifiers that remain stable
between different runs. However, it's possible to force the use of standard
features only by defining the macro `ENTT_STANDARD_CPP`. In this case, there is
no guarantee that the identifiers are stable across executions though. Moreover,
identifiers are generated at runtime and are no longer a compile-time thing.
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::void_d<decltype(Type::custom_id())>> {
static constexpr ENTT_ID_TYPE id() ENTT_NOEXCEPT {
return Type::custom_id();
}
};
```
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.<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/>
Since the default model is based on the name of the classes, if the types 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. Note that
runtime identifiers don't suffer from the sam problem. 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.
# Traits
This section contains a handful of utilities and traits not present in the
standard template library but which can be useful in everyday life.
## 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>;
```
## Tags
Since in `EnTT` the type identified by `ENTT_ID_TYPE` is very important and
widely used, 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.assign<entt::tag<"enemy"_hs>>(entity);
```
However, this isn't the only permitted use. Literally any value convertible to
`ENTT_ID_TYPE` is a good candidate, such as the named constants of an unscoped
enum.

File diff suppressed because it is too large Load Diff

View File

@@ -50,10 +50,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`
@@ -127,13 +132,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
```

View File

@@ -6,12 +6,9 @@
# 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)
* [Runtime reflection system](#runtime-reflection-system)
* [Allocations: the dark side of the force](#allocations-the-dark-side-of-the-force)
* [The EnTT way](#the-entt-way)
* [Meta context](#meta-context)
* [Memory management](#memory-management)
<!--
@endcond TURN_OFF_DOXYGEN
-->
@@ -19,178 +16,88 @@
# Introduction
`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 to 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.
As an example:
In general, this class doesn't arouse much interest. The only exception is in
case of conflicts between identifiers (definitely uncommon though) or where the
default solution proposed by `EnTT` isn't suitable for the user's purposes.<br/>
The section dedicated to this core class contains all the details to get around
the problem in a concise and elegant way. Please refer to the specific
documentation.
```cpp
struct my_type { /* ... */ };
template<>
struct entt::named_type_traits<my_type> {
static constexpr auto value = "my_type"_hs;
};
```
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:
```cpp
ENTT_NAMED_STRUCT(nested::ns, my_type, { /* struct definition */})
```
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.
## Conflicts
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.
# Runtime reflection system
# Meta context
The runtime reflection system deserves a special mention when it comes to using
it across boundaries.<br/>
As in all other cases, it's necessary to give a name to the types. However, this
time this isn't enough to get the job done.
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.
The runtime reflection system is linked to a static context to which the visible
components are linked. A component is visible when it's given a name, so the
named components are implicitly visible.<br/>
Different contexts don't relate to each other. Therefore, to allow the use of
named types across boundaries, a context must also be shared.
Sharing a context is trivial though. First of all, the local one must be
acquired in the main space:
Sharing a context is straightforward. First of all, the local one must be
acquired:
```
```cpp
entt::meta_ctx ctx{};
```
Then, it must be pushed across boundaries and the receiving space must 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:
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::meta_ctx::bind(ctx);
```
From now on, both spaces will refer to the same context and on it will be
associated the new visible meta types, no matter _where_ they are created.
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:
```
```cpp
entt::meta_ctx::bind{entt::meta_ctx{});
```
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

@@ -46,9 +46,15 @@ I hope this list can grow much more in the future:
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++.
* Engines and the like:
* [Fling Engine](https://github.com/flingengine/FlingEngine): A Vulkan game
* [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
@@ -60,12 +66,16 @@ 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.
* Articles and blog posts:
* [Some posts](https://skypjack.github.io/tags/#entt) on my personal
@@ -92,6 +102,8 @@ I hope this list can grow much more in the future:
[Collector](https://play.google.com/store/apps/details?id=com.esri.arcgis.collector)
and
[Navigator](https://play.google.com/store/apps/details?id=com.esri.navigator).
* [Sequentity](https://github.com/alanjfs/sequentity): A MIDI-like
sequencer/tracker for C++ and `ImGui` (with `Magnum` and `EnTT`).
* [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.
@@ -99,10 +111,10 @@ I hope this list can grow much more in the future:
[Tieto](https://www.tieto.com/): they published a job post where `EnTT` was
listed on their software stack.
* [Godot meets EnTT](https://github.com/portaloffreedom/godot_entt_example/):
A simple example on how to use `EnTT` within
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
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

View File

@@ -32,7 +32,7 @@ which it belongs and not vice versa.
# 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/>
@@ -227,12 +227,27 @@ entt::meta_any any{std::ref(value)};
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.
exceeds that of the container.<br/>
Similarly, to create a copy that works as a light reference for the managed
object, it's possible to dereference a given `meta_any`:
```cpp
entt::meta_any ref = *any;
```
It doesn't matter if the starting container actually holds an object or acts 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 has also 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.
`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
@@ -264,8 +279,8 @@ resolve([](auto type) {
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 or destroy instances of the
underlying type.<br/>
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:
@@ -279,20 +294,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_:
@@ -354,9 +358,8 @@ 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) {
@@ -364,17 +367,20 @@ 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
@@ -417,10 +423,10 @@ There are a few alternatives available at the moment:
```
* 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/>
It allows to build wrappers that act as aliases for the objects that generated
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/>
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).

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

View File

@@ -37,7 +37,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.
@@ -79,9 +79,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 +107,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 +142,24 @@ 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, note that members of a class 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.
# Signals
Signal handlers work with references to classes, function pointers and pointers
@@ -199,10 +217,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();
```

View File

@@ -3,54 +3,65 @@
#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_DISABLE_ETO
#include <type_traits>
#define ENTT_ENABLE_ETO(Type) std::is_empty_v<Type>
#else // ENTT_DISABLE_ETO
// sfinae-friendly definition
#define ENTT_ENABLE_ETO(Type) (false && std::is_empty_v<Type>)
#endif // ENTT_DISABLE_ETO
#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_DISABLE_ETO
# include <type_traits>
# define ENTT_ENABLE_ETO(Type) std::is_empty_v<Type>
#else
# // sfinae-friendly definition
# define ENTT_ENABLE_ETO(Type) (false && std::is_empty_v<Type>)
#endif
#ifndef ENTT_STANDARD_CPP
# if defined _MSC_VER
# define ENTT_PRETTY_FUNCTION __FUNCSIG__
# define ENTT_PRETTY_FUNCTION_CONSTEXPR ENTT_PRETTY_FUNCTION
# elif defined __clang__ || (defined __GNUC__ && __GNUC__ > 8)
# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
# define ENTT_PRETTY_FUNCTION_CONSTEXPR ENTT_PRETTY_FUNCTION
# elif defined __GNUC__
# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
# endif
#endif
#endif

View File

@@ -2,10 +2,9 @@
#define ENTT_CONFIG_VERSION_H
#define ENTT_VERSION "3.2.2"
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 2
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION_MINOR 3
#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>
@@ -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,7 +2,6 @@
#define ENTT_CORE_FAMILY_HPP
#include <type_traits>
#include "../config/config.h"
@@ -34,4 +33,4 @@ public:
}
#endif // ENTT_CORE_FAMILY_HPP
#endif

View File

@@ -3,6 +3,7 @@
#include <cstddef>
#include <cstdint>
#include "../config/config.h"
@@ -24,6 +25,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 +33,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;
};
@@ -67,8 +70,14 @@ 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);
static constexpr ENTT_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:
@@ -93,8 +102,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);
static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT {
return helper(str);
}
/**
@@ -102,8 +111,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);
static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT {
return helper(wrapper.str);
}
/**
@@ -112,7 +121,7 @@ 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 {
static hash_type value(const value_type *str, std::size_t size) ENTT_NOEXCEPT {
ENTT_ID_TYPE partial{traits_type::offset};
while(size--) { partial = (partial^(str++)[0])*traits_type::prime; }
return partial;
@@ -139,7 +148,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,7 +157,7 @@ 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)}
{}
/**
@@ -167,14 +176,14 @@ public:
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 */
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.
*/
constexpr operator hash_type() const ENTT_NOEXCEPT { return value(); }
/**
* @brief Compares two hashed strings.
@@ -250,4 +259,4 @@ constexpr entt::hashed_wstring operator"" ENTT_HWS_SUFFIX(const wchar_t *str, st
}
#endif // ENTT_CORE_HASHED_STRING_HPP
#endif

View File

@@ -3,6 +3,7 @@
#include <tuple>
#include <cstddef>
#include <utility>
#include <type_traits>
#include "../config/config.h"
@@ -43,7 +44,7 @@ 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 constexpr ENTT_ID_TYPE get(std::index_sequence<Indexes...>) {
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{}));
}
@@ -61,4 +62,4 @@ public:
}
#endif // ENTT_CORE_IDENT_HPP
#endif

View File

@@ -2,7 +2,6 @@
#define ENTT_CORE_MONOSTATE_HPP
#include <cassert>
#include "../config/config.h"
@@ -59,4 +58,4 @@ inline monostate<Value> monostate_v = {};
}
#endif // ENTT_CORE_MONOSTATE_HPP
#endif

View File

@@ -0,0 +1,80 @@
#ifndef ENTT_CORE_TYPE_INFO_HPP
#define ENTT_CORE_TYPE_INFO_HPP
#include "../config/config.h"
#include "../core/attribute.h"
#include "hashed_string.hpp"
#ifndef ENTT_PRETTY_FUNCTION
# define ENTT_TYPE_ID_API ENTT_API
#else
# define ENTT_TYPE_ID_API
#endif
namespace entt {
#ifndef ENTT_PRETTY_FUNCTION
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
struct ENTT_API type_id_generator {
static ENTT_ID_TYPE next() ENTT_NOEXCEPT {
static ENTT_ID_TYPE value{};
return value++;
}
};
}
/**
* Internal details not to be documented.
* @endcond TURN_OFF_DOXYGEN
*/
#endif
/**
* @brief Types identifiers.
* @tparam Type Type for which to generate an identifier.
*/
template<typename Type, typename = void>
struct ENTT_TYPE_ID_API 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
static constexpr ENTT_ID_TYPE id() ENTT_NOEXCEPT {
constexpr auto value = entt::hashed_string::value(ENTT_PRETTY_FUNCTION_CONSTEXPR);
return value;
}
#elif defined ENTT_PRETTY_FUNCTION
static ENTT_ID_TYPE id() ENTT_NOEXCEPT {
static const auto value = entt::hashed_string::value(ENTT_PRETTY_FUNCTION);
return value;
}
#else
static ENTT_ID_TYPE id() ENTT_NOEXCEPT {
static const ENTT_ID_TYPE value = internal::type_id_generator::next();
return value;
}
#endif
};
}
#endif

View File

@@ -3,6 +3,7 @@
#include <cstddef>
#include <utility>
#include <type_traits>
#include "../config/config.h"
#include "../core/hashed_string.hpp"
@@ -169,57 +170,46 @@ template<class Type>
constexpr auto is_equality_comparable_v = is_equality_comparable<Type>::value;
/*! @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 Extracts the class of a non-static member object or function.
* @tparam Member A pointer to a non-static member object or function.
*/
template<typename Type>
struct named_type_traits<const Type>
: named_type_traits<Type>
{};
template<typename Member>
class member_class {
static_assert(std::is_member_pointer_v<Member>);
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 Type Potentially named type.
* @tparam Member A pointer to a non-static member object or function.
*/
template<typename Type>
using named_type_traits_t = typename named_type_traits<Type>::type;
template<typename Member>
using member_class_t = typename member_class<Member>::type;
/**
* @brief Helper variable template.
* @tparam Type Potentially named type.
* @brief Alias template to ease the creation of named values.
* @tparam Value A constant value at least convertible to `ENTT_ID_TYPE`.
*/
template<class Type>
constexpr auto named_type_traits_v = named_type_traits<Type>::value;
template<ENTT_ID_TYPE Value>
using tag = std::integral_constant<ENTT_ID_TYPE, Value>;
/**
* @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, typename = std::void_t<>>
struct is_named_type: std::false_type {};
/*! @copydoc is_named_type */
template<typename Type>
struct is_named_type<Type, std::void_t<named_type_traits_t<std::decay_t<Type>>>>: std::true_type {};
/**
* @brief Helper variable template.
* @tparam Type Potentially named type.
*/
template<class Type>
constexpr auto is_named_type_v = is_named_type<Type>::value;
}
/**
@@ -230,104 +220,10 @@ constexpr auto is_named_type_v = is_named_type<Type>::value;
*/
#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);\
constexpr auto to_integral(const clazz id) ENTT_NOEXCEPT {\
return static_cast<std::underlying_type_t<clazz>>(id);\
}\
static_assert(true)
}
/**
* @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::remove_cv_t<type>, type>);\
static_assert(std::is_object_v<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"
@@ -24,11 +25,11 @@ struct identity {
/**
* @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; }
@@ -36,12 +37,12 @@ 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>
constexpr auto overload(Func *func) ENTT_NOEXCEPT { return func; }
/**
@@ -58,8 +59,8 @@ struct overloaded: Func... {
* @brief Deduction guide.
* @tparam Func Types of function objects.
*/
template<class... Type>
overloaded(Type...) -> overloaded<Type...>;
template<class... Func>
overloaded(Func...) -> overloaded<Func...>;
/**
@@ -101,4 +102,4 @@ private:
}
#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"
@@ -35,6 +34,21 @@ struct basic_actor {
: entt{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);
@@ -130,7 +129,7 @@ struct basic_actor {
* @return True if the actor has all the components, false otherwise.
*/
template<typename... Component>
bool has() const ENTT_NOEXCEPT {
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 {
decltype(auto) get() const {
return std::as_const(*reg).template get<Component...>(entt);
}
/*! @copydoc get */
template<typename... Component>
decltype(auto) get() ENTT_NOEXCEPT {
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 {
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 {
auto try_get() {
return reg->template try_get<Component...>(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 {
explicit operator bool() const {
return reg && reg->valid(entt);
}
@@ -204,4 +203,4 @@ private:
}
#endif // ENTT_ENTITY_ACTOR_HPP
#endif

View File

@@ -5,6 +5,7 @@
#include <cstdint>
#include <type_traits>
#include "../config/config.h"
#include "../core/type_traits.hpp"
namespace entt {
@@ -127,7 +128,7 @@ public:
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));
return (to_integral(entity) & traits_type<Entity>::entity_mask) == to_integral(static_cast<Entity>(*this));
}
template<typename Entity>
@@ -171,4 +172,4 @@ constexpr auto null = internal::null{};
}
#endif // ENTT_ENTITY_ENTITY_HPP
#endif

View File

@@ -8,6 +8,39 @@
namespace entt {
/**
* @brief Alias for exclusion lists.
* @tparam Type List of types.
*/
template<typename... Type>
struct exclude_t: type_list<Type...> {};
/**
* @brief Variable template for exclusion lists.
* @tparam Type List of types.
*/
template<typename... Type>
constexpr exclude_t<Type...> exclude{};
/**
* @brief Alias for lists of observed components.
* @tparam Type List of types.
*/
template<typename... Type>
struct get_t: type_list<Type...>{};
/**
* @brief Variable template for lists of observed components.
* @tparam Type List of types.
*/
template<typename... Type>
constexpr get_t<Type...> get{};
/*! @class basic_registry */
template <typename>
class basic_registry;
@@ -47,9 +80,6 @@ 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 Alias declaration for the most common use case. */
using registry = basic_registry<entity>;
@@ -89,4 +119,4 @@ using group = basic_group<entity, Types...>;
}
#endif // ENTT_ENTITY_FWD_HPP
#endif

View File

@@ -9,7 +9,6 @@
#include "../core/type_traits.hpp"
#include "sparse_set.hpp"
#include "storage.hpp"
#include "utility.hpp"
#include "fwd.hpp"
@@ -29,11 +28,9 @@ class basic_group;
/**
* @brief Non-owning group.
*
* A non-owning group returns all the entities and only the entities that have
* at least the given components. Moreover, it's guaranteed that the entity list
* is tightly packed in memory for fast iterations.<br/>
* In general, non-owning groups don't stay true to the order of any set of
* components unless users explicitly sort them.
* A non-owning group returns all entities and only the entities that have at
* least the given components. Moreover, it's guaranteed that the entity list
* is tightly packed in memory for fast iterations.
*
* @b Important
*
@@ -44,18 +41,17 @@ class basic_group;
* given components is removed from the entity to which the iterator points).
* * The entity currently pointed is destroyed.
*
* In all the other cases, modifying the pools of the given components in any
* way invalidates all the iterators and using them results in undefined
* behavior.
* In all other cases, modifying the pools iterated by the group in any way
* invalidates all the iterators and using them results in undefined behavior.
*
* @note
* Groups share references to the underlying data structures of the registry
* that generated them. Therefore any change to the entities and to the
* components made by means of the registry are immediately reflected by all the
* groups.<br/>
* Moreover, sorting a non-owning group affects all the instance of the same
* Moreover, sorting a non-owning group affects all the instances of the same
* group (it means that users don't have to call `sort` on each instance to sort
* all of them because they share the set of entities).
* all of them because they _share_ entities and components).
*
* @warning
* Lifetime of a group must overcome the one of the registry that generated it.
@@ -73,10 +69,10 @@ class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>> {
template<typename Component>
using pool_type = std::conditional_t<std::is_const_v<Component>, const storage<Entity, std::remove_const_t<Component>>, storage<Entity, Component>>;
// we could use pool_type<Type> *..., but vs complains about it and refuses to compile for unknown reasons (most likely a bug)
basic_group(sparse_set<Entity> *ref, storage<Entity, std::remove_const_t<Get>> *... gpool) ENTT_NOEXCEPT
: handler{ref},
pools{gpool...}
// we could use pool_type<Type> &..., but vs complains about it and refuses to compile for unknown reasons (most likely a bug)
basic_group(sparse_set<Entity> &ref, storage<Entity, std::remove_const_t<Get>> &... gpool) ENTT_NOEXCEPT
: handler{&ref},
pools{&gpool...}
{}
template<typename Func, typename... Weak>
@@ -131,11 +127,9 @@ public:
}
/**
* @brief Checks whether the group or the pools of the given components are
* empty.
* @brief Checks whether a group or some pools are empty.
* @tparam Component Types of components in which one is interested.
* @return True if the group or the pools of the given components are empty,
* false otherwise.
* @return True if the group or the pools are empty, false otherwise.
*/
template<typename... Component>
bool empty() const ENTT_NOEXCEPT {
@@ -237,13 +231,33 @@ public:
return handler->end();
}
/**
* @brief Returns the first entity that has the given components, if any.
* @return The first entity that has the given components if one exists, the
* null entity otherwise.
*/
entity_type front() const {
const auto it = begin();
return it != end() ? *it : null;
}
/**
* @brief Returns the last entity that has the given components, if any.
* @return The last entity that has the given components if one exists, the
* null entity otherwise.
*/
entity_type back() const {
const auto it = std::make_reverse_iterator(end());
return it != std::make_reverse_iterator(begin()) ? *it : null;
}
/**
* @brief Finds an entity.
* @param entt A valid entity identifier.
* @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 {
iterator_type find(const entity_type entt) const {
const auto it = handler->find(entt);
return it != end() && *it == entt ? it : end();
}
@@ -253,7 +267,7 @@ public:
* @param pos Position of the element to return.
* @return The identifier that occupies the given position.
*/
entity_type operator[](const size_type pos) const ENTT_NOEXCEPT {
entity_type operator[](const size_type pos) const {
return begin()[pos];
}
@@ -262,15 +276,15 @@ public:
* @param entt A valid entity identifier.
* @return True if the group contains the given entity, false otherwise.
*/
bool contains(const entity_type entt) const ENTT_NOEXCEPT {
return find(entt) != end();
bool contains(const entity_type entt) const {
return handler->has(entt);
}
/**
* @brief Returns the components assigned to the given entity.
*
* Prefer this function instead of `registry::get` during iterations. It has
* far better performance than its companion function.
* far better performance than its counterpart.
*
* @warning
* Attempting to use an invalid component type results in a compilation
@@ -284,7 +298,7 @@ public:
* @return The components assigned to the entity.
*/
template<typename... Component>
decltype(auto) get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT {
decltype(auto) get([[maybe_unused]] const entity_type entt) const {
ENTT_ASSERT(contains(entt));
if constexpr(sizeof...(Component) == 1) {
@@ -409,7 +423,7 @@ public:
* @brief Sort the shared pool of entities according to the given component.
*
* Non-owning groups of the same type share with the registry a pool of
* entities with its own order that doesn't depend on the order of any pool
* entities with its own order that doesn't depend on the order of any pool
* of components. Users can order the underlying data structure so that it
* respects the order of the pool of the given component.
*
@@ -435,15 +449,15 @@ private:
/**
* @brief Owning group.
*
* Owning groups return all the entities and only the entities that have at
* least the given components. Moreover:
* Owning groups return all entities and only the entities that have at least
* the given components. Moreover:
*
* * It's guaranteed that the entity list is tightly packed in memory for fast
* iterations.
* * It's guaranteed that the lists of owned components are tightly packed in
* memory for even faster iterations and to allow direct access.
* * They stay true to the order of the owned components and all the owned
* components have the same order in memory.
* * They stay true to the order of the owned components and all instances have
* the same order in memory.
*
* The more types of components are owned by a group, the faster it is to
* iterate them.
@@ -457,9 +471,8 @@ private:
* given components is removed from the entity to which the iterator points).
* * The entity currently pointed is destroyed.
*
* In all the other cases, modifying the pools of the given components in any
* way invalidates all the iterators and using them results in undefined
* behavior.
* In all other cases, modifying the pools iterated by the group in any way
* invalidates all the iterators and using them results in undefined behavior.
*
* @note
* Groups share references to the underlying data structures of the registry
@@ -490,11 +503,11 @@ class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>, Owned...> {
template<typename Component>
using component_iterator_type = decltype(std::declval<pool_type<Component>>().begin());
// we could use pool_type<Type> *..., but vs complains about it and refuses to compile for unknown reasons (most likely a bug)
basic_group(const std::size_t *ref, const std::size_t *extent, storage<Entity, std::remove_const_t<Owned>> *... opool, storage<Entity, std::remove_const_t<Get>> *... gpool) ENTT_NOEXCEPT
: pools{opool..., gpool...},
length{extent},
super{ref}
// we could use pool_type<Type> &..., but vs complains about it and refuses to compile for unknown reasons (most likely a bug)
basic_group(const std::size_t &ref, const std::size_t &extent, storage<Entity, std::remove_const_t<Owned>> &... opool, storage<Entity, std::remove_const_t<Get>> &... gpool) ENTT_NOEXCEPT
: pools{&opool..., &gpool...},
length{&extent},
super{&ref}
{}
template<typename Func, typename... Strong, typename... Weak>
@@ -544,11 +557,9 @@ public:
}
/**
* @brief Checks whether the group or the pools of the given components are
* empty.
* @brief Checks whether a group or some pools are empty.
* @tparam Component Types of components in which one is interested.
* @return True if the group or the pools of the given components are empty,
* false otherwise.
* @return True if the group or the pools are empty, false otherwise.
*/
template<typename... Component>
bool empty() const ENTT_NOEXCEPT {
@@ -656,13 +667,33 @@ public:
return std::get<0>(pools)->sparse_set<entity_type>::end();
}
/**
* @brief Returns the first entity that has the given components, if any.
* @return The first entity that has the given components if one exists, the
* null entity otherwise.
*/
entity_type front() const {
const auto it = begin();
return it != end() ? *it : null;
}
/**
* @brief Returns the last entity that has the given components, if any.
* @return The last entity that has the given components if one exists, the
* null entity otherwise.
*/
entity_type back() const {
const auto it = std::make_reverse_iterator(end());
return it != std::make_reverse_iterator(begin()) ? *it : null;
}
/**
* @brief Finds an entity.
* @param entt A valid entity identifier.
* @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 {
iterator_type find(const entity_type entt) const {
const auto it = std::get<0>(pools)->find(entt);
return it != end() && it >= begin() && *it == entt ? it : end();
}
@@ -672,7 +703,7 @@ public:
* @param pos Position of the element to return.
* @return The identifier that occupies the given position.
*/
entity_type operator[](const size_type pos) const ENTT_NOEXCEPT {
entity_type operator[](const size_type pos) const {
return begin()[pos];
}
@@ -681,15 +712,15 @@ public:
* @param entt A valid entity identifier.
* @return True if the group contains the given entity, false otherwise.
*/
bool contains(const entity_type entt) const ENTT_NOEXCEPT {
return find(entt) != end();
bool contains(const entity_type entt) const {
return std::get<0>(pools)->has(entt) && (std::get<0>(pools)->index(entt) < (*length));
}
/**
* @brief Returns the components assigned to the given entity.
*
* Prefer this function instead of `registry::get` during iterations. It has
* far better performance than its companion function.
* far better performance than its counterpart.
*
* @warning
* Attempting to use an invalid component type results in a compilation
@@ -703,7 +734,7 @@ public:
* @return The components assigned to the entity.
*/
template<typename... Component>
decltype(auto) get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT {
decltype(auto) get([[maybe_unused]] const entity_type entt) const {
ENTT_ASSERT(contains(entt));
if constexpr(sizeof...(Component) == 1) {
@@ -856,4 +887,4 @@ private:
}
#endif // ENTT_ENTITY_GROUP_HPP
#endif

View File

@@ -3,9 +3,10 @@
#include <type_traits>
#include "../core/type_traits.hpp"
#include "../config/config.h"
#include "../signal/sigh.hpp"
#include "registry.hpp"
#include "fwd.hpp"
namespace entt {
@@ -110,30 +111,7 @@ 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.
*/
template<ENTT_ID_TYPE Value>
using tag = std::integral_constant<ENTT_ID_TYPE, Value>;
}
#endif // ENTT_ENTITY_HELPER_HPP
#endif

View File

@@ -6,7 +6,6 @@
#include <cstddef>
#include <cstdint>
#include <utility>
#include <algorithm>
#include <type_traits>
#include "../config/config.h"
#include "../core/type_traits.hpp"
@@ -177,15 +176,18 @@ class basic_observer {
template<typename... Reject, typename... Require, typename 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) {
static void maybe_valid_if(basic_observer &obs, const basic_registry<Entity> &reg, const Entity entt) {
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);
if(auto *comp = obs.view.try_get(entt); !comp) {
obs.view.construct(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);
}
@@ -210,15 +212,18 @@ class basic_observer {
template<typename... Reject, typename... Require, typename... NoneOf, typename... 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) {
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 has<NoneOf>(entt) || ...) && !(reg.template has<Reject>(entt) || ...)) {
auto *comp = obs.view.try_get(entt);
(comp ? *comp : obs.view.construct(entt)) |= (1 << Index);
if(auto *comp = obs.view.try_get(entt); !comp) {
obs.view.construct(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);
}
@@ -265,7 +270,7 @@ public:
using iterator_type = typename sparse_set<Entity>::iterator_type;
/*! @brief Default constructor. */
basic_observer() ENTT_NOEXCEPT
basic_observer()
: target{}, release{}, view{}
{}
@@ -280,7 +285,7 @@ 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{}
@@ -313,7 +318,7 @@ public:
disconnect();
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. */
@@ -382,14 +387,13 @@ public:
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:
@@ -404,19 +408,17 @@ 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.
@@ -437,4 +439,4 @@ private:
}
#endif // ENTT_ENTITY_OBSERVER_HPP
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,6 @@
#include <iterator>
#include <cassert>
#include <vector>
#include <utility>
#include <algorithm>
@@ -65,20 +64,20 @@ class basic_runtime_view {
class iterator {
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}
using direct_type = std::vector<const sparse_set<Entity> *>;
iterator(const direct_type *all, underlying_iterator_type curr) ENTT_NOEXCEPT
: pools{all},
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);
bool valid() const {
return std::all_of(pools->begin()++, pools->end(), [entt = *it](const auto *curr) {
return curr->has(entt);
});
}
@@ -87,40 +86,49 @@ class basic_runtime_view {
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 iterator_category = std::bidirectional_iterator_tag;
iterator() ENTT_NOEXCEPT = default;
iterator & operator++() ENTT_NOEXCEPT {
return (++begin != end && !valid()) ? ++(*this) : *this;
iterator & operator++() {
while(++it != (*pools)[0]->end() && !valid());
return *this;
}
iterator operator++(int) ENTT_NOEXCEPT {
iterator operator++(int) {
iterator orig = *this;
return ++(*this), orig;
return operator++(), orig;
}
iterator & operator--() ENTT_NOEXCEPT {
while(--it != (*pools)[0]->begin() && !valid());
return *this;
}
iterator operator--(int) ENTT_NOEXCEPT {
iterator orig = *this;
return operator--(), orig;
}
bool operator==(const iterator &other) const ENTT_NOEXCEPT {
return other.begin == begin;
return other.it == it;
}
bool operator!=(const iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
pointer operator->() const ENTT_NOEXCEPT {
return begin.operator->();
pointer operator->() const {
return it.operator->();
}
reference operator*() const ENTT_NOEXCEPT {
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 direct_type *pools;
underlying_iterator_type it;
};
basic_runtime_view(std::vector<const sparse_set<Entity> *> others) ENTT_NOEXCEPT
@@ -134,7 +142,7 @@ class basic_runtime_view {
std::rotate(pools.begin(), it, pools.end());
}
bool valid() const ENTT_NOEXCEPT {
bool valid() const {
return !pools.empty() && pools.front();
}
@@ -150,7 +158,7 @@ public:
* @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 {
size_type size() const {
return valid() ? pools.front()->size() : size_type{};
}
@@ -158,7 +166,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 {
bool empty() const {
return !valid() || pools.front()->empty();
}
@@ -176,13 +184,11 @@ public:
*
* @return An iterator to the first entity that has the given components.
*/
iterator_type begin() const ENTT_NOEXCEPT {
iterator_type begin() const {
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() };
it = { &pools, pools[0]->begin() };
}
return it;
@@ -203,12 +209,11 @@ public:
* @return An iterator to the entity following the last entity that has the
* given components.
*/
iterator_type end() const ENTT_NOEXCEPT {
iterator_type end() const {
iterator_type it{};
if(valid()) {
const auto &pool = *pools.front();
it = { pool.end(), pool.end(), nullptr, nullptr };
it = { &pools, pools[0]->end() };
}
return it;
@@ -219,7 +224,7 @@ 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 {
bool contains(const entity_type entt) const {
return valid() && std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *view) {
return view->find(entt) != view->end();
});
@@ -242,7 +247,9 @@ public:
*/
template<typename Func>
void each(Func func) const {
std::for_each(begin(), end(), func);
for(const auto entity: *this) {
func(entity);
}
}
private:
@@ -253,4 +260,4 @@ private:
}
#endif // ENTT_ENTITY_RUNTIME_VIEW_HPP
#endif

View File

@@ -388,7 +388,7 @@ class basic_continuous_loader {
}
template<typename Other, typename Type, typename Member>
void update(Other &instance, Member Type:: *member) {
void update([[maybe_unused]] Other &instance, [[maybe_unused]] Member Type:: *member) {
if constexpr(!std::is_same_v<Other, Type>) {
return;
} else if constexpr(std::is_same_v<Member, Entity>) {
@@ -417,7 +417,7 @@ class basic_continuous_loader {
const auto local = ref.second.first;
if(reg->valid(local)) {
reg->template reset<Component>(local);
reg->template remove_if_exists<Component>(local);
}
}
}
@@ -602,4 +602,4 @@ private:
}
#endif // ENTT_ENTITY_SNAPSHOT_HPP
#endif

View File

@@ -8,7 +8,6 @@
#include <vector>
#include <memory>
#include <cstddef>
#include <numeric>
#include <type_traits>
#include "../config/config.h"
#include "../core/algorithm.hpp"
@@ -56,10 +55,10 @@ class sparse_set {
class iterator {
friend class sparse_set<Entity>;
using direct_type = const std::vector<Entity>;
using direct_type = std::vector<Entity>;
using index_type = typename traits_type::difference_type;
iterator(direct_type *ref, const index_type idx) ENTT_NOEXCEPT
iterator(const direct_type *ref, const index_type idx) ENTT_NOEXCEPT
: direct{ref}, index{idx}
{}
@@ -78,7 +77,7 @@ class sparse_set {
iterator operator++(int) ENTT_NOEXCEPT {
iterator orig = *this;
return ++(*this), orig;
return operator++(), orig;
}
iterator & operator--() ENTT_NOEXCEPT {
@@ -87,7 +86,7 @@ class sparse_set {
iterator operator--(int) ENTT_NOEXCEPT {
iterator orig = *this;
return --(*this), orig;
return operator--(), orig;
}
iterator & operator+=(const difference_type value) ENTT_NOEXCEPT {
@@ -111,7 +110,7 @@ class sparse_set {
return other.index - index;
}
reference operator[](const difference_type value) const ENTT_NOEXCEPT {
reference operator[](const difference_type value) const {
const auto pos = size_type(index-value-1);
return (*direct)[pos];
}
@@ -140,37 +139,40 @@ class sparse_set {
return !(*this < other);
}
pointer operator->() const ENTT_NOEXCEPT {
pointer operator->() const {
const auto pos = size_type(index-1);
return &(*direct)[pos];
}
reference operator*() const ENTT_NOEXCEPT {
reference operator*() const {
return *operator->();
}
private:
direct_type *direct;
const direct_type *direct;
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);
}
auto page(const Entity entt) const ENTT_NOEXCEPT {
return std::size_t{(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);
auto offset(const Entity entt) const ENTT_NOEXCEPT {
return std::size_t{to_integral(entt) & (entt_per_page - 1)};
}
Entity * assure(const std::size_t pos) {
if(!(pos < reverse.size())) {
reverse.resize(pos+1);
}
if(!reverse[pos]) {
reverse[pos] = std::make_unique<entity_type[]>(entt_per_page);
// null is safe in all cases for our purposes
std::fill_n(reverse[pos].get(), entt_per_page, null);
}
return reverse[pos].get();
}
public:
@@ -194,8 +196,7 @@ public:
{
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());
std::copy_n(other.reverse[pos].get(), entt_per_page, assure(pos));
}
}
}
@@ -204,7 +205,7 @@ public:
sparse_set(sparse_set &&) = default;
/*! @brief Default destructor. */
virtual ~sparse_set() ENTT_NOEXCEPT = default;
virtual ~sparse_set() = default;
/**
* @brief Copy assignment operator.
@@ -352,7 +353,7 @@ 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 {
iterator_type find(const entity_type entt) const {
return has(entt) ? --(end() - index(entt)) : end();
}
@@ -361,10 +362,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);
bool has(const entity_type entt) const {
const auto curr = page(entt);
// testing against null permits to avoid accessing the direct vector
return (page < reverse.size() && reverse[page] && reverse[page][offset] != null);
return (curr < reverse.size() && reverse[curr] && reverse[curr][offset(entt)] != null);
}
/**
@@ -379,10 +380,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 {
size_type index(const entity_type entt) const {
ENTT_ASSERT(has(entt));
auto [page, offset] = map(entt);
return size_type(reverse[page][offset]);
return size_type(reverse[page(entt)][offset(entt)]);
}
/**
@@ -398,9 +398,7 @@ public:
*/
void construct(const entity_type entt) {
ENTT_ASSERT(!has(entt));
auto [page, offset] = map(entt);
assure(page);
reverse[page][offset] = entity_type(direct.size());
assure(page(entt))[offset(entt)] = entity_type(direct.size());
direct.push_back(entt);
}
@@ -413,17 +411,15 @@ 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) {
void construct(It first, It last) {
std::for_each(first, last, [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++);
assure(page(entt))[offset(entt)] = entity_type(next++);
});
direct.insert(direct.end(), first, last);
@@ -442,11 +438,11 @@ public:
*/
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;
const auto curr = page(entt);
const auto pos = offset(entt);
direct[size_type(reverse[curr][pos])] = entity_type(direct.back());
reverse[page(direct.back())][offset(direct.back())] = reverse[curr][pos];
reverse[curr][pos] = null;
direct.pop_back();
}
@@ -465,11 +461,9 @@ public:
* @param lhs A valid entity identifier.
* @param rhs A valid entity identifier.
*/
virtual void swap(const entity_type lhs, const entity_type rhs) ENTT_NOEXCEPT {
auto [src_page, src_offset] = map(lhs);
auto [dst_page, dst_offset] = map(rhs);
auto &from = reverse[src_page][src_offset];
auto &to = reverse[dst_page][dst_offset];
virtual void swap(const entity_type lhs, const entity_type rhs) {
auto &from = reverse[page(lhs)][offset(lhs)];
auto &to = reverse[page(rhs)][offset(rhs)];
std::swap(direct[size_type(from)], direct[size_type(to)]);
std::swap(from, to);
}
@@ -526,8 +520,7 @@ public:
algo(from, to, std::move(compare), std::forward<Args>(args)...);
for(size_type pos = skip, end = skip+length; pos < end; ++pos) {
auto [page, offset] = map(direct[pos]);
reverse[page][offset] = entity_type(pos);
reverse[page(direct[pos])][offset(direct[pos])] = entity_type(pos);
}
}
@@ -576,8 +569,7 @@ public:
while(curr != next) {
apply(direct[curr], direct[next]);
auto [page, offset] = map(direct[curr]);
reverse[page][offset] = entity_type(curr);
reverse[page(direct[curr])][offset(direct[curr])] = entity_type(curr);
curr = next;
next = index(direct[curr]);
@@ -605,7 +597,7 @@ public:
*
* @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();
@@ -625,9 +617,9 @@ public:
}
/**
* @brief Resets a sparse set.
* @brief Clears a sparse set.
*/
void reset() {
void clear() ENTT_NOEXCEPT {
reverse.clear();
direct.clear();
}
@@ -641,4 +633,4 @@ private:
}
#endif // ENTT_ENTITY_SPARSE_SET_HPP
#endif

View File

@@ -4,7 +4,6 @@
#include <algorithm>
#include <iterator>
#include <numeric>
#include <utility>
#include <vector>
#include <cstddef>
@@ -48,13 +47,13 @@ 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> {
using underlying_type = sparse_set<Entity>;
using traits_type = entt_traits<std::underlying_type_t<Entity>>;
template<bool Const>
class iterator {
friend class basic_storage<Entity, Type>;
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;
@@ -78,7 +77,7 @@ class basic_storage: public sparse_set<Entity> {
iterator operator++(int) ENTT_NOEXCEPT {
iterator orig = *this;
return ++(*this), orig;
return operator++(), orig;
}
iterator & operator--() ENTT_NOEXCEPT {
@@ -87,7 +86,7 @@ class basic_storage: public sparse_set<Entity> {
iterator operator--(int) ENTT_NOEXCEPT {
iterator orig = *this;
return --(*this), orig;
return operator--(), orig;
}
iterator & operator+=(const difference_type value) ENTT_NOEXCEPT {
@@ -277,12 +276,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 {
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 {
object_type & get(const entity_type entt) {
return const_cast<object_type &>(std::as_const(*this).get(entt));
}
@@ -291,12 +290,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 {
const object_type * try_get(const entity_type entt) const {
return underlying_type::has(entt) ? (instances.data() + underlying_type::index(entt)) : nullptr;
}
/*! @copydoc try_get */
object_type * try_get(const entity_type entt) ENTT_NOEXCEPT {
object_type * try_get(const entity_type entt) {
return const_cast<object_type *>(std::as_const(*this).try_get(entt));
}
@@ -316,27 +315,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 construct(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();
}
/**
* @brief Assigns one or more entities to a storage and default constructs
* or copy constructs their objects.
*
* The object type must be at least move and default insertable if no
* arguments are provided, move and copy insertable otherwise.
* @brief Assigns one or more entities to a storage and constructs their
* objects with from a given instance.
*
* @warning
* Attempting to assign an entity that already belongs to the storage
@@ -344,25 +338,37 @@ 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 Args Types of arguments to use to construct the object.
* @tparam It Type of input iterator.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @param args Parameters to use to construct an object for the 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, typename... Args>
iterator_type batch(It first, It last, Args &&... args) {
if constexpr(sizeof...(Args) == 0) {
instances.resize(instances.size() + std::distance(first, last));
} else {
instances.resize(instances.size() + std::distance(first, last), Type{std::forward<Args>(args)...});
}
template<typename It>
std::enable_if_t<std::is_same_v<typename std::iterator_traits<It>::value_type, entity_type>, void>
construct(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::construct(first, last);
}
// entity goes after component in case constructor throws
underlying_type::batch(first, last);
return begin();
/**
* @brief Assigns one or more entities to a storage and constructs their
* objects from a given range.
*
* @sa construct
*
* @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 An iterator to the first element of the range of objects.
*/
template<typename EIt, typename CIt>
std::enable_if_t<std::is_same_v<typename std::iterator_traits<EIt>::value_type, entity_type>, void>
construct(EIt first, EIt last, CIt value) {
instances.insert(instances.end(), value, value + std::distance(first, last));
// entities go after components in case constructors throw
underlying_type::construct(first, last);
}
/**
@@ -395,7 +401,7 @@ public:
* @param lhs A valid entity identifier.
* @param rhs A valid entity identifier.
*/
void swap(const entity_type lhs, const entity_type rhs) ENTT_NOEXCEPT override {
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);
}
@@ -466,9 +472,9 @@ public:
}
}
/*! @brief Resets a storage. */
void reset() {
underlying_type::reset();
/*! @brief Clears a storage. */
void clear() {
underlying_type::clear();
instances.clear();
}
@@ -477,14 +483,14 @@ private:
};
/*! @copydoc basic_storage */
/*! @copydoc storage */
template<typename Entity, typename Type>
class basic_storage<Entity, Type, std::enable_if_t<ENTT_ENABLE_ETO(Type)>>: public sparse_set<Entity> {
class storage<Entity, Type, std::enable_if_t<ENTT_ENABLE_ETO(Type)>>: public sparse_set<Entity> {
using traits_type = entt_traits<std::underlying_type_t<Entity>>;
using underlying_type = sparse_set<Entity>;
class iterator {
friend class basic_storage<Entity, Type>;
friend class storage<Entity, Type>;
using index_type = typename traits_type::difference_type;
@@ -507,7 +513,7 @@ class basic_storage<Entity, Type, std::enable_if_t<ENTT_ENABLE_ETO(Type)>>: publ
iterator operator++(int) ENTT_NOEXCEPT {
iterator orig = *this;
return ++(*this), orig;
return operator++(), orig;
}
iterator & operator--() ENTT_NOEXCEPT {
@@ -516,7 +522,7 @@ class basic_storage<Entity, Type, std::enable_if_t<ENTT_ENABLE_ETO(Type)>>: publ
iterator operator--(int) ENTT_NOEXCEPT {
iterator orig = *this;
return --(*this), orig;
return operator--(), orig;
}
iterator & operator+=(const difference_type value) ENTT_NOEXCEPT {
@@ -651,15 +657,30 @@ public:
* @param entt A valid entity identifier.
* @return The object associated with the entity.
*/
object_type get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT {
object_type get([[maybe_unused]] const entity_type entt) const {
ENTT_ASSERT(underlying_type::has(entt));
return {};
}
/**
* @brief Assigns one or more entities to a storage.
* @brief Assigns an entity to a storage and constructs its object.
*
* The object type must be at least default constructible.
* @warning
* 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 already contains the given entity.
*
* @tparam Args Types of arguments to use to construct the object.
* @param entt A valid entity identifier.
*/
template<typename... Args>
void construct(const entity_type entt, Args &&...) {
underlying_type::construct(entt);
}
/**
* @brief Assigns one or more entities to a storage.
*
* @warning
* Attempting to assign an entity that already belongs to the storage
@@ -667,16 +688,14 @@ 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.
*/
template<typename It>
iterator_type batch(It first, It last, const object_type & = {}) {
underlying_type::batch(first, last);
return begin();
std::enable_if_t<std::is_same_v<typename std::iterator_traits<It>::value_type, entity_type>, void>
construct(It first, It last, const object_type & = {}) {
underlying_type::construct(first, last);
}
/*! @copydoc storage::sort */
@@ -692,12 +711,8 @@ public:
}
};
/*! @copydoc basic_storage */
template<typename Entity, typename Type>
struct storage: basic_storage<Entity, Type> {};
}
#endif // ENTT_ENTITY_STORAGE_HPP
#endif

View File

@@ -1,46 +0,0 @@
#ifndef ENTT_ENTITY_UTILITY_HPP
#define ENTT_ENTITY_UTILITY_HPP
#include "../core/type_traits.hpp"
namespace entt {
/**
* @brief Alias for exclusion lists.
* @tparam Type List of types.
*/
template<typename... Type>
struct exclude_t: type_list<Type...> {};
/**
* @brief Variable template for exclusion lists.
* @tparam Type List of types.
*/
template<typename... Type>
constexpr exclude_t<Type...> exclude{};
/**
* @brief Alias for lists of observed components.
* @tparam Type List of types.
*/
template<typename... Type>
struct get_t: type_list<Type...>{};
/**
* @brief Variable template for lists of observed components.
* @tparam Type List of types.
*/
template<typename... Type>
constexpr get_t<Type...> get{};
}
#endif // ENTT_ENTITY_UTILITY_HPP

View File

@@ -12,7 +12,6 @@
#include "../core/type_traits.hpp"
#include "sparse_set.hpp"
#include "storage.hpp"
#include "utility.hpp"
#include "entity.hpp"
#include "fwd.hpp"
@@ -35,11 +34,8 @@ class basic_view;
*
* Multi component views iterate over those entities that have at least all the
* given components in their bags. During initialization, a multi component view
* looks at the number of entities available for each component and picks up a
* reference to the smallest set of candidate entities in order to get a
* performance boost when iterate.<br/>
* Order of elements during iterations are highly dependent on the order of the
* underlying data structures. See sparse_set for more details.
* looks at the number of entities available for each component and uses the
* smallest set in order to get a performance boost when iterate.
*
* @b Important
*
@@ -50,9 +46,8 @@ class basic_view;
* given components is removed from the entity to which the iterator points).
* * The entity currently pointed is destroyed.
*
* In all the other cases, modifying the pools of the given components in any
* way invalidates all the iterators and using them results in undefined
* behavior.
* In all other cases, modifying the pools iterated by the view in any way
* invalidates all the iterators and using them results in undefined behavior.
*
* @note
* Views share references to the underlying data structures of the registry that
@@ -81,24 +76,25 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
using underlying_iterator_type = typename sparse_set<Entity>::iterator_type;
using unchecked_type = std::array<const sparse_set<Entity> *, (sizeof...(Component) - 1)>;
using filter_type = std::array<const sparse_set<Entity> *, sizeof...(Exclude)>;
using traits_type = entt_traits<std::underlying_type_t<Entity>>;
class iterator {
friend class basic_view<Entity, exclude_t<Exclude...>, Component...>;
iterator(underlying_iterator_type first, underlying_iterator_type last, unchecked_type other, filter_type ignore) ENTT_NOEXCEPT
: begin{first},
end{last},
iterator(const sparse_set<Entity> *candidate, unchecked_type other, filter_type ignore, underlying_iterator_type curr) ENTT_NOEXCEPT
: view{candidate},
unchecked{other},
filter{ignore}
filter{ignore},
it{curr}
{
if(begin != end && !valid()) {
if(it != view->end() && !valid()) {
++(*this);
}
}
bool valid() const ENTT_NOEXCEPT {
return std::all_of(unchecked.cbegin(), unchecked.cend(), [this](const sparse_set<Entity> *view) { return view->has(*begin); })
&& std::none_of(filter.cbegin(), filter.cend(), [this](const sparse_set<Entity> *view) { return view->has(*begin); });
bool valid() const {
return std::all_of(unchecked.cbegin(), unchecked.cend(), [entt = *it](const sparse_set<Entity> *curr) { return curr->has(entt); })
&& std::none_of(filter.cbegin(), filter.cend(), [entt = *it](const sparse_set<Entity> *curr) { return curr->has(entt); });
}
public:
@@ -106,46 +102,56 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
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 iterator_category = std::bidirectional_iterator_tag;
iterator() ENTT_NOEXCEPT = default;
iterator & operator++() ENTT_NOEXCEPT {
return (++begin != end && !valid()) ? ++(*this) : *this;
iterator & operator++() {
while(++it != view->end() && !valid());
return *this;
}
iterator operator++(int) ENTT_NOEXCEPT {
iterator operator++(int) {
iterator orig = *this;
return ++(*this), orig;
return operator++(), orig;
}
iterator & operator--() ENTT_NOEXCEPT {
while(--it != view->begin() && !valid());
return *this;
}
iterator operator--(int) ENTT_NOEXCEPT {
iterator orig = *this;
return operator--(), orig;
}
bool operator==(const iterator &other) const ENTT_NOEXCEPT {
return other.begin == begin;
return other.it == it;
}
bool operator!=(const iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
pointer operator->() const ENTT_NOEXCEPT {
return begin.operator->();
pointer operator->() const {
return it.operator->();
}
reference operator*() const ENTT_NOEXCEPT {
reference operator*() const {
return *operator->();
}
private:
underlying_iterator_type begin;
underlying_iterator_type end;
const sparse_set<Entity> *view;
unchecked_type unchecked;
filter_type filter;
underlying_iterator_type it;
};
// we could use pool_type<Component> *..., but vs complains about it and refuses to compile for unknown reasons (likely a bug)
basic_view(storage<Entity, std::remove_const_t<Component>> *... component, storage<Entity, std::remove_const_t<Exclude>> *... epool) ENTT_NOEXCEPT
: pools{component...},
filter{epool...}
// we could use pool_type<Component> &..., but vs complains about it and refuses to compile for unknown reasons (likely a bug)
basic_view(storage<Entity, std::remove_const_t<Component>> &... component, storage<Entity, std::remove_const_t<Exclude>> &... epool) ENTT_NOEXCEPT
: pools{&component..., &epool...}
{}
const sparse_set<Entity> * candidate() const ENTT_NOEXCEPT {
@@ -154,7 +160,7 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
});
}
unchecked_type unchecked(const sparse_set<Entity> *view) const ENTT_NOEXCEPT {
unchecked_type unchecked(const sparse_set<Entity> *view) const {
std::size_t pos{};
unchecked_type other{};
((std::get<pool_type<Component> *>(pools) == view ? nullptr : (other[pos++] = std::get<pool_type<Component> *>(pools))), ...);
@@ -162,7 +168,7 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
}
template<typename Comp, typename Other>
decltype(auto) get([[maybe_unused]] component_iterator_type<Comp> it, [[maybe_unused]] pool_type<Other> *cpool, [[maybe_unused]] const Entity entt) const ENTT_NOEXCEPT {
decltype(auto) get([[maybe_unused]] component_iterator_type<Comp> it, [[maybe_unused]] pool_type<Other> *cpool, [[maybe_unused]] const Entity entt) const {
if constexpr(std::is_same_v<Comp, Other>) {
return *it;
} else {
@@ -170,33 +176,32 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
}
}
template<typename Comp, typename Func, typename... Other, typename... Type>
void traverse(Func func, type_list<Other...>, type_list<Type...>) const {
const auto end = std::get<pool_type<Comp> *>(pools)->sparse_set<Entity>::end();
auto begin = std::get<pool_type<Comp> *>(pools)->sparse_set<Entity>::begin();
template<typename Comp, typename Func, typename... Type>
void traverse(Func func, type_list<Type...>) const {
if constexpr(std::disjunction_v<std::is_same<Comp, Type>...>) {
std::for_each(begin, end, [this, raw = std::get<pool_type<Comp> *>(pools)->begin(), &func](const auto entity) mutable {
auto curr = raw++;
auto it = std::get<pool_type<Comp> *>(pools)->begin();
if((std::get<pool_type<Other> *>(pools)->has(entity) && ...) && (!std::get<pool_type<Exclude> *>(filter)->has(entity) && ...)) {
for(const auto entt: static_cast<const sparse_set<entity_type> &>(*std::get<pool_type<Comp> *>(pools))) {
auto curr = it++;
if(((std::is_same_v<Comp, Component> || std::get<pool_type<Component> *>(pools)->has(entt)) && ...) && (!std::get<pool_type<Exclude> *>(pools)->has(entt) && ...)) {
if constexpr(std::is_invocable_v<Func, decltype(get<Type>({}))...>) {
func(get<Comp, Type>(curr, std::get<pool_type<Type> *>(pools), entity)...);
func(get<Comp, Type>(curr, std::get<pool_type<Type> *>(pools), entt)...);
} else {
func(entity, get<Comp, Type>(curr, std::get<pool_type<Type> *>(pools), entity)...);
func(entt, get<Comp, Type>(curr, std::get<pool_type<Type> *>(pools), entt)...);
}
}
});
}
} else {
std::for_each(begin, end, [this, &func](const auto entity) {
if((std::get<pool_type<Other> *>(pools)->has(entity) && ...) && (!std::get<pool_type<Exclude> *>(filter)->has(entity) && ...)) {
for(const auto entt: static_cast<const sparse_set<entity_type> &>(*std::get<pool_type<Comp> *>(pools))) {
if(((std::is_same_v<Comp, Component> || std::get<pool_type<Component> *>(pools)->has(entt)) && ...) && (!std::get<pool_type<Exclude> *>(pools)->has(entt) && ...)) {
if constexpr(std::is_invocable_v<Func, decltype(get<Type>({}))...>) {
func(std::get<pool_type<Type> *>(pools)->get(entity)...);
func(std::get<pool_type<Type> *>(pools)->get(entt)...);
} else {
func(entity, std::get<pool_type<Type> *>(pools)->get(entity)...);
func(entt, std::get<pool_type<Type> *>(pools)->get(entt)...);
}
}
});
}
}
}
@@ -230,16 +235,14 @@ public:
}
/**
* @brief Checks whether the view or the pools of the given components are
* empty.
* @brief Checks whether a view or some pools are empty.
*
* The view is definitely empty if one of the pools of the given components
* is empty. In all other cases, the view may be empty and not return
* entities even if this function returns false.
* The view is definitely empty if one of the pools it uses is empty. In all
* other cases, the view may be empty and not return entities even if this
* function returns false.
*
* @tparam Comp Types of components in which one is interested.
* @return True if the view or the pools of the given components are empty,
* false otherwise.
* @return True if the view or the pools are empty, false otherwise.
*/
template<typename... Comp>
bool empty() const ENTT_NOEXCEPT {
@@ -302,10 +305,10 @@ public:
*
* @return An iterator to the first entity that has the given components.
*/
iterator_type begin() const ENTT_NOEXCEPT {
iterator_type begin() const {
const auto *view = candidate();
const filter_type ignore{std::get<pool_type<Exclude> *>(filter)...};
return iterator_type{view->begin(), view->end(), unchecked(view), ignore};
const filter_type ignore{std::get<pool_type<Exclude> *>(pools)...};
return iterator_type{view, unchecked(view), ignore, view->begin()};
}
/**
@@ -323,10 +326,30 @@ public:
* @return An iterator to the entity following the last entity that has the
* given components.
*/
iterator_type end() const ENTT_NOEXCEPT {
iterator_type end() const {
const auto *view = candidate();
const filter_type ignore{std::get<pool_type<Exclude> *>(filter)...};
return iterator_type{view->end(), view->end(), unchecked(view), ignore};
const filter_type ignore{std::get<pool_type<Exclude> *>(pools)...};
return iterator_type{view, unchecked(view), ignore, view->end()};
}
/**
* @brief Returns the first entity that has the given components, if any.
* @return The first entity that has the given components if one exists, the
* null entity otherwise.
*/
entity_type front() const {
const auto it = begin();
return it != end() ? *it : null;
}
/**
* @brief Returns the last entity that has the given components, if any.
* @return The last entity that has the given components if one exists, the
* null entity otherwise.
*/
entity_type back() const {
const auto it = std::make_reverse_iterator(end());
return it != std::make_reverse_iterator(begin()) ? *it : null;
}
/**
@@ -335,10 +358,10 @@ 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 {
iterator_type find(const entity_type entt) const {
const auto *view = candidate();
const filter_type ignore{std::get<pool_type<Exclude> *>(filter)...};
iterator_type it{view->find(entt), view->end(), unchecked(view), ignore};
const filter_type ignore{std::get<pool_type<Exclude> *>(pools)...};
iterator_type it{view, unchecked(view), ignore, view->find(entt)};
return (it != end() && *it == entt) ? it : end();
}
@@ -347,15 +370,16 @@ 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 find(entt) != end();
bool contains(const entity_type entt) const {
return (std::get<pool_type<Component> *>(pools)->has(entt) && ...)
&& (!std::get<pool_type<Exclude> *>(pools)->has(entt) && ...);
}
/**
* @brief Returns the components assigned to the given entity.
*
* Prefer this function instead of `registry::get` during iterations. It has
* far better performance than its companion function.
* far better performance than its counterpart.
*
* @warning
* Attempting to use an invalid component type results in a compilation
@@ -369,7 +393,7 @@ public:
* @return The components assigned to the entity.
*/
template<typename... Comp>
decltype(auto) get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT {
decltype(auto) get([[maybe_unused]] const entity_type entt) const {
ENTT_ASSERT(contains(entt));
if constexpr(sizeof...(Comp) == 0) {
@@ -443,8 +467,7 @@ public:
*/
template<typename Comp, typename Func>
void each(Func func) const {
using other_type = type_list_cat_t<std::conditional_t<std::is_same_v<Comp, Component>, type_list<>, type_list<Component>>...>;
traverse<Comp>(std::move(func), other_type{}, type_list<Component...>{});
traverse<Comp>(std::move(func), type_list<Component...>{});
}
/**
@@ -502,14 +525,12 @@ public:
*/
template<typename Comp, typename Func>
void less(Func func) const {
using other_type = type_list_cat_t<std::conditional_t<std::is_same_v<Comp, Component>, type_list<>, type_list<Component>>...>;
using non_empty_type = type_list_cat_t<std::conditional_t<ENTT_ENABLE_ETO(Component), type_list<>, type_list<Component>>...>;
traverse<Comp>(std::move(func), other_type{}, non_empty_type{});
traverse<Comp>(std::move(func), non_empty_type{});
}
private:
const std::tuple<pool_type<Component> *...> pools;
const std::tuple<pool_type<Exclude> *...> filter;
const std::tuple<pool_type<Component> *..., pool_type<Exclude> *...> pools;
};
@@ -518,9 +539,7 @@ private:
*
* Single component views are specialized in order to get a boost in terms of
* performance. This kind of views can access the underlying data structure
* directly and avoid superfluous checks.<br/>
* Order of elements during iterations are highly dependent on the order of the
* underlying data structure. See sparse_set for more details.
* directly and avoid superfluous checks.
*
* @b Important
*
@@ -531,7 +550,7 @@ private:
* component is removed from the entity to which the iterator points).
* * The entity currently pointed is destroyed.
*
* In all the other cases, modifying the pool of the given component in any way
* In all other cases, modifying the pool iterated by the view in any way
* invalidates all the iterators and using them results in undefined behavior.
*
* @note
@@ -553,8 +572,8 @@ class basic_view<Entity, exclude_t<>, Component> {
using pool_type = std::conditional_t<std::is_const_v<Component>, const storage<Entity, std::remove_const_t<Component>>, storage<Entity, Component>>;
basic_view(pool_type *ref) ENTT_NOEXCEPT
: pool{ref}
basic_view(pool_type &ref) ENTT_NOEXCEPT
: pool{&ref}
{}
public:
@@ -576,7 +595,7 @@ public:
}
/**
* @brief Checks whether the view is empty.
* @brief Checks whether a view is empty.
* @return True if the view is empty, false otherwise.
*/
bool empty() const ENTT_NOEXCEPT {
@@ -652,13 +671,33 @@ public:
return pool->sparse_set<Entity>::end();
}
/**
* @brief Returns the first entity that has the given component, if any.
* @return The first entity that has the given component if one exists, the
* null entity otherwise.
*/
entity_type front() const {
const auto it = begin();
return it != end() ? *it : null;
}
/**
* @brief Returns the last entity that has the given component, if any.
* @return The last entity that has the given component if one exists, the
* null entity otherwise.
*/
entity_type back() const {
const auto it = std::make_reverse_iterator(end());
return it != std::make_reverse_iterator(begin()) ? *it : null;
}
/**
* @brief Finds an entity.
* @param entt A valid entity identifier.
* @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 {
iterator_type find(const entity_type entt) const {
const auto it = pool->find(entt);
return it != end() && *it == entt ? it : end();
}
@@ -668,7 +707,7 @@ public:
* @param pos Position of the element to return.
* @return The identifier that occupies the given position.
*/
entity_type operator[](const size_type pos) const ENTT_NOEXCEPT {
entity_type operator[](const size_type pos) const {
return begin()[pos];
}
@@ -677,15 +716,15 @@ 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 find(entt) != end();
bool contains(const entity_type entt) const {
return pool->has(entt);
}
/**
* @brief Returns the component assigned to the given entity.
*
* Prefer this function instead of `registry::get` during iterations. It has
* far better performance than its companion function.
* far better performance than its counterpart.
*
* @warning
* Attempting to use an entity that doesn't belong to the view results in
@@ -697,7 +736,7 @@ public:
* @return The component assigned to the entity.
*/
template<typename Comp = Component>
decltype(auto) get(const entity_type entt) const ENTT_NOEXCEPT {
decltype(auto) get(const entity_type entt) const {
static_assert(std::is_same_v<Comp, Component>);
ENTT_ASSERT(contains(entt));
return pool->get(entt);
@@ -729,11 +768,15 @@ public:
template<typename Func>
void each(Func func) const {
if constexpr(std::is_invocable_v<Func, decltype(get({}))>) {
std::for_each(pool->begin(), pool->end(), std::move(func));
for(auto &&component: *pool) {
func(component);
}
} else {
std::for_each(pool->sparse_set<Entity>::begin(), pool->sparse_set<Entity>::end(), [&func, raw = pool->begin()](const auto entt) mutable {
auto raw = pool->begin();
for(const auto entt: *this) {
func(entt, *(raw++));
});
}
}
}
@@ -773,7 +816,9 @@ public:
func();
}
} else {
std::for_each(pool->sparse_set<Entity>::begin(), pool->sparse_set<Entity>::end(), std::move(func));
for(const auto entt: *this) {
func(entt);
}
}
} else {
each(std::move(func));
@@ -788,4 +833,4 @@ private:
}
#endif // ENTT_ENTITY_VIEW_HPP
#endif

View File

@@ -1,8 +1,10 @@
#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"
@@ -15,7 +17,6 @@
#include "entity/snapshot.hpp"
#include "entity/sparse_set.hpp"
#include "entity/storage.hpp"
#include "entity/utility.hpp"
#include "entity/view.hpp"
#include "locator/locator.hpp"
#include "meta/factory.hpp"

View File

@@ -108,4 +108,4 @@ private:
}
#endif // ENTT_LOCATOR_LOCATOR_HPP
#endif

View File

@@ -10,6 +10,7 @@
#include <type_traits>
#include "../config/config.h"
#include "../core/type_traits.hpp"
#include "../core/utility.hpp"
#include "policy.hpp"
#include "meta.hpp"
@@ -86,7 +87,7 @@ meta_any construct(meta_any * const args, std::index_sequence<Indexes...>) {
template<bool Const, typename Type, auto Data>
bool setter([[maybe_unused]] meta_handle handle, [[maybe_unused]] meta_any index, [[maybe_unused]] meta_any value) {
bool setter([[maybe_unused]] meta_any instance, [[maybe_unused]] meta_any index, [[maybe_unused]] meta_any value) {
bool accepted = false;
if constexpr(!Const) {
@@ -94,7 +95,7 @@ bool setter([[maybe_unused]] meta_handle handle, [[maybe_unused]] meta_any index
using helper_type = meta_function_helper_t<decltype(Data)>;
using data_type = std::tuple_element_t<!std::is_member_function_pointer_v<decltype(Data)>, typename helper_type::args_type>;
static_assert(std::is_invocable_v<decltype(Data), Type &, data_type>);
auto * const clazz = meta_any{handle}.try_cast<Type>();
auto * const clazz = instance.try_cast<Type>();
auto * const direct = value.try_cast<data_type>();
if(clazz && (direct || value.convert<data_type>())) {
@@ -104,7 +105,7 @@ bool setter([[maybe_unused]] meta_handle handle, [[maybe_unused]] meta_any index
} else if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
using data_type = std::remove_cv_t<std::remove_reference_t<decltype(std::declval<Type>().*Data)>>;
static_assert(std::is_invocable_v<decltype(Data), Type *>);
auto * const clazz = meta_any{handle}.try_cast<Type>();
auto * const clazz = instance.try_cast<Type>();
if constexpr(std::is_array_v<data_type>) {
using underlying_type = std::remove_extent_t<data_type>;
@@ -152,10 +153,10 @@ bool setter([[maybe_unused]] meta_handle handle, [[maybe_unused]] meta_any index
template<typename Type, auto Data, typename Policy>
meta_any getter([[maybe_unused]] meta_handle handle, [[maybe_unused]] meta_any index) {
meta_any getter([[maybe_unused]] meta_any instance, [[maybe_unused]] meta_any index) {
auto dispatch = [](auto &&value) {
if constexpr(std::is_same_v<Policy, as_void_t>) {
return meta_any{std::in_place_type<void>};
return meta_any{std::in_place_type<void>, std::forward<decltype(value)>(value)};
} else if constexpr(std::is_same_v<Policy, as_alias_t>) {
return meta_any{std::ref(std::forward<decltype(value)>(value))};
} else {
@@ -166,12 +167,12 @@ meta_any getter([[maybe_unused]] meta_handle handle, [[maybe_unused]] meta_any i
if constexpr(std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>> || std::is_member_function_pointer_v<decltype(Data)>) {
static_assert(std::is_invocable_v<decltype(Data), Type &>);
auto * const clazz = meta_any{handle}.try_cast<Type>();
auto * const clazz = instance.try_cast<Type>();
return clazz ? dispatch(std::invoke(Data, *clazz)) : meta_any{};
} else if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
using data_type = std::remove_cv_t<std::remove_reference_t<decltype(std::declval<Type>().*Data)>>;
static_assert(std::is_invocable_v<decltype(Data), Type *>);
auto * const clazz = meta_any{handle}.try_cast<Type>();
auto * const clazz = instance.try_cast<Type>();
if constexpr(std::is_array_v<data_type>) {
auto * const idx = index.try_cast<std::size_t>();
@@ -193,7 +194,7 @@ meta_any getter([[maybe_unused]] meta_handle handle, [[maybe_unused]] meta_any i
template<typename Type, auto Candidate, typename Policy, std::size_t... Indexes>
meta_any invoke([[maybe_unused]] meta_handle handle, meta_any *args, std::index_sequence<Indexes...>) {
meta_any invoke([[maybe_unused]] meta_any instance, meta_any *args, std::index_sequence<Indexes...>) {
using helper_type = meta_function_helper_t<decltype(Candidate)>;
auto dispatch = [](auto *... params) {
@@ -208,20 +209,20 @@ meta_any invoke([[maybe_unused]] meta_handle handle, meta_any *args, std::index_
}
};
[[maybe_unused]] const auto direct = std::make_tuple([](meta_any *any, auto *instance) {
using arg_type = std::remove_reference_t<decltype(*instance)>;
[[maybe_unused]] const auto direct = std::make_tuple([](meta_any *any, auto *value) {
using arg_type = std::remove_reference_t<decltype(*value)>;
if(!instance && any->convert<arg_type>()) {
instance = any->try_cast<arg_type>();
if(!value && any->convert<arg_type>()) {
value = any->try_cast<arg_type>();
}
return instance;
return value;
}(args+Indexes, (args+Indexes)->try_cast<std::tuple_element_t<Indexes, typename helper_type::args_type>>())...);
if constexpr(std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Candidate)>>>) {
return (std::get<Indexes>(direct) && ...) ? dispatch(std::get<Indexes>(direct)...) : meta_any{};
} else {
auto * const clazz = meta_any{handle}.try_cast<Type>();
auto * const clazz = instance.try_cast<Type>();
return (clazz && (std::get<Indexes>(direct) && ...)) ? dispatch(clazz, std::get<Indexes>(direct)...) : meta_any{};
}
}
@@ -236,68 +237,173 @@ meta_any invoke([[maybe_unused]] meta_handle handle, meta_any *args, std::index_
*/
template<typename, typename...>
class extended_meta_factory;
/**
* @brief Meta factory to be used for reflection purposes.
*
* The meta factory is an utility class used to reflect types, data members and
* functions of all sorts. This class ensures that the underlying web of types
* is built correctly and performs some checks in debug mode to ensure that
* there are no subtle errors at runtime.
*/
template<typename...>
class meta_factory;
/**
* @brief A meta factory to be used for reflection purposes.
*
* A meta factory is an utility class used to reflect types, data and functions
* of all sorts. This class ensures that the underlying web of types is built
* correctly and performs some checks in debug mode to ensure that there are no
* subtle errors at runtime.
*
* @brief Extended meta factory to be used for reflection purposes.
* @tparam Type Reflected type for which the factory was created.
* @tparam Spec Property specialization pack used to disambiguate overloads.
*/
template<typename Type>
class meta_factory {
template<typename Node>
bool duplicate(const Node *candidate, const Node *node) ENTT_NOEXCEPT {
return node && (node == candidate || duplicate(candidate, node->next));
template<typename Type, typename... Spec>
class meta_factory<Type, Spec...>: public meta_factory<Type> {
bool exists(const meta_any &key, const internal::meta_prop_node *node) ENTT_NOEXCEPT {
return node && (node->key() == key || exists(key, node->next));
}
template<typename Node>
bool duplicate(const ENTT_ID_TYPE identifier, const Node *node) ENTT_NOEXCEPT {
return node && (node->identifier == identifier || duplicate(identifier, node->next));
template<std::size_t Step = 0, std::size_t... Index, typename... Property, typename... Other>
void unpack(std::index_sequence<Index...>, std::tuple<Property...> property, Other &&... other) {
unroll<Step>(choice<3>, std::move(std::get<Index>(property))..., std::forward<Other>(other)...);
}
auto record(const ENTT_ID_TYPE identifier) ENTT_NOEXCEPT {
auto * const node = internal::meta_info<Type>::resolve();
template<std::size_t Step = 0, typename... Property, typename... Other>
void unroll(choice_t<3>, std::tuple<Property...> property, Other &&... other) {
unpack<Step>(std::index_sequence_for<Property...>{}, std::move(property), std::forward<Other>(other)...);
}
ENTT_ASSERT(!duplicate(identifier, *internal::meta_info<>::global));
ENTT_ASSERT(!duplicate(node, *internal::meta_info<>::global));
node->identifier = identifier;
node->next = *internal::meta_info<>::global;
*internal::meta_info<>::global = node;
template<std::size_t Step = 0, typename... Property, typename... Other>
void unroll(choice_t<2>, std::pair<Property...> property, Other &&... other) {
assign<Step>(std::move(property.first), std::move(property.second));
unroll<Step+1>(choice<3>, std::forward<Other>(other)...);
}
return extended_meta_factory<Type>{&node->prop};
template<std::size_t Step = 0, typename Property, typename... Other>
std::enable_if_t<!std::is_invocable_v<Property>>
unroll(choice_t<1>, Property &&property, Other &&... other) {
assign<Step>(std::forward<Property>(property));
unroll<Step+1>(choice<3>, std::forward<Other>(other)...);
}
template<std::size_t Step = 0, typename Func, typename... Other>
void unroll(choice_t<0>, Func &&invocable, Other &&... other) {
unroll<Step>(choice<3>, std::forward<Func>(invocable)(), std::forward<Other>(other)...);
}
template<std::size_t>
void unroll(choice_t<0>) {}
template<std::size_t = 0, typename Key, typename... Value>
void assign(Key &&key, Value &&... value) {
static const auto property{std::make_tuple(std::forward<Key>(key), std::forward<Value>(value)...)};
static internal::meta_prop_node node{
nullptr,
[]() -> meta_any {
return std::get<0>(property);
},
[]() -> meta_any {
if constexpr(sizeof...(Value) == 0) {
return {};
} else {
return std::get<1>(property);
}
}
};
ENTT_ASSERT(!exists(node.key(), *curr));
node.next = *curr;
*curr = &node;
}
public:
/**
* @brief Extends a meta type by assigning it an identifier.
*
* This function is intended only for unnamed types.
*
* @param identifier Unique identifier.
* @return An extended meta factory for the parent type.
* @brief Constructs an extended factory from a given node.
* @param target The underlying node to which to assign the properties.
*/
auto type(const ENTT_ID_TYPE identifier) ENTT_NOEXCEPT {
static_assert(!is_named_type_v<Type>);
return record(identifier);
meta_factory(entt::internal::meta_prop_node **target) ENTT_NOEXCEPT
: curr{target}
{}
/**
* @brief Assigns a property to the last meta object created.
*
* Both the key and the value (if any) must be at least copy constructible.
*
* @tparam PropertyOrKey Type of the property or property key.
* @tparam Value Optional type of the property value.
* @param property_or_key Property or property key.
* @param value Optional property value.
* @return A meta factory for the parent type.
*/
template<typename PropertyOrKey, typename... Value>
auto prop(PropertyOrKey &&property_or_key, Value &&... value) && {
if constexpr(sizeof...(Value) == 0) {
unroll(choice<3>, std::forward<PropertyOrKey>(property_or_key));
} else {
assign(std::forward<PropertyOrKey>(property_or_key), std::forward<Value>(value)...);
}
return meta_factory<Type, Spec..., PropertyOrKey, Value...>{curr};
}
/**
* @brief Extends a meta type by assigning it an identifier.
* @brief Assigns properties to the last meta object created.
*
* This function is intended only for named types
* Both the keys and the values (if any) must be at least copy
* constructible.
*
* @return An extended meta factory for the parent type.
* @tparam Property Types of the properties.
* @param property Properties to assign to the last meta object created.
* @return A meta factory for the parent type.
*/
auto type() ENTT_NOEXCEPT {
static_assert(is_named_type_v<Type>);
return record(named_type_traits_t<Type>::value);
template <typename... Property>
auto props(Property... property) && {
unroll(choice<3>, std::forward<Property>(property)...);
return meta_factory<Type, Spec..., Property...>{curr};
}
private:
entt::internal::meta_prop_node **curr;
};
/**
* @brief Basic meta factory to be used for reflection purposes.
* @tparam Type Reflected type for which the factory was created.
*/
template<typename Type>
class meta_factory<Type> {
template<typename Node>
bool exists(const Node *candidate, const Node *node) ENTT_NOEXCEPT {
return node && (node == candidate || exists(candidate, node->next));
}
template<typename Node>
bool exists(const ENTT_ID_TYPE alias, const Node *node) ENTT_NOEXCEPT {
return node && (node->alias == alias || exists(alias, node->next));
}
public:
/**
* @brief Extends a meta type by assigning it an alias.
* @param value Unique identifier.
* @return An extended meta factory for the given type.
*/
auto alias(const ENTT_ID_TYPE value) ENTT_NOEXCEPT {
auto * const node = internal::meta_info<Type>::resolve();
ENTT_ASSERT(!exists(value, *internal::meta_info<>::global));
ENTT_ASSERT(!exists(node, *internal::meta_info<>::global));
node->alias = value;
node->next = *internal::meta_info<>::global;
*internal::meta_info<>::global = node;
return meta_factory<Type, Type>{&node->prop};
}
/*! @copydoc alias */
[[deprecated("Use ::alias instead")]]
auto type(const ENTT_ID_TYPE value) ENTT_NOEXCEPT {
return alias(value);
}
/**
@@ -322,7 +428,7 @@ public:
}
};
ENTT_ASSERT(!duplicate(&node, type->base));
ENTT_ASSERT(!exists(&node, type->base));
node.next = type->base;
type->base = &node;
@@ -352,7 +458,7 @@ public:
}
};
ENTT_ASSERT(!duplicate(&node, type->conv));
ENTT_ASSERT(!exists(&node, type->conv));
node.next = type->conv;
type->conv = &node;
@@ -385,7 +491,7 @@ public:
}
};
ENTT_ASSERT(!duplicate(&node, type->conv));
ENTT_ASSERT(!exists(&node, type->conv));
node.next = type->conv;
type->conv = &node;
@@ -422,11 +528,11 @@ public:
}
};
ENTT_ASSERT(!duplicate(&node, type->ctor));
ENTT_ASSERT(!exists(&node, type->ctor));
node.next = type->ctor;
type->ctor = &node;
return extended_meta_factory<Type, std::integral_constant<decltype(Func), Func>>{&node.prop};
return meta_factory<Type, std::integral_constant<decltype(Func), Func>>{&node.prop};
}
/**
@@ -455,11 +561,11 @@ public:
}
};
ENTT_ASSERT(!duplicate(&node, type->ctor));
ENTT_ASSERT(!exists(&node, type->ctor));
node.next = type->ctor;
type->ctor = &node;
return extended_meta_factory<Type, Type(Args...)>{&node.prop};
return meta_factory<Type, Type(Args...)>{&node.prop};
}
/**
@@ -485,14 +591,10 @@ public:
static internal::meta_dtor_node node{
type,
[](meta_handle handle) {
const auto valid = (handle.type() == internal::meta_info<Type>::resolve());
if(valid) {
std::invoke(Func, *meta_any{handle}.try_cast<Type>());
[](void *instance) {
if(instance) {
std::invoke(Func, *static_cast<Type *>(instance));
}
return valid;
}
};
@@ -512,11 +614,11 @@ public:
*
* @tparam Data The actual variable to attach to the meta type.
* @tparam Policy Optional policy (no policy set by default).
* @param identifier Unique identifier.
* @param alias Unique identifier.
* @return An extended meta factory for the parent type.
*/
template<auto Data, typename Policy = as_is_t>
auto data(const ENTT_ID_TYPE identifier) ENTT_NOEXCEPT {
auto data(const ENTT_ID_TYPE alias) ENTT_NOEXCEPT {
auto * const type = internal::meta_info<Type>::resolve();
internal::meta_data_node *curr = nullptr;
@@ -531,8 +633,8 @@ public:
true,
true,
&internal::meta_info<Type>::resolve,
[](meta_handle, meta_any, meta_any) { return false; },
[](meta_handle, meta_any) -> meta_any { return Data; }
[](meta_any, meta_any, meta_any) { return false; },
[](meta_any, meta_any) -> meta_any { return Data; }
};
curr = &node;
@@ -571,13 +673,13 @@ public:
curr = &node;
}
ENTT_ASSERT(!duplicate(identifier, type->data));
ENTT_ASSERT(!duplicate(curr, type->data));
curr->identifier = identifier;
ENTT_ASSERT(!exists(alias, type->data));
ENTT_ASSERT(!exists(curr, type->data));
curr->alias = alias;
curr->next = type->data;
type->data = curr;
return extended_meta_factory<Type, std::integral_constant<decltype(Data), Data>>{&curr->prop};
return meta_factory<Type, std::integral_constant<decltype(Data), Data>>{&curr->prop};
}
/**
@@ -597,11 +699,11 @@ public:
* @tparam Setter The actual function to use as a setter.
* @tparam Getter The actual function to use as a getter.
* @tparam Policy Optional policy (no policy set by default).
* @param identifier Unique identifier.
* @param alias Unique identifier.
* @return An extended meta factory for the parent type.
*/
template<auto Setter, auto Getter, typename Policy = as_is_t>
auto data(const ENTT_ID_TYPE identifier) ENTT_NOEXCEPT {
auto data(const ENTT_ID_TYPE alias) ENTT_NOEXCEPT {
using underlying_type = std::invoke_result_t<decltype(Getter), Type &>;
static_assert(std::is_invocable_v<decltype(Setter), Type &, underlying_type>);
auto * const type = internal::meta_info<Type>::resolve();
@@ -618,13 +720,13 @@ public:
&internal::getter<Type, Getter, Policy>
};
ENTT_ASSERT(!duplicate(identifier, type->data));
ENTT_ASSERT(!duplicate(&node, type->data));
node.identifier = identifier;
ENTT_ASSERT(!exists(alias, type->data));
ENTT_ASSERT(!exists(&node, type->data));
node.alias = alias;
node.next = type->data;
type->data = &node;
return extended_meta_factory<Type, std::integral_constant<decltype(Setter), Setter>, std::integral_constant<decltype(Getter), Getter>>{&node.prop};
return meta_factory<Type, std::integral_constant<decltype(Setter), Setter>, std::integral_constant<decltype(Getter), Getter>>{&node.prop};
}
/**
@@ -637,11 +739,11 @@ public:
*
* @tparam Candidate The actual function to attach to the meta type.
* @tparam Policy Optional policy (no policy set by default).
* @param identifier Unique identifier.
* @param alias Unique identifier.
* @return An extended meta factory for the parent type.
*/
template<auto Candidate, typename Policy = as_is_t>
auto func(const ENTT_ID_TYPE identifier) ENTT_NOEXCEPT {
auto func(const ENTT_ID_TYPE alias) ENTT_NOEXCEPT {
using helper_type = internal::meta_function_helper_t<decltype(Candidate)>;
auto * const type = internal::meta_info<Type>::resolve();
@@ -655,18 +757,18 @@ public:
!std::is_member_function_pointer_v<decltype(Candidate)>,
&internal::meta_info<std::conditional_t<std::is_same_v<Policy, as_void_t>, void, typename helper_type::return_type>>::resolve,
&helper_type::arg,
[](meta_handle handle, meta_any *any) {
return internal::invoke<Type, Candidate, Policy>(handle, any, helper_type::index_sequence);
[](meta_any instance, meta_any *args) {
return internal::invoke<Type, Candidate, Policy>(std::move(instance), args, helper_type::index_sequence);
}
};
ENTT_ASSERT(!duplicate(identifier, type->func));
ENTT_ASSERT(!duplicate(&node, type->func));
node.identifier = identifier;
ENTT_ASSERT(!exists(alias, type->func));
ENTT_ASSERT(!exists(&node, type->func));
node.alias = alias;
node.next = type->func;
type->func = &node;
return extended_meta_factory<Type, std::integral_constant<decltype(Candidate), Candidate>>{&node.prop};
return meta_factory<Type, std::integral_constant<decltype(Candidate), Candidate>>{&node.prop};
}
/**
@@ -676,127 +778,45 @@ public:
* functions and properties, as well as its constructors, destructors and
* conversion functions if any.<br/>
* Base classes aren't reset but the link between the two types is removed.
*
* @return An extended meta factory for the given type.
*/
void reset() ENTT_NOEXCEPT {
internal::meta_info<Type>::reset();
}
};
auto reset() ENTT_NOEXCEPT {
auto * const node = internal::meta_info<Type>::resolve();
auto **it = internal::meta_info<>::global;
while(*it && *it != node) {
it = &(*it)->next;
}
/**
* @brief Extended meta factory to be used for reflection purposes.
* @tparam Type Reflected type for which the factory was created.
* @tparam Spec Property specialization pack used to disambiguate overloads.
*/
template<typename Type, typename... Spec>
class extended_meta_factory: public meta_factory<Type> {
bool duplicate(const meta_any &key, const internal::meta_prop_node *node) ENTT_NOEXCEPT {
return node && (node->key() == key || duplicate(key, node->next));
}
if(*it) {
*it = (*it)->next;
}
template<std::size_t Step = 0, std::size_t... Index, typename... Property, typename... Other>
void unpack(std::index_sequence<Index...>, std::tuple<Property...> property, Other &&... other) {
unroll<Step>(choice<3>, std::move(std::get<Index>(property))..., std::forward<Other>(other)...);
}
template<std::size_t Step = 0, typename... Property, typename... Other>
void unroll(choice_t<3>, std::tuple<Property...> property, Other &&... other) {
unpack<Step>(std::index_sequence_for<Property...>{}, std::move(property), std::forward<Other>(other)...);
}
template<std::size_t Step = 0, typename... Property, typename... Other>
void unroll(choice_t<2>, std::pair<Property...> property, Other &&... other) {
assign<Step>(std::move(property.first), std::move(property.second));
unroll<Step+1>(choice<3>, std::forward<Other>(other)...);
}
template<std::size_t Step = 0, typename Property, typename... Other>
std::enable_if_t<!std::is_invocable_v<Property>>
unroll(choice_t<1>, Property &&property, Other &&... other) {
assign<Step>(std::forward<Property>(property));
unroll<Step+1>(choice<3>, std::forward<Other>(other)...);
}
template<std::size_t Step = 0, typename Func, typename... Other>
void unroll(choice_t<0>, Func &&invocable, Other &&... other) {
unroll<Step>(choice<3>, std::forward<Func>(invocable)(), std::forward<Other>(other)...);
}
template<std::size_t>
void unroll(choice_t<0>) {}
template<std::size_t = 0, typename Key, typename... Value>
void assign(Key &&key, Value &&... value) {
static auto property{std::make_tuple(std::forward<Key>(key), std::forward<Value>(value)...)};
static internal::meta_prop_node node{
nullptr,
[]() -> meta_any {
return std::get<0>(property);
},
[]() -> meta_any {
if constexpr(sizeof...(Value) == 0) {
return {};
} else {
return std::get<1>(property);
const auto unregister_all = y_combinator{
[](auto &&self, auto **curr, auto... member) {
while(*curr) {
auto *prev = *curr;
(self(&(prev->*member)), ...);
*curr = prev->next;
prev->next = nullptr;
}
}
};
ENTT_ASSERT(!duplicate(node.key(), *curr));
node.next = *curr;
*curr = &node;
unregister_all(&node->prop);
unregister_all(&node->base);
unregister_all(&node->conv);
unregister_all(&node->ctor, &internal::meta_ctor_node::prop);
unregister_all(&node->data, &internal::meta_data_node::prop);
unregister_all(&node->func, &internal::meta_func_node::prop);
node->alias = {};
node->next = nullptr;
node->dtor = nullptr;
return meta_factory<Type, Type>{&node->prop};
}
public:
/**
* @brief Constructs an extended factory from a given node.
* @param target The underlying node to which to assign the properties.
*/
extended_meta_factory(entt::internal::meta_prop_node **target)
: curr{target}
{}
/**
* @brief Assigns a property to the last meta object created.
*
* Both the key and the value (if any) must be at least copy constructible.
*
* @tparam PropertyOrKey Type of the property or property key.
* @tparam Value Optional type of the property value.
* @param property_or_key Property or property key.
* @param value Optional property value.
* @return A meta factory for the parent type.
*/
template<typename PropertyOrKey, typename... Value>
auto prop(PropertyOrKey &&property_or_key, Value &&... value) && {
if constexpr(sizeof...(Value) == 0) {
unroll(choice<3>, std::forward<PropertyOrKey>(property_or_key));
} else {
assign(std::forward<PropertyOrKey>(property_or_key), std::forward<Value>(value)...);
}
return extended_meta_factory<Type, Spec..., PropertyOrKey, Value...>{curr};
}
/**
* @brief Assigns properties to the last meta object created.
*
* Both the keys and the values (if any) must be at least copy
* constructible.
*
* @tparam Property Types of the properties.
* @param property Properties to assign to the last meta object created.
* @return A meta factory for the parent type.
*/
template <typename... Property>
auto props(Property... property) && {
unroll(choice<3>, std::forward<Property>(property)...);
return extended_meta_factory<Type, Spec..., Property...>{curr};
}
private:
entt::internal::meta_prop_node **curr{nullptr};
};
@@ -809,11 +829,13 @@ private:
* dedicated factory.
*
* @tparam Type Type to reflect.
* @return A meta factory for the given type.
* @return An meta factory for the given type.
*/
template<typename Type>
inline meta_factory<Type> meta() ENTT_NOEXCEPT {
return meta_factory<Type>{};
auto * const node = internal::meta_info<Type>::resolve();
// extended meta factory to allow assigning properties to opaque meta types
return meta_factory<Type, Type>{&node->prop};
}
@@ -829,13 +851,13 @@ inline meta_type resolve() ENTT_NOEXCEPT {
/**
* @brief Returns the meta type associated with a given identifier.
* @param identifier Unique identifier.
* @return The meta type associated with the given identifier, if any.
* @brief Returns the meta type associated with a given alias.
* @param alias Unique identifier.
* @return The meta type associated with the given alias, if any.
*/
inline meta_type resolve(const ENTT_ID_TYPE identifier) ENTT_NOEXCEPT {
return internal::find_if([identifier](auto *node) {
return node->identifier == identifier;
inline meta_type resolve(const ENTT_ID_TYPE alias) ENTT_NOEXCEPT {
return internal::find_if([alias](const auto *curr) {
return curr->alias == alias;
}, *internal::meta_info<>::global);
}
@@ -847,12 +869,12 @@ inline meta_type resolve(const ENTT_ID_TYPE identifier) ENTT_NOEXCEPT {
*/
template<typename Op>
inline std::enable_if_t<std::is_invocable_v<Op, meta_type>, void>
resolve(Op op) ENTT_NOEXCEPT {
internal::iterate<meta_type>(std::move(op), *internal::meta_info<>::global);
resolve(Op op) {
internal::visit<meta_type>(std::move(op), *internal::meta_info<>::global);
}
}
#endif // ENTT_META_FACTORY_HPP
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -24,4 +24,4 @@ struct as_void_t {};
}
#endif // ENTT_META_POLICY_HPP
#endif

View File

@@ -171,7 +171,7 @@ public:
using delta_type = Delta;
/*! @brief Default destructor. */
virtual ~process() ENTT_NOEXCEPT {
virtual ~process() {
static_assert(std::is_base_of_v<process, Derived>);
}
@@ -183,7 +183,7 @@ 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;
@@ -337,4 +337,4 @@ struct process_adaptor: process<process_adaptor<Func, Delta>, Delta>, private Fu
}
#endif // ENTT_PROCESS_PROCESS_HPP
#endif

View File

@@ -114,7 +114,7 @@ public:
using size_type = std::size_t;
/*! @brief Default constructor. */
scheduler() ENTT_NOEXCEPT = default;
scheduler() = default;
/*! @brief Default move constructor. */
scheduler(scheduler &&) = default;
@@ -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

@@ -176,7 +176,7 @@ struct cache {
* @param id Unique resource identifier.
* @return True if the cache contains the resource, false otherwise.
*/
bool contains(const id_type id) const ENTT_NOEXCEPT {
bool contains(const id_type id) const {
return (resources.find(id) != resources.cend());
}
@@ -188,7 +188,7 @@ struct cache {
*
* @param id Unique resource identifier.
*/
void discard(const id_type id) ENTT_NOEXCEPT {
void discard(const id_type id) {
if(auto it = resources.find(id); it != resources.end()) {
resources.erase(it);
}
@@ -237,4 +237,4 @@ private:
}
#endif // ENTT_RESOURCE_CACHE_HPP
#endif

View File

@@ -21,4 +21,4 @@ class loader;
}
#endif // ENTT_RESOURCE_FWD_HPP
#endif

View File

@@ -93,7 +93,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); }
explicit operator bool() const ENTT_NOEXCEPT {
return static_cast<bool>(resource);
}
private:
std::shared_ptr<Resource> resource;
@@ -103,4 +105,4 @@ private:
}
#endif // ENTT_RESOURCE_HANDLE_HPP
#endif

View File

@@ -62,4 +62,4 @@ class 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,36 +23,32 @@ 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<const Payload *, const Type *>>>
auto to_function_pointer(Ret(*)(Type &, Args...), const Payload *) -> Ret(*)(Args...);
template<typename Ret, typename Type, typename... Args, typename Other>
auto function_pointer(Ret(*)(Type, Args...), Other &&) -> Ret(*)(Args...);
template<typename Ret, typename... Args, typename Type, typename Payload, typename = std::enable_if_t<std::is_convertible_v<const Payload *, const Type *>>>
auto to_function_pointer(Ret(*)(Type *, Args...), const Payload *) -> 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 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 Ret, typename... Args>
auto to_function_pointer(Ret(Class:: *)(Args...) const, const Class *) -> 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... Type>
using to_function_pointer_t = decltype(internal::to_function_pointer(std::declval<Type>()...));
using function_pointer_t = decltype(internal::function_pointer(std::declval<Type>()...));
template<typename Ret, typename... Args>
template<typename... Class, typename Ret, typename... Args>
constexpr auto index_sequence_for(Ret(*)(Args...)) {
return std::index_sequence_for<Args...>{};
return std::index_sequence_for<Class..., Args...>{};
}
@@ -92,49 +87,39 @@ 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 &&...>);
using proto_fn_type = Ret(const void *, 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>
auto wrap(std::index_sequence<Index...>) ENTT_NOEXCEPT {
return [](const void *, Args... args) -> Ret {
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;
fn = [](const void *payload, std::tuple<Args &&...> args) -> Ret {
auto wrap(Type &, std::index_sequence<Index...>) ENTT_NOEXCEPT {
return [](const void *payload, Args... args) -> Ret {
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));
// 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))...));
return Ret(std::invoke(Candidate, *curr, 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;
fn = [](const void *payload, std::tuple<Args &&...> args) -> Ret {
auto wrap(Type *, std::index_sequence<Index...>) ENTT_NOEXCEPT {
return [](const void *payload, Args... args) -> Ret {
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));
// 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))...));
return Ret(std::invoke(Candidate, curr, std::forward<std::tuple_element_t<Index, std::tuple<Args...>>>(std::get<Index>(arguments))...));
};
}
@@ -148,56 +133,53 @@ 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
template<auto Candidate>
delegate(connect_arg_t<Candidate>) ENTT_NOEXCEPT
: delegate{}
{
connect<Function>();
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_arg_t<Candidate>, Type &&value_or_instance) ENTT_NOEXCEPT
: delegate{}
{
connect<Candidate>(value_or_instance);
connect<Candidate>(std::forward<Type>(value_or_instance));
}
/**
* @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.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid pointer that fits the purpose.
* @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, typename Type>
delegate(connect_arg_t<Candidate>, Type *value_or_instance) ENTT_NOEXCEPT
: delegate{}
{
connect<Candidate>(value_or_instance);
}
/**
* @brief Connects a free function to a delegate.
* @tparam Function A valid free function pointer.
*/
template<auto Function>
template<auto Candidate>
void connect() ENTT_NOEXCEPT {
connect<Function>(internal::index_sequence_for(internal::to_function_pointer_t<decltype(Function)>{}));
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 member function for a given instance or a free function
* with payload to a delegate.
* @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
@@ -206,33 +188,46 @@ public:
* 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 {
connect<Candidate>(value_or_instance, internal::index_sequence_for(internal::to_function_pointer_t<decltype(Candidate), Type *>{}));
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 member function for a given instance or a free function
* with payload to a delegate.
* @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/>
* 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.
* @sa connect(Type &)
*
* @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 pointer that fits the purpose.
*/
template<auto Candidate, typename Type>
void connect(Type *value_or_instance) ENTT_NOEXCEPT {
connect<Candidate>(value_or_instance, internal::index_sequence_for(internal::to_function_pointer_t<decltype(Candidate), Type *>{}));
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>{}));
}
}
/**
@@ -269,7 +264,7 @@ 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)...);
}
/**
@@ -278,7 +273,7 @@ public:
*/
explicit operator bool() const ENTT_NOEXCEPT {
// no need to test also data
return fn;
return !(fn == nullptr);
}
/**
@@ -312,46 +307,24 @@ bool operator!=(const delegate<Ret(Args...)> &lhs, const delegate<Ret(Args...)>
/**
* @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<internal::to_function_pointer_t<decltype(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.
*
* @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 &) ENTT_NOEXCEPT
-> delegate<std::remove_pointer_t<internal::to_function_pointer_t<decltype(Candidate), Type *>>>;
/**
* @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.
*
* @tparam Candidate Member or free function 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 *) ENTT_NOEXCEPT
-> delegate<std::remove_pointer_t<internal::to_function_pointer_t<decltype(Candidate), Type *>>>;
delegate(connect_arg_t<Candidate>, Type &&) ENTT_NOEXCEPT
-> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate), Type>>>;
}
#endif // ENTT_SIGNAL_DELEGATE_HPP
#endif

View File

@@ -6,11 +6,9 @@
#include <memory>
#include <cstddef>
#include <utility>
#include <algorithm>
#include <type_traits>
#include "../config/config.h"
#include "../core/family.hpp"
#include "../core/type_traits.hpp"
#include "../core/type_info.hpp"
#include "sigh.hpp"
@@ -31,19 +29,15 @@ namespace entt {
* crashes.
*/
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 clear() = 0;
virtual void clear() ENTT_NOEXCEPT = 0;
virtual ENTT_ID_TYPE type_id() const ENTT_NOEXCEPT = 0;
};
template<typename Event>
struct signal_wrapper: base_wrapper {
struct pool_handler: basic_pool {
using signal_type = sigh<void(const Event &)>;
using sink_type = typename signal_type::sink_type;
@@ -57,7 +51,7 @@ class dispatcher {
events.erase(events.cbegin(), events.cbegin()+length);
}
void clear() override {
void clear() ENTT_NOEXCEPT override {
events.clear();
}
@@ -67,7 +61,7 @@ class dispatcher {
template<typename... Args>
void trigger(Args &&... args) {
signal.publish({ std::forward<Args>(args)... });
signal.publish(Event{std::forward<Args>(args)...});
}
template<typename... Args>
@@ -75,63 +69,32 @@ class dispatcher {
events.emplace_back(std::forward<Args>(args)...);
}
ENTT_ID_TYPE type_id() const ENTT_NOEXCEPT override {
return type_info<Event>::id();
}
private:
signal_type signal{};
std::vector<Event> events;
};
struct wrapper_data {
std::unique_ptr<base_wrapper> wrapper;
ENTT_ID_TYPE runtime_type;
};
template<typename Event>
static auto type() ENTT_NOEXCEPT {
if constexpr(is_named_type_v<Event>) {
return named_type_traits_v<Event>;
} else {
return event_family::type<std::decay_t<Event>>;
}
}
pool_handler<Event> & assure() {
static_assert(std::is_same_v<Event, std::decay_t<Event>>);
static std::size_t index{pools.size()};
template<typename Event>
signal_wrapper<Event> & assure() {
const auto wtype = type<Event>();
wrapper_data *wdata = nullptr;
if(const auto length = pools.size(); !(index < length) || pools[index]->type_id() != type_info<Event>::id()) {
for(index = {}; index < length && pools[index]->type_id() != type_info<Event>::id(); ++index);
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);
}
wdata = &wrappers[wtype];
if(wdata->wrapper && wdata->runtime_type != wtype) {
wrappers.emplace_back();
std::swap(wrappers[wtype], wrappers.back());
wdata = &wrappers[wtype];
if(index == pools.size()) {
pools.emplace_back(new pool_handler<Event>{});
}
}
if(!wdata->wrapper) {
wdata->wrapper = std::make_unique<signal_wrapper<Event>>();
wdata->runtime_type = wtype;
}
return static_cast<signal_wrapper<Event> &>(*wdata->wrapper);
return static_cast<pool_handler<Event> &>(*pools[index]);
}
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.
*
@@ -150,7 +113,7 @@ public:
* @return A temporary sink object.
*/
template<typename Event>
sink_type<Event> sink() ENTT_NOEXCEPT {
auto sink() {
return assure<Event>().sink();
}
@@ -221,15 +184,13 @@ public:
* @tparam Event Type of events to discard.
*/
template<typename... Event>
void discard() {
void clear() {
if constexpr(sizeof...(Event) == 0) {
std::for_each(wrappers.begin(), wrappers.end(), [](auto &&wdata) {
if(wdata.wrapper) {
wdata.wrapper->clear();
}
});
for(auto &&cpool: pools) {
cpool->clear();
}
} else {
(assure<std::decay_t<Event>>().clear(), ...);
(assure<Event>().clear(), ...);
}
}
@@ -255,19 +216,17 @@ 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) {
if(auto &wdata = wrappers[pos-1]; wdata.wrapper) {
wdata.wrapper->publish();
}
for(auto pos = pools.size(); pos; --pos) {
pools[pos-1]->publish();
}
}
private:
std::vector<wrapper_data> wrappers;
std::vector<std::unique_ptr<basic_pool>> pools;
};
}
#endif // ENTT_SIGNAL_DISPATCHER_HPP
#endif

View File

@@ -9,9 +9,9 @@
#include <memory>
#include <vector>
#include <list>
#include <iterator>
#include "../config/config.h"
#include "../core/family.hpp"
#include "../core/type_traits.hpp"
#include "../core/type_info.hpp"
namespace entt {
@@ -29,7 +29,7 @@ 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.
@@ -40,16 +40,15 @@ namespace entt {
*/
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 ENTT_ID_TYPE type_id() const ENTT_NOEXCEPT = 0;
};
template<typename Event>
struct event_handler: base_handler {
struct pool_handler: basic_pool {
using listener_type = std::function<void(const Event &, Derived &)>;
using element_type = std::pair<bool, listener_type>;
using container_type = std::list<element_type>;
@@ -64,9 +63,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 +84,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) {
@@ -95,77 +98,56 @@ class emitter {
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; });
}
ENTT_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_v<Event>;
} else {
return handler_family::type<std::decay_t<Event>>;
const pool_handler<Event> & assure() const {
static_assert(std::is_same_v<Event, std::decay_t<Event>>);
static std::size_t index{pools.size()};
if(const auto length = pools.size(); !(index < length) || pools[index]->type_id() != type_info<Event>::id()) {
for(index = {}; index < length && pools[index]->type_id() != type_info<Event>::id(); ++index);
if(index == pools.size()) {
pools.emplace_back(new pool_handler<Event>{});
}
}
return static_cast<pool_handler<Event> &>(*pools[index]);
}
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());
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,27 +159,27 @@ 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 {
virtual ~emitter() {
static_assert(std::is_base_of_v<emitter<Derived>, Derived>);
}
@@ -220,7 +202,7 @@ public:
*/
template<typename Event, typename... Args>
void publish(Args &&... args) {
assure<Event>()->publish({ std::forward<Args>(args)... }, *static_cast<Derived *>(this));
assure<Event>().publish(Event{std::forward<Args>(args)...}, *static_cast<Derived *>(this));
}
/**
@@ -245,7 +227,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));
}
/**
@@ -270,7 +252,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 +265,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 +278,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 +289,9 @@ 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) {
cpool->clear();
}
}
/**
@@ -318,8 +300,8 @@ public:
* @return True if there are no listeners registered, false otherwise.
*/
template<typename Event>
bool empty() const ENTT_NOEXCEPT {
return assure<Event>()->empty();
bool empty() const {
return assure<Event>().empty();
}
/**
@@ -327,17 +309,17 @@ public:
* @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();
return std::all_of(pools.cbegin(), pools.cend(), [](auto &&cpool) {
return 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

@@ -9,6 +9,19 @@ namespace entt {
template<typename>
class delegate;
/*! @class dispatcher */
class dispatcher;
/*! @class emitter */
template<typename>
class emitter;
/*! @class connection */
class connection;
/*! @class scoped_connection */
struct scoped_connection;
/*! @class sink */
template<typename>
class sink;
@@ -21,4 +34,4 @@ class sigh;
}
#endif // ENTT_SIGNAL_FWD_HPP
#endif

View File

@@ -97,9 +97,9 @@ public:
* @param args Arguments to use to invoke listeners.
*/
void publish(Args... args) const {
std::for_each(calls.cbegin(), calls.cend(), [&args...](auto &&call) {
for(auto &&call: std::as_const(calls)) {
call(args...);
});
}
}
/**
@@ -162,38 +162,6 @@ public:
/*! @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.
@@ -225,30 +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 {
using connection::operator bool;
using connection::release;
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();
}
/**
@@ -258,30 +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;
explicit operator bool() const ENTT_NOEXCEPT {
return static_cast<bool>(conn);
}
/*! @brief Breaks the connection. */
void release() {
conn.release();
}
private:
connection conn;
};
@@ -309,9 +271,9 @@ class sink<Ret(Args...)> {
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:
@@ -333,7 +295,8 @@ public:
}
/**
* @brief Returns a sink that connects before a given function.
* @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 sink object.
*/
@@ -351,38 +314,17 @@ public:
}
/**
* @brief Returns a sink that connects before a given member function or
* free function with payload.
* @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 reference that fits the purpose.
* @param value_or_instance A valid object that fits the purpose.
* @return A properly initialized sink object.
*/
template<auto Candidate, typename Type>
sink before(Type &value_or_instance) {
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 member function or
* free function with payload.
* @tparam Candidate Member or free function to look for.
* @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<auto Candidate, typename Type>
sink before(Type *value_or_instance) {
delegate<Ret(Args...)> call{};
call.template connect<Candidate>(value_or_instance);
call.template connect<Candidate>(std::forward<Type>(value_or_instance));
const auto &calls = signal->calls;
const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call));
@@ -438,29 +380,29 @@ public:
}
/**
* @brief Connects a free function to a signal.
* @brief Connects a free function or an unbound member to a signal.
*
* The signal handler performs checks to avoid multiple connections for free
* functions.
* The signal handler performs checks to avoid multiple connections for the
* same function.
*
* @tparam Function A valid free function pointer.
* @tparam Candidate Function or member to connect to the signal.
* @return A properly initialized connection object.
*/
template<auto Function>
template<auto Candidate>
connection connect() {
disconnect<Function>();
disconnect<Candidate>();
delegate<Ret(Args...)> call{};
call.template connect<Function>();
call.template connect<Candidate>();
signal->calls.insert(signal->calls.end() - offset, std::move(call));
delegate<void(void *)> conn{};
conn.template connect<&release<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.
@@ -471,13 +413,13 @@ public:
* 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 signal.
* @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.
* @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{};
@@ -485,85 +427,40 @@ public:
signal->calls.insert(signal->calls.end() - offset, std::move(call));
delegate<void(void *)> conn{};
conn.template connect<&release<Candidate, Type &>>(value_or_instance);
conn.template connect<&release<Candidate, Type>>(value_or_instance);
return { std::move(conn), signal };
}
/**
* @brief Connects a member function or a free function with payload 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
* 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.
*
* @tparam Candidate Member or free function to connect to the signal.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid pointer that fits the purpose.
* @return A properly initialized connection object.
* @brief Disconnects a free function or an unbound member from a signal.
* @tparam Candidate Function or member to disconnect from the delegate.
*/
template<auto Candidate, typename Type>
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<&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.
*/
template<auto Function>
template<auto Candidate>
void disconnect() {
auto &calls = signal->calls;
delegate<Ret(Args...)> call{};
call.template connect<Function>();
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 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>
void disconnect(Type &value_or_instance) {
void disconnect(Type &&value_or_instance) {
auto &calls = signal->calls;
delegate<Ret(Args...)> call{};
call.template connect<Candidate>(value_or_instance);
call.template connect<Candidate>(std::forward<Type>(value_or_instance));
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.
* @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 disconnect(Type *value_or_instance) {
auto &calls = signal->calls;
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 object that fits the purpose.
*/
@@ -573,10 +470,10 @@ public:
}
/**
* @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 pointer that fits the purpose.
* @param value_or_instance A valid object that fits the purpose.
*/
template<typename Type>
void disconnect(Type *value_or_instance) {
@@ -615,4 +512,4 @@ sink(sigh<Ret(Args...)> &) ENTT_NOEXCEPT -> sink<Ret(Args...)>;
}
#endif // ENTT_SIGNAL_SIGH_HPP
#endif

View File

View File

@@ -2,128 +2,201 @@
# 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 -Wshadow>)
target_compile_options(${LIB_TARGET} PRIVATE $<$<CXX_COMPILER_ID:MSVC>:/EHsc /W2>)
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>
)
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 -Wshadow>)
target_compile_options(${TEST_NAME} PRIVATE $<$<CXX_COMPILER_ID:MSVC>:/EHsc /W2>)
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})
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME})
endmacro()
endfunction()
function(SETUP_LIB_TEST TEST_NAME)
add_library(_${TEST_NAME} SHARED lib/${TEST_NAME}/lib.cpp)
SETUP_TARGET(_${TEST_NAME})
SETUP_BASIC_TEST(lib_${TEST_NAME} lib/${TEST_NAME}/main.cpp)
target_compile_definitions(_${TEST_NAME} PRIVATE ENTT_API_EXPORT ${ARGV1})
target_compile_definitions(lib_${TEST_NAME} PRIVATE ENTT_API_IMPORT ${ARGV1})
target_link_libraries(lib_${TEST_NAME} PRIVATE _${TEST_NAME})
endfunction()
function(SETUP_PLUGIN_TEST TEST_NAME)
add_library(_${TEST_NAME} MODULE lib/${TEST_NAME}/plugin.cpp)
SETUP_TARGET(_${TEST_NAME})
SETUP_BASIC_TEST(lib_${TEST_NAME} lib/${TEST_NAME}/main.cpp)
target_include_directories(_${TEST_NAME} PRIVATE ${cr_INCLUDE_DIR})
target_include_directories(lib_${TEST_NAME} PRIVATE ${cr_INCLUDE_DIR})
target_compile_definitions(lib_${TEST_NAME} PRIVATE NOMINMAX PLUGIN="$<TARGET_FILE:_${TEST_NAME}>" ${ARGV1})
target_compile_definitions(_${TEST_NAME} PRIVATE NOMINMAX ${ARGV1})
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 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_LIB_TEST(dispatcher_std ENTT_STANDARD_CPP)
SETUP_LIB_TEST(emitter_std ENTT_STANDARD_CPP)
SETUP_LIB_TEST(meta_std ENTT_STANDARD_CPP)
SETUP_LIB_TEST(registry_std ENTT_STANDARD_CPP)
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)
SETUP_PLUGIN_TEST(dispatcher_plugin)
SETUP_PLUGIN_TEST(emitter_plugin)
SETUP_PLUGIN_TEST(meta_plugin)
SETUP_PLUGIN_TEST(registry_plugin)
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(dispatcher_plugin_std ENTT_STANDARD_CPP)
SETUP_PLUGIN_TEST(emitter_plugin_std ENTT_STANDARD_CPP)
SETUP_PLUGIN_TEST(meta_plugin_std ENTT_STANDARD_CPP)
SETUP_PLUGIN_TEST(registry_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(helper entt/entity/helper.cpp)
SETUP_BASIC_TEST(observer entt/entity/observer.cpp)
SETUP_BASIC_TEST(registry entt/entity/registry.cpp)
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 entt/meta/meta.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 {
@@ -44,9 +45,9 @@ void pathological(Func func) {
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); }
});
@@ -113,7 +114,9 @@ TEST(Benchmark, ConstructManyWithComponents) {
std::cout << "Constructing 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.assign<position>(entities.begin(), entities.end());
registry.assign<velocity>(entities.begin(), entities.end());
timer.elapsed();
}
@@ -167,7 +170,7 @@ TEST(Benchmark, IterateSingleComponentRuntime1M) {
}
auto test = [&registry](auto func) {
entt::component types[] = { registry.type<position>() };
ENTT_ID_TYPE types[] = { entt::type_info<position>::id() };
timer timer;
registry.runtime_view(std::begin(types), std::end(types)).each(func);
@@ -332,7 +335,10 @@ TEST(Benchmark, IterateTwoComponentsRuntime1M) {
}
auto test = [&registry](auto func) {
entt::component types[] = { registry.type<position>(), registry.type<velocity>() };
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);
@@ -360,7 +366,10 @@ TEST(Benchmark, IterateTwoComponentsRuntime1MHalf) {
}
auto test = [&registry](auto func) {
entt::component types[] = { registry.type<position>(), registry.type<velocity>() };
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);
@@ -388,7 +397,10 @@ TEST(Benchmark, IterateTwoComponentsRuntime1MOne) {
}
auto test = [&registry](auto func) {
entt::component types[] = { registry.type<position>(), registry.type<velocity>() };
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);
@@ -561,7 +573,11 @@ TEST(Benchmark, IterateThreeComponentsRuntime1M) {
}
auto test = [&registry](auto func) {
entt::component types[] = { registry.type<position>(), registry.type<velocity>(), registry.type<comp<0>>() };
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);
@@ -591,7 +607,11 @@ TEST(Benchmark, IterateThreeComponentsRuntime1MHalf) {
}
auto test = [&registry](auto func) {
entt::component types[] = { registry.type<position>(), registry.type<velocity>(), registry.type<comp<0>>() };
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);
@@ -621,7 +641,11 @@ TEST(Benchmark, IterateThreeComponentsRuntime1MOne) {
}
auto test = [&registry](auto func) {
entt::component types[] = { registry.type<position>(), registry.type<velocity>(), registry.type<comp<0>>() };
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);
@@ -835,12 +859,12 @@ TEST(Benchmark, IterateFiveComponentsRuntime1M) {
}
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>>()
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;
@@ -875,12 +899,12 @@ TEST(Benchmark, IterateFiveComponentsRuntime1MHalf) {
}
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>>()
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;
@@ -915,12 +939,12 @@ TEST(Benchmark, IterateFiveComponentsRuntime1MOne) {
}
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>>()
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;

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

@@ -52,15 +52,15 @@ TEST(HashedString, ToValue) {
const char *foobar = "foobar";
ASSERT_EQ(entt::hashed_string::to_value(foobar), 0xbf9cf968);
ASSERT_EQ(entt::hashed_string::value(foobar), 0xbf9cf968);
// how would you test a constexpr otherwise?
(void)std::integral_constant<hash_type, entt::hashed_string::to_value("quux")>{};
(void)std::integral_constant<hash_type, entt::hashed_string::value("quux")>{};
}
TEST(HashedString, StringView) {
std::string str{"__foobar__"};
std::string_view view{str.data()+2, 6};
ASSERT_EQ(entt::hashed_string::to_value(view.data(), view.size()), 0xbf9cf968);
ASSERT_EQ(entt::hashed_string::value(view.data(), view.size()), 0xbf9cf968);
}
TEST(HashedWString, Functionalities) {
@@ -111,15 +111,15 @@ TEST(HashedWString, ToValue) {
const wchar_t *foobar = L"foobar";
ASSERT_EQ(entt::hashed_wstring::to_value(foobar), 0xbf9cf968);
ASSERT_EQ(entt::hashed_wstring::value(foobar), 0xbf9cf968);
// how would you test a constexpr otherwise?
(void)std::integral_constant<hash_type, entt::hashed_wstring::to_value(L"quux")>{};
(void)std::integral_constant<hash_type, entt::hashed_wstring::value(L"quux")>{};
}
TEST(HashedWString, StringView) {
std::wstring str{L"__foobar__"};
std::wstring_view view{str.data()+2, 6};
ASSERT_EQ(entt::hashed_wstring::to_value(view.data(), view.size()), 0xbf9cf968);
ASSERT_EQ(entt::hashed_wstring::value(view.data(), view.size()), 0xbf9cf968);
}
TEST(BasicHashedString, DeductionGuide) {

View File

@@ -0,0 +1,9 @@
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/core/type_info.hpp>
TEST(TypeId, Functionalities) {
ASSERT_NE(entt::type_info<int>::id(), entt::type_info<const int>::id());
ASSERT_NE(entt::type_info<int>::id(), entt::type_info<char>::id());
ASSERT_EQ(entt::type_info<int>::id(), entt::type_info<int>::id());
}

View File

@@ -1,10 +1,8 @@
#include <gtest/gtest.h>
#include <entt/config/config.h>
#include <entt/core/hashed_string.hpp>
#include <entt/core/type_traits.hpp>
ENTT_NAMED_TYPE(int);
ENTT_NAMED_STRUCT(named_struct, {});
ENTT_NAMED_CLASS(named_class, {});
TEST(Choice, Functionalities) {
ASSERT_TRUE((std::is_base_of_v<entt::choice_t<0>, entt::choice_t<1>>));
ASSERT_FALSE((std::is_base_of_v<entt::choice_t<1>, entt::choice_t<0>>));
@@ -27,13 +25,19 @@ TEST(IsEqualityComparable, Functionalities) {
ASSERT_FALSE(entt::is_equality_comparable_v<void>);
}
TEST(NamedTypes, Functionalities) {
ASSERT_TRUE(entt::is_named_type_v<int>);
ASSERT_TRUE(entt::is_named_type_v<named_struct>);
ASSERT_TRUE(entt::is_named_type_v<named_class>);
ASSERT_FALSE(entt::is_named_type_v<char>);
TEST(MemberClass, Functionalities) {
struct clazz {
char foo(int) { return {}; }
int bar(double, float) const { return {}; }
bool quux;
};
ASSERT_EQ(entt::named_type_traits_t<int>::value, "int"_hs);
ASSERT_EQ(entt::named_type_traits_t<named_struct>::value, "named_struct"_hs);
ASSERT_EQ(entt::named_type_traits_t<named_class>::value, "named_class"_hs);
ASSERT_TRUE((std::is_same_v<clazz, entt::member_class_t<decltype(&clazz::foo)>>));
ASSERT_TRUE((std::is_same_v<clazz, entt::member_class_t<decltype(&clazz::bar)>>));
ASSERT_TRUE((std::is_same_v<clazz, entt::member_class_t<decltype(&clazz::quux)>>));
}
TEST(Tag, Functionalities) {
ASSERT_EQ(entt::tag<"foobar"_hs>::value, entt::hashed_string::value("foobar"));
ASSERT_TRUE((std::is_same_v<typename entt::tag<"foobar"_hs>::value_type, ENTT_ID_TYPE>));
}

View File

@@ -1,121 +0,0 @@
load("//bazel:copts.bzl", "entt_copts")
cc_test(
name = "actor",
copts = entt_copts,
srcs = ["actor.cpp"],
deps = [
"//:entt",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "entity",
copts = entt_copts,
srcs = ["entity.cpp"],
deps = [
"//:entt",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "group",
copts = entt_copts,
srcs = ["group.cpp"],
deps = [
"//:entt",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "helper",
copts = entt_copts,
srcs = ["helper.cpp"],
deps = [
"//:entt",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "observer",
copts = entt_copts,
srcs = ["observer.cpp"],
deps = [
"//:entt",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "prototype",
copts = entt_copts,
srcs = ["prototype.cpp"],
deps = [
"//:entt",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "registry",
copts = entt_copts,
srcs = ["registry.cpp"],
deps = [
"//:entt",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "runtime_view",
copts = entt_copts,
srcs = ["runtime_view.cpp"],
deps = [
"//:entt",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "snapshot",
copts = entt_copts,
srcs = ["snapshot.cpp"],
deps = [
"//:entt",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "sparse_set",
copts = entt_copts,
srcs = ["sparse_set.cpp"],
deps = [
"//:entt",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "storage",
copts = entt_copts,
srcs = ["storage.cpp"],
deps = [
"//:entt",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "view",
copts = entt_copts,
srcs = ["view.cpp"],
deps = [
"//:entt",
"@com_google_googletest//:gtest_main",
],
)

View File

@@ -12,6 +12,7 @@ TEST(Entity, Null) {
registry.assign<int>(entity, 42);
ASSERT_FALSE(entt::entity{} == entt::null);
ASSERT_TRUE(entt::entity{traits_type::entity_mask} == entt::null);
ASSERT_TRUE(entt::entity{~typename traits_type::entity_type{}} == entt::null);

View File

@@ -468,7 +468,11 @@ TEST(NonOwningGroup, TrackEntitiesOnComponentDestruction) {
TEST(NonOwningGroup, Less) {
entt::registry registry;
const auto entity = std::get<0>(registry.create<int, char, entt::tag<"empty"_hs>>());
const auto entity = registry.create();
registry.assign<int>(entity);
registry.assign<char>(entity);
registry.assign<entt::tag<"empty"_hs>>(entity);
registry.group(entt::get<int, char, entt::tag<"empty"_hs>>).less([entity](const auto entt, int, char) {
ASSERT_EQ(entity, entt);
@@ -486,9 +490,31 @@ TEST(NonOwningGroup, Less) {
registry.group(entt::get<int, char, double>).less([](const auto, int, char, double) { FAIL(); });
}
TEST(NonOwningGroup, FrontBack) {
entt::registry registry;
auto group = registry.group<>(entt::get<const int, const char>);
ASSERT_EQ(group.front(), static_cast<entt::entity>(entt::null));
ASSERT_EQ(group.back(), static_cast<entt::entity>(entt::null));
const auto e0 = registry.create();
registry.assign<int>(e0);
registry.assign<char>(e0);
const auto e1 = registry.create();
registry.assign<int>(e1);
registry.assign<char>(e1);
const auto entity = registry.create();
registry.assign<char>(entity);
ASSERT_EQ(group.front(), e1);
ASSERT_EQ(group.back(), e0);
}
TEST(NonOwningGroup, SignalRace) {
entt::registry registry;
registry.on_construct<double>().connect<&entt::registry::assign_or_replace<int>>(registry);
registry.on_construct<double>().connect<&entt::registry::assign_or_replace<int>>();
registry.group(entt::get<int, double>);
auto entity = registry.create();
@@ -1040,7 +1066,11 @@ TEST(OwningGroup, TrackEntitiesOnComponentDestruction) {
TEST(OwningGroup, Less) {
entt::registry registry;
const auto entity = std::get<0>(registry.create<int, char, entt::tag<"empty"_hs>>());
const auto entity = registry.create();
registry.assign<int>(entity);
registry.assign<char>(entity);
registry.assign<entt::tag<"empty"_hs>>(entity);
registry.group<int>(entt::get<char, entt::tag<"empty"_hs>>).less([entity](const auto entt, int, char) {
ASSERT_EQ(entity, entt);
@@ -1058,9 +1088,31 @@ TEST(OwningGroup, Less) {
registry.group<double>(entt::get<int, char>).less([](const auto, double, int, char) { FAIL(); });
}
TEST(OwningGroup, FrontBack) {
entt::registry registry;
auto group = registry.group<const char>(entt::get<const int>);
ASSERT_EQ(group.front(), static_cast<entt::entity>(entt::null));
ASSERT_EQ(group.back(), static_cast<entt::entity>(entt::null));
const auto e0 = registry.create();
registry.assign<int>(e0);
registry.assign<char>(e0);
const auto e1 = registry.create();
registry.assign<int>(e1);
registry.assign<char>(e1);
const auto entity = registry.create();
registry.assign<char>(entity);
ASSERT_EQ(group.front(), e1);
ASSERT_EQ(group.back(), e0);
}
TEST(OwningGroup, SignalRace) {
entt::registry registry;
registry.on_construct<double>().connect<&entt::registry::assign_or_replace<int>>(registry);
registry.on_construct<double>().connect<&entt::registry::assign_or_replace<int>>();
registry.group<int>(entt::get<double>);
auto entity = registry.create();

View File

@@ -10,9 +10,8 @@ TEST(Helper, AsView) {
([](entt::view<entt::exclude_t<>, int>) {})(entt::as_view{registry});
([](entt::view<entt::exclude_t<int>, char, double>) {})(entt::as_view{registry});
([](entt::view<entt::exclude_t<const int>, char, double>) {})(entt::as_view{registry});
([](entt::view<entt::exclude_t<const int>, const char, double>) {})(entt::as_view{registry});
([](entt::view<entt::exclude_t<const int>, const char, const double>) {})(entt::as_view{registry});
([](entt::view<entt::exclude_t<int>, const char, double>) {})(entt::as_view{registry});
([](entt::view<entt::exclude_t<int>, const char, const double>) {})(entt::as_view{registry});
}
TEST(Helper, AsGroup) {
@@ -20,32 +19,6 @@ TEST(Helper, AsGroup) {
const entt::registry cregistry;
([](entt::group<entt::exclude_t<int>, entt::get_t<char>, double>) {})(entt::as_group{registry});
([](entt::group<entt::exclude_t<const int>, entt::get_t<char>, double>) {})(entt::as_group{registry});
([](entt::group<entt::exclude_t<const int>, entt::get_t<const char>, double>) {})(entt::as_group{registry});
([](entt::group<entt::exclude_t<const int>, entt::get_t<const char>, const double>) {})(entt::as_group{registry});
}
TEST(Helper, Tag) {
entt::registry registry;
const auto entity = registry.create();
registry.assign<entt::tag<"foobar"_hs>>(entity);
registry.assign<int>(entity, 42);
int counter{};
ASSERT_FALSE(registry.has<entt::tag<"barfoo"_hs>>(entity));
ASSERT_TRUE(registry.has<entt::tag<"foobar"_hs>>(entity));
for(auto entt: registry.view<int, entt::tag<"foobar"_hs>>()) {
(void)entt;
++counter;
}
ASSERT_NE(counter, 0);
for(auto entt: registry.view<entt::tag<"foobar"_hs>>()) {
(void)entt;
--counter;
}
ASSERT_EQ(counter, 0);
([](entt::group<entt::exclude_t<int>, entt::get_t<const char>, double>) {})(entt::as_group{registry});
([](entt::group<entt::exclude_t<int>, entt::get_t<const char>, const double>) {})(entt::as_group{registry});
}

View File

@@ -14,7 +14,8 @@ TEST(Observer, Functionalities) {
ASSERT_EQ(observer.data(), nullptr);
ASSERT_EQ(observer.begin(), observer.end());
const auto entity = std::get<0>(registry.create<int>());
const auto entity = registry.create();
registry.assign<int>(entity);
ASSERT_EQ(observer.size(), 1u);
ASSERT_FALSE(observer.empty());
@@ -80,7 +81,7 @@ TEST(Observer, AllOf) {
observer.disconnect();
registry.assign_or_replace<int>(entity);
registry.assign_or_replace<char>(entity);
registry.reset<float>(entity);
registry.remove_if_exists<float>(entity);
ASSERT_TRUE(observer.empty());
}
@@ -263,7 +264,8 @@ TEST(Observer, CrossRulesCornerCase) {
TEST(Observer, Each) {
entt::registry registry;
entt::observer observer{registry, entt::collector.group<int>()};
const auto entity = std::get<0>(registry.create<int>());
const auto entity = registry.create();
registry.assign<int>(entity);
ASSERT_FALSE(observer.empty());
ASSERT_EQ(observer.size(), 1u);

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
#include <iterator>
#include <algorithm>
#include <gtest/gtest.h>
#include <entt/core/type_info.hpp>
#include <entt/entity/registry.hpp>
#include <entt/entity/runtime_view.hpp>
@@ -11,7 +12,7 @@ TEST(RuntimeView, Functionalities) {
registry.reserve<int>(0);
registry.reserve<char>(0);
entt::component types[] = { registry.type<int>(), registry.type<char>() };
ENTT_ID_TYPE types[] = { entt::type_info<int>::id(), entt::type_info<char>::id() };
auto view = registry.runtime_view(std::begin(types), std::end(types));
ASSERT_TRUE(view.empty());
@@ -26,13 +27,13 @@ TEST(RuntimeView, Functionalities) {
registry.assign<char>(e1);
auto it = registry.runtime_view(std::begin(types), std::end(types)).begin();
auto it = view.begin();
ASSERT_EQ(*it, e1);
ASSERT_EQ(++it, (registry.runtime_view(std::begin(types), std::end(types)).end()));
ASSERT_EQ(++it, (view.end()));
ASSERT_NO_THROW((registry.runtime_view(std::begin(types), std::end(types)).begin()++));
ASSERT_NO_THROW((++registry.runtime_view(std::begin(types), std::end(types)).begin()));
ASSERT_NO_THROW((view.begin()++));
ASSERT_NO_THROW((++view.begin()));
ASSERT_NE(view.begin(), view.end());
ASSERT_EQ(view.size(), decltype(view.size()){1});
@@ -54,7 +55,7 @@ TEST(RuntimeView, Iterator) {
registry.assign<int>(entity);
registry.assign<char>(entity);
entt::component types[] = { registry.type<int>(), registry.type<char>() };
ENTT_ID_TYPE types[] = { entt::type_info<int>::id(), entt::type_info<char>::id() };
auto view = registry.runtime_view(std::begin(types), std::end(types));
using iterator_type = typename decltype(view)::iterator_type;
@@ -67,8 +68,14 @@ TEST(RuntimeView, Iterator) {
ASSERT_EQ(end, view.end());
ASSERT_NE(begin, end);
ASSERT_EQ(view.begin()++, view.begin());
ASSERT_EQ(++view.begin(), view.end());
ASSERT_EQ(begin++, view.begin());
ASSERT_EQ(begin--, view.end());
ASSERT_EQ(++begin, view.end());
ASSERT_EQ(--begin, view.begin());
ASSERT_EQ(*begin, entity);
ASSERT_EQ(*begin.operator->(), entity);
}
TEST(RuntimeView, Contains) {
@@ -84,7 +91,7 @@ TEST(RuntimeView, Contains) {
registry.destroy(e0);
entt::component types[] = { registry.type<int>(), registry.type<char>() };
ENTT_ID_TYPE types[] = { entt::type_info<int>::id(), entt::type_info<char>::id() };
auto view = registry.runtime_view(std::begin(types), std::end(types));
ASSERT_FALSE(view.contains(e0));
@@ -103,7 +110,7 @@ TEST(RuntimeView, Empty) {
registry.assign<char>(e1);
registry.assign<float>(e1);
entt::component types[] = { registry.type<char>(), registry.type<int>(), registry.type<float>() };
ENTT_ID_TYPE types[] = { entt::type_info<int>::id(), entt::type_info<char>::id(), entt::type_info<float>::id() };
auto view = registry.runtime_view(std::begin(types), std::end(types));
view.each([](auto) { FAIL(); });
@@ -123,7 +130,7 @@ TEST(RuntimeView, Each) {
registry.assign<int>(e1);
registry.assign<char>(e1);
entt::component types[] = { registry.type<int>(), registry.type<char>() };
ENTT_ID_TYPE types[] = { entt::type_info<int>::id(), entt::type_info<char>::id() };
auto view = registry.runtime_view(std::begin(types), std::end(types));
std::size_t cnt = 0;
@@ -145,7 +152,7 @@ TEST(RuntimeView, EachWithHoles) {
registry.assign<int>(e0, 0);
registry.assign<int>(e2, 2);
entt::component types[] = { registry.type<int>(), registry.type<char>() };
ENTT_ID_TYPE types[] = { entt::type_info<int>::id(), entt::type_info<char>::id() };
auto view = registry.runtime_view(std::begin(types), std::end(types));
view.each([e0](auto entity) {
@@ -159,7 +166,7 @@ TEST(RuntimeView, MissingPool) {
const auto e0 = registry.create();
registry.assign<int>(e0);
entt::component types[] = { registry.type<int>(), registry.type<char>() };
ENTT_ID_TYPE types[] = { entt::type_info<int>::id(), entt::type_info<char>::id() };
auto view = registry.runtime_view(std::begin(types), std::end(types));
ASSERT_TRUE(view.empty());
@@ -182,7 +189,7 @@ TEST(RuntimeView, EmptyRange) {
const auto e0 = registry.create();
registry.assign<int>(e0);
const entt::component *ptr = nullptr;
const ENTT_ID_TYPE *ptr = nullptr;
auto view = registry.runtime_view(ptr, ptr);
ASSERT_TRUE(view.empty());

View File

@@ -102,7 +102,7 @@ TEST(Snapshot, Dump) {
.destroyed(output)
.component<int, char, double, a_component, another_component>(output);
registry.reset();
registry.clear();
ASSERT_FALSE(registry.valid(e0));
ASSERT_FALSE(registry.valid(e1));
@@ -173,7 +173,7 @@ TEST(Snapshot, Partial) {
.destroyed(output)
.component<char, int>(output);
registry.reset();
registry.clear();
ASSERT_FALSE(registry.valid(e0));
ASSERT_FALSE(registry.valid(e1));
@@ -201,7 +201,7 @@ TEST(Snapshot, Partial) {
.entities(output)
.destroyed(output);
registry.reset();
registry.clear();
ASSERT_FALSE(registry.valid(e0));
ASSERT_FALSE(registry.valid(e1));
@@ -247,13 +247,13 @@ TEST(Snapshot, Iterator) {
const auto size = view.size();
registry.snapshot().component<another_component>(output, view.begin(), view.end());
registry.reset();
registry.clear();
registry.loader().component<another_component>(input);
ASSERT_EQ(registry.view<another_component>().size(), size);
registry.view<another_component>().each([](const auto entity, const auto &) {
ASSERT_TRUE(entt::to_integer(entity) % 2);
ASSERT_TRUE(entt::to_integral(entity) % 2);
});
}
@@ -285,7 +285,7 @@ TEST(Snapshot, Continuous) {
src.create();
}
src.reset();
src.clear();
for(int i = 0; i < 5; ++i) {
entity = src.create();
@@ -470,7 +470,7 @@ TEST(Snapshot, Continuous) {
component.bar = entity;
});
dst.reset<a_component>();
dst.clear<a_component>();
a_component_cnt = src.size<a_component>();
src.snapshot()
@@ -491,7 +491,7 @@ TEST(Snapshot, Continuous) {
ASSERT_EQ(dst.size<a_component>(), a_component_cnt);
src.reset<a_component>();
src.clear<a_component>();
a_component_cnt = {};
src.snapshot()
@@ -563,7 +563,7 @@ TEST(Snapshot, SyncDataMembers) {
src.create();
src.create();
src.reset();
src.clear();
auto parent = src.create();
auto child = src.create();

View File

@@ -70,7 +70,7 @@ TEST(SparseSet, Functionalities) {
ASSERT_FALSE(other.empty());
ASSERT_EQ(other.index(entt::entity{42}), 0u);
other.reset();
other.clear();
ASSERT_TRUE(other.empty());
ASSERT_EQ(other.size(), 0u);
@@ -124,7 +124,7 @@ TEST(SparseSet, BatchAdd) {
entities[1] = entt::entity{42};
set.construct(entt::entity{12});
set.batch(std::begin(entities), std::end(entities));
set.construct(std::begin(entities), std::end(entities));
set.construct(entt::entity{24});
ASSERT_TRUE(set.has(entities[0]));
@@ -414,7 +414,7 @@ TEST(SparseSet, SortRange) {
TEST(SparseSet, ArrangOrdered) {
entt::sparse_set<entt::entity> set;
entt::entity entities[5]{entt::entity{42}, entt::entity{12}, entt::entity{9}, entt::entity{7}, entt::entity{3}};
set.batch(std::begin(entities), std::end(entities));
set.construct(std::begin(entities), std::end(entities));
set.arrange(set.begin(), set.end(), [](auto...) { FAIL(); }, std::less{});
@@ -436,7 +436,7 @@ TEST(SparseSet, ArrangOrdered) {
TEST(SparseSet, ArrangeReverse) {
entt::sparse_set<entt::entity> set;
entt::entity entities[5]{entt::entity{3}, entt::entity{7}, entt::entity{9}, entt::entity{12}, entt::entity{42}};
set.batch(std::begin(entities), std::end(entities));
set.construct(std::begin(entities), std::end(entities));
set.arrange(set.begin(), set.end(), [&set, &entities](const auto lhs, const auto rhs) {
std::swap(entities[set.index(lhs)], entities[set.index(rhs)]);
@@ -460,7 +460,7 @@ TEST(SparseSet, ArrangeReverse) {
TEST(SparseSet, ArrangeUnordered) {
entt::sparse_set<entt::entity> set;
entt::entity entities[5]{entt::entity{9}, entt::entity{7}, entt::entity{3}, entt::entity{12}, entt::entity{42}};
set.batch(std::begin(entities), std::end(entities));
set.construct(std::begin(entities), std::end(entities));
set.arrange(set.begin(), set.end(), [&set, &entities](const auto lhs, const auto rhs) {
std::swap(entities[set.index(lhs)], entities[set.index(rhs)]);
@@ -484,7 +484,7 @@ TEST(SparseSet, ArrangeUnordered) {
TEST(SparseSet, ArrangeRange) {
entt::sparse_set<entt::entity> set;
entt::entity entities[5]{entt::entity{9}, entt::entity{7}, entt::entity{3}, entt::entity{12}, entt::entity{42}};
set.batch(std::begin(entities), std::end(entities));
set.construct(std::begin(entities), std::end(entities));
set.arrange(set.end(), set.end(), [](const auto, const auto) { FAIL(); }, std::less{});
@@ -530,7 +530,7 @@ TEST(SparseSet, ArrangeRange) {
TEST(SparseSet, ArrangeCornerCase) {
entt::sparse_set<entt::entity> set;
entt::entity entities[5]{entt::entity{0}, entt::entity{1}, entt::entity{4}, entt::entity{3}, entt::entity{2}};
set.batch(std::begin(entities), std::end(entities));
set.construct(std::begin(entities), std::end(entities));
set.arrange(++set.begin(), set.end(), [&set, &entities](const auto lhs, const auto rhs) {
std::swap(entities[set.index(lhs)], entities[set.index(rhs)]);

View File

@@ -60,7 +60,7 @@ TEST(Storage, Functionalities) {
ASSERT_EQ(*pool.try_get(entt::entity{41}), 12);
ASSERT_EQ(pool.try_get(entt::entity{99}), nullptr);
pool.reset();
pool.clear();
ASSERT_TRUE(pool.empty());
ASSERT_EQ(pool.size(), 0u);
@@ -100,7 +100,7 @@ TEST(Storage, BatchAdd) {
entities[0] = entt::entity{3};
entities[1] = entt::entity{42};
auto it = pool.batch(std::begin(entities), std::end(entities));
pool.construct(std::begin(entities), std::end(entities), {});
ASSERT_TRUE(pool.has(entities[0]));
ASSERT_TRUE(pool.has(entities[1]));
@@ -109,35 +109,6 @@ TEST(Storage, BatchAdd) {
ASSERT_EQ(pool.size(), 2u);
ASSERT_EQ(pool.get(entities[0]), 0);
ASSERT_EQ(pool.get(entities[1]), 0);
it[0] = 1;
it[1] = 2;
ASSERT_EQ(pool.get(entities[0]), 2);
ASSERT_EQ(pool.get(entities[1]), 1);
}
TEST(Storage, BatchAddByCopy) {
entt::storage<entt::entity, int> pool;
entt::entity entities[2];
entities[0] = entt::entity{3};
entities[1] = entt::entity{42};
auto it = pool.batch(std::begin(entities), std::end(entities), 3);
ASSERT_TRUE(pool.has(entities[0]));
ASSERT_TRUE(pool.has(entities[1]));
ASSERT_FALSE(pool.empty());
ASSERT_EQ(pool.size(), 2u);
ASSERT_EQ(pool.get(entities[0]), 3);
ASSERT_EQ(pool.get(entities[1]), 3);
it[0] = 1;
it[1] = 2;
ASSERT_EQ(pool.get(entities[0]), 2);
ASSERT_EQ(pool.get(entities[1]), 1);
}
TEST(Storage, BatchAddEmptyType) {
@@ -147,7 +118,7 @@ TEST(Storage, BatchAddEmptyType) {
entities[0] = entt::entity{3};
entities[1] = entt::entity{42};
pool.batch(std::begin(entities), std::end(entities));
pool.construct(std::begin(entities), std::end(entities));
ASSERT_TRUE(pool.has(entities[0]));
ASSERT_TRUE(pool.has(entities[1]));
@@ -169,7 +140,8 @@ TEST(Storage, AggregatesMustWork) {
TEST(Storage, TypesFromStandardTemplateLibraryMustWork) {
// see #37 - this test shouldn't crash, that's all
entt::storage<entt::entity, std::unordered_set<int>> pool;
pool.construct(entt::entity{0}).insert(42);
pool.construct(entt::entity{0});
pool.get(entt::entity{0}).insert(42);
pool.destroy(entt::entity{0});
}

View File

@@ -192,8 +192,14 @@ TEST(SingleComponentView, Find) {
TEST(SingleComponentView, Less) {
entt::registry registry;
const auto entity = std::get<0>(registry.create<int, entt::tag<"empty"_hs>>());
registry.create<char>();
auto create = [&](auto... component) {
const auto entt = registry.create();
(registry.assign<decltype(component)>(entt, component), ...);
return entt;
};
const auto entity = create(0, entt::tag<"empty"_hs>{});
create('c');
registry.view<entt::tag<"empty"_hs>>().less([entity](const auto entt) {
ASSERT_EQ(entity, entt);
@@ -214,6 +220,23 @@ TEST(SingleComponentView, Less) {
});
}
TEST(SingleComponentView, FrontBack) {
entt::registry registry;
auto view = registry.view<const int>();
ASSERT_EQ(view.front(), static_cast<entt::entity>(entt::null));
ASSERT_EQ(view.back(), static_cast<entt::entity>(entt::null));
const auto e0 = registry.create();
registry.assign<int>(e0);
const auto e1 = registry.create();
registry.assign<int>(e1);
ASSERT_EQ(view.front(), e1);
ASSERT_EQ(view.back(), e0);
}
TEST(MultiComponentView, Functionalities) {
entt::registry registry;
auto view = registry.view<int, char>();
@@ -286,8 +309,14 @@ TEST(MultiComponentView, Iterator) {
ASSERT_EQ(end, view.end());
ASSERT_NE(begin, end);
ASSERT_EQ(view.begin()++, view.begin());
ASSERT_EQ(++view.begin(), view.end());
ASSERT_EQ(begin++, view.begin());
ASSERT_EQ(begin--, view.end());
ASSERT_EQ(++begin, view.end());
ASSERT_EQ(--begin, view.begin());
ASSERT_EQ(*begin, entity);
ASSERT_EQ(*begin.operator->(), entity);
}
TEST(MultiComponentView, Contains) {
@@ -520,8 +549,16 @@ TEST(MultiComponentView, ExcludedComponents) {
TEST(MultiComponentView, Less) {
entt::registry registry;
const auto entity = std::get<0>(registry.create<int, char, double, entt::tag<"empty"_hs>>());
registry.create<int, char>();
const auto entity = registry.create();
registry.assign<int>(entity);
registry.assign<char>(entity);
registry.assign<double>(entity);
registry.assign<entt::tag<"empty"_hs>>(entity);
const auto other = registry.create();
registry.assign<int>(other);
registry.assign<char>(other);
registry.view<int, char, entt::tag<"empty"_hs>>().less([entity](const auto entt, int, char) {
ASSERT_EQ(entity, entt);
@@ -549,3 +586,25 @@ TEST(MultiComponentView, Less) {
ASSERT_EQ(entity, entt);
});
}
TEST(MultiComponentView, FrontBack) {
entt::registry registry;
auto view = registry.view<const int, const char>();
ASSERT_EQ(view.front(), static_cast<entt::entity>(entt::null));
ASSERT_EQ(view.back(), static_cast<entt::entity>(entt::null));
const auto e0 = registry.create();
registry.assign<int>(e0);
registry.assign<char>(e0);
const auto e1 = registry.create();
registry.assign<int>(e1);
registry.assign<char>(e1);
const auto entity = registry.create();
registry.assign<char>(entity);
ASSERT_EQ(view.front(), e1);
ASSERT_EQ(view.back(), e0);
}

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +0,0 @@
load("//bazel:copts.bzl", "entt_copts")
cc_test(
name = "process",
srcs = ["process.cpp"],
copts = entt_copts,
deps = [
"//:entt",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "scheduler",
srcs = ["scheduler.cpp"],
copts = entt_copts,
deps = [
"//:entt",
"@com_google_googletest//:gtest_main",
],
)

View File

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

View File

@@ -1,41 +0,0 @@
load("//bazel:copts.bzl", "entt_copts")
cc_test(
name = "delegate",
srcs = ["delegate.cpp"],
copts = entt_copts,
deps = [
"//:entt",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "dispatcher",
srcs = ["dispatcher.cpp"],
copts = entt_copts,
deps = [
"//:entt",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "emitter",
srcs = ["emitter.cpp"],
copts = entt_copts,
deps = [
"//:entt",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "sigh",
srcs = ["sigh.cpp"],
copts = entt_copts,
deps = [
"//:entt",
"@com_google_googletest//:gtest_main",
],
)

View File

@@ -352,3 +352,19 @@ TEST(Delegate, TheLessTheBetter) {
ASSERT_EQ(delegate(3, 'c'), 6);
}
TEST(Delegate, UnboundDataMember) {
entt::delegate<int(const delegate_functor &)> delegate;
delegate.connect<&delegate_functor::data_member>();
delegate_functor functor;
ASSERT_EQ(delegate(functor), 42);
}
TEST(Delegate, UnboundMemberFunction) {
entt::delegate<int(delegate_functor *, const int &i)> delegate;
delegate.connect<&delegate_functor::operator()>();
delegate_functor functor;
ASSERT_EQ(delegate(&functor, 3), 6);
}

View File

@@ -7,8 +7,6 @@ struct an_event {};
struct another_event {};
struct one_more_event {};
ENTT_NAMED_TYPE(an_event);
struct receiver {
static void forward(entt::dispatcher &dispatcher, const an_event &event) {
dispatcher.enqueue(event);
@@ -44,11 +42,11 @@ TEST(Dispatcher, Functionalities) {
ASSERT_EQ(receiver.cnt, 3);
dispatcher.enqueue<an_event>();
dispatcher.discard<an_event>();
dispatcher.clear<an_event>();
dispatcher.update();
dispatcher.enqueue<an_event>();
dispatcher.discard();
dispatcher.clear();
dispatcher.update();
ASSERT_EQ(receiver.cnt, 3);

View File

@@ -8,30 +8,31 @@ struct foo_event { int i; char c; };
struct bar_event {};
struct quux_event {};
ENTT_NAMED_TYPE(foo_event);
TEST(Emitter, Clear) {
test_emitter emitter;
ASSERT_TRUE(emitter.empty());
ASSERT_TRUE(emitter.empty<quux_event>());
emitter.on<foo_event>([](const auto &, const auto &){});
emitter.once<quux_event>([](const auto &, const auto &){});
ASSERT_FALSE(emitter.empty());
ASSERT_FALSE(emitter.empty<foo_event>());
ASSERT_FALSE(emitter.empty<quux_event>());
ASSERT_TRUE(emitter.empty<bar_event>());
emitter.clear<bar_event>();
ASSERT_FALSE(emitter.empty());
ASSERT_FALSE(emitter.empty<foo_event>());
ASSERT_FALSE(emitter.empty<quux_event>());
ASSERT_TRUE(emitter.empty<bar_event>());
emitter.clear<foo_event>();
ASSERT_TRUE(emitter.empty());
ASSERT_FALSE(emitter.empty());
ASSERT_TRUE(emitter.empty<foo_event>());
ASSERT_FALSE(emitter.empty<quux_event>());
ASSERT_TRUE(emitter.empty<bar_event>());
emitter.on<foo_event>([](const auto &, const auto &){});
@@ -39,6 +40,7 @@ TEST(Emitter, Clear) {
ASSERT_FALSE(emitter.empty());
ASSERT_FALSE(emitter.empty<foo_event>());
ASSERT_FALSE(emitter.empty<quux_event>());
ASSERT_FALSE(emitter.empty<bar_event>());
emitter.clear();
@@ -50,19 +52,25 @@ TEST(Emitter, Clear) {
TEST(Emitter, ClearPublishing) {
test_emitter emitter;
bool invoked = false;
ASSERT_TRUE(emitter.empty());
emitter.on<bar_event>([&invoked](const auto &, auto &em){
invoked = true;
em.clear();
emitter.once<foo_event>([](const auto &, auto &em){
em.template once<foo_event>([](const auto &, auto &){});
em.template clear<foo_event>();
});
emitter.on<bar_event>([](const auto &, auto &em){
em.template once<bar_event>([](const auto &, auto &){});
em.template clear<bar_event>();
});
ASSERT_FALSE(emitter.empty());
emitter.publish<foo_event>();
emitter.publish<bar_event>();
ASSERT_TRUE(emitter.empty());
ASSERT_TRUE(invoked);
}
TEST(Emitter, On) {

View File

@@ -281,13 +281,14 @@ TEST_F(SigH, ScopedConnectionConstructorsAndOperators) {
sigh_listener listener;
entt::sigh<void(int)> sigh;
entt::sink sink{sigh};
entt::scoped_connection conn;
{
ASSERT_FALSE(listener.k);
ASSERT_FALSE(conn);
entt::scoped_connection inner{};
ASSERT_TRUE(sigh.empty());
ASSERT_FALSE(listener.k);
ASSERT_FALSE(inner);
inner = sink.connect<&sigh_listener::g>(listener);
sigh.publish(42);
@@ -307,16 +308,8 @@ TEST_F(SigH, ScopedConnectionConstructorsAndOperators) {
ASSERT_FALSE(sigh.empty());
ASSERT_FALSE(listener.k);
ASSERT_TRUE(inner);
conn = std::move(inner);
ASSERT_FALSE(inner);
ASSERT_TRUE(conn);
}
ASSERT_TRUE(conn);
conn.release();
sigh.publish(42);
ASSERT_TRUE(sigh.empty());
@@ -423,3 +416,29 @@ TEST_F(SigH, BeforeListenerNotPresent) {
ASSERT_EQ(functor.value, 2);
}
TEST_F(SigH, UnboundDataMember) {
sigh_listener listener;
entt::sigh<bool &(sigh_listener &)> sigh;
entt::sink sink{sigh};
ASSERT_FALSE(listener.k);
sink.connect<&sigh_listener::k>();
sigh.collect([](bool &value) { value = !value; }, listener);
ASSERT_TRUE(listener.k);
}
TEST_F(SigH, UnboundMemberFunction) {
sigh_listener listener;
entt::sigh<void(sigh_listener *, int)> sigh;
entt::sink sink{sigh};
ASSERT_FALSE(listener.k);
sink.connect<&sigh_listener::g>();
sigh.publish(&listener, 42);
ASSERT_TRUE(listener.k);
}

View File

@@ -1,16 +0,0 @@
load("//bazel:copts.bzl", "entt_copts")
cc_test(
name = "lib",
srcs = [
"types.h",
"a_module.cpp",
"another_module.cpp",
"lib.cpp",
],
copts = entt_copts,
deps = [
"//:entt",
"@com_google_googletest//:gtest_main",
],
)

View File

@@ -1,60 +0,0 @@
#include <entt/entity/registry.hpp>
#include <entt/meta/factory.hpp>
#include <entt/signal/dispatcher.hpp>
#include <entt/signal/emitter.hpp>
#include "types.h"
#ifndef LIB_EXPORT
#if defined _WIN32 || defined __CYGWIN__
#define LIB_EXPORT __declspec(dllexport)
#elif defined __GNUC__
#define LIB_EXPORT __attribute__((visibility("default")))
#else
#define LIB_EXPORT
#endif
#endif
LIB_EXPORT typename entt::component a_module_int_type() {
entt::registry registry;
(void)registry.type<double>();
(void)registry.type<float>();
return registry.type<int>();
}
LIB_EXPORT typename entt::component a_module_char_type() {
entt::registry registry;
(void)registry.type<double>();
(void)registry.type<float>();
return registry.type<char>();
}
LIB_EXPORT void update_position(int delta, entt::registry &registry) {
registry.view<position, velocity>().each([delta](auto &pos, auto &vel) {
pos.x += delta * vel.dx;
pos.y += delta * vel.dy;
});
}
LIB_EXPORT void trigger_another_event(entt::dispatcher &dispatcher) {
dispatcher.trigger<another_event>();
}
LIB_EXPORT void emit_another_event(test_emitter &emitter) {
emitter.publish<another_event>();
}
LIB_EXPORT void a_module_bind_ctx(entt::meta_ctx context) {
entt::meta_ctx::bind(context);
}
LIB_EXPORT void a_module_meta_init() {
entt::meta<char>().type().data<'c'>("c"_hs);
}
LIB_EXPORT void a_module_meta_deinit() {
entt::meta<char>().reset();
}

View File

@@ -1,65 +0,0 @@
#include <entt/entity/registry.hpp>
#include <entt/meta/factory.hpp>
#include <entt/signal/dispatcher.hpp>
#include <entt/signal/emitter.hpp>
#include "types.h"
#ifndef LIB_EXPORT
#if defined _WIN32 || defined __CYGWIN__
#define LIB_EXPORT __declspec(dllexport)
#elif defined __GNUC__
#define LIB_EXPORT __attribute__((visibility("default")))
#else
#define LIB_EXPORT
#endif
#endif
LIB_EXPORT typename entt::component another_module_int_type() {
entt::registry registry;
(void)registry.type<char>();
(void)registry.type<double>();
(void)registry.type<const int>();
(void)registry.type<const char>();
(void)registry.type<float>();
return registry.type<int>();
}
LIB_EXPORT typename entt::component another_module_char_type() {
entt::registry registry;
(void)registry.type<int>();
(void)registry.type<float>();
(void)registry.type<const char>();
(void)registry.type<const int>();
(void)registry.type<double>();
return registry.type<char>();
}
LIB_EXPORT void assign_velocity(int vel, entt::registry &registry) {
for(auto entity: registry.view<position>()) {
registry.assign<velocity>(entity, vel, vel);
}
}
LIB_EXPORT void trigger_an_event(int payload, entt::dispatcher &dispatcher) {
dispatcher.trigger<an_event>(payload);
}
LIB_EXPORT void emit_an_event(int payload, test_emitter &emitter) {
emitter.publish<an_event>(payload);
}
LIB_EXPORT void another_module_bind_ctx(entt::meta_ctx context) {
entt::meta_ctx::bind(context);
}
LIB_EXPORT void another_module_meta_init() {
entt::meta<int>().type().data<0>("0"_hs);
}
LIB_EXPORT void another_module_meta_deinit() {
entt::meta<int>().reset();
}

View File

@@ -0,0 +1,8 @@
#include <entt/core/attribute.h>
#include <entt/signal/dispatcher.hpp>
#include "types.h"
ENTT_API void trigger(entt::dispatcher &dispatcher) {
dispatcher.trigger<event>();
dispatcher.trigger<message>(42);
}

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