Compare commits

..

1499 Commits

Author SHA1 Message Date
Michele Caini
fef921132c update single include file 2022-12-02 09:29:20 +01:00
Michele Caini
e52a93f8ac ready to cut v3.11.1 2022-12-02 09:23:16 +01:00
Michele Caini
cd541f335a storage:
* move storage_type[_t] and storage_for[_t] to fwd.hpp
* no need to include storage.hpp when forward defining views
2022-11-30 16:24:46 +01:00
Michele Caini
255b8be8cc view: avoid shadow warnings 2022-11-30 16:23:02 +01:00
Michele Caini
8cd7f064ad storage: suppress warnings with non copyable nor default constructible types 2022-11-30 16:22:54 +01:00
Michele Caini
58ae4117c9 group: suppress warnings for unused variables in case of empty types 2022-11-30 16:22:44 +01:00
Michele Caini
cfa1e805bd meta: [[maybe_unused]] variable to avoid warnings with corner cases 2022-11-30 16:22:35 +01:00
Michele Caini
ccedacec8c dense_set: suppress warnings due to possible narrowing conversions 2022-11-30 16:22:21 +01:00
Michele Caini
17578dc8cc dense_map: suppress warnings due to possible narrowing conversions 2022-11-30 16:22:14 +01:00
Michele Caini
76298bc26a update single include file 2022-11-09 12:13:00 +01:00
Michele Caini
7c84ce666e sigh: rollback some changes that apparently create issues with (very old) compilers (ie clang 6) 2022-10-26 12:37:46 +02:00
Michele Caini
a02a7c67cb doc: fix typo 2022-10-26 11:53:43 +02:00
Michele Caini
2bb913b898 any: suppress warnings C4706 due to asserts 2022-10-26 10:57:58 +02:00
Michele Caini
94a8bb57dd doc: review resource.md 2022-10-26 10:50:31 +02:00
Michele Caini
f06852b5c2 doc: fixed typos 2022-10-25 14:50:18 +02:00
Michele Caini
fb14d26015 config: refine the ENTT_ASSERT vs ENTT_ASSERT_CONSTEXPR work 2022-10-25 11:05:50 +02:00
Michele Caini
6a53c8eaca doc: review locator.md 2022-10-25 11:04:13 +02:00
Michele Caini
4a98874431 doc: review faq.md 2022-10-25 11:03:59 +02:00
Michele Caini
baf60502c6 doc: review config.md 2022-10-25 11:03:39 +02:00
Michele Caini
2a01b78b32 any: forward/centralize requests internally 2022-10-24 14:38:55 +02:00
Michele Caini
b02e4efe4d doc: updated links (and thanks @Naios for these) 2022-10-24 12:20:20 +02:00
Michele Caini
3c8f6d2831 doc: minor changes 2022-10-24 11:53:23 +02:00
Michele Caini
95b443af16 tuple: is_tuple[_v] implementation 2022-10-24 11:15:34 +02:00
Michele Caini
b54766d2b4 sigh: small cleanup 2022-10-23 11:42:19 +02:00
Michele Caini
25929d6188 doc: ENTT_ASSERT_CONSTEXPR 2022-10-22 19:10:48 +02:00
Michele Caini
4398c962e7 memory:
* use ENTT_ASSERT_CONSTEXPR
* turn the power-of-two toolset into a constexpr one as it ought to be
2022-10-22 19:10:40 +02:00
Michele Caini
fe8919c540 config: introduce ENTT_ASSERT_CONSTEXPR to facilitate extreme customizations 2022-10-22 19:08:13 +02:00
Michele Caini
bdaeef856d delegate: just an extra safe check 2022-10-22 18:56:55 +02:00
Michele Caini
57ad7696be memory: turn the power-of-two/fast_mod toolset into a non-constexpr one 2022-10-21 11:01:18 +02:00
Michele Caini
7c58c83ee0 memory: review propagate_on_container_swap 2022-10-21 10:57:37 +02:00
Michele Caini
dd7aaa30ca organizer: minor changes 2022-10-20 15:34:57 +02:00
Michele Caini
76f95945fc registry: better use of entt:: to avoid ambiguities 2022-10-20 09:36:51 +02:00
Michele Caini
869836ba79 registry: use allocator to allocate_shared groups with uses-allocator policy 2022-10-20 09:10:20 +02:00
Michele Caini
2358552c27 *: internal review of some traits usage 2022-10-19 12:08:41 +02:00
Michele Caini
e22ea5e189 meta: minor changes 2022-10-18 11:03:03 +02:00
Michele Caini
496fefdd01 meta: favor top-level conversion functions over bases lookup 2022-10-18 10:54:31 +02:00
Michele Caini
26f85050c1 any: debug-only check to detect nasty bugs in the client code, if any 2022-10-18 10:53:51 +02:00
Michele Caini
d478b5bc6a test: minor changes to avoid UBs 2022-10-18 10:50:16 +02:00
Michele Caini
c8337e529e natvis: review a bunch of definitions 2022-10-17 09:54:13 +02:00
Michele Caini
f0995297cf emitter: update destructor check on derived type 2022-10-17 09:53:27 +02:00
Michele Caini
b395df0f56 doc: cleanup 2022-10-14 19:05:42 +02:00
Michele Caini
69a6cc6656 meta: better doc, minor changes to propagate the context to the meta handles 2022-10-14 18:52:05 +02:00
Michele Caini
bf73216a76 meta: minor changes 2022-10-14 17:01:54 +02:00
Michele Caini
621860718b meta: rebind elements to the right context upon invocation if possible 2022-10-14 15:40:31 +02:00
Michele Caini
c34c5da277 meta: minor changes 2022-10-14 14:31:55 +02:00
Michele Caini
8dcabba31b meta: clarify utilities behavior with regard to context, instances and arguments 2022-10-14 12:24:25 +02:00
Michele Caini
294064f11f meta: args are always rebinded to the right context (with non-regression tests) 2022-10-14 11:47:27 +02:00
Michele Caini
d125229312 doc: more about the new meta context rework 2022-10-14 11:46:35 +02:00
Michele Caini
11c1e23a91 meta: context aware copy/move ctor for meta_any 2022-10-14 08:49:44 +02:00
Michele Caini
82bc2e1fe4 test: no need to test mixing meta contexts actually, dropped the todo 2022-10-13 13:12:55 +02:00
Michele Caini
92a00f591d test: context aware meta handle 2022-10-13 11:15:48 +02:00
Michele Caini
04716a7fde meta: minor changes 2022-10-13 09:38:22 +02:00
Michele Caini
b6155080b2 test: a few more tests for meta_handle 2022-10-13 09:37:13 +02:00
Michele Caini
0a61de4a09 test: context aware meta_any 2022-10-13 09:33:43 +02:00
Michele Caini
7121da100a test: context aware meta sequence containers 2022-10-13 09:24:20 +02:00
Michele Caini
7bee969ebf test: context aware meta pointers (dereferencing picks the type from the right context) 2022-10-13 09:12:45 +02:00
Michele Caini
4a0a534796 test: context aware forward_as_meta 2022-10-13 09:09:11 +02:00
Michele Caini
a0041d9062 meta: resolve an ambiguity and avoid redundant default ctors (win-win) 2022-10-13 08:59:16 +02:00
Michele Caini
3dd768bb9b meta: context aware meta template 2022-10-12 19:02:30 +02:00
Michele Caini
2a307a681f test: context aware meta prop 2022-10-12 18:30:21 +02:00
Michele Caini
dd1211d021 meta: combine enable_if_t and a meta policy detection utility to avoid API changes 2022-10-12 16:06:25 +02:00
Michele Caini
09a600d68a meta: propagate API changes to the internal functions 2022-10-12 15:36:31 +02:00
Michele Caini
7c25b46513 test: minor changes 2022-10-12 15:14:58 +02:00
Michele Caini
dc42486de8 test: drop redundant using namespace 2022-10-12 14:57:46 +02:00
Michele Caini
36411faf43 test: context aware meta dtor 2022-10-12 14:56:27 +02:00
Michele Caini
a9cd565d65 test: context aware meta conv 2022-10-12 12:49:39 +02:00
Michele Caini
0adbb0ad16 test: context aware meta ctor 2022-10-12 12:43:58 +02:00
Michele Caini
db31f77d63 test: context aware meta func 2022-10-12 12:22:26 +02:00
Michele Caini
c275afef28 test: context aware meta data 2022-10-12 12:16:35 +02:00
Michele Caini
4bad4f3067 test: context aware meta bases 2022-10-12 10:28:41 +02:00
Michele Caini
d2048c037a test: bare minimum tests for meta types from different contexts 2022-10-12 10:04:28 +02:00
Michele Caini
2a39717893 test: add all desired tests for meta context support 2022-10-12 09:59:03 +02:00
Michele Caini
49c2ae0e77 test: context aware meta resolve 2022-10-12 09:58:20 +02:00
Michele Caini
869c18f150 test: prepare to test meta context support 2022-10-12 09:57:58 +02:00
Michele Caini
d79cb5ca98 meta: turn meta_handle into a context aware wrapper 2022-10-11 15:24:23 +02:00
Michele Caini
acc6390312 meta: drop make_meta, unfortunately it doesn't fit well with the context aware design 2022-10-11 14:56:25 +02:00
Michele Caini
2e59fbbccc meta:
* review pending functions to make them context aware if they aren't already
* suppress a meta_associative_container::insert warning (rvalue-to-lvalue binding)
2022-10-11 14:40:16 +02:00
Michele Caini
bec0037797 meta: fully context aware meta_any 2022-10-11 12:20:25 +02:00
Michele Caini
d08c7ddb58 meta: review meta_associative_container::insert/::erase 2022-10-11 12:11:54 +02:00
Michele Caini
696681fefa meta: context aware meta associative container 2022-10-11 11:53:17 +02:00
Michele Caini
5f9faf7a32 meta: context aware meta sequence container 2022-10-11 11:14:22 +02:00
Michele Caini
f160503aaf meta: unroll meta_type::forward_to_bases to return always scoped elements 2022-10-11 11:09:33 +02:00
Michele Caini
b1e9f75a28 meta: meta_type::construct always returns scoped meta_any objects 2022-10-11 11:01:50 +02:00
Michele Caini
91da98353d meta: keep track of the pending changes 2022-10-11 10:28:48 +02:00
Michele Caini
bc86ceb707 meta: drop resolve trampoline function, no longer required 2022-10-10 18:13:59 +02:00
Michele Caini
0baf316e5f meta: add partial support for meta contexts to meta containers 2022-10-10 18:13:20 +02:00
Michele Caini
082605f9d3 meta: prepare meta containers to support context injection 2022-10-10 18:03:30 +02:00
Michele Caini
0fdd7d7a3a meta: meta_any::allow_cast returns a scoped value now 2022-10-10 16:46:43 +02:00
Michele Caini
eee59e6af9 meta: track pending changes as much as possible 2022-10-10 16:46:43 +02:00
Michele Caini
223a8628e3 meta: meta_any::allow_cast propagates the context now 2022-10-10 16:46:42 +02:00
Michele Caini
c0bb603210 meta: meta_any::operator* propagates the context now 2022-10-10 16:46:42 +02:00
Michele Caini
2981d8746a meta: avoid returning contex-less meta_any from meta objects 2022-10-10 16:46:42 +02:00
Michele Caini
c06280894b meta: make internal meta_invoke accept meta contexts and use them properly 2022-10-10 16:46:42 +02:00
Michele Caini
2f2bdcac4d meta:
* make meta_getter use properly meta contexts
* remove the meta context from the argument list of meta setters (not required)
2022-10-10 16:46:42 +02:00
Michele Caini
2739964a1f meta: make meta_dispatch support and use meta contexts 2022-10-10 16:46:42 +02:00
Michele Caini
c3a2a44830 meta: drop useless [[maybe_unused]] 2022-10-10 16:46:42 +02:00
Michele Caini
37a2c3b76c meta: make meta_construct support and use meta contexts 2022-10-10 16:46:42 +02:00
Michele Caini
146a7b2aa3 meta: rollback redundant functions (reduce the number of generated symbols) 2022-10-10 16:46:42 +02:00
Michele Caini
7e093c951f meta:
* use different function names to disambiguate utility calls
* drop base class meta_policy for policies, no longer required
2022-10-10 16:46:42 +02:00
Michele Caini
4901188409 meta: use meta_any context aware constructors if possible 2022-10-10 16:46:42 +02:00
Michele Caini
15d78f80a6 meta: context aware constructors for meta_any 2022-10-10 16:46:42 +02:00
Michele Caini
d61dff45c1 meta: introduce meta_ctx_arg[_t] 2022-10-10 16:46:42 +02:00
Michele Caini
34abddfbbd meta: move the meta context argument to the first slot of the argument lists 2022-10-10 16:46:42 +02:00
Michele Caini
238ebe4e8f meta: try to get around invalid code that msvc accepts happily otherwise 2022-10-10 16:46:42 +02:00
Michele Caini
502accabbc meta: make default_constructor forward the context to the returned element 2022-10-10 16:46:42 +02:00
Michele Caini
9d1df1dae4 meta: make from_void forward the context to the returned element 2022-10-10 16:46:42 +02:00
Michele Caini
a39cfb9e99 meta: always forward the context to meta_type 2022-10-10 16:46:42 +02:00
Michele Caini
27d0e5f55e meta: always forward the context to meta_func 2022-10-10 16:46:42 +02:00
Michele Caini
a0e5d2c495 meta: always forward the context to meta_data 2022-10-10 16:46:42 +02:00
Michele Caini
d2c89d2a40 meta: always forward the context to meta_prop 2022-10-10 16:46:42 +02:00
Michele Caini
54f66c9094 meta: make meta_range_iterator forward the context to the returned elements 2022-10-10 16:46:42 +02:00
Michele Caini
10be00b352 meta: suppress a bunch of shadow warnings 2022-10-10 16:46:42 +02:00
Michele Caini
9032051bf7 meta: review meta_any::try_cast to use the local context 2022-10-10 16:46:42 +02:00
Michele Caini
2d2e4c784e meta: reintroduce as_ref constructor 2022-10-10 16:46:41 +02:00
Michele Caini
0d4674d8e2 meta: refined meta_conv_node definition 2022-10-10 16:46:41 +02:00
Michele Caini
ac06ad35c1 meta: added bare minimum support for contexts to forward_as_meta 2022-10-10 16:46:41 +02:00
Michele Caini
27e492d750 meta: minor changes 2022-10-10 16:46:41 +02:00
Michele Caini
f8281f2a37 meta: add bare minimum support for contexts to meta_any 2022-10-10 16:46:41 +02:00
Michele Caini
9fc717fa6e meta: track pending points for future refinement 2022-10-10 16:46:41 +02:00
Michele Caini
4a0f2f3fe9 meta: drop redundant constructors for meta_any 2022-10-10 16:46:41 +02:00
Michele Caini
ebdd4118fd meta: context aware internal try_cast 2022-10-10 16:46:41 +02:00
Michele Caini
54ae48936f meta: refine includes 2022-10-10 16:46:41 +02:00
Michele Caini
3c9045a3a8 test: avoid using the trampoline resolve function 2022-10-10 16:46:41 +02:00
Michele Caini
f80f179499 meta: small refinement 2022-10-10 16:46:41 +02:00
Michele Caini
69c6ee0675 meta: high level meta types are now context aware 2022-10-10 16:46:41 +02:00
Michele Caini
4e28d96bef meta: remove the last use of the trampoline resolve function from the meta factory 2022-10-10 16:46:41 +02:00
Michele Caini
6552cc4599 meta: refine factory.hpp as it's likely done now 2022-10-10 16:46:41 +02:00
Michele Caini
75b10cfa09 meta: make meta and meta_reset context aware 2022-10-10 16:46:41 +02:00
Michele Caini
dc394f62c6 meta: fully context aware meta factory 2022-10-10 16:46:41 +02:00
Michele Caini
ea6d6662b4 meta: make meta_ctor_node and meta_func_node fully context aware 2022-10-10 16:46:41 +02:00
Michele Caini
de3ecbdd28 meta: make meta_conv_node context aware 2022-10-10 16:46:41 +02:00
Michele Caini
e5c6559836 meta: make meta_data_node fully context aware 2022-10-10 16:46:41 +02:00
Michele Caini
0d422236d6 doc: minor changes 2022-10-10 16:46:41 +02:00
Michele Caini
199b065fe3 meta: make it compile after the last changes as it ought to be 2022-10-10 16:46:41 +02:00
Michele Caini
c46e107107 meta: try to follow my own coding style :) 2022-10-10 16:46:41 +02:00
Michele Caini
a3f66387b2 meta: further refine meta_type_node to make it context aware 2022-10-10 16:46:41 +02:00
Michele Caini
33383bd0c3 meta: try to follow my own naming convention :) 2022-10-10 16:46:41 +02:00
Michele Caini
35b8d95671 meta: make meta_type_node even more context aware 2022-10-10 16:46:40 +02:00
Michele Caini
6e254fb39d meta: make meta_type_node partially context aware 2022-10-10 16:46:40 +02:00
Michele Caini
f50a03dcb4 meta: make meta_ctor_node context aware 2022-10-10 16:46:40 +02:00
Michele Caini
71554b2c19 meta: drop temporary trampoline for meta_arg 2022-10-10 16:46:40 +02:00
Michele Caini
e7762980ec meta: make meta_ctor_node and meta_func_node partially context aware 2022-10-10 16:46:40 +02:00
Michele Caini
c62099969e meta: make meta_data_node partially context aware 2022-10-10 16:46:40 +02:00
Michele Caini
c45ee64800 meta: make meta_arg context aware 2022-10-10 16:46:40 +02:00
Michele Caini
b53c243ef7 meta: refactor context argument as the last one 2022-10-10 16:46:40 +02:00
Michele Caini
9ee084fc40 meta: make meta_template_node context aware 2022-10-10 16:46:40 +02:00
Michele Caini
9c969fb6ac meta: context aware meta_prop_node 2022-10-10 16:46:40 +02:00
Michele Caini
6a8a9c4b31 meta: context aware meta_arg_node function (with internal temporary trampoline) 2022-10-10 16:46:40 +02:00
Michele Caini
bc0233e34b meta: context aware resolve functions (with internal temporary trampoline) 2022-10-10 16:46:40 +02:00
Michele Caini
afd91b4e5a container: suppress a few shadow warnings 2022-10-10 16:46:26 +02:00
Michele Caini
50349dc2af any/meta: work around the bugs of toolset v141 to also make it happy if possible 2022-10-08 19:51:49 +02:00
Michele Caini
4b10981d75 meta: better support to rvalue references for forward_as_meta 2022-10-08 19:00:13 +02:00
Michele Caini
b16cefd534 any: better support to rvalue references for forward_as_any 2022-10-08 19:00:03 +02:00
Michele Caini
46d6625467 dispatcher: minor changes 2022-10-08 17:08:32 +02:00
Michele Caini
a1a8e134d2 doc: mino changes 2022-10-07 12:25:42 +02:00
Michele Caini
0b6bbcc6a3 signal: move connect_arg_t to fwd.hpp 2022-10-07 12:24:11 +02:00
Michele Caini
5fac237153 any: favor delegating constructors if possible 2022-10-07 12:13:17 +02:00
Michele Caini
d2cc1c880c meta: any: favor delegating constructors if possible 2022-10-07 12:09:01 +02:00
Michele Caini
ac18ad5823 any: favor delegating constructors if possible 2022-10-07 12:08:54 +02:00
Michele Caini
9ac5f6d046 meta: simplify container traits definitions 2022-10-06 19:02:32 +02:00
Michele Caini
08adb29d91 registry: also support empty queries to get and try_get (with tests) 2022-10-06 08:36:14 +02:00
Michele Caini
1721d07746 meta: drop useless [[maybe_unused]] 2022-10-06 08:27:38 +02:00
Michele Caini
9401d560b0 any: drop useless [[maybe_unused]] 2022-10-06 08:27:26 +02:00
Michele Caini
7eae8523cb container: drop useless [[maybe_unused]] 2022-10-06 08:27:12 +02:00
Michele Caini
45b5eeeaa0 doc: update doxy file (and required doxygen version) 2022-10-05 18:51:04 +02:00
Michele Caini
12b436e5b8 sigh: small changes to make doxygen happy 2022-10-05 12:20:43 +02:00
Michele Caini
534dc7e7b7 registry: allocate_shared pools to further improve the use of the allocator 2022-10-04 12:30:25 +02:00
Michele Caini
5dfac37dac dispatcher: just a comment for the future me 2022-10-04 12:29:43 +02:00
Michele Caini
f17a4e7f8e locator: just a comment for the future me 2022-10-04 12:29:22 +02:00
Michele Caini
4b586efe66 registry: use the allocator a bit more if possible 2022-10-03 16:04:42 +02:00
Michele Caini
db6dc1e21b sigh: reduce the number of instantiations and the size of the generated symbols 2022-09-30 10:29:47 +02:00
Michele Caini
da11771dab meta: drop using definitions that are pointless/no longer required 2022-09-29 15:48:43 +02:00
Michele Caini
475e3a70b0 meta: minor changes 2022-09-29 09:48:51 +02:00
Michele Caini
5255afc80d meta: drop useless scope 2022-09-28 18:14:12 +02:00
Michele Caini
7a3f59cc5d delegate: drop useless scope 2022-09-28 18:14:04 +02:00
Michele Caini
9470133e33 registry: don't pass the allocator to the fake storage 2022-09-28 12:54:21 +02:00
Michele Caini
5ab0f9e4c0 dispatcher: const assure 2022-09-27 15:08:29 +02:00
Michele Caini
ef91cd5375 registry: rollback some no longer required changes 2022-09-26 15:43:15 +02:00
Michele Caini
cd9553ca10 registry: ::storage(id) returns a pointer rather than an utterly annoying iterator 2022-09-26 15:42:57 +02:00
Michele Caini
8b27a846a6 view: avoid moving the callback with multi-type views 2022-09-23 11:06:30 +02:00
Michele Caini
9d961ad8aa view: minor changes 2022-09-23 11:00:45 +02:00
Michele Caini
0952f3ce03 view: reduce the number of symbols 2022-09-23 10:59:23 +02:00
Michele Caini
ab33111192 view/group: make ::handle [[nodiscard]] 2022-09-22 17:42:38 +02:00
Michele Caini
9a457c5aca registry: prepare to drop static storage within const assure 2022-09-22 17:42:21 +02:00
Michele Caini
4f1c86c52e view: prepare for partially working empty views 2022-09-22 17:42:15 +02:00
Michele Caini
c605e3b5a1 view: prepare for partially working empty views 2022-09-22 17:42:11 +02:00
Michele Caini
5a8c9b8a95 test: ASSERT_NO_THROW -> ASSERT_NO_FATAL_FAILURE 2022-09-22 17:42:01 +02:00
Michele Caini
2b8e9cab18 view: drop useless constexpr branch 2022-09-22 17:41:59 +02:00
Michele Caini
c3dec4b04e view: added ::refresh function to reinitialize the leading pool 2022-09-21 15:42:38 +02:00
Michele Caini
699aa5cd96 meta: rework internal function meta_arg_node 2022-09-21 14:49:54 +02:00
Michele Caini
fea0210b6e meta: support const/non-const overloads of the same function 2022-09-21 13:57:05 +02:00
Michele Caini
cf12765738 registry: pass the allocator to the pools 2022-09-20 16:04:14 +02:00
Michele Caini
17f47fbc92 storage_type[_t]/storage_for[_t]: allocator support 2022-09-20 11:01:58 +02:00
Michele Caini
1e36b6c4b6 registry: strict check on value_type for allocators 2022-09-20 10:25:29 +02:00
Michele Caini
28692fc142 registry: also adjust construction and swap orders 2022-09-19 15:03:51 +02:00
Dominic Koepke
6eff5a88f6 registry: update (and test) member destruction order (#930) 2022-09-19 13:06:37 +02:00
tocic
5742100973 doc: fix typos (#934) 2022-09-19 12:09:30 +02:00
Michele Caini
c52eea7db7 doc: update README ( thanks @Alan-FGR ) 2022-09-16 15:46:20 +02:00
Michele Caini
2f59f5a8fa meta: refine internal::meta_arg_node + code coverage 2022-09-16 10:06:56 +02:00
Michele Caini
e77fe56e8f natvis: meta 2022-09-15 14:09:50 +02:00
Michele Caini
eea1e7b1f8 meta: further reduce the number of instantiations and allocations 2022-09-15 14:09:44 +02:00
Michele Caini
b0fbccdf10 meta: minor changes 2022-09-15 10:20:29 +02:00
Michele Caini
4f3faef153 meta: remove a few pointless moves 2022-09-15 10:08:50 +02:00
Michele Caini
3ae3c82fbb meta: refine meta_construct to make it work with lambdas (+tests) 2022-09-14 16:29:11 +02:00
Michele Caini
1063fbccbe meta: const correctness 2022-09-14 16:26:14 +02:00
Michele Caini
b9e60c679a meta: added meta_function_descriptor_traits to avoid repetition and errors 2022-09-14 09:46:11 +02:00
Michele Caini
88fd79069b locator: minor changes 2022-09-14 09:22:43 +02:00
Michele Caini
ab859f7fa7 meta: review meta_base_node::cast and meta_any::try_cast to reduce their sizes 2022-09-13 15:57:44 +02:00
Michele Caini
cc4d4dfc2b meta: also make vs2017 happy if possible 2022-09-13 12:45:49 +02:00
Michele Caini
bd62e817dc meta: reduce the size of the erased conversion function 2022-09-13 12:33:14 +02:00
Michele Caini
de34a109e1 meta: internal meta_arg_node is back to its old, unsafe version 2022-09-13 12:08:25 +02:00
Michele Caini
e0e45719fa meta: minor changes 2022-09-12 10:59:08 +02:00
Michele Caini
b21b254258 meta: avoid non-standard rvalue-to-lvalue bindings :) 2022-09-12 10:57:04 +02:00
Michele Caini
ca3ef33f46 meta: further reduce the size of the resolve function 2022-09-12 10:46:53 +02:00
Michele Caini
8c4803d906 meta: minor changes 2022-09-12 10:46:52 +02:00
Michele Caini
ffb9e4b0eb meta: shrink function templates a bit 2022-09-12 10:46:49 +02:00
Michele Caini
6010dbc368 meta: pre-instantiate the details section when constructing a factory 2022-09-12 10:46:46 +02:00
Michele Caini
a6343bd152 doc: updated links (close #933) 2022-09-09 14:14:22 +02:00
Michele Caini
9e4a7f48a4 meta: try to also make clang happy 2022-09-09 12:02:57 +02:00
Michele Caini
6ad7424d4a meta: minor changes 2022-09-08 18:02:07 +02:00
Michele Caini
218fa1ba35 meta: share common code if possible 2022-09-08 15:11:09 +02:00
Michele Caini
b45db747a8 meta: review meta_arg[_node] + increase code coverage 2022-09-08 13:29:10 +02:00
Michele Caini
eedecc07fd meta: avoid tricks and whatnot with meta_arg_node 2022-09-08 10:28:49 +02:00
Michele Caini
dcea36de7a meta: try to share common code if possibile 2022-09-07 17:51:11 +02:00
Michele Caini
ed0e3f4435 meta: share common code 2022-09-07 16:17:29 +02:00
Michele Caini
8595e4bd99 meta: review meta containers to further reduce memory usage 2022-09-07 14:58:44 +02:00
Michele Caini
e31b81294d meta: minor changes for consistency 2022-09-07 14:54:34 +02:00
Michele Caini
ac6e9cf2f0 meta: further reduce the number of instantiations 2022-09-07 14:32:15 +02:00
Michele Caini
dac4ae94c0 meta: further reduce the number of instantiations 2022-09-07 12:48:40 +02:00
Michele Caini
b31b585805 natvis: update meta 2022-09-07 11:33:15 +02:00
Michele Caini
1368c56655 meta: avoid storing meta type nodes by reference in the meta factory 2022-09-07 11:32:49 +02:00
Michele Caini
6cab12a14f test: code coverage 2022-09-06 23:42:12 +02:00
Michele Caini
924a09a9c5 meta: minor changes (to suppress warnings) 2022-09-06 23:42:12 +02:00
Michele Caini
e4f6293f42 meta: use the hot/cold model with meta_type_node 2022-09-06 23:42:12 +02:00
Michele Caini
f197b32b02 meta: cleanup 2022-09-06 23:42:12 +02:00
Michele Caini
b80ab36418 meta: use the hot/cold model with meta_func_node 2022-09-06 23:42:12 +02:00
Michele Caini
d5588333f7 meta: use the hot/cold model with meta_data_node 2022-09-06 23:42:12 +02:00
Michele Caini
10755275ea meta: use the hot/cold model with meta_prop_node 2022-09-06 23:42:12 +02:00
Michele Caini
a30cecdf36 meta: use a shared_pointer to void for meta properties 2022-09-06 23:42:12 +02:00
Michele Caini
0068afa06c meta: cleanup 2022-09-06 23:42:12 +02:00
Michele Caini
38597a6499 meta: hot/cold data to allow copies of meta nodes 2022-09-06 23:42:12 +02:00
Michele Caini
65ee240e8e meta: add a test (and a fix) for a corner case of meta_reset 2022-09-06 23:42:12 +02:00
Michele Caini
336bcf4d11 test: minor changes 2022-09-06 23:42:12 +02:00
Michele Caini
ed74a4c213 meta: explicit initialization of all values of a meta object 2022-09-06 23:42:12 +02:00
Michele Caini
3e29617da8 doc: meta 2022-09-06 23:42:11 +02:00
Michele Caini
715b3e450f meta: make meta_factory<...>::type id argument required 2022-09-06 23:42:11 +02:00
Michele Caini
1ab10f3b28 meta: range iterator is an input iterator but models a random access iterator (close #927) 2022-09-06 23:42:11 +02:00
Michele Caini
304d8a80be meta: avoid warnings due to unused variables 2022-09-06 23:42:11 +02:00
Michele Caini
560a3f664e meta: drop meta_func_node::watermark, compare pointers to invoke instead 2022-09-06 23:42:11 +02:00
Michele Caini
41081204f9 tests: minor changes 2022-09-06 23:42:11 +02:00
Michele Caini
b895dfc901 nativs: review meta after the rework 2022-09-06 23:42:11 +02:00
Michele Caini
93a1d65103 meta: meta_type_node is no longer static 2022-09-06 23:42:11 +02:00
Michele Caini
443d53400d meta: decouple meta containers and meta_type_node 2022-09-06 23:42:11 +02:00
Michele Caini
9725582089 meta: use insert_or_assign rather than operator[] 2022-09-06 23:42:11 +02:00
Michele Caini
cd2634a5d4 meta: use lvalue refs to meta any to construct meta handles 2022-09-06 23:42:11 +02:00
Michele Caini
798d1a4f9e meta: add missing template keywords (love you, msvc) 2022-09-06 23:42:11 +02:00
Michele Caini
e13996f68a meta avoid shadow warnings 2022-09-06 23:42:11 +02:00
Michele Caini
1ee077d511 meta: minor internal changes (try to make all compilers happy) 2022-09-06 23:42:11 +02:00
Michele Caini
ccf301c73d test: review/minor changes 2022-09-06 23:42:11 +02:00
Michele Caini
461fe715b7 meta: decouple meta_template_node and meta_type_node 2022-09-06 23:42:11 +02:00
Michele Caini
9549bb4f4d meta: decouple meta_func_node and meta_type_node 2022-09-06 23:42:11 +02:00
Michele Caini
0215722103 meta: decouple meta_data_node and meta_type_node 2022-09-06 23:42:11 +02:00
Michele Caini
7c6606f27e meta: decouple meta_base_node and meta_type_node 2022-09-06 23:42:11 +02:00
Michele Caini
f0b10965fb meta: decouple meta_prop_node and meta_type_node 2022-09-06 23:42:10 +02:00
Michele Caini
53f2bb7701 meta: meta_func_node is no longer static 2022-09-06 23:42:10 +02:00
Michele Caini
945e61d668 meta:
* meta_prop_node is no longer static
* we can attach multiple properties at different times
* meta property keys are of type id_type only
* removed meta_factory::props (use prop instead)
* meta range returns id and meta object pairs now
* removed mera_prop::key (id returned by meta ranges)
2022-09-06 23:42:10 +02:00
Michele Caini
e74c4bb684 meta: improve internal meta_from_void function 2022-09-06 23:42:10 +02:00
Michele Caini
a8eebcb0e2 meta: make meta_range iterators return an id and meta object pair 2022-09-06 23:42:10 +02:00
Michele Caini
1581aa1cf5 meta: meta_ctor_node is no longer static 2022-09-06 23:42:10 +02:00
Michele Caini
839a2cc86d meta: removed meta_data::id 2022-09-06 23:42:10 +02:00
Michele Caini
3062114f0c meta: meta_data_node is no longer static 2022-09-06 23:42:10 +02:00
Michele Caini
ab3abf23ed meta: avoid allocations for meta_template_node 2022-09-06 23:42:10 +02:00
Michele Caini
b5a7812ab4 meta: added meta_dtor_node for consistency 2022-09-06 23:42:10 +02:00
Michele Caini
565de36155 meta: meta_base is no longer static 2022-09-06 23:42:10 +02:00
Michele Caini
c76f078834 meta: prepare for iterator based meta range 2022-09-06 23:42:10 +02:00
Michele Caini
358a9742c5 meta: removed meta_type::base(id_type) because pointless 2022-09-06 23:42:10 +02:00
Michele Caini
7f9e200cfb meta: meta_conv_node is no longer static 2022-09-06 23:42:10 +02:00
Michele Caini
a12710af70 meta: meta_template_node is no longer static 2022-09-06 23:42:10 +02:00
Michele Caini
871234ec76 any: avoid declaring nested types in an union 2022-09-06 23:41:45 +02:00
Michele Caini
0c062cc351 any: refine replacement for std aligned storage (see #919) 2022-09-06 23:09:44 +02:00
Michele Caini
67271e4672 registry: start to introduce the allocator (work in progress) 2022-09-06 09:43:34 +02:00
Michele Caini
2489a95dab registry: don't store the allocator aside anymore 2022-09-06 08:33:10 +02:00
Michele Caini
70b8bd27b8 any/poly: avoid using aligned_storage_t (deprecated - close #919) 2022-09-05 15:32:49 +02:00
Michele Caini
ee88cf16d1 any: avoid using storage_type to compute in_situ 2022-09-05 15:30:29 +02:00
Michele Caini
d83e818517 test: avoid using aligned_storage_t if possible (see #919) 2022-09-02 15:23:47 +02:00
Michele Caini
6462ceb1f5 meta: turn meta_any::operator!= into an in-class method (close #917) 2022-09-02 10:10:10 +02:00
Michele Caini
e2f8ea110a any: turn operator!= into an in-class method 2022-09-02 10:09:02 +02:00
Kyriet
f1acbbedf9 doc: add section about sorting and reverse-iterating views (#918) 2022-08-31 11:29:46 +02:00
Philipp Kurth
8510f535a5 storage: storage_iterator doesn't refer to the right specialization of component_traits (#924) 2022-08-30 12:16:52 +02:00
Michele Caini
9a97a57423 storage: test the proper use of component traits by the storage iterator 2022-08-30 12:13:57 +02:00
Michele Caini
a50cc628ec natvis: drop meta_type_node is_pointer 2022-08-29 14:30:44 +02:00
Michele Caini
dc922c9e4c storage: how the code coverage tool can be hated sometimes :) 2022-08-29 12:35:34 +02:00
Michele Caini
2f2b0cf98a test: minor changes (help the code coverage tool) 2022-08-29 12:11:55 +02:00
Michele Caini
798d5eeb86 storage: minor changes (help the code coverage tool) 2022-08-29 12:04:35 +02:00
Michele Caini
02d8cefcde locator: avoid shadow warnings 2022-08-29 11:48:17 +02:00
Michele Caini
f627593d63 build system: add explicit dependencies for plugin tests 2022-08-29 11:32:16 +02:00
Michele Caini
9aeddc2cb1 locator: lib plugin test 2022-08-29 11:25:37 +02:00
Michele Caini
6650071d24 locator: lib test 2022-08-29 11:08:03 +02:00
Michele Caini
006822c7fc build system: set PIC property for odr target explicitly 2022-08-29 09:48:23 +02:00
Michele Caini
8bed15e469 natvis: update registry snippet 2022-08-28 18:55:58 +02:00
Michele Caini
4200bb9ebb locator: ::handle and extended ::reset 2022-08-27 17:35:42 +02:00
Michele Caini
fdae346aa6 doc: typo (thanks @DNKpp) 2022-08-25 15:07:34 +02:00
Michele Caini
01acb15fab meta: avoid using traits if not needed 2022-08-22 16:58:00 +02:00
Michele Caini
3a6764a685 meta: any and meta_any catchall ctors work the same 2022-08-22 16:56:02 +02:00
Michele Caini
e2e8a575c6 *: minor changes 2022-08-22 16:35:35 +02:00
Michele Caini
7205fb63fa dense_set: minor changes 2022-08-22 16:35:32 +02:00
Michele Caini
c7b59fdc14 entity/*: minor changes 2022-08-22 16:35:28 +02:00
Michele Caini
e9a59bb014 registry: swap 2022-08-11 12:15:49 +02:00
Michele Caini
76ba063f29 registry:
* accept allocator upon construction
* no allocator-extended move ctor due to type erasure (we can't pass it to pools)
2022-08-11 11:40:57 +02:00
Michele Caini
2988a74fe4 registry: prepare allocator support (still an unused parameter) 2022-08-10 11:10:18 +02:00
Michele Caini
72bc9e3973 doc: cleanup 2022-08-10 10:13:12 +02:00
Michele Caini
31fb2bc2c3 group: basic_registry is no longer a friend class 2022-08-10 08:18:52 +02:00
Michele Caini
768f8cd74c storage mixin: prepare for cross registry mixin 2022-08-10 07:59:42 +02:00
Michele Caini
751e044ce2 sigh_storage_mixin: use the allocator as it ought to be 2022-08-10 07:57:00 +02:00
Michele Caini
3406250cf6 iwyu: cleanup 2022-08-09 18:04:38 +02:00
Michele Caini
e510bdf4f2 dot: updated includes 2022-08-09 18:02:43 +02:00
Michele Caini
fccdff05e3 dense_map: equal_range 2022-08-09 09:50:55 +02:00
Michele Caini
e530b3d6f6 dense_set: equal_range 2022-08-09 09:50:52 +02:00
Michele Caini
0f0b70be23 iwyu: use imp file rather than intrusive pragma exports 2022-08-08 10:59:27 +02:00
Michele Caini
e39f235287 test: review includes 2022-08-06 17:36:04 +02:00
Michele Caini
af5acb8d32 test: minor changes 2022-08-05 18:40:17 +02:00
Michele Caini
a2f70cb5e4 emitter: updated includes 2022-08-05 18:38:53 +02:00
Michele Caini
25d10f03bb iwyu: export all fwd includes 2022-08-05 17:47:42 +02:00
Michele Caini
85c1ad060f test: minor changes 2022-08-05 12:28:22 +02:00
Michele Caini
d9b1cc0db3 iwyu: begin/end exports directive for entt.hpp/fwd.hpp (see #777) 2022-08-05 12:28:05 +02:00
Michele Caini
34205014e9 test: review includes 2022-08-05 12:06:10 +02:00
Michele Caini
749106aa47 test: suppress iwyu warnings for odr.cpp 2022-08-05 12:05:45 +02:00
Michele Caini
b828e0295a build system: update analyzer workflow 2022-08-05 11:46:53 +02:00
Michele Caini
2345002c1c build system: add iwyu workflow (see #777) 2022-08-05 08:56:35 +02:00
Michele Caini
2ce18a47b4 cleanup TODO file 2022-08-05 08:54:20 +02:00
Michele Caini
76b380d7c0 meta: review meta_any::allow_cast 2022-08-04 15:14:43 +02:00
Michele Caini
d3244ab4da cleanup TODO file 2022-08-04 10:58:00 +02:00
Michele Caini
d06ccfb8e3 test: remove unused variable 2022-08-03 18:16:22 +02:00
Michele Caini
6f807d6f9e dense_set: added count 2022-08-03 18:10:03 +02:00
Michele Caini
2b0045e23e dense_map: added count 2022-08-03 18:09:52 +02:00
Michele Caini
57067bc362 dense_set: add max_size 2022-08-03 17:47:54 +02:00
Michele Caini
abe185c003 dense_map: add max_size 2022-08-03 17:47:45 +02:00
Michele Caini
85ca2f3562 doc: updated entity.md and README files 2022-08-03 11:24:23 +02:00
Michele Caini
3d03b01439 storage: full support for non-movable types (close #905) 2022-08-03 10:57:51 +02:00
Michele Caini
879e7d775f component: yet another piece to fully support non-movable types (see #905) 2022-08-02 14:06:35 +02:00
Michele Caini
e9dbd10db4 storage: fix cross range-erase can break when using built-in iterators (close #914) 2022-08-02 09:56:24 +02:00
Michele Caini
acc95bd9b7 doc: registry context 2022-08-02 08:41:45 +02:00
Michele Caini
504c7b5d6d registry (close #911):
* added context emplace_as
* deprecated context emplace_hint
2022-08-02 08:40:06 +02:00
Michele Caini
b20ab29d82 registry: context insert_or_assign 2022-08-02 08:35:10 +02:00
Michele Caini
95b16a0b04 sparse_set: fix cross range-erase can break when using built-in iterators (close #913) 2022-08-01 11:04:56 +02:00
Michele Caini
6ee64d1dc2 *: add a couple of <functional> because of equal_to 2022-07-29 12:06:25 +02:00
Michele Caini
83ecd2d1d9 test: suppress all death tests in release 2022-07-29 10:49:03 +02:00
Michele Caini
325a5dd353 test: utilities to suppress death tests in release 2022-07-29 10:46:15 +02:00
Michele Caini
e0ee35da61 sparse_set/storage/sigh_storage_mixin:
* prepare to support non-movable, non-copyable types
* improve destroying entities
2022-07-29 09:26:28 +02:00
Michele Caini
b3fde98020 build system: make pkg-config work with includedir as absolute paths (close #890) 2022-07-28 11:14:32 +02:00
Michele Caini
3c7c21cea3 cmake: add hand made join_paths (because we don't use cmake v3.20+ yet) 2022-07-28 11:13:12 +02:00
Michele Caini
c73d6c52bf registry context: add get, deprecate at (close #911) 2022-07-28 08:54:43 +02:00
Michele Caini
c38c1454b4 test: code coverage 2022-07-27 10:09:09 +02:00
Michele Caini
06d1d23273 test: minor changes 2022-07-27 09:48:47 +02:00
Michele Caini
4081b46302 test: clean up 2022-07-27 08:55:11 +02:00
Michele Caini
914bf49656 natvis: update emitter definition 2022-07-27 08:54:42 +02:00
Michele Caini
dc634c61c4 natvis: runtime view 2022-07-27 08:54:19 +02:00
Michele Caini
566878b29c natvis: update registry context definition 2022-07-27 08:54:03 +02:00
Michele Caini
0060479e33 cmake: remove aob workflow 2022-07-26 10:23:04 +02:00
Michele Caini
1a0d892201 doc: cleanup 2022-07-26 09:41:10 +02:00
Michele Caini
0424b63bf1 registry: decouple asserts 2022-07-26 09:41:01 +02:00
Michele Caini
de9d3c04e2 registry: remove redundant check 2022-07-25 17:08:19 +02:00
Michele Caini
b0e4a853cf view: drop redundant asserts 2022-07-25 16:35:09 +02:00
Michele Caini
ac8dfe29ae group: drop redundant asserts 2022-07-25 16:35:03 +02:00
Michele Caini
9d33a38ec4 registry: bypassing the check is possible already, remove it as a whole then 2022-07-25 15:25:06 +02:00
Michele Caini
e3076fa5d1 registry: drop useless validity check from remove 2022-07-21 17:14:59 +02:00
Michele Caini
2067b2aea6 registry: remove redundant validity check from orphan 2022-07-21 15:05:01 +02:00
Michele Caini
a14af95f9d registry: remove useless validity check from a get 2022-07-21 11:39:31 +02:00
Michele Caini
8fd2ce8d47 registry: remove useless validity check from erase 2022-07-21 10:56:53 +02:00
Michele Caini
d6edc64d65 registry: remove useless check when invoking patch 2022-07-21 10:19:36 +02:00
Michele Caini
f882158387 registry: renaming for consistency 2022-07-20 17:59:32 +02:00
Michele Caini
6f852c2dcb registry: minor changes 2022-07-20 17:59:22 +02:00
Michele Caini
e47e74fc76 registry: remove validity check from erase 2022-07-20 14:49:49 +02:00
Michele Caini
e604060369 registry: remove validity check from try_get 2022-07-20 14:47:54 +02:00
Michele Caini
80f8051c57 registry: remove validity check from patch 2022-07-20 12:53:44 +02:00
Michele Caini
612c499a3d registry: remove validity check from get 2022-07-20 12:52:11 +02:00
Michele Caini
cceffe6ac4 registry: renaming for consistency 2022-07-19 19:15:04 +02:00
Michele Caini
a74416c730 registry: remove validity check from any_of/all_of 2022-07-19 16:47:33 +02:00
Michele Caini
0830704484 container/*: minor changes 2022-07-19 15:14:08 +02:00
Michele Caini
79f7eaaf9a handle: include null as it ought to be 2022-07-19 08:08:43 +02:00
Michele Caini
389e038445 registry/context: prepare to allocator support 2022-07-18 15:18:00 +02:00
Michele Caini
8f84bd7091 emitter: allocator support 2022-07-18 14:29:31 +02:00
Michele Caini
2146e3ded9 dispatcher: minor changes 2022-07-18 14:28:59 +02:00
Michele Caini
97c0582765 emitter: cleanup/simplification 2022-07-15 08:43:44 +02:00
Michele Caini
c3f230956c organizer: cleanup/remove dependency from <algorithm> 2022-07-15 08:41:46 +02:00
Michele Caini
b914a4a93e meta: try to please all compilers (again) 2022-07-14 09:22:57 +02:00
Michele Caini
befa0fe213 doc: meta 2022-07-14 08:46:06 +02:00
Michele Caini
61ef5a44e0 meta:
* improved sequence container support for non-contiguous containers
* added support to (standard) deque as sequence containers
2022-07-14 08:45:59 +02:00
Michele Caini
131fd0d778 doc: updated readme 2022-07-14 08:43:33 +02:00
Michele Caini
8973757b43 updated TODO (gh stuff) 2022-07-13 11:48:30 +02:00
Michele Caini
14ce88730f meta: minimal (and still inefficient) support for lists as sequence containers 2022-07-13 11:48:30 +02:00
Michele Caini
84cbfb2f91 meta: use clear rather than reserve to spot dynamic sequence containers 2022-07-13 11:48:30 +02:00
Michele Caini
e8f8520251 dense_set: fix an issue when erasing movable keys 2022-07-13 11:24:40 +02:00
Michele Caini
c26558cd6d dense_map: fix an issue when erasing movable keys 2022-07-13 11:24:29 +02:00
Paolo Monteverde
71feb0516d adjacency_matrix: update allocator aware constructor (#909) 2022-07-13 11:22:20 +02:00
Michele Caini
382187089c meta: make meta_type::from_void const as it ought to be 2022-07-11 10:31:08 +02:00
Michele Caini
4faeb5c44e dispatcher: removed unused/useless code 2022-07-07 10:41:53 +02:00
Michele Caini
ac8f569280 nativs: updated meta type representation 2022-07-06 12:16:47 +02:00
Michele Caini
669420d31c meta: try to please all compilers (as usual) 2022-07-06 11:52:32 +02:00
Michele Caini
df48cc9471 meta: minor changes 2022-07-06 11:30:45 +02:00
Michele Caini
274a08070f meta: meta_type::from_void support 2022-07-06 11:30:43 +02:00
Michele Caini
c0e20825ac metx: ctx.hpp -> context.hpp 2022-07-06 11:30:40 +02:00
Michele Caini
15d821f2f8 helper: review includes 2022-07-05 08:39:48 +02:00
Michele Caini
acbd38c1ed container/entity: noexcept-ness review 2022-07-05 08:39:16 +02:00
Michele Caini
92f5f97d83 entity/*: updated includes 2022-07-05 08:36:24 +02:00
Michele Caini
1128b9de81 dispatcher: remove unused include 2022-07-04 17:46:33 +02:00
Michele Caini
fcd60467a7 memory: minor/internal changes 2022-07-04 17:46:03 +02:00
Michele Caini
2f51341633 meta: a few optimizations/improvements (size bench review) 2022-07-04 08:27:05 +02:00
Michele Caini
5799f407ae meta: minor changes 2022-07-04 08:27:02 +02:00
Michele Caini
c7a5e09c0e meta container: small changes 2022-07-04 08:26:58 +02:00
Michele Caini
3318ac2699 meta: reduce a little the number and size of vtables/fake functions 2022-07-04 08:26:53 +02:00
Michele Caini
437f1fea54 view: suppress shadow warnings 2022-07-03 12:23:08 +02:00
Michele Caini
3ab43f894f organizer: suppress shadow warnings 2022-07-03 12:23:03 +02:00
Michele Caini
86f98f40a1 test: added missing include 2022-07-03 12:22:53 +02:00
Michele Caini
655f1dd906 doc: minor changes 2022-07-01 18:56:02 +02:00
Michele Caini
243a382485 registry: drop some lambdas and calls to std:: 2022-07-01 18:09:37 +02:00
Michele Caini
4b74163306 meta: turn the meta_range class into an alias of iterable_adaptor 2022-07-01 18:09:35 +02:00
Michele Caini
50e67d7c15 view: drop <algorithm> (no longer required) 2022-07-01 16:00:47 +02:00
Michele Caini
c5afd8f589 dense_map: remove <algorithm> (no longer required) 2022-07-01 16:00:44 +02:00
Michele Caini
c6ab65d552 meta: coding style 2022-07-01 09:50:55 +02:00
Michele Caini
1a853fe145 doc: core, iterator.hpp 2022-07-01 09:18:00 +02:00
Michele Caini
72abc8e4c4 doc: minor changes 2022-07-01 09:17:49 +02:00
Michele Caini
e15ad2e21b storage: minor changes (for consistency) 2022-06-30 18:35:38 +02:00
Michele Caini
5afc5529c2 doc: runtime_view 2022-06-30 18:32:57 +02:00
Michele Caini
7eed368dc2 sparse_set: minor changes 2022-06-30 16:09:28 +02:00
Michele Caini
600303bb5b observer: derive from storage rather than using one internally 2022-06-30 15:52:43 +02:00
Michele Caini
bcfd6d1b4f runtime view: allocator support 2022-06-29 18:27:43 +02:00
Michele Caini
e65b3a790d runtime view: const/non-const support 2022-06-29 18:27:41 +02:00
Michele Caini
a28467d393 dense set/map: drop <algorithm> and save some bytes here and there 2022-06-29 18:27:25 +02:00
Michele Caini
bf13d9363d updated TODO 2022-06-29 18:27:21 +02:00
Michele Caini
ff9c472f86 registry: remove unused header 2022-06-29 18:27:17 +02:00
Michele Caini
46e601db6e natvis: print both the index and the entity for the storage representation 2022-06-29 18:27:14 +02:00
Michele Caini
d4482d0d26 oeganizer: use a flow builder under the hood 2022-06-29 18:27:11 +02:00
Michele Caini
281289c46a flow: set-with-mode function to ease the transition of the observer class 2022-06-29 18:27:09 +02:00
Michele Caini
bc85d96938 meta: remove a few lambdas 2022-06-29 18:26:12 +02:00
Michele Caini
c2475381fd meta: make meta_associative_container::erase return the number of elements removed 2022-06-29 18:25:47 +02:00
Michele Caini
6ac14f35ad doc: minor changes 2022-06-29 18:25:32 +02:00
Michele Caini
de18834c91 view: remove useless member 2022-06-24 10:51:08 +02:00
Michele Caini
fc7d28c723 test: view constructors 2022-06-24 08:55:31 +02:00
Michele Caini
7ac2ce50a0 registry: use internal type member storage_for_type 2022-06-24 08:53:45 +02:00
Michele Caini
9cf9661484 doc: minor changes 2022-06-24 08:22:51 +02:00
Michele Caini
8dbf00ad3a flow: minor changes 2022-06-23 14:43:22 +02:00
Michele Caini
e0e623fa36 doc: links 2022-06-23 08:57:34 +02:00
Michele Caini
4094669222 doc: typo 2022-06-23 08:51:12 +02:00
Michele Caini
9e19244659 flow: added a couple of [[nodiscard]] 2022-06-23 08:37:17 +02:00
Michele Caini
d41ccb6b34 doc: tabs -> spaces 2022-06-23 08:36:28 +02:00
Michele Caini
72c1da2507 view: storage based model 2022-06-22 09:07:25 +02:00
Michele Caini
2e8c5ea2de doc: typo 2022-06-22 09:05:31 +02:00
Michele Caini
65561fe431 group: minor changes 2022-06-22 09:02:37 +02:00
Michele Caini
cf401aa0b5 doc: minor changes 2022-06-22 09:00:46 +02:00
Michele Caini
9b2c9936c2 doc: dot 2022-06-21 18:30:26 +02:00
Michele Caini
e4aabc7feb adjacency_matrix: single edge iterator 2022-06-21 18:11:36 +02:00
Michele Caini
610951771e adjacency_matrix: add error messages 2022-06-21 14:31:59 +02:00
Michele Caini
f76959af63 test: suppress warnings due to unused variables 2022-06-21 14:24:49 +02:00
Michele Caini
0eb5b0437b dot: directed/undirected support 2022-06-21 12:36:23 +02:00
Michele Caini
c1ac684f9a adjacency_matrix: directed/undirected tag 2022-06-21 12:28:59 +02:00
Michele Caini
473c36aba5 adjacency_matrix:
* make swap accept the right type
* make resize work properly
2022-06-21 11:02:23 +02:00
Michele Caini
38e6722358 dot: try to also please gcc 2022-06-21 09:49:47 +02:00
Michele Caini
d2a0f86908 dot: first draft 2022-06-21 09:26:32 +02:00
Michele Caini
c6707bec97 test: handle 2022-06-20 12:49:33 +02:00
Michele Caini
5c6a11cb8f test: minor changes 2022-06-20 08:30:26 +02:00
Michele Caini
600cc5e167 handle: since it's a reference type, we don't need two overloads for storage 2022-06-20 08:27:57 +02:00
Michele Caini
c441a9ad97 handle: remove visit, use iterable objects instead 2022-06-20 08:22:57 +02:00
Michele Caini
c2d2ba29ea doc: typo 2022-06-18 16:21:37 +02:00
Michele Caini
25af23d3ff registry: minor changes 2022-06-18 16:17:25 +02:00
Michele Caini
cbd6fd8aee view: minor changes 2022-06-18 16:17:07 +02:00
Michele Caini
7a7fc20438 doc: typo (close #898) 2022-06-17 17:10:50 +02:00
Michele Caini
ca4b2b68a7 updated TODO 2022-06-17 17:01:47 +02:00
Michele Caini
f3ae553706 doc: minor changes - thanks @morbo84 2022-06-17 16:27:04 +02:00
Michele Caini
fe6696b107 graph: sync point 2022-06-17 16:06:49 +02:00
Michele Caini
3a85d0f179 doc: graph 2022-06-17 11:39:07 +02:00
Michele Caini
9845d2c87d adjacency_matrix:
* iterator review
* in_edges iterable
2022-06-17 11:36:11 +02:00
Michele Caini
ea8730c2a9 doc: typo (close #896) 2022-06-16 09:53:51 +02:00
Michele Caini
c81868f8f7 flow: removed ::tasks, added operator[] 2022-06-16 09:51:18 +02:00
Michele Caini
33d1839b75 flow: task -> bind 2022-06-16 09:37:12 +02:00
Michele Caini
df10a01af9 flow: increase code coverage, simplify the internals 2022-06-16 09:30:21 +02:00
Michele Caini
4aa40aba4c build system: try to satisfy codecov requirements 2022-06-16 09:01:01 +02:00
Michele Caini
696c94e2ea flow: suppress shadow warnings 2022-06-16 08:23:30 +02:00
Michele Caini
bc5dfb9371 graph: flow (first draft) 2022-06-16 08:11:45 +02:00
Michele Caini
88c63b941d natvis: graph 2022-06-16 07:48:41 +02:00
Michele Caini
b8e12dc42a doc: minor changes 2022-06-15 09:21:49 +02:00
Michele Caini
2974e959e5 doc:: minor changes 2022-06-15 09:11:36 +02:00
Michele Caini
67b2fc085c view: prepare to storage based model 2022-06-15 09:01:57 +02:00
Michele Caini
cb202c15a5 group: minor changes 2022-06-15 09:01:41 +02:00
Michele Caini
850abd440b doc: typo 2022-06-15 08:28:00 +02:00
Michele Caini
01d125f53d group: storage based model 2022-06-14 08:07:01 +02:00
Michele Caini
3f0572c5ce graph: class vs struct definition 2022-06-13 00:39:53 +02:00
Michele Caini
cc6c45f591 adjacency_matrix: edges -> out_edges 2022-06-04 11:40:52 +02:00
Michele Caini
8029777c4e graph: (currently directed only) adjacency_matrix 2022-06-04 10:52:28 +02:00
Michele Caini
a6171551bd input_iterator_pointer: operator* 2022-06-04 09:16:18 +02:00
Michele Caini
6bb67ab5da process: fwd file 2022-06-03 19:42:37 +02:00
Michele Caini
a31c492c83 core: iota_iterator (waiting for C++20) 2022-06-03 19:25:47 +02:00
Michele Caini
c36d5e4a9c organizer: minor changes 2022-06-03 18:56:52 +02:00
Michele Caini
17b3fc93b0 updated todo file 2022-06-01 09:42:02 +02:00
Michele Caini
d2f4ffc433 registry/storage: minor changes 2022-06-01 09:41:41 +02:00
Michele Caini
1c8a296f49 doc: minor changes 2022-06-01 09:21:43 +02:00
Michele Caini
de0e5862dd meta: use a more explicit static_cast (!! tricks some tools) 2022-05-31 10:37:04 +02:00
Michele Caini
e822b8c99e meta: minor changes 2022-05-31 10:33:27 +02:00
Michele Caini
48d767749d meta: removed redundant meta_traits::is_pointer 2022-05-31 10:23:34 +02:00
Michele Caini
fa48c76dd1 any: drop useless remove_const_t 2022-05-29 12:10:32 +02:00
Michele Caini
754fe9f4ec any: make things explicit to avoid cast to same type warnings 2022-05-29 12:09:23 +02:00
Michele Caini
1ad3adfff0 any: drop pointless cast 2022-05-29 12:03:42 +02:00
Michele Caini
0c0fd4d7f7 storage_for: minor changes 2022-05-29 10:08:07 +02:00
Michele Caini
72f5df82b2 doc: some links :) 2022-05-29 09:41:56 +02:00
Michele Caini
c7407f5b6a view:
* reduce (a lot) the number of instantiations due to tuples
* review operator|
2022-05-27 19:03:28 +02:00
Michele Caini
f57975acca view: avoid delegating constructors with tuples 2022-05-27 18:12:38 +02:00
Michele Caini
a8a0c9e571 test: minor changes 2022-05-27 16:39:54 +02:00
Michele Caini
e05277d56a registry: use storage_for[_t] 2022-05-26 11:50:31 +02:00
Michele Caini
0b1985ca7e view: use storage_for[_t] 2022-05-26 11:50:22 +02:00
Michele Caini
637764ee4e group: use storage_for[_t] 2022-05-26 08:46:53 +02:00
Michele Caini
c6e613c317 storage: storage_for[_t] utility 2022-05-26 08:25:47 +02:00
Michele Caini
aa3e769463 doc: type_list_transform 2022-05-25 23:04:15 +02:00
Michele Caini
630ba195d1 input_iterator_pointer: no reason for which it shouldn't be copyable 2022-05-25 16:51:44 +02:00
Michele Caini
13276d2f10 input_iterator_pointer: minor changes 2022-05-25 16:48:07 +02:00
Michele Caini
d59407a339 any: makes assign work for all basic_any types 2022-05-25 16:46:50 +02:00
Michele Caini
92c2fd9409 hashed_string: nit 2022-05-25 11:42:10 +02:00
Michele Caini
063e6160e0 natvis: explicit entity-component mapping 2022-05-25 11:39:42 +02:00
Michele Caini
6155a10037 doc: minor changes 2022-05-25 11:24:20 +02:00
Michele Caini
7428cb353c storage: add default entity type to storage_type 2022-05-25 10:32:40 +02:00
Michele Caini
fe3b5b03eb entity/invoke: properly decay deduced argument to avoid nasty errors with reference types 2022-05-24 14:11:19 +02:00
Michele Caini
497fb90b12 doc: updated core.md 2022-05-24 10:59:50 +02:00
Michele Caini
48ab892de1 type_traits: type_list_transform[_t] utility 2022-05-24 10:59:40 +02:00
Michele Caini
a85ad88d5a entitiy/invoke: use nth_argument_t and avoid explicitly mentioning the registry 2022-05-24 08:31:14 +02:00
Michele Caini
cf2566ff74 doc: nth_argument (core) 2022-05-23 11:47:04 +02:00
Michele Caini
619f51518a type_traits: nth_argument utility (with tests) 2022-05-23 11:46:50 +02:00
Michele Caini
b26330adab delegate: minor changes 2022-05-23 11:35:11 +02:00
Michele Caini
83009bac02 resource: remove use_count (close #889) 2022-05-21 14:18:05 +02:00
Michele Caini
8780d536c7 resource: add member function handle 2022-05-21 14:14:15 +02:00
Michele Caini
99f4134d9d resource: add type member handle_type 2022-05-21 14:12:31 +02:00
Michele Caini
ca02ab7d5d resource: add type member element_type 2022-05-21 14:11:25 +02:00
kcalbCube
dc295ca0e1 doc: add an notice about possible undefined behaviour. (#887) 2022-05-20 10:44:25 +02:00
Michele Caini
8ee91095c5 organizer: make it depend on the registry type and not the entity type 2022-05-20 09:07:32 +02:00
Michele Caini
49c7b2f8f4 entity/*: renaming things to make them clear 2022-05-19 09:00:04 +02:00
Michele Caini
5d80cf11ef helper: decouple invoke and to_entity from the registry type 2022-05-19 08:15:17 +02:00
Michele Caini
bc929bcb90 snapshot: suppress warnings/errors due to shadow template parameters 2022-05-18 15:21:35 +02:00
Michele Caini
26a3057acf handle: decouple from entity type, make it work with all registry types 2022-05-18 15:02:25 +02:00
Michele Caini
96cf0a6d02 handle: prepare for a registry oriented version 2022-05-18 11:12:53 +02:00
Michele Caini
12e773f3fb as_group/as_view: make them transparent to the registry type 2022-05-17 12:40:47 +02:00
Michele Caini
c321997591 snapshot/loader: support all types of registry (allocator oriented) 2022-05-17 12:35:45 +02:00
Michele Caini
b2d98452f1 observer: support all types of registry (allocator oriented) 2022-05-17 12:28:54 +02:00
Michele Caini
16e48aa10f storage_type_t: simplify the definition to make it work with default template arguments for storage_type 2022-05-17 12:17:44 +02:00
Michele Caini
4da85a5f4a storage_type[_t]: flip entity type and value type 2022-05-17 12:07:21 +02:00
Michele Caini
a5c9d3a809 basic_storage: flip entity type and value type 2022-05-17 11:55:53 +02:00
Michele Caini
f288ba744d test: cleanup 2022-05-17 11:35:32 +02:00
Michele Caini
ba5b85de00 storage: use storage_type_t everywhere 2022-05-16 08:59:04 +02:00
Michele Caini
bd2f412225 storage: add storage_type_t 2022-05-16 08:25:50 +02:00
Michele Caini
a86cf32f55 entity: storage_traits -> storage_type 2022-05-16 08:25:23 +02:00
Michele Caini
0c8178c753 view/group/registry: storage_type -> storage_for (prepare for storage_type_t) 2022-05-15 10:45:33 +02:00
Michele Caini
b59e06ec89 storage_traits: storage_type -> type 2022-05-13 14:11:23 +02:00
Michele Caini
10153a371b sparse_set: use null values as watchdogs 2022-05-13 12:12:48 +02:00
Michele Caini
a93c1478c0 sparse_set: check against null rather than tombstone (prepare support for enable/disable) 2022-05-13 11:10:08 +02:00
Michele Caini
215b7a19c9 test: suppress warning due to integral conversions 2022-05-12 17:51:03 +02:00
Michele Caini
14ed666b91 helper: suppress warning with gcc from to_entity 2022-05-12 17:50:13 +02:00
Michele Caini
ac1d61b2c4 sparse_set: refine the check of the bump function 2022-05-12 17:32:25 +02:00
Michele Caini
acb6e90158 sparse_set: bump doesn't allow to set a tombstone version (yet) 2022-05-12 15:58:38 +02:00
Michele Caini
d3609737f5 view: minor changes 2022-05-12 14:23:45 +02:00
Michele Caini
95b91cd7dd runtime_view: remove the dependency on the sparse set class 2022-05-12 12:43:00 +02:00
Michele Caini
affd2a3b91 registry: ::storage<T> doesn't accept const types anymore 2022-05-11 13:48:03 +02:00
Michele Caini
d392259364 TODO: cleanup 2022-05-11 07:49:37 +02:00
Michele Caini
e03f4fac64 sigh_storage_mixin: added fwd decl to entity/fwd.hpp 2022-05-11 07:49:14 +02:00
Michele Caini
538840351a natvis: updated definition for meta_type_node 2022-05-11 07:48:42 +02:00
Michele Caini
1ddad3577c registry: minor changes 2022-05-11 07:47:58 +02:00
Michele Caini
d44d1325fc meta: add meta_type::is_integral and meta_type::is_signed (close #884) 2022-05-10 14:43:08 +02:00
Michele Caini
dc07af6ad1 sigh: updated noexcept token 2022-05-09 12:16:57 +02:00
Michele Caini
c6f1809d60 sigh:
* cleanup
* conditionally noexcept
2022-05-09 12:07:28 +02:00
Michele Caini
261e73bf3e y_combinator: conditionally noexcept 2022-05-09 12:07:13 +02:00
Michele Caini
8c7d2a1e96 allocation_deleter: conditionally noexcept 2022-05-09 12:06:42 +02:00
Michele Caini
72507977bd input_iterator_pointer: conditionally noexcept 2022-05-09 12:05:56 +02:00
Michele Caini
270829f05b iterable_adaptor: constructors are conditionally noexcept now 2022-05-09 08:32:11 +02:00
Michele Caini
b92e4f7e65 iterable_adaptor: move iterators on construction 2022-05-09 08:23:51 +02:00
Michele Caini
8e890135fe compressed pair: make the full class conditionally noexcept 2022-05-06 12:25:07 +02:00
Michele Caini
a6601939d5 compressed_pair: make compressed_pair_element conditionally noexcept 2022-05-06 12:19:14 +02:00
Michele Caini
c608e735d2 test: use page size variables 2022-05-06 12:01:37 +02:00
Michele Caini
e0a1a338a5 view: security net and explicit arrays to avoid regressions 2022-05-06 09:31:23 +02:00
Michele Caini
7aa276df5a entity: oops, typo :) 2022-05-06 08:44:03 +02:00
Michele Caini
d939a72219 config: drop ENTT_NOEXCEPT[_IF] 2022-05-06 08:41:22 +02:00
Michele Caini
96fc59eee5 updated TODO 2022-05-06 08:40:33 +02:00
Michele Caini
989f8568cf helper: use the right page size in all cases 2022-05-06 08:40:17 +02:00
Michele Caini
66f451124f entity: prepare to remove ENTT_NOEXCEPT[_IF] 2022-05-06 08:37:41 +02:00
Michele Caini
e59ff47f6f core: prepare to remove ENTT_NOEXCEPT[_IF] 2022-05-06 08:30:49 +02:00
Michele Caini
b176be9ef7 meta: prepare to remove ENTT_NOEXCEPT[_IF] 2022-05-06 08:23:52 +02:00
Michele Caini
942879bbba signal: prepare to remove ENTT_NOEXCEPT[_IF] 2022-05-06 08:23:42 +02:00
Michele Caini
2065c4294f resource: prepare to remove ENTT_NOEXCEPT[_IF] 2022-05-06 08:23:30 +02:00
Michele Caini
cb02f0621c process: prepare to remove ENTT_NOEXCEPT[_IF] 2022-05-06 08:23:22 +02:00
Michele Caini
971f371eab poly: prepare to remove ENTT_NOEXCEPT[_IF] 2022-05-06 08:23:14 +02:00
Michele Caini
f21c56bd98 locator: prepare to remove ENTT_NOEXCEPT[_IF] 2022-05-06 08:23:03 +02:00
Michele Caini
363299a27d containter: prepare to remove ENTT_NOEXCEPT[_IF] 2022-05-06 08:22:55 +02:00
Michele Caini
10e74dad41 doc: drop mentions of ENTT_NOEXCEPT 2022-05-05 14:54:04 +02:00
Michele Caini
dc041b7ea0 test: cleanup/update 2022-05-05 14:53:05 +02:00
Michele Caini
584cfdf64c test: avoid using ENTT_NOEXCEPT in the test suite 2022-05-05 09:24:02 +02:00
Michele Caini
74c498cf15 doc: avoid using ENTT_NOEXCEPT in the doc 2022-05-05 09:02:38 +02:00
Michele Caini
a1e5855f2b updated TODO 2022-05-05 09:00:03 +02:00
Michele Caini
d8e5a97ef3 entity: iterator constexpr-ness review (close #883) 2022-05-04 16:18:37 +02:00
Michele Caini
96804b0b28 meta: constexpr all the way (see #883) 2022-05-04 16:10:37 +02:00
Michele Caini
b0d69c50da resource_cache: constexpr all the way (see #883) 2022-05-04 16:06:54 +02:00
Michele Caini
de6b2e139b container: constexpr all the way (see #883) 2022-05-04 16:04:55 +02:00
Michele Caini
7204840a8a resource: fixed typo 2022-05-04 09:30:23 +02:00
Michele Caini
76369a01ad group: avoid unnecessary instantiations and try to please all compilers 2022-05-04 08:38:28 +02:00
Michele Caini
3a92a93604 y_combinator: constexpr-ness all the way (see #883) 2022-05-03 18:51:03 +02:00
Michele Caini
f1c968372f memory: constexpr-ness all the way (see #883) 2022-05-03 18:48:54 +02:00
Michele Caini
759d9a642f config: add ENTT_ENTT_CONSTEXPR to use in combination with ENTT_TRY and the like (waiting for C++20) 2022-05-03 18:48:11 +02:00
Michele Caini
86d86c3044 iterator: constexpr-ness all the way (see #883) 2022-05-03 18:41:15 +02:00
Michele Caini
c38708c483 compressed_pair: constexpr-ness all the way (see #883) 2022-05-03 18:37:39 +02:00
Michele Caini
1673a5ade9 doc: minor changes 2022-05-03 17:07:06 +02:00
Michele Caini
c56a49ddce group: prepare to the storage reference semantics move 2022-05-02 15:36:05 +02:00
Michele Caini
03477ce400 type_traits: type_list_index[_v] 2022-05-02 10:58:11 +02:00
Michele Caini
14d213d13e ident/family: change names for consistency 2022-05-02 09:14:53 +02:00
Michele Caini
8d86816d8d view: tuple constructor 2022-04-30 11:56:04 +02:00
Michele Caini
12cf3fd8e7 *: spot and clear some misues of = default 2022-04-29 12:35:11 +02:00
Michele Caini
8c23f2de8b core/entity: spot and remove useless add_const_t 2022-04-29 12:24:47 +02:00
Michele Caini
837481a854 group: support both const and non-const excluded types 2022-04-29 12:22:54 +02:00
Michele Caini
b682e58df7 view:
* prepare to storage getters for filters
* support both const and non-const excluded types
2022-04-29 12:18:12 +02:00
Michele Caini
c4f430d0d0 view: minor changes 2022-04-28 23:02:00 +02:00
Michele Caini
c7930ab349 natvis: update registry definition 2022-04-28 16:56:54 +02:00
Michele Caini
045baba7ea compressed_pair: re-add base_type to avoid breaking natvis 2022-04-28 16:51:04 +02:00
Michele Caini
d17ac421ec updated authors and todo files 2022-04-28 16:36:55 +02:00
Qix
049e529f66 test: add mention of storage initialization workaround (ref #827) (#879) 2022-04-28 16:34:45 +02:00
Dominic Koepke
ef6e47f625 tuple: forward_apply functional type (#876) 2022-04-28 16:14:25 +02:00
Michele Caini
1330cb9126 doc: typo 2022-04-27 16:55:43 +02:00
Michele Caini
f0ee8bc89d meta: minor changes 2022-04-27 09:32:32 +02:00
Michele Caini
ebc0c18534 test: add missing template keywords (thanks msvc) 2022-04-27 09:22:07 +02:00
Michele Caini
379a4bdda6 meta: re-added meta_type::remove_pointer (close #878) 2022-04-27 09:17:18 +02:00
Michele Caini
2ce106c50f meta: internal changes 2022-04-26 19:46:57 +02:00
Michele Caini
0ebdfe59ee meta: minor changes 2022-04-26 19:01:15 +02:00
Michele Caini
80c700b1f8 sigh_storage_mixin: allocator support 2022-04-26 11:43:59 +02:00
Michele Caini
103f935d61 meta (close #872):
* container traits don't really support plain arrays anymore (if they ever did)
* fix an issue with insert/erase of meta sequence containers
* add non-regression tests
2022-04-24 17:17:47 +02:00
Michele Caini
164476721a compressed_pair: cleanup 2022-04-22 10:40:10 +02:00
Michele Caini
54dd716d02 any: avoid duplicating operation/policy for all specializations 2022-04-22 10:35:01 +02:00
Michele Caini
ab681ce637 registry: minor changes to ease the transition 2022-04-22 00:02:55 +02:00
Michele Caini
a355de8e97 registry: delegating constructor 2022-04-21 23:39:15 +02:00
Michele Caini
56921d6449 config/component: ENTT_IGNORE_IF_EMPTY -> ENTT_ETO_TYPE 2022-04-21 16:54:50 +02:00
Michele Caini
7e900626bc test: suppress shadow warnings 2022-04-21 13:59:27 +02:00
Michele Caini
dc30c29f4e config/component/storage:
* ENTT_IGNORE_IF_EMPTY is an internal def-only macro
* component_traits review to also support void type
* updated tests for component_traits
* added a non-regression test for storage<void>
2022-04-21 11:43:42 +02:00
Michele Caini
370865bb57 doc: add billy engine 2022-04-21 09:10:23 +02:00
Michele Caini
80015e83d4 sigh/dispatcher: use allocator<void> 2022-04-20 16:40:19 +02:00
Michele Caini
c37c35b52b registry: avoid code duplication 2022-04-19 15:35:39 +02:00
Corentin Schreiber
069004d4eb registry: overflow of version and max number of entities (#864) 2022-04-19 10:58:17 +02:00
Michele Caini
f11695200e doc: updated config.md 2022-04-19 09:10:01 +02:00
Michele Caini
ef2f8a1a18 config: decouple ENTT_NOEXCEPT and exceptions handling (close #867) 2022-04-19 09:09:25 +02:00
Michele Caini
54cc687c50 doc: typo 2022-04-19 00:08:25 +02:00
Michele Caini
51f279160f resource: added more comparison operators for resource handles 2022-04-16 11:38:45 +02:00
Michele Caini
e3113f6976 sparse_set: nodiscard get(s) 2022-04-15 16:02:53 +02:00
Michele Caini
485e29a672 meta: reduce number of generated symbols 2022-04-15 15:10:40 +02:00
Michele Caini
00e67c50ca nativs: updated definition for type_info 2022-04-15 11:46:08 +02:00
Michele Caini
3e7b3edb9c group: reduce instantiations due to extended iterators and merge their definitions 2022-04-15 11:46:08 +02:00
Michele Caini
3c6c9d9462 emitter: minor changes 2022-04-15 11:46:08 +02:00
Michele Caini
6b06a9ff2b storage: cleanup 2022-04-15 11:46:08 +02:00
Michele Caini
803db476bb entity:
* drop utility.hpp
* turn get_t/exclude_t into aliases for type_list
* updated api for basic_registry<...>::group/group_if_exists
* doc review to reflect all the changes above
2022-04-15 11:46:08 +02:00
Michele Caini
c134ea3098 doc: updated signal.md 2022-04-15 11:46:08 +02:00
Michele Caini
6e64f36b39 natvis: review both dispatcher and emitter 2022-04-15 11:46:08 +02:00
Michele Caini
36af39e2b4 emitter: full review 2022-04-15 11:46:08 +02:00
Michele Caini
88929ab730 natvis: minor changes 2022-04-15 11:46:08 +02:00
Michele Caini
02f00e5339 dispatcher: assert within assure as it ought to be 2022-04-15 11:46:08 +02:00
Michele Caini
ee67032af1 sigh: cleanup to reduce redundancy 2022-04-15 11:46:08 +02:00
Michele Caini
8aea00f527 delegate: cleanup to reduce redundancy 2022-04-15 11:46:07 +02:00
Michele Caini
50faed4a50 dispatcher: minor changes 2022-04-15 11:46:07 +02:00
Michele Caini
b982817a56 now working on v3.11.0 2022-04-15 11:45:56 +02:00
Michele Caini
e5172a9240 update single include file 2022-04-15 11:44:22 +02:00
Michele Caini
dcac7942fa type_info: a default constructible type isn't a good idea actually 2022-04-08 12:17:14 +02:00
Michele Caini
4e13529adb registry: ctor that reserves enough memory for count pools 2022-04-08 09:58:05 +02:00
Michele Caini
c553835228 doc: add the kindest feedback ever received to the README 2022-04-07 18:48:38 +02:00
Michele Caini
1b87193fc1 doc: add Ember Sword to the list of games :) 2022-04-07 18:47:40 +02:00
Michele Caini
25fca56319 doc: update AUTHORS file 2022-04-07 18:47:15 +02:00
Alexandr Timofeev
bf4252394c *: protect against minmax (#862) 2022-04-07 14:28:30 +02:00
Michele Caini
22757e064f *: review remove_cv/remove_ref tokens after spotting an error by chance 2022-04-06 15:57:26 +02:00
Michele Caini
fc9fa37e4e doc: minor changes 2022-04-06 15:52:49 +02:00
Michele Caini
2d5e3402fa type_info: allow to construct type_info objects directly 2022-04-06 11:25:44 +02:00
Michele Caini
b7dd26121a type_info: cleanup 2022-04-06 09:55:31 +02:00
Michele Caini
23bdbeef4e doc: updated type_info section 2022-04-06 09:50:42 +02:00
Michele Caini
9ac81c5219 natvis: updated type_info snippet 2022-04-06 09:49:48 +02:00
Michele Caini
6b9d346b8b type_info:
* reserve a seq identifier for empty type_info objects
* require type_index to return non-zero values
* make type_info default constructible
* add type_info::operator bool()
2022-04-06 08:53:55 +02:00
Michele Caini
9e68eb4d2c type_id: type_info from variable 2022-04-05 08:50:08 +02:00
Michele Caini
f1f47ee44a entity: internal changes/cleanup 2022-04-05 08:42:36 +02:00
Michele Caini
436b2b3140 core: use fold expressions rather than std::disjunction if possible 2022-04-05 08:42:20 +02:00
Michele Caini
f6cfa8ae49 enum_as_bitmask: internal changes 2022-04-05 08:41:36 +02:00
Michele Caini
13949a8d1f updated internal TODO list 2022-04-04 17:13:49 +02:00
Michele Caini
88db623dc1 view: avoid unnecessary parameter pack 2022-04-04 12:27:28 +02:00
Michele Caini
976413173a any: invoke type_id with decayed types to avoid unnecessary instantiations 2022-04-04 09:46:52 +02:00
Michele Caini
91d3349585 view: slightly reduce instantiations 2022-04-04 09:46:20 +02:00
Michele Caini
6e1a774921 sigh: slightly reduce instantiations 2022-04-04 09:46:00 +02:00
Michele Caini
6b53b509fa registry/storage: minor changes 2022-04-04 09:18:10 +02:00
Michele Caini
b8b6203ecd doc: minor changes 2022-04-04 09:17:40 +02:00
Michele Caini
81e1500675 dense_set: slightly reduce instantiations 2022-04-04 09:17:13 +02:00
Michele Caini
d2cd18cb1e test: minor changes (try to also please codecov) 2022-04-02 16:10:00 +02:00
Michele Caini
f5ac73f681 resource_cache: use actual template parameters on sfinae expressions 2022-04-01 14:48:52 +02:00
Michele Caini
413630813a resource: updated doc 2022-04-01 14:41:38 +02:00
Michele Caini
73b6ef2293 resource: updated natvis file 2022-04-01 14:41:15 +02:00
Michele Caini
428bfbd810 tests: updated tests for the resource module 2022-03-31 15:43:44 +02:00
Michele Caini
c54409bdfb resource: fully review the resource module 2022-03-31 15:43:07 +02:00
Michele Caini
b63aebfdaf dense_set: strong exception guarantee (emplace/insert) 2022-03-30 15:34:45 +02:00
Michele Caini
335f876a46 dense_map: strong exception guarantee (emplace/insert) 2022-03-30 15:26:39 +02:00
Michele Caini
c5e99342a3 tests: minor changes 2022-03-30 15:14:26 +02:00
Michele Caini
1710eb9249 dense_map: minor changes 2022-03-29 16:56:49 +02:00
Michele Caini
d03770f91f meta: avoid using =default to avoid tricking VS toolset v141 2022-03-29 16:26:01 +02:00
Michele Caini
9fe6f7a6ea entity: avoid using =default to avoid tricking VS toolset v141 2022-03-29 16:25:53 +02:00
Michele Caini
2fc605fc3e container: avoid using =default to avoid tricking VS toolset v141 2022-03-29 16:25:37 +02:00
Michele Caini
edcd5c4713 dense_set: use underlying iterators to exploit their checks 2022-03-28 16:25:09 +02:00
Michele Caini
bd3df1e06a dense_map: use underlying iterators to exploit their checks 2022-03-28 16:24:52 +02:00
Michele Caini
b785d1c82b meta: minor changes (coding style) 2022-03-27 23:25:50 +02:00
Michele Caini
93bf14f84f organizer: cleanup + renaming to avoid conflicts 2022-03-27 23:24:54 +02:00
Michele Caini
5a84646282 dense_map: updated built-in doc 2022-03-25 17:34:44 +01:00
Michele Caini
2e6cfc08f0 registry: refine storage proxy iterator and guarantee iterator conversions 2022-03-25 17:20:23 +01:00
Michele Caini
4b6d3d47ec registry: suppress a false warning from clang 2022-03-25 15:12:25 +01:00
Michele Caini
985abaa12a dense_map/set: minor changes 2022-03-24 17:38:51 +01:00
Michele Caini
a381a7253a resource: updated doc 2022-03-23 15:32:11 +01:00
Michele Caini
b8bb509d5c resource:
* drop the resource_loader class
* made the loader a template parameter of the resource_cache
* updated the API of the resource_cache
2022-03-23 15:31:55 +01:00
Michele Caini
f48dbd0e19 resource_handle: updated doc 2022-03-23 15:29:44 +01:00
Michele Caini
31c1278374 config: removed ENTT_LAUNDER (unused) 2022-03-22 12:44:21 +01:00
Michele Caini
f6aaafd60f config: add ENTT_NOEXCEPT_IF (for future uses) 2022-03-22 12:43:47 +01:00
Michele Caini
ae3dec19fd registry: internal changes to avoid ambiguous calls 2022-03-22 12:43:24 +01:00
Michele Caini
990baa0929 test: typo 2022-03-21 16:14:20 +01:00
Michele Caini
7ecb65a141 doc: updated links 2022-03-21 10:41:53 +01:00
Michele Caini
a1e55103be enum: typo 2022-03-21 10:41:45 +01:00
Michele Caini
1b4fc54405 dense_set:
* emplace create-and-discard mode if strictly required only
* insert uses the insert_or_do_nothing utility internally
* opaque value_to_bucket function
2022-03-21 10:41:28 +01:00
Michele Caini
e5dcea1fa0 dense_map: emplace create-and-discard mode if strictly required only 2022-03-21 10:19:47 +01:00
Michele Caini
9f25195629 dense_map: use insert_or_do_nothing from within insert 2022-03-21 10:17:55 +01:00
Michele Caini
6936fa1c1c dense_map: opaque key_to_bucket function 2022-03-21 08:42:44 +01:00
Michele Caini
7b25eacfc8 dense_map:
* try_emplace should not consume arguments if the element exists
* clean up
2022-03-18 10:51:16 +01:00
Michele Caini
33989a61e3 dense_set: claen up 2022-03-18 10:49:52 +01:00
Michele Caini
08d9e8d076 dense_map/set: minor changes 2022-03-17 14:23:29 +01:00
Michele Caini
da43ef0982 dense_set: avoid potential UBs 2022-03-17 12:36:00 +01:00
Michele Caini
02f12bde81 dense_map: avoid potential UBs 2022-03-17 12:35:46 +01:00
Michele Caini
b55d5375ac *: added missing messages to static asserts 2022-03-17 11:39:45 +01:00
Michele Caini
281f40bd56 dense_set: avoid dispatching based on iterator category if possible 2022-03-17 11:39:45 +01:00
Michele Caini
dcb7c0c27e dense_map: constrain the allocator type 2022-03-17 11:39:45 +01:00
Michele Caini
d3c44a6fa6 dense_map: avoid dispatching based on iterator category if possible 2022-03-17 11:39:45 +01:00
Michele Caini
f432dc6bdc dense_map:
* uses-allocator construction
* iterators model forward/random access iter types but are in the input iter category
* iter value_type is pair<const T &, U &>/pair<const T &, const U &> (zip iter types)
* no risky UBs due to type punning or destroy/construct within a vector
2022-03-17 11:39:45 +01:00
Michele Caini
ce21ee6001 test: minor changes 2022-03-17 11:39:36 +01:00
Michele Caini
a431f5a674 registry: allow creating pools during a destroy (close #853) 2022-03-16 17:18:14 +01:00
Michele Caini
f6153f17ad storage: return a meaningful msg when the type is not movable 2022-03-16 17:17:38 +01:00
Michele Caini
c17ecbc78c resource: add operator==/!= to resource handles 2022-03-16 17:17:35 +01:00
Michele Caini
0feb9f0c56 resource_handle: update member types 2022-03-16 17:17:33 +01:00
Michele Caini
e69cb3d950 memory: suppress warnings due to unreferenced parameters 2022-03-16 17:17:30 +01:00
Michele Caini
e7c03dd512 memory: minor changes 2022-03-16 17:17:26 +01:00
Michele Caini
0c1371ffae test: suppress warning due to unused variables 2022-03-16 17:16:54 +01:00
Michele Caini
8a350acf39 dispatcher: allocator support 2022-03-16 17:16:50 +01:00
Michele Caini
594b3b5531 component: use ignore_as_empty_v to simplify the code here and there 2022-03-16 17:16:36 +01:00
Michele Caini
e13d06fe20 component: reintroduce utility ignore_as_empty_v (it's actually useful sometimes) 2022-03-16 17:16:32 +01:00
Michele Caini
805bb84c8f component_traits:
* removed ::ignore_if_empty member
* use ::page_size 0 for empty types
2022-03-16 17:16:21 +01:00
Michele Caini
1a62338349 storage: split sigh_storage_mixin 2022-03-16 17:15:49 +01:00
Michele Caini
4795ba83ba config: remove module-specific mismatch checks (we cannot protect everyone from themselves) 2022-03-16 17:15:41 +01:00
Michele Caini
ad5b362bca config: use pragma detect_mismatch if possible 2022-03-16 17:15:28 +01:00
Michele Caini
8a735452de config: detect version mismatch 2022-03-16 17:15:25 +01:00
Michele Caini
03d0f3e9ca test: merge no-eto tests with registry tests 2022-03-16 17:15:18 +01:00
Michele Caini
ff0983cc42 storage: more about uses-allocator construction 2022-03-16 17:15:00 +01:00
Michele Caini
159a413c7d meta: avoid creating internal namespace that messes with friend declarations 2022-03-16 17:14:37 +01:00
Michele Caini
6a753d1316 meta: remove unused data member 2022-03-16 17:14:34 +01:00
Michele Caini
c89bf30e05 meta: dereferencing a pointer-like object which converts to bool works in all cases (false implies empty meta_any) 2022-03-16 17:14:32 +01:00
Michele Caini
ba4b5ef9f2 meta: minor changes 2022-03-16 17:14:30 +01:00
Michele Caini
5b39ee221f meta: reduce the number of instantiations due to basic_meta_sequence_container_traits 2022-03-16 17:14:28 +01:00
Michele Caini
65c4432325 meta: use adl lookup within basic_meta_sequence_container_traits 2022-03-16 17:14:26 +01:00
Michele Caini
32bbb27c9f meta: further reduce the number of instantiations due to meta sequence containers 2022-03-16 17:14:24 +01:00
Michele Caini
ef3c030576 meta: further optimize meta sequence containers' iterators 2022-03-16 17:14:22 +01:00
Michele Caini
554a182229 meta: reduce instantiations due to meta containers (drop get_fn) 2022-03-16 17:14:20 +01:00
Michele Caini
9154d78e58 meta: make the meta associative container's iterator an internal class as all other iterators 2022-03-16 17:14:18 +01:00
Michele Caini
82d933aa3d meta: make the meta sequence container's iterator an internal class as all other iterators 2022-03-16 17:14:16 +01:00
Michele Caini
ef0fe9b13c meta:
* relax meta sequence container requirements (::clear is no longer required)
* reduce instantiations due to meta sequence containers
2022-03-16 17:14:14 +01:00
Michele Caini
f6700b7094 meta: drop meta_sequence_container::meta_iterator::base() 2022-03-16 17:14:12 +01:00
Michele Caini
21f00cf251 meta: make meta_sequence_container::meta_iterator model a bidirectional iterator (and keep staying in the input iterator category) 2022-03-16 17:14:10 +01:00
Michele Caini
900d2bf816 meta: prepare meta_sequence_container::meta_iterator to model a bidirectional iterator 2022-03-16 17:14:07 +01:00
Michele Caini
3846fa3f28 meta: begin/end adl lookup 2022-03-16 17:14:04 +01:00
Michele Caini
52f77d61ba sigh:
* fixed doc
* test review/cleanup
2022-03-16 17:13:16 +01:00
Michele Caini
039341de69 poly: fixed forward file 2022-03-16 17:13:09 +01:00
Michele Caini
5adf332a5f registry: ::sortable -> ::owned for component types (close #847) 2022-03-16 17:13:04 +01:00
Michele Caini
efbb32a498 sparse_set: added the ::bump function 2022-03-16 17:12:56 +01:00
Michele Caini
3156dd1321 sigh: constrain the allocator type 2022-03-16 17:12:45 +01:00
Michele Caini
2fbeeafbb8 dense_set: constrain the allocator type 2022-03-16 17:12:42 +01:00
Michele Caini
db680f3bcd snapshot: try to get around an issue of VS, toolset v141 2022-03-16 17:12:01 +01:00
Michele Caini
4a20fa5275 snapshot: internal changes 2022-03-16 17:11:55 +01:00
Michele Caini
3fb2da7174 group: internal changes 2022-03-16 17:11:51 +01:00
Michele Caini
7f8bebbdd5 view: internal changes 2022-03-16 17:11:47 +01:00
Michele Caini
597fc0265f natvis: dense_set 2022-03-16 17:11:31 +01:00
Michele Caini
d5d5770dac natvis: dispatcher 2022-03-16 17:11:26 +01:00
Michele Caini
772ac64e3b config: add ENTT_VERSION 2022-03-16 17:11:03 +01:00
Michele Caini
92d7337f04 config: macro.h (just in case) 2022-03-16 17:10:55 +01:00
Michele Caini
200425114c test: address some feedback by iwyu 2022-03-16 17:10:40 +01:00
Michele Caini
b92a73a2a2 workflow: minor changes 2022-03-16 17:10:34 +01:00
Michele Caini
e83c77b99f build system: updated workflow(s) 2022-03-16 17:10:23 +01:00
Michele Caini
28aa35160d hashed_string: minor changes 2022-02-27 17:46:03 +01:00
Michele Caini
18c9bfa134 compressed_pair: minor changes 2022-02-27 17:45:56 +01:00
Michele Caini
bdb7763237 sigh: rebind allocator internally 2022-02-27 17:45:50 +01:00
Michele Caini
bc9ee46b8f dispatcher: minor changes 2022-02-27 17:45:45 +01:00
Michele Caini
b92b734db2 doc: fixed typo 2022-02-25 16:38:13 +01:00
Michele Caini
2748a6cd3c dense_set: remove the constraint on the allocator type 2022-02-25 15:15:09 +01:00
Michele Caini
888876185d doc: fixed typo 2022-02-25 15:14:14 +01:00
Michele Caini
03fea6358b view/runtime_view: make iterator_type private 2022-02-25 14:10:15 +01:00
Michele Caini
59b0dc6735 test: minor changes 2022-02-25 14:09:22 +01:00
Michele Caini
35a42b2172 dense_set: cleanup 2022-02-24 18:48:28 +01:00
Michele Caini
daa4056f45 registry/storage/sparse_set: minor changes to get around an issue of gcc7.5 2022-02-24 18:48:06 +01:00
Michele Caini
bb5abe633f dense_set: review constructors/assignment operators 2022-02-24 11:46:42 +01:00
Michele Caini
dc238db355 compressed_pair: correctly manage references with piecewise construction 2022-02-24 11:33:12 +01:00
Michele Caini
4c692c23bb test: refine tracked_memory_resource::do_is_equal 2022-02-24 11:33:04 +01:00
Michele Caini
b093e82db3 memory: uninitialized_construct_using_allocator (waiting for C++20) 2022-02-23 09:41:32 +01:00
Michele Caini
7a1c2108a1 dispatcher: enable fetching the number of pending events (#848)
Co-authored-by: WoLfulus <WoLfulus@users.noreply.github.com>
2022-02-23 08:42:33 +01:00
Michele Caini
4aa2b49649 updated TODO file 2022-02-23 08:41:56 +01:00
Michele Caini
cdaced4df6 memory: uses_allocator_construction_args pairs support 2022-02-23 00:19:55 +01:00
Michele Caini
cdd029853b any:
* strong exception guarantee applies
* a test to avoid future regressions
2022-02-22 15:03:07 +01:00
Michele Caini
507bafdd9c any/meta_any: revert changes to introduce ::hash 2022-02-22 12:15:09 +01:00
Michele Caini
07a36123e5 dense_map/set: get around a nasty issue of gcc 7.5 with deduced return types and friend functions 2022-02-22 12:03:41 +01:00
Michele Caini
a205e7ada9 test: it's not a good idea to use discarded values, the sanitizer may complain :) 2022-02-22 09:55:03 +01:00
Michele Caini
25e9330bb4 memory: avoid ambiguous call with C++20 2022-02-22 09:45:03 +01:00
Michele Caini
b2edfa454c test: minor changes (to please all compilers) 2022-02-22 09:30:46 +01:00
Michele Caini
07767a09d9 doc: fixed typo 2022-02-22 08:40:25 +01:00
Michele Caini
e8d85d9269 resource: updated doc/tests 2022-02-22 08:15:09 +01:00
Michele Caini
5cf0ba079f memory: make_obj_using_allocator utility (waiting for C++20) 2022-02-21 16:31:36 +01:00
Michele Caini
e08b1f82ce memory: uses_allocator_construction_args (waiting for C++20) 2022-02-21 08:07:01 +01:00
Michele Caini
c2196149ee test: minor changes 2022-02-20 23:55:04 +01:00
Michele Caini
65b5ce7880 doc: minor changes 2022-02-20 23:44:33 +01:00
Michele Caini
9c254ec3e0 doc: mark C++20 feature (to help the future me find them) 2022-02-20 09:57:24 +01:00
Michele Caini
f4418e0205 test: increase code coverage 2022-02-19 10:46:18 +01:00
Michele Caini
a314f7b42e test: minor changes (code coverage) 2022-02-19 10:41:35 +01:00
Michele Caini
dc5a72cda5 dense set:
* cleaned up implementation
* uses-allocator construction support
* a few more tests
2022-02-19 09:49:47 +01:00
Michele Caini
380cb41ca9 entity: cleanup 2022-02-18 17:08:29 +01:00
Michele Caini
83f7e633a3 tests: allocate_unique with types that don't support uses-allocator construction 2022-02-18 17:08:07 +01:00
Michele Caini
ab1c920435 tests: storage with types that don't support uses-allocator construction 2022-02-18 17:07:44 +01:00
Michele Caini
7094d658f8 test: minor changes 2022-02-18 13:47:59 +01:00
Michele Caini
11617291e3 container: minor changes 2022-02-18 13:47:14 +01:00
Michele Caini
790aa03834 scheduler: allow attaching tasks from already running processes (close #845) 2022-02-18 11:34:42 +01:00
Michele Caini
bcd16ea5ea entity: adjust noexcept policy 2022-02-17 12:17:07 +01:00
Michele Caini
81c044760b core: adjust noexcept policy 2022-02-17 12:16:46 +01:00
Michele Caini
b0e0e80b69 meta: adjust noexcept policy 2022-02-17 09:33:48 +01:00
Michele Caini
280bb1d84f dense_set: suppress shadow warning 2022-02-17 09:22:57 +01:00
Michele Caini
bb42dceaca natvis: updated container/dense_set snippet 2022-02-17 09:22:55 +01:00
Michele Caini
d0873ed551 dense_map: adjust noexcept policy 2022-02-17 09:03:28 +01:00
Michele Caini
0fbddfc47b dense_set: adjust noexcept policy 2022-02-17 09:03:11 +01:00
Michele Caini
3f1dee2650 dense_set: uses-allocator construction 2022-02-17 09:02:35 +01:00
Michele Caini
a4b8b93e7f dense_set: emplace always consumes its arguments 2022-02-17 09:02:21 +01:00
Michele Caini
02d1417ef4 process/scheduler: update noexcept policy 2022-02-16 17:36:49 +01:00
Michele Caini
50812a908c signal: update noexcept policy 2022-02-16 12:40:33 +01:00
Michele Caini
1a1ed55485 resource_handle: minor changes 2022-02-16 12:33:55 +01:00
Michele Caini
09fa322a0e poly: update noexcept policy 2022-02-16 12:33:38 +01:00
Michele Caini
1a9a423c35 locator: adjust noexcept policy 2022-02-16 12:33:22 +01:00
Michele Caini
7b0449213c doc: minor changes 2022-02-16 12:32:33 +01:00
Michele Caini
c0251f5c29 sparse_set/storage: support entity and component types with custom swap functions 2022-02-16 12:29:45 +01:00
Michele Caini
4509d8a60b snapshot: swap properly internally 2022-02-16 12:29:43 +01:00
Michele Caini
b1738b0902 natvis: updated signal/delegate snippet 2022-02-16 12:28:29 +01:00
Michele Caini
45259462aa any: remove call to ENTT_LAUNDER, no longer required due to do p1971r0 2022-02-16 12:27:52 +01:00
Michele Caini
772a7427a2 doc: another update for the EnTT in Action list 2022-02-16 12:26:29 +01:00
Michele Caini
afa58b81f6 doc: added D&D Dark Alliance to the EnTT in Action list 2022-02-16 12:26:25 +01:00
Michele Caini
6822c0f0e3 test: minor changes 2022-02-13 18:40:14 +01:00
Michele Caini
0a155ecb68 test: uses-allocator construction guaranteed for allocate_unique 2022-02-13 18:33:31 +01:00
Michele Caini
15fff94447 test: uses-allocator construction guaranteed for storage classes 2022-02-13 18:32:58 +01:00
Michele Caini
ff06df2acb test: tracked_memory_resource utility 2022-02-13 18:32:10 +01:00
Michele Caini
df9301d591 bjuild system/tests: check for <version> (old compilers support) 2022-02-13 18:31:26 +01:00
Michele Caini
e51b1bbb25 compressed_pair: avoid noexcept specification which cannot be supported 2022-02-13 18:29:34 +01:00
Chris Ohk
aa275f4b1c doc: fix minor typos (#842) 2022-02-12 15:52:43 +01:00
Michele Caini
ed11bda9fd component: automatic traits deduction 2022-02-11 10:35:43 +01:00
Michele Caini
ed34a3f5d4 locator:
* service_locator -> locator
* API review + allocator support
* updated doc and tests
2022-02-10 11:31:55 +01:00
Michele Caini
a99b8b7984 build system: get prepared for the windows-latest switch on GH 2022-02-10 10:32:06 +01:00
Michele Caini
f750254cc2 allocate_unique: static assert that Type isn't an array type 2022-02-09 12:32:36 +01:00
Michele Caini
21ece182f5 any: let the wrapper decide what it means to be an owner 2022-02-09 09:42:49 +01:00
Michele Caini
6439dca43a memory: suppress a (shadow) warning from gcc 2022-02-08 11:56:52 +01:00
Michele Caini
ca424a4577 type_traits: try to also please clang-cl 2022-02-08 11:50:53 +01:00
Michele Caini
87bc2cb1ab any: try to also please gcc7 2022-02-08 11:20:49 +01:00
Michele Caini
58f22cc3b5 meta_any: ::hash function for hashable types (+ bonus doc review) 2022-02-08 11:04:14 +01:00
Michele Caini
fa8362f000 any: ::hash function for hashable types (close #629) 2022-02-08 11:02:30 +01:00
Michele Caini
1816206dfc type_traits: is_std_hashable[_v] 2022-02-08 09:58:26 +01:00
Michele Caini
fcfa994152 doc: compressed_pair, std::tuple_size and std::tuple_element specializations 2022-02-08 09:57:10 +01:00
Michele Caini
e7262660c2 dispatcher: API review to support named queues 2022-02-07 10:52:04 +01:00
Michele Caini
a1e14d5740 doc: memory (power of two, fast modulus, allocate_unique, ...) 2022-02-05 00:18:57 +01:00
Michele Caini
60c11f3f30 dispatcher: minor changes 2022-02-05 00:18:42 +01:00
Michele Caini
3fe584d366 any: rename local variable to make it easier for me to read too :) 2022-02-05 00:18:18 +01:00
Michele Caini
28e38321ee test: put basic test allocator in its own file 2022-02-05 00:18:03 +01:00
Michele Caini
1faeeeeabc memory: allocation_deleter/allocate_unique 2022-02-05 00:14:31 +01:00
Michele Caini
75a0c672b1 iterable_adaptor: decouple iterator types 2022-02-04 08:54:16 +01:00
Michele Caini
db829b21aa doc: minor changes 2022-02-04 08:53:23 +01:00
Michele Caini
24bdc29c27 input_iterator_pointer: minor changes 2022-02-03 08:12:35 +01:00
Michele Caini
692a78984f doc: updated documentation about context variables 2022-02-03 08:09:52 +01:00
Michele Caini
77a399738c registry: support to named context variables (see emplace_hint) 2022-02-03 08:06:52 +01:00
Michele Caini
5d7358899d registry: cleanup the standalone context class 2022-02-03 07:52:27 +01:00
Michele Caini
aca25b9999 build: updated build workflow 2022-02-02 11:55:08 +01:00
Paolo Monteverde
182c6c534b meta: rearrange member data of meta nodes to optimize memory usage (#833) 2022-02-02 08:15:51 +01:00
Michele Caini
0500f155e6 delegate: ::instance() -> ::data() 2022-02-02 08:02:03 +01:00
Michele Caini
125b826f62 meta/signal: added missing includes 2022-02-01 23:11:25 +01:00
Michele Caini
922bfbf724 test: fixed includes (while trying iwyu) 2022-02-01 23:01:04 +01:00
Michele Caini
f1e584fad9 scheduler: add missing header 2022-02-01 14:12:23 +01:00
Michele Caini
0c4dcdea71 config/core: include cstdint also when ENTT_ID_TYPE is defined 2022-02-01 14:11:41 +01:00
Michele Caini
ec4b383198 build: minor changes to workflows 2022-02-01 11:31:44 +01:00
Michele Caini
3328c7e78b doc: fixed funding.yml 2022-02-01 11:30:23 +01:00
Michele Caini
357fcd44d7 poly: make Member type explicit to avoid errors with older compilers 2022-01-31 14:48:20 +01:00
Michele Caini
5a0a682d35 meta: relaxed checks on conversion functions 2022-01-31 09:55:53 +01:00
Michele Caini
1fcb7e5aba dispatcher: minor changes 2022-01-31 08:44:40 +01:00
Michele Caini
eec867bc9e enum:
* remove unused functions
* avoid errors due to narrowing conversions
2022-01-31 08:44:24 +01:00
Michele Caini
5a956d2914 poly: compile-time checks on embedded vtables + code coverage 2022-01-30 16:53:27 +01:00
Michele Caini
e0e29cc363 test: cleanup 2022-01-30 16:52:51 +01:00
Michele Caini
9fc8e03f72 poly: refined fwd file, updated (typed-)tests 2022-01-29 17:32:15 +01:00
Michele Caini
b054e93f0e build system: removed the in-source build check 2022-01-29 17:31:37 +01:00
Michele Caini
653680a356 doc: minor changes 2022-01-29 17:30:20 +01:00
Michele Caini
40035f2e52 gh: updated funding file 2022-01-29 17:29:05 +01:00
Michele Caini
a70b7daec4 build system: workflow cleanup 2022-01-29 17:25:34 +01:00
Michele Caini
0f3e7b5dac doc: minor changes to suppress warnings 2022-01-29 16:15:40 +01:00
Michele Caini
2fbc08b606 test: a handful of minor changes to please all compilers from the CI and remove some lambdas 2022-01-29 11:31:57 +01:00
Michele Caini
e4897f709e test: merge poly tests in a single file, use typed tests to reduce the boilerplate 2022-01-29 10:51:25 +01:00
Michele Caini
84445aa28b test: use gtest typed test suite for enums 2022-01-29 10:50:33 +01:00
Michele Caini
0795f9a9f2 test: separate death tests as suggested in the gtest doc 2022-01-29 10:47:22 +01:00
Michele Caini
44ae0d67f6 sparse_set: set the free list to tombstone rather than null within ::clear 2022-01-28 15:53:36 +01:00
Michele Caini
a74f0ca370 test: code coverage 2022-01-28 14:06:22 +01:00
Michele Caini
266c1fcdcb registry: better/faster remove 2022-01-28 14:06:22 +01:00
Michele Caini
977c3407e7 registry: better/faster erase 2022-01-28 14:06:22 +01:00
Michele Caini
99c2299ed4 test: added missing typename keyword 2022-01-28 14:06:22 +01:00
Michele Caini
39a08f17da sparse_set: clear no longer creates tombstones 2022-01-28 14:06:22 +01:00
Michele Caini
57de1187fc sparse_set/storage:
* reintroduce the swap_and_pop vs in_place_pop split for perf reasons
* rollback to a model with two iterators on virtual functions
* even faster range-erase/clear
2022-01-28 14:06:22 +01:00
Michele Caini
2bba6d4c0d test: code coverage 2022-01-28 14:06:22 +01:00
Michele Caini
2f03b225f7 sparse_set: remove the cursed ::slot function (finally) 2022-01-28 14:06:22 +01:00
Michele Caini
533bc1ce94 sparse_set: review ::clear 2022-01-28 14:06:22 +01:00
Michele Caini
add9df6058 doc: updated todo list 2022-01-28 14:06:21 +01:00
Michele Caini
d3496f3ff5 sparse_set: apparently using an invalid iterator isn't a good idea :) 2022-01-28 14:06:21 +01:00
Michele Caini
e97f7ee725 doc: minor changes 2022-01-28 14:06:21 +01:00
Michele Caini
37497295fb sparse_set/storage:
* refine the try_erase to make it accept an iterator and a count (easier to work with at the call site)
* added a test for the stable clear
* added a note for future improvements
2022-01-28 14:06:21 +01:00
Michele Caini
2e89b3b5b9 sparse_set/storage: make try_erase accept a range of entities 2022-01-28 14:06:21 +01:00
Michele Caini
518bbc651e sparse_set/storage:
* try_emplace returns a basic iterator
* try_erase accepts a basic iterator (prepare for an even faster remove/erase/clear/whatever)
2022-01-28 14:06:21 +01:00
Michele Caini
ffd1d97e03 natvis: updated container.natvis to use the right names 2022-01-28 14:05:41 +01:00
Michele Caini
e503d431f1 doc: minor changes 2022-01-28 14:00:54 +01:00
Michele Caini
49a066714a test: minor changes 2022-01-28 14:00:33 +01:00
Michele Caini
7eec610d21 container:
* dense_hash_map -> dense_map
* dense_hash_set -> dense_set
2022-01-28 13:35:16 +01:00
Michele Caini
530507c36b meta: some compile-time checks to avoid subtle runtime errors 2022-01-27 22:49:35 +01:00
Michele Caini
4d2584d8fc doc: added home hearth to the links page 2022-01-25 18:09:37 +01:00
Michele Caini
0f5057920e doc: added gaia-ecs to the list of similar projects 2022-01-25 18:00:34 +01:00
Michele Caini
a98000cf3b test: I am the winner of the best error message ever but I remain humble :) 2022-01-25 14:56:00 +01:00
Michele Caini
0cec8de164 dense_hash_map/set: suppress warnings due to integral conversions 2022-01-25 09:09:37 +01:00
Michele Caini
1caa8d923d sparse_set/storage: iterator's ::index returns the right offset 2022-01-23 13:05:45 +01:00
Michele Caini
4fa51f8c52 type_traits: removed is_iterator_type[_v], it may be ambiguous and cause errors 2022-01-22 16:59:17 +01:00
Michele Caini
a8be765aa8 registry: disable optimizations based on is_iterator_type, free pools allow to implement them on the user side and more efficiently when needed 2022-01-22 16:52:45 +01:00
Michele Caini
d2d35d490a doc: updated entity.md 2022-01-22 16:45:05 +01:00
Michele Caini
26ba137424 registry: template ::storage support to const qualified types 2022-01-21 19:00:49 +01:00
Michele Caini
e6006663ec registry: remove [[nodiscard]] from the template ::storage method 2022-01-21 11:08:07 +01:00
Michele Caini
74eb2772b6 natvis: support standalone registry context 2022-01-20 15:58:32 +01:00
Michele Caini
3878a696af registry: standalone context type (close #575) 2022-01-20 09:37:31 +01:00
Michele Caini
49ac34c405 doc: avoid confusion with context variables 2022-01-19 16:02:04 +01:00
Michele Caini
a7d4a980ba *: more consistent policy for final classes 2022-01-19 12:41:50 +01:00
Michele Caini
2196db562e registry:: non-template storage(id) returns an iterator rather than a naked pointer 2022-01-19 12:15:22 +01:00
Michele Caini
2ad3e0ed4c hashed_string: updated natvis representation 2022-01-19 09:30:35 +01:00
Michele Caini
6e03fe77ae doc: updated copyright 2022-01-19 07:43:58 +01:00
Michele Caini
af8ec7b5dd registry: remove/erase dispatch based on the iterator type 2022-01-18 08:30:43 +01:00
Michele Caini
7fa29e7341 registry: strict check on storage types to prevent subtle bugs in case of errors 2022-01-18 07:45:03 +01:00
Michele Caini
9c6950d163 hashed_string: I managed to crash clang but I remain humble and try to help it :) 2022-01-17 14:02:02 +01:00
Michele Caini
5d4cbfe8e7 hashed_string: make it work with vs toolset v141 2022-01-17 13:49:11 +01:00
Michele Caini
4747422110 *: added a bunch of final here and there 2022-01-17 13:43:55 +01:00
Michele Caini
7d1416ac74 hashed_string: store size information (close #824) 2022-01-17 08:25:56 +01:00
Michele Caini
2efaa7af7b *: allocator aware classes fail to compile if allocator's value type and container's value type differ 2022-01-15 16:48:33 +01:00
Michele Caini
68e259870f sparse_set/storage:
* support ::erase suppression
* support ::emplace with invalid entities when suppressed
* support ::erase/::remove with invalid entities when suppressed
2022-01-15 00:48:07 +01:00
Michele Caini
29c2c94784 doc: suppress warnings from doc generation 2022-01-15 00:45:35 +01:00
Michele Caini
3f2e1d078f sparse_set/storage: replace the whole try_insert machinery with a force_back parameter to try_emplace 2022-01-15 00:44:47 +01:00
Michele Caini
0a259da05a sparse_set: opaque insert always appends elements 2022-01-14 14:06:41 +01:00
Michele Caini
1b22fe6de2 storage: insert for non-empty types always appends elements 2022-01-13 15:01:06 +01:00
Michele Caini
9b700c3bd2 natvis: removed count filter from sparse set, it makes it harder to debug internal issues 2022-01-13 15:00:34 +01:00
Michele Caini
3b72e30c36 sparse_set: use the right iterator in the catch branch of insert 2022-01-13 14:44:27 +01:00
Michele Caini
9eba103de9 storage: insert for empty types always appends elements 2022-01-13 14:05:33 +01:00
Michele Caini
ecadee3876 sparse_set: added basic virtual try_insert (insert does not use it yet) 2022-01-13 13:47:22 +01:00
Michele Caini
699f9105ae storage: arguments are always ignored for empty types 2022-01-13 13:41:32 +01:00
Michele Caini
8a19e8dafe storage: support chained constructors i.e. parent-to-child propagation 2022-01-13 12:00:15 +01:00
Michele Caini
588c056205 sparse_set/storage: minor changes, more tests 2022-01-11 22:56:22 +01:00
Michele Caini
866a7fb641 natvis: added info about the sparse array of a sparse set 2022-01-11 18:18:41 +01:00
Michele Caini
a5fe42268f test: cleanup 2022-01-11 08:15:09 +01:00
Michele Caini
0bd6816bdd sparse_set: pop_at(pos) -> try_erase(entt) (prepare for inhibited deletions) 2022-01-11 08:06:20 +01:00
Michele Caini
1c6670c7a1 sparse_set: merge ::swap_and_pop and ::in_place_pop 2022-01-10 22:55:46 +01:00
Michele Caini
699a0eb934 sigh_storage_mixin:
* always check the owner in debug
* fixed a test that doesn't invoke bind
2022-01-10 17:22:34 +01:00
Michele Caini
72440ab937 sigh_storage_mixin: always trigger an emplace request notification (in sync with unbalanced destroy) 2022-01-10 00:23:18 +01:00
Michele Caini
9ad807bd58 storage: doc cleanup 2022-01-10 00:16:10 +01:00
Michele Caini
9fb1970ac0 sparse_set/storage: rollback ::policy(pol), incompatible with views 2022-01-09 16:15:47 +01:00
Michele Caini
48dfac2588 storage:
* also support (in theory) fancy pointers
* removed the internal ::construct function
2022-01-09 15:06:41 +01:00
Michele Caini
c6d3714e6f sparse_set: also support (in theory) fancy pointers 2022-01-09 15:06:17 +01:00
Michele Caini
1345c257c3 storage: make final virtual functions private (moving towards private-only hooks) 2022-01-09 14:42:56 +01:00
Michele Caini
19112a8f27 storage: cleanup 2022-01-09 14:25:41 +01:00
Michele Caini
e0979fcf3f sparse_set/storage: make empty virtual functions private (moving towards private-only hooks) 2022-01-08 16:14:54 +01:00
Michele Caini
d514ecbd5e doc: updated documentation about deletion policies 2022-01-08 14:36:04 +01:00
Michele Caini
a53dbb6950 registry: deletion policy is checked at runtime for groups since we can change it dynamically now 2022-01-08 14:34:21 +01:00
Michele Caini
a03b88e0eb dense_hash_[map/set]: suppress super annoying warnings 2022-01-07 14:07:35 +01:00
Michele Caini
4472eb1c40 updated TODO file 2022-01-06 00:23:58 +01:00
Michele Caini
3443987127 meta: meta_type::invoke also searches into the base types for a candidate function 2022-01-04 14:55:30 +01:00
Michele Caini
c73a79fd42 view: cleanup 2022-01-04 13:53:00 +01:00
Michele Caini
dedbe3d431 storage: make shrink_to_size check for tombstones only when needed 2022-01-03 21:30:53 +01:00
Michele Caini
87ef713a17 dense_hash_[map/set]: cleanup 2022-01-03 21:17:37 +01:00
Michele Caini
4ba8ac4fa1 sparse_set: opaque emplace/insert return (eventually invalid) iterators to the inserted elements 2022-01-02 17:21:21 +01:00
Michele Caini
e74e0531bd sparse_set/storage: minor changes 2022-01-02 16:51:08 +01:00
Michele Caini
b67cb4bd1f sparse_set/storage: minor changes to make it easier to read for the future me :) 2022-01-01 17:09:34 +01:00
Michele Caini
22628c9ac4 sparse_set/storage: prepare them for a faster and opaque ::insert 2021-12-31 17:23:50 +01:00
Michele Caini
4dd0862fad runtime_view:
* added ::iterate and ::exclude member functions to attach pools at runtime
* removed vector-based constructor
* removed basic_registry<...>::runtime_view member function
* updated benchmarks accordingly (also reviewed them as a whole just because)
2021-12-30 14:41:05 +01:00
Michele Caini
0ec57fbb8d sparse_set: removed redundant check 2021-12-30 14:38:51 +01:00
Michele Caini
19ab584d32 doc: updated FAQs 2021-12-29 16:34:26 +01:00
Michele Caini
97fe978d14 runtime_view: removed redundant validity check 2021-12-29 00:25:11 +01:00
Michele Caini
2a74c7d897 runtime_view: support to all types of sparse set 2021-12-28 09:28:41 +01:00
Michele Caini
cdf9c2fc6f registry: ctx internal review 2021-12-27 16:18:17 +01:00
Michele Caini
235f84a0d4 registry: minor changes 2021-12-27 15:58:09 +01:00
Michele Caini
ccaa490a2f registry: removed unnecessary dispatching from non-const try_ctx 2021-12-27 15:54:49 +01:00
Michele Caini
8eaf11b510 sparse_set: added the possibility to change the deletion policy at runtime 2021-12-27 15:08:42 +01:00
Michele Caini
57a5faa448 sparse_set: updated doc 2021-12-26 23:31:36 +01:00
Michele Caini
be87191d0a sparse_set:
* opaque emplace returns bool
* opaque insert returnes the number of entities actually emplaced
2021-12-26 17:04:10 +01:00
Michele Caini
40aabe76c7 sparse_set: fixed a throwing case that could leave the set in an inconsistent state 2021-12-26 16:36:30 +01:00
Michele Caini
9d304cb35c doc: fixed typo about in_place_delete 2021-12-23 16:31:07 +01:00
Michele Caini
ee08a4966f doc: updated list of links, added D2R to it too 2021-12-23 14:35:34 +01:00
Michele Caini
aeca69903e doc: updated similar projects 2021-12-22 17:53:45 +01:00
Michele Caini
e1b3f2b95a registry: added weak ::storage for opaque cross registry operations 2021-12-22 16:21:55 +01:00
Michele Caini
25c7436652 doc: minor changes 2021-12-21 14:10:33 +01:00
Michele Caini
771c449621 bump the version number 2021-12-21 13:27:04 +01:00
Michele Caini
61ecaa5756 storage: review to avoid code duplication 2021-12-21 08:33:46 +01:00
Michele Caini
c05dddc56e runtime_view: slightly optimized iterators 2021-12-21 08:33:46 +01:00
Michele Caini
8e0747fd50 updated single include file 2021-12-21 08:32:48 +01:00
Michele Caini
1bcc87c916 runtime_view: removed an useless check, slightly improved performance 2021-12-19 10:25:55 +01:00
Michele Caini
45797907d3 doc: updated toc in the README file 2021-12-18 17:51:02 +01:00
Michele Caini
3498dea486 registry (close #521):
* remove visit
* added storage_proxy_iterator (input iterator category, modeled as a random access iterator)
* added ::storage() (const and non-const) to return an iterable object over all pools
2021-12-17 17:05:14 +01:00
Michele Caini
a4aab8458b group/storage/view: minor changes 2021-12-17 16:57:29 +01:00
Michele Caini
534744d615 iterator: minor changes 2021-12-17 16:57:26 +01:00
Michele Caini
dc5450b95e view: natvis friendly representation 2021-12-17 16:57:24 +01:00
Michele Caini
9e0e276740 dense_hash_set/map: removed unnecessary type members 2021-12-17 16:57:22 +01:00
Michele Caini
1c9c02f3ce view: avoid exposing internal details from iterators 2021-12-17 16:57:20 +01:00
Michele Caini
7774f9f402 storage: review + avoid exposing internal details from iterators 2021-12-17 16:57:18 +01:00
Michele Caini
5607219945 sparse_set: minor changes 2021-12-17 16:57:14 +01:00
Michele Caini
69ef3efd2d dense_hash_map/set: avoid exposing internal details from iterators 2021-12-17 16:57:10 +01:00
Michele Caini
36c118922b group/view:
* added default constructors to input iterators
* make sure that a default constructed iterator compares equal to end()
2021-12-17 08:39:54 +01:00
Michele Caini
1d014953e3 storage: try to also please gcc-9 2021-12-16 14:33:24 +01:00
Michele Caini
c5cb1d9bcc storage/view/group:
* added extended_storage_iterator and storage<T...>::each function to return an extended iterable object
* use extended_storage_iterator and iterable_adaptor everywhere to avoid code duplication
2021-12-16 12:31:44 +01:00
Michele Caini
86d12cf317 doc: brief -> copydoc 2021-12-16 12:30:29 +01:00
Michele Caini
db9e59ede1 core: added iterable_adaptor 2021-12-16 12:29:20 +01:00
Michele Caini
cc9b1b0a05 sparse_set: added const_iterator/const_reverse_iterator and cbegin/cend/crbegin/crend 2021-12-16 08:52:13 +01:00
Michele Caini
5c29de2013 updated TODO 2021-12-15 13:13:16 +01:00
Michele Caini
559db104f8 view: suppress some warnings due to initialization order 2021-12-15 10:24:08 +01:00
Michele Caini
365c57be4c build system: allow compiling the tests with clang-cl (thanks gtest for failing otherwise) 2021-12-15 10:23:34 +01:00
Michele Caini
6f0c84fc68 dense_hash_map/dense_hash_set: remove [[nodiscard]] from friend function declaration 2021-12-15 09:33:23 +01:00
Michele Caini
75782d9e99 registry: review/cleanup 2021-12-14 23:15:57 +01:00
Michele Caini
69ddf4936e registry: remove the visit overload that also takes an entity 2021-12-14 22:57:10 +01:00
Michele Caini
4b3108283f dense_hash_map/dense_hash_set: avoid pointer overflow for end iterators (close #804) 2021-12-14 16:44:34 +01:00
Michele Caini
12d177a6c8 registry: removed ::orphans, use ::each/::orphan instead (no way to further optimize it) 2021-12-13 10:00:32 +01:00
Michele Caini
54717dc7e2 test: minor changes 2021-12-12 17:45:13 +01:00
Michele Caini
631c909abd group: drop conceptually broken reverse iterators 2021-12-12 17:45:06 +01:00
Michele Caini
208746f28f view: get rid of the internal::storage_tuple function, back to the uniform model 2021-12-12 17:05:37 +01:00
Michele Caini
61c5f8ae22 registry/view/group: cleanup 2021-12-12 16:51:48 +01:00
Michele Caini
4e0cca3a08 view: clean up 2021-12-12 01:18:41 +01:00
Michele Caini
b86ffc2370 view: drop conceptually broken reverse iterators 2021-12-12 01:11:20 +01:00
Michele Caini
33574c9885 doc: updated README file 2021-12-10 10:24:08 +01:00
Michele Caini
672c542f99 group: removed ::data, use ::handle<T>().data() and ::storage<T>().data() instead 2021-12-10 09:30:16 +01:00
Michele Caini
f741fe48a1 group: added ::handle to non-owning groups 2021-12-10 08:25:46 +01:00
Michele Caini
4347ebcee2 group: added base_type type member 2021-12-09 17:50:51 +01:00
Michele Caini
4e3e5a5361 doc: updated doc for the ecs module 2021-12-09 17:49:30 +01:00
Michele Caini
8e6439c3d1 group: removed ::raw, use ::storage().raw() instead 2021-12-09 17:49:18 +01:00
Michele Caini
b54d358feb view: removed ::data for single type views, use ::storage().data() instead 2021-12-09 09:37:17 +01:00
Michele Caini
78baa7b12d view: removed ::raw for single type views, use ::storage().raw() instead 2021-12-09 09:21:37 +01:00
Michele Caini
19f1dd22e9 view: minor changes to avoid confusion 2021-12-09 09:12:08 +01:00
Michele Caini
73f583f68a registry: doc cleanup 2021-12-09 09:05:14 +01:00
Michele Caini
52de43e329 registry: removed ::empty<T>(), use ::view<T>().empty() or ::storage<T>().empty() instead 2021-12-09 08:20:34 +01:00
Michele Caini
49ddacaac0 registry: removed ::size<T>(), use ::view<T>().size() or ::storage<T>().size() instead 2021-12-08 15:17:35 +01:00
Michele Caini
4533b3a1c0 registry: minor chages (doc/internals) 2021-12-05 17:01:40 +01:00
Michele Caini
f30ea02794 registry:
* removed registry::reserve<Component> (use registry.storage<Component>.reserve(v) instead)
* removed registry::capacity<Component> (use registry.storage<Component>.capacity() instead)
* removed registry::shrink_to_fit<Component> (use registry.storage<Component>.shrink_to_fit() instead)
2021-12-05 16:20:14 +01:00
Michele Caini
7112dda299 build_system: added all headers to the EnTT target 2021-12-05 16:01:32 +01:00
Michele Caini
02636a7108 view: minor changes 2021-12-05 15:55:40 +01:00
Michele Caini
36a9456e0d test: minor changes to make all major compilers agree at least on something :) 2021-12-04 15:32:38 +01:00
Michele Caini
113893dcea meta: added meta_any::owner 2021-12-04 15:28:52 +01:00
Michele Caini
1abbe9c7b2 test: minor changes (code coverage) 2021-12-04 15:21:49 +01:00
Michele Caini
7205ac791c meta: make policies check function return types 2021-12-03 18:25:11 +01:00
Michele Caini
3335b81ad4 meta:
* dtor support to member functions
* review to introduce strict checks on policies
2021-12-03 17:56:09 +01:00
Michele Caini
d9c9438f3c test: minor changes 2021-12-03 14:39:50 +01:00
Michele Caini
37250843e1 sigh: allocator support 2021-12-02 11:30:54 +01:00
Michele Caini
072ab88721 build system: avoid using windows-2016 (deprecated by GH) 2021-12-01 10:33:43 +01:00
Michele Caini
c208e6b107 natvis: avoid errors due to null/tombstone definition not available in some contexts 2021-11-30 14:44:12 +01:00
Michele Caini
65f3185b39 entity: add page_size to entt_traits to make it more natvis friendly and customizable on a per-type basis 2021-11-30 13:48:12 +01:00
Michele Caini
34f73f8a2b registry: added base_type member type 2021-11-30 11:23:15 +01:00
Michele Caini
cb4142a97d entity: simplified to_version 2021-11-30 09:46:15 +01:00
Michele Caini
80d08d5a34 view:
* fixed init list order to suppress a warning
* try to please gcc that fails to compile valid code
2021-11-30 09:46:14 +01:00
Michele Caini
052aecf533 natvis: basic_view (single and multi type) 2021-11-30 09:46:14 +01:00
Michele Caini
7bde714f18 view: minor changes 2021-11-30 09:46:14 +01:00
Michele Caini
5ff429f656 view:
* added single type view overloads for ::storage to make it compatible with the multi-type view version
* removed the useless tuple from single type views
* updated operator|
2021-11-30 09:46:14 +01:00
Michele Caini
92f27299e1 group: added the ::storage method (with tests) 2021-11-30 09:46:14 +01:00
Michele Caini
f9013375f8 view: added the ::storage method (with tests) 2021-11-30 09:46:14 +01:00
Michele Caini
f18b0045ba registry: get around an issue of the toolset v141 2021-11-30 09:46:14 +01:00
Michele Caini
18173cc4c0 view: minor changes 2021-11-30 09:46:12 +01:00
Michele Caini
6a1ffdd7a5 natvis: bypass some natvis idiosyncrasies to display storage information and content in all cases 2021-11-30 09:45:37 +01:00
Michele Caini
8db78b2190 nativs:
* don't abuse Intrinsic, it also has drawbacks
* refine natvis snippet for dispatcher class
2021-11-30 09:45:37 +01:00
Michele Caini
3fcccae241 component traits:
* added page_size to tune the page size on a per-type basis
* turned in_place_delete into a boolean value
* turned ignore_if_empty into a boolean value
* removed in_place_delete_v (no longer required)
2021-11-30 09:45:37 +01:00
Michele Caini
6ff8add164 entity: added missing includes 2021-11-30 09:45:37 +01:00
Michele Caini
58a8c3c82a natvis: make the ecs stuff work with entity classes 2021-11-30 09:45:37 +01:00
Michele Caini
92c5c656c7 natvis: dispatcher 2021-11-30 09:45:37 +01:00
Michele Caini
61856cd648 natvis: poly 2021-11-30 09:45:37 +01:00
Michele Caini
687f82b5c2 natvis: basic_registry 2021-11-30 09:45:37 +01:00
Michele Caini
920c864bed natvis: tombstone/null 2021-11-30 09:45:37 +01:00
Michele Caini
1360c985b7 natvis: when you discover the intrinsic tag... :) 2021-11-30 09:45:37 +01:00
Michele Caini
b6f953e13b natvis: added simple views + minor changes 2021-11-30 09:45:37 +01:00
Michele Caini
796b96071b registry: minor changes 2021-11-30 09:45:37 +01:00
Michele Caini
ec56ccc690 view: minor changes 2021-11-30 09:45:35 +01:00
Michele Caini
95931b67e1 registry: turn storage_type in a public member type 2021-11-30 09:43:48 +01:00
Michele Caini
706bbb83c9 storage: clean up 2021-11-30 09:43:48 +01:00
Michele Caini
43099f47d0 meta: try to suppress a wrong warning from clang 2021-11-30 09:43:48 +01:00
Michele Caini
eca86999c3 doc: added a section about runtime stuff 2021-11-30 09:43:48 +01:00
Michele Caini
a00b44a5fd example: stamp is back 2021-11-30 09:43:48 +01:00
Michele Caini
5b8dcff2a4 helper: make to_entity also work for stable types (close #768) 2021-11-30 09:43:48 +01:00
Michele Caini
e4d36e4982 registry: decouple ::storage/::assure 2021-11-30 09:43:48 +01:00
Michele Caini
adfdbf138d registry:
* public and runtime pools
* removed basic_registry<...>::prepare
2021-11-30 09:43:48 +01:00
Michele Caini
8d1ba26492 registry: try to also please vs2015 ¯\_(ツ)_/¯ 2021-11-30 09:43:48 +01:00
Michele Caini
792625f763 registry: minor changes/cleanup 2021-11-30 09:43:47 +01:00
Michele Caini
5e8eae4ef8 natvis: delegate, sigh, sink, connection, scoped_connection 2021-11-30 09:43:47 +01:00
Michele Caini
148212da96 natvis: minor changes 2021-11-30 09:43:47 +01:00
Michele Caini
23ddb12529 registry: removed pdata container, pools are standalone objects and thus ready to go public 2021-11-30 09:43:47 +01:00
Michele Caini
186a5e8f7b natvis: updated definition for sparse set and storage 2021-11-30 09:43:47 +01:00
Michele Caini
dfb5fe1e04 sparse_set/storage: opaque get from base 2021-11-30 09:43:47 +01:00
Michele Caini
640f75373e doc: minor changes 2021-11-30 09:43:47 +01:00
Michele Caini
46e5d96ced sparse_set/storage: value type from base, if any 2021-11-30 09:43:47 +01:00
Michele Caini
ad3fbe2052 sparse_set/storage: no virtual swap (the silliest thing I've ever done) 2021-11-30 09:43:47 +01:00
Michele Caini
cd6c60fd46 sparse_set/storage: emplace-from-below with value 2021-11-30 09:43:47 +01:00
Michele Caini
f7be386d5a sparse_set: rename context to bind 2021-11-30 09:43:47 +01:00
Michele Caini
89b2131052 doc:
* const thread safe registry
* detached views
2021-11-30 09:43:47 +01:00
Michele Caini
e5d1ca2e3c registry:
* const thread safe implementation
* views created from const registires can refer placeholder pools (that is, they don't refresh)
2021-11-30 09:43:47 +01:00
Michele Caini
32b8224afd runtime_view: simplify the construction process 2021-11-30 09:43:47 +01:00
Michele Caini
00e7ab1db8 entity: fully remove the poly storage support 2021-11-30 09:43:47 +01:00
Michele Caini
e376a53b09 entity:
* custom move ctor/op for the registry class
* no registry argument from storage emplace, remove, ...
* added sparse set context to forward variables to mixins
* removed sparse set user data
2021-11-30 09:43:47 +01:00
Michele Caini
17a9cd5666 cache: use identity as hashing function 2021-11-30 09:43:47 +01:00
Michele Caini
fc3de662c3 registry: combine dense hash maps and identity when the key is a hashed string already 2021-11-30 09:43:47 +01:00
Michele Caini
8de568c2fc *: combine dense hash map and identity when the key is a hashed string already 2021-11-30 09:43:47 +01:00
Michele Caini
55661883d9 registry: use a dense hash map for context variables 2021-11-30 09:43:47 +01:00
Michele Caini
b59d3ebeaa *: dense_hash_map being a drop-in replacement for unordered_map has its advantages :) 2021-11-30 09:43:47 +01:00
Michele Caini
2866dcdd03 doc: updated documentation about making EnTT work smoothly across boundaries 2021-11-30 09:43:47 +01:00
Michele Caini
b147f9d58c registry: make it run smoothly across boundaries (close #719, close #729) 2021-11-30 09:43:47 +01:00
Michele Caini
8e2a6470ac emitter: make it run smoothly across boundaries 2021-11-30 09:43:47 +01:00
Michele Caini
19230f7672 dispatcher: make it run smoothly across boundaries 2021-11-30 09:43:47 +01:00
Michele Caini
764dfbe46a doc: minor changes 2021-11-30 09:43:47 +01:00
Michele Caini
0ec7631310 view: make all unoptimized single type views check for tombstones 2021-11-30 08:58:51 +01:00
Michele Caini
0b39ece990 meta: fix an issue with meta_factory<T>::prop when using tuple of properties 2021-11-18 15:44:12 +01:00
Michele Caini
97500dff57 meta: fixed an issue with meta_factory::conv when used with member functions + code coverage 2021-11-18 14:53:17 +01:00
Michele Caini
dc6299a029 doc: fixed typos 2021-11-18 09:00:52 +01:00
Michele Caini
58b1e2fb98 hashed string: warnings to clarify that it does not take ownerships of the string or copy it (close #798) 2021-11-16 18:05:17 +01:00
Michele Caini
0302fdaaba build system: update gtest upstream branch name 2021-11-16 09:33:32 +01:00
Michele Caini
09b260e0af view: use explicit types to also please clang 6.x 2021-11-15 14:43:20 +01:00
Michele Caini
24b7e998b2 registry: minor changes 2021-11-13 18:30:35 +01:00
Michele Caini
19068ea5c7 doc: updated links 2021-11-13 12:19:51 +01:00
Michele Caini
b5cd3eb468 natvis: hashed_string + minor changes 2021-11-11 14:40:20 +01:00
Michele Caini
91a2b5a5f7 natvis: expand meta_any as a meta_type_node 2021-11-11 11:16:51 +01:00
Michele Caini
0096f6d962 natvis: meta 2021-11-11 10:04:45 +01:00
Michele Caini
87cfe41b15 natvis: minor changes 2021-11-11 10:04:21 +01:00
Michele Caini
c490086097 license: just make it clear that this is about EnTT :) 2021-11-10 11:02:32 +01:00
Michele Caini
5b3fd7537e meta: avoid a (maybe) UB in meta_any 2021-11-08 15:16:22 +01:00
Michele Caini
2cab2583c0 any: minor changes 2021-11-08 13:41:16 +01:00
Michele Caini
a40a3b594d storage: make try_emplace forward user data to base classes 2021-11-06 23:35:25 +01:00
Michele Caini
0fa586a3d8 type_id: reduce the number of static variables due to type_id 2021-11-06 23:31:57 +01:00
Michele Caini
25977cc569 test: use deduction guides :) 2021-11-05 14:17:33 +01:00
Michele Caini
0be7064493 view:
* support for multiple components of the same type
* removed view.each<T>(F), use view.use<T>().each(F) instead
* removed view.each<T>(), use view.use<T>().each() instead
* added use<integral value>() to set leading pool by index
* added get<integral value...>() to get components by index
2021-11-05 11:02:15 +01:00
Michele Caini
0b13dc254a registry: minor changes 2021-11-05 10:58:30 +01:00
Michele Caini
c1e953ab6e registry: removed redundant compile-time check 2021-11-04 18:45:55 +01:00
Michele Caini
a069764af1 type traits:
* unpack_as_t -> unpack_as_type
* unpack_as_v -> unpack_as_value
2021-11-04 14:17:21 +01:00
Michele Caini
3dbe6c3902 meta: cleanup/review 2021-11-04 12:37:20 +01:00
Michele Caini
9f0b380024 container:
* review dense_hash_map_iterator::operator[]
* review dense_hash_set_iterator::operator[]
* code coverage
2021-11-02 09:29:10 +01:00
Michele Caini
193b102013 entity:
* review sparse_set_iterator::operator[]
* review storage_iterator::operator[]
* code coverage
2021-11-02 09:19:57 +01:00
Michele Caini
511fde91fa natvis: snippet for dense_hash_set 2021-10-30 13:49:11 +02:00
Michele Caini
16e3cfc589 container: added dense_hash_set 2021-10-30 13:45:02 +02:00
Michele Caini
ed2012a36c dense_hash_map: minor changes + test coverage 2021-10-30 13:44:00 +02:00
Michele Caini
46598fde32 dense_hash_map: less risky but still on the edge :) 2021-10-29 12:32:09 +02:00
Michele Caini
94d0c5fa5c compressed_pair: added first_type/second_type member types 2021-10-28 12:18:59 +02:00
Michele Caini
4e72291588 meta: define meta_associative_container_traits for dense_hash_map 2021-10-28 08:22:20 +02:00
Michele Caini
74cdf000b4 doc: fixed a couple of typos 2021-10-26 15:44:05 +02:00
Michele Caini
565dc3327b natvis: snippet for meta_any 2021-10-26 15:01:04 +02:00
Michele Caini
462af21793 entity: avoid shadow warnings 2021-10-26 14:19:55 +02:00
Michele Caini
94af659c86 natvis:
* minor changes to entity.natvis
* snippet for dense_hash_map
2021-10-26 11:04:19 +02:00
Michele Caini
b63c9fc67b dense_hash_map: always guarantee that load factor is less than max load factor 2021-10-26 11:04:19 +02:00
Michele Caini
f519b55a47 natvis: compressed_pair 2021-10-26 11:04:19 +02:00
Michele Caini
2fb039b429 entity: minor changes 2021-10-26 11:04:19 +02:00
Michele Caini
4bce5aed77 natvis: snippets for type_info and basic_any 2021-10-26 11:04:19 +02:00
Michele Caini
eca6032306 entity:
* small changes to the entt_traits class
* natvis snippets for basic_sparse_set and basic_storage (with tombstone detection)
2021-10-26 11:04:19 +02:00
Michele Caini
dd2f515af1 build_system: avoid using INTERNAL for cmake variables since it also implies STRING 2021-10-26 11:04:19 +02:00
Michele Caini
df25482643 build_system: support for natvis files (optionally added to the target) 2021-10-26 11:04:19 +02:00
Michele Caini
df016b3bf9 natvis: prepare support for natvis info 2021-10-26 11:04:19 +02:00
Michele Caini
8699e96609 build_system:
* ENTT_USE_LIBCPP default is OFF
* added ENTT_INCLUDE_HEADERS to make adding headers to the EnTT target optional
* slightly better message handling
2021-10-26 11:04:19 +02:00
Michele Caini
b0dcaaf744 build_system: refine ENTT_USE_SANITIZER usage 2021-10-26 11:04:19 +02:00
Michele Caini
7db001995c build system: make EnTT files show up in IDEs automatically (close #776) 2021-10-26 11:04:19 +02:00
Michele Caini
c88adf9314 meta: avoid unnecessary moves (see #794) 2021-10-26 11:04:19 +02:00
Michele Caini
b13713ce98 meta: support transparently base members when attached to meta types for derived classes 2021-10-26 11:03:29 +02:00
Michele Caini
66b89d170f type_traits: tuple-like type support for is_equality_comparable[_v] 2021-10-22 16:50:00 +02:00
Michele Caini
4311e2e686 type_traits: make is_iterator also support void * 2021-10-22 10:25:02 +02:00
Michele Caini
fd566eff7a code coverage: move to codecov action v2 2021-10-21 11:00:42 +02:00
Michele Caini
efaa3f9e55 *: revert unnecessary changes ¯\_(ツ)_/¯ 2021-10-20 12:18:18 +02:00
Michele Caini
b625ff0902 dense_hash_map: iterator review 2021-10-20 11:44:48 +02:00
Michele Caini
fee80321ef meta_[sequence|associative]_container: iterator review 2021-10-20 10:08:49 +02:00
Michele Caini
4b9331f086 sparse_set: iterator review 2021-10-20 09:41:27 +02:00
Michele Caini
df9316b896 runtime_view: iterator review 2021-10-20 09:32:55 +02:00
Michele Caini
dae5f86d4e storage: iterator review 2021-10-20 09:10:29 +02:00
Michele Caini
75a0cefcb3 meta_range: iterator review 2021-10-20 09:06:54 +02:00
Michele Caini
2271aa5b89 registry: make all_of, any_of and a few other functions remove const qualifiers 2021-10-19 08:45:58 +02:00
Michele Caini
b668e2a967 meta: removed unused reference qualifiers 2021-10-18 16:27:49 +02:00
Michele Caini
ce44c59547 meta: suppress warnings due to unreachable code 2021-10-18 11:27:22 +02:00
Michele Caini
84802d931d clean up todo list 2021-10-12 16:56:12 +02:00
Michele Caini
8183c4383e meta: suppress a shadow warning 2021-10-12 16:55:57 +02:00
Michele Caini
af249098cd container: sparse set based dense hash map 2021-10-12 16:50:28 +02:00
Michele Caini
8fdd063f00 entity: include fwd.hpp from entity.hpp (close #790) 2021-10-12 15:14:36 +02:00
Michele Caini
fe328f6a75 identity: add is_transparent member type 2021-10-10 16:31:45 +02:00
Michele Caini
ef7163acd6 storage: non-const to const iterator conversion guaranteed 2021-10-10 10:58:58 +02:00
Michele Caini
6e45f0d5b8 meta_range: non-const to const iterator conversion guaranteed 2021-10-10 10:46:34 +02:00
Michele Caini
ff41faa3fe memory: all of a sudden I remembered what greater than or equal to means :) 2021-10-10 00:42:25 +02:00
Michele Caini
acb2d332ea type_traits: added is_transparent[_v] utility 2021-10-09 19:19:18 +02:00
Michele Caini
0fb86f21e4 type_traits: add tests for is_ebco_eligible_v 2021-10-09 19:03:28 +02:00
Michele Caini
e4991d367e memory: fast_mod no longer requires a compile-time modulus (but it's still a constexpr function) 2021-10-08 20:24:07 +02:00
Michele Caini
3144cfafd5 memory: added constexpr function next_power_of_two 2021-10-07 16:53:22 +02:00
Michele Caini
c538067544 *: cleanup, minor changes 2021-10-06 11:52:32 +02:00
Michele Caini
f8e137a1dc test: suppress warnings due to missing virtual destructor 2021-10-05 08:40:07 +02:00
Michele Caini
36ea38fd91 resource class vs struct definition 2021-10-05 08:39:54 +02:00
Michele Caini
baae5a7215 test: code coverage 2021-10-05 08:38:06 +02:00
Michele Caini
a4b267227e meta: split meta_invoke_with_args function 2021-10-05 08:37:35 +02:00
Michele Caini
58dd159699 meta: add and typename keywords for fun (thanks to msvc that also accepts invalid code) 2021-10-05 08:14:03 +02:00
Michele Caini
80082f9d51 meta: static functions that require the parent type as first argument are treated as (eventually const) member functions 2021-10-04 18:20:16 +02:00
Michele Caini
7975ffc10b meta: minor changes 2021-10-04 16:36:50 +02:00
Michele Caini
012083a4a1 meta: review utilities + code coverage 2021-10-04 16:21:55 +02:00
Michele Caini
3c67723e83 doc: minor changes 2021-10-04 11:52:22 +02:00
Michele Caini
7f0598b3cb meta: cleanup headers 2021-10-04 09:01:13 +02:00
Michele Caini
c5b27e0ae0 test: minor changes (code coverage) 2021-10-03 16:58:58 +02:00
Michele Caini
4e7ad31f18 resource: dynamic resource handle cast example (close #786) 2021-10-03 00:26:46 +02:00
Michele Caini
c7a3e9d4ac resource: cache const correctness review 2021-10-03 00:01:59 +02:00
Michele Caini
a05e7a84c4 resource: handle as value type, const correctness review 2021-10-02 23:52:22 +02:00
Michele Caini
389cb85410 resource: strict check on resource type for cache 2021-10-02 23:35:40 +02:00
Michele Caini
81ae2bed25 resource: added resource_handle::resource_type 2021-10-02 16:26:44 +02:00
Michele Caini
0ab71bc2e7 view: return a const reference to the leading storage 2021-10-01 16:26:11 +02:00
Michele Caini
81e6bbe643 test: minor changes (thanks msvc for accepting invalid code) 2021-10-01 11:05:21 +02:00
Michele Caini
c4723f3b24 view/group: added missing operator-> here and there 2021-10-01 10:57:32 +02:00
Michele Caini
df4d8e0411 meta: make meta_iterator::operator-> const as it ought to be 2021-10-01 10:57:16 +02:00
Michele Caini
4f1820bcad iterator: review of input iterator pointer type 2021-10-01 10:24:05 +02:00
Michele Caini
e375097e43 doc: minor changes 2021-10-01 09:46:41 +02:00
Michele Caini
c51bec0034 meta: add meta_range::operator-> 2021-09-30 17:34:16 +02:00
Michele Caini
081c3fdb5b iterator: added [[nodiscard]] to input_iterator_proxy::operator-> 2021-09-30 16:57:20 +02:00
Michele Caini
f1e7602775 meta: added operator-> to meta containers' iterators 2021-09-30 16:56:58 +02:00
Michele Caini
acd75ffe1c iterator: added utility input_iterator_proxy 2021-09-30 16:34:09 +02:00
Michele Caini
fc813cb59b doc: minor changes 2021-09-30 14:41:33 +02:00
Michele Caini
044d14f542 *: minor changes 2021-09-29 16:12:39 +02:00
Michele Caini
28b9f07b99 *: code coverage, cleanup 2021-09-29 11:27:13 +02:00
Michele Caini
6f20dd4e45 test: suppress a wrong warning with toolset v141 2021-09-29 11:09:37 +02:00
Michele Caini
2fc05e5b90 meta: add missing template keywords (thanks MSVC for accepting invalid code) 2021-09-29 11:03:08 +02:00
Michele Caini
9afde31f97 meta: make meta_any::assign work with mutating this pointers 2021-09-29 10:57:31 +02:00
Michele Caini
e835ce0697 meta: make meta_any::allow_cast work with mutating this pointers 2021-09-29 09:49:22 +02:00
Michele Caini
6030f56c76 meta: make meta_any::try_cast work with mutating this pointers 2021-09-28 18:50:03 +02:00
Michele Caini
ba89b53c63 doc: cleanup 2021-09-28 15:11:29 +02:00
Michele Caini
1bfb877a3b entity: adds to_entity and to_version to the entt namespace (see #788) 2021-09-28 14:52:52 +02:00
Michele Caini
72776d6fb6 entity: make return type of to_integral explicit 2021-09-28 14:31:09 +02:00
Michele Caini
7bb93420c4 storage: avoid reinventing the wheel :) 2021-09-27 08:55:50 +02:00
Michele Caini
bb1b0e0581 sparse_set: minor changes 2021-09-26 23:28:32 +02:00
Michele Caini
45ddcf0df9 sparse_set: compare allocators before moving in the move operator assignment 2021-09-26 11:08:55 +02:00
Michele Caini
9c4d7a286f memory: avoid warnings due to unused parameters 2021-09-26 11:06:36 +02:00
Michele Caini
7b290b43b5 sparse_set: try not to reinvent the wheel :) 2021-09-26 10:59:00 +02:00
Michele Caini
2e2782b030 sparse_set: partial sorting not supported in case of tombstones 2021-09-25 18:29:45 +02:00
Michele Caini
48f23bd1ba doc: cleanup 2021-09-24 16:22:42 +02:00
Michele Caini
3ece33b26a any: avoid using addressof on the result of an operator assignment without a good reason :) 2021-09-24 13:29:16 +02:00
Michele Caini
efa98379b0 meta: internal changes and cleanup 2021-09-24 12:49:43 +02:00
Michele Caini
324aeba70f meta: suppress a warning due to -Wignored-qualifiers (close #785) 2021-09-24 11:50:51 +02:00
Michele Caini
7b6715c7b5 test: code coverage for the storage class for empty types 2021-09-24 11:18:11 +02:00
Michele Caini
1ea392b6c9 test: minor changes to suppress a bunch of warnings + formatting 2021-09-24 10:19:13 +02:00
Michele Caini
8b1a3849e6 meta_any: support assigning the value wrapped by a meta_any 2021-09-24 10:08:59 +02:00
Michele Caini
e1d7e98c92 any: minor changes 2021-09-23 10:36:30 +02:00
Michele Caini
04b2e1152c any:
* when moving fails, try to copy anyway as a fallback
* code coverage
2021-09-22 17:13:02 +02:00
Michele Caini
d392eba4a0 test: code coverage 2021-09-22 16:49:42 +02:00
Michele Caini
799dec4f1c sparse_set: suppress a warning due to -Wreorder 2021-09-22 16:41:40 +02:00
Michele Caini
11df6ec927 type_id: cannot have a static variable in a constexpr function (right msvc?) 2021-09-22 16:38:41 +02:00
Michele Caini
03e363f5d9 any: added the ::assign function to copy/move assign the wrapped variable 2021-09-22 16:27:56 +02:00
Michele Caini
39211889b0 any (close #782):
* make const type_info * a first citizen
* further reduce vtable size
2021-09-22 12:48:25 +02:00
Michele Caini
db44549276 type_info/type_id:
* type_info is no longer default constructible
* users cannot construct type_info objects anymore
* make type_id refer to an object with static storage duration
2021-09-22 12:41:09 +02:00
Michele Caini
49e702c0c7 hashed_string: minor changes 2021-09-22 08:34:31 +02:00
Michele Caini
c9c5214b3e type_info: operator<, operator<=, operator> and operator>= 2021-09-22 08:34:18 +02:00
Michele Caini
24a537d154 meta: review conversion_helper for meta type nodes 2021-09-21 17:11:28 +02:00
Michele Caini
c32ecf171b sparse_set: added user_data field 2021-09-21 15:54:26 +02:00
Michele Caini
a62ae21628 any: const reference instead of pointer as default argument to ::data 2021-09-21 09:03:39 +02:00
Michele Caini
12f05e8f72 doc: fixed typo (close #784) 2021-09-20 08:07:05 +02:00
Michele Caini
81fb727d20 registry: improved ctx and the like (close #782) 2021-09-19 15:14:43 +02:00
Michele Caini
e7655c761d test: code coverage 2021-09-18 23:22:00 +02:00
Michele Caini
464fcf4300 test: fix a test that I've broken a couple of commits ago :) 2021-09-18 00:06:50 +02:00
Michele Caini
73b67ea9df *: review enums 2021-09-18 00:02:35 +02:00
Michele Caini
04a9c80c14 *: removed pointless continue; 2021-09-17 23:44:15 +02:00
Michele Caini
80b5ed3d22 introduced clang-format (close #733) 2021-09-17 23:42:43 +02:00
Michele Caini
be5ad379b6 *: another handful of changes to please clang-format 2021-09-17 17:21:26 +02:00
Michele Caini
13d901fbfa *: a handful of changes to please clang-format 2021-09-17 16:43:15 +02:00
Michele Caini
f7e7273e91 *: minor changes 2021-09-16 23:09:22 +02:00
Michele Caini
4b3bd265a6 meta: minor changes 2021-09-15 12:43:31 +02:00
Michele Caini
42dcec961b meta: support for multi-setters on meta data members 2021-09-15 09:32:12 +02:00
Michele Caini
6b2a3b2916 meta: prepare for multi-setters on meta data members 2021-09-14 14:57:17 +02:00
Michele Caini
930f9ca436 any: minor changes 2021-09-14 08:52:44 +02:00
Michele Caini
a96a767b9b test: code coverage 2021-09-13 19:32:23 +02:00
Michele Caini
36627dbe78 meta:
* removed meta_ctor, no alternatives provided
* removed meta_type::ctor overloads, no alternatives provided
2021-09-13 19:03:22 +02:00
Michele Caini
f86bc31cbb meta: shared lookup function to avoid unnecessary casts when searching for a meta constructor 2021-09-13 16:37:58 +02:00
Michele Caini
4a08ca4b5e meta: minor changes 2021-09-13 14:58:08 +02:00
Michele Caini
e857aa0640 meta: extrapolate can_cast_or_convert 2021-09-13 12:57:06 +02:00
Michele Caini
d3c98ad334 meta_factory: cleanup 2021-09-13 12:56:39 +02:00
Michele Caini
00dadfe14d meta: reduce the size of instantiated symbols in meta_factory 2021-09-13 12:55:07 +02:00
Michele Caini
f26fa92d47 handle: extended operator==/!= 2021-09-12 18:39:20 +02:00
Michele Caini
7458072df2 hashed_string (close #781):
* operator< and operator<=
* operator> and operator>=
2021-09-12 18:26:08 +02:00
Michele Caini
89aa2a1902 *: operator== is not defined as member if unnecessary 2021-09-12 17:52:29 +02:00
Michele Caini
7ab08779dc type_info: temporarily reintroduce type_info::index for performance reasons 2021-09-10 11:29:05 +02:00
Michele Caini
46b686910b any:
* reintroduced operation::TYPE to avoid fat any types
* added optional req parameter to data
* much faster any_cast
* much faster operator==
* internal review
2021-09-10 11:22:07 +02:00
Michele Caini
940a799207 * : minor changes 2021-09-10 00:04:23 +02:00
Michele Caini
778cee3b27 any:
* operator bool doesn't rely on the vtable
* valid but unspecified state after move converts to true
* compare type info objects to avoid future errors if the logic changes
2021-09-09 17:03:36 +02:00
Michele Caini
fb3252732b type_info: name rollback, hash_code -> hash (why should I introduce a breaking change to match a terrible name from the standard library after all?) 2021-09-09 14:49:39 +02:00
Michele Caini
608f7b75e2 any: avoid creating constexpr variables since constexpr-ness isn't guaranteed 2021-09-09 14:14:58 +02:00
Michele Caini
83853c3dee any:
* ::type() returns the object type if any, type_id<void>() otherwise
* avoid indirection when accessing the type
2021-09-09 14:00:08 +02:00
Michele Caini
8a0343ad6b any: avoid creating type_info objects if unnecessary 2021-09-09 11:44:36 +02:00
Michele Caini
92d43ce620 core: rename type_seq to type_index 2021-09-09 09:53:29 +02:00
Michele Caini
6943822fed type_info:
* make the API closer to that of std::type_info
* remove the ::seq member function
* rename :.hash to ::hash_code
2021-09-09 09:44:58 +02:00
Michele Caini
aa146c4125 type_info: avoid testing constexpr-ness because older compilers don't guarantee it 2021-09-09 09:04:04 +02:00
Michele Caini
b8ec6babb5 any:
* reduced vtable size
* internal rework
2021-09-09 00:40:06 +02:00
Michele Caini
505f4a2b4f type_info:
* constexpr support
* reduce instantiations
* reduce function calls/indirection
2021-09-09 00:20:20 +02:00
Michele Caini
1bb0d017c3 type_info: removed useless friend declaration 2021-09-08 23:43:28 +02:00
Michele Caini
f33ce615d9 meta: suppress warnings due to ignored qualifiers on return types 2021-09-08 11:35:50 +02:00
Michele Caini
0edb49d7c6 core: minor changes 2021-09-08 09:13:54 +02:00
Michele Caini
1fa5a03605 test: code coverage 2021-09-07 17:13:53 +02:00
Michele Caini
fc6bda5cd8 meta: turned an useless function into a constexpr value 2021-09-07 08:54:34 +02:00
Michele Caini
59589b6a71 meta: refine the conversion helper (mostly to please the toolset v141) 2021-09-06 14:51:35 +02:00
Michele Caini
57b0624a0a storage: suppress warnings for unused variables 2021-09-06 12:29:52 +02:00
Michele Caini
2f22395eea meta:
* a more robust meta_any::allow_cast (reviewed all overloads)
* removed internal meta_info dispatcher to reduce useless instantiations
* reviewed meta_conversion_helper
2021-09-06 10:19:23 +02:00
Michele Caini
21bc8d4dfb meta: add meta_conv support to references 2021-09-04 11:43:47 +02:00
Michele Caini
2e74fd8196 meta: const correctness 2021-09-04 11:42:03 +02:00
Michele Caini
fee48ab04c meta: also please toolset v141 :) 2021-09-03 18:55:51 +02:00
Michele Caini
591f885e67 meta: automatic conversion function to underlying type for enums (see #735) 2021-09-03 18:23:49 +02:00
Michele Caini
ef803016c0 meta: suppress a shadow warning 2021-09-03 17:31:15 +02:00
Michele Caini
bac51045e5 meta: automatic arithmetic conversions (see #735) 2021-09-03 14:37:52 +02:00
Michele Caini
cb5e9393a4 meta: internal changes 2021-09-03 11:42:20 +02:00
Michele Caini
097509dd2a view: add operator[] to access (eventually unwrapped) components by entity (close #775) 2021-09-02 16:05:46 +02:00
Michele Caini
6cd44248c7 core: added tuple header and unwrap_tuple utility 2021-09-02 15:32:52 +02:00
Michele Caini
1fd61fbcef meta: added missing [[maybe_unused]] to suppress warnings 2021-09-01 10:06:28 +02:00
Michele Caini
cd2245ef03 doc: fixed typo 2021-09-01 10:03:16 +02:00
Michele Caini
5ddc746915 memory: turn is_power_of_two in a constexpr function 2021-08-31 17:39:47 +02:00
Michele Caini
f7d67067ad test: increase code coverage 2021-08-30 17:06:31 +02:00
Michele Caini
d1a824f76e sparse_set/storage: cleanup 2021-08-30 16:53:20 +02:00
Michele Caini
d7b0fc09d7 storage: refine ::release_memory 2021-08-30 16:36:45 +02:00
Michele Caini
2d07a6ec1f storage:
* decouple iterator from allocator
* test iterator aliases to avoid future regressions
* internal changes/rework
2021-08-30 16:18:28 +02:00
Michele Caini
014ba5a7aa sparse_set:
* test iterator aliases to avoid future regressions
* internal changes/rework
2021-08-30 16:09:59 +02:00
Michele Caini
2f12e524dd config/sparse_set/storage: avoid power-of-two check at config level 2021-08-29 23:27:04 +02:00
Michele Caini
ea6af75e8e memory: added utility function fast_mod 2021-08-29 23:04:40 +02:00
Michele Caini
a595c6ec6f memory:
* added utility class is_power_of_two
* added variable template is_power_of_two_v
2021-08-29 22:15:33 +02:00
Michele Caini
98f785d199 memory: make to_address [[nodiscard]] 2021-08-29 22:00:05 +02:00
Michele Caini
a0ed0c864f entt_traits: removed the (mostly useless) difference_type alias 2021-08-29 19:25:23 +02:00
Michele Caini
d4ac42749e storage: minor changes 2021-08-29 19:10:18 +02:00
Michele Caini
86414671d6 sparse_set: minor changes 2021-08-29 19:09:21 +02:00
Michele Caini
6cb935982d storage: removed a couple of old (and wrong) static asserts 2021-08-29 18:43:35 +02:00
Michele Caini
39ad040d8a sparse_set: decouple iterator from allocator 2021-08-29 18:41:59 +02:00
Michele Caini
4cc3824653 test: basic_registry<...>::get_or_emplace must work for empty types 2021-08-28 09:52:46 +02:00
Michele Caini
6d3e0600a1 storage:
* increase code coverage
* remove a pretty much useless if constexpr
2021-08-28 09:52:19 +02:00
Michele Caini
b52c9c1676 test: use storage (and sigh storage mixin) from base type 2021-08-27 16:47:08 +02:00
Michele Caini
f7506251fe build system: increase ctest timeout because ASSERT_DEATH is... expensive, at least :) 2021-08-27 11:03:35 +02:00
Michele Caini
5b06f298f1 sparse_set/storage: make storage safe to use from the base (to be tested) 2021-08-26 17:52:16 +02:00
Michele Caini
dc5488a198 storage: generalized ::insert for all mixins 2021-08-26 17:14:51 +02:00
Michele Caini
23d162e9e4 sparse_set: ::emplace no longer returns the position 2021-08-26 16:13:26 +02:00
Michele Caini
de6b660b12 sparse_set/storage:
* removed basic_sparse_set<...>::emplace_back
* general cleanup
2021-08-26 15:22:29 +02:00
Michele Caini
60c175a51d storage: ::insert always fills holes before appending 2021-08-26 15:09:30 +02:00
Michele Caini
c0a39c8064 sparse_set: insert always fills holes before appending 2021-08-26 14:56:15 +02:00
Michele Caini
61f4f93cc6 sparse_set: internals review 2021-08-26 14:55:47 +02:00
Michele Caini
764404a472 doc: minor changes, updated todo list 2021-08-26 12:19:43 +02:00
Michele Caini
04da85c88a storage: avoid invoking ::page more than once in a row 2021-08-26 12:11:54 +02:00
Michele Caini
9551b7a597 storage: added empty ::get method to storage for empty types 2021-08-25 17:05:58 +02:00
Michele Caini
f1d537d035 compressed_pair:
* disable structured binding support for clang 6
* avoid using structured binding with compressed pair in the codebase
2021-08-24 13:11:21 +02:00
Michele Caini
b43e723c84 meta: reintroduce meta_type::is_array support 2021-08-24 10:13:56 +02:00
Michele Caini
5466039c1a meta: generic begin/end for meta containers 2021-08-24 09:48:59 +02:00
Michele Caini
74e11117be storage: minor changes 2021-08-24 09:00:55 +02:00
Michele Caini
e0abab2dc4 storage:
* fixed typo in the doc
* get around msvc that also accepts invalid code (again) O.o
2021-08-23 18:18:58 +02:00
Michele Caini
08ff3ff55c sparse_set: erase assertion 2021-08-23 18:15:23 +02:00
Michele Caini
0b79d99cac storage: non-copyable allocator aware container 2021-08-23 18:13:24 +02:00
Michele Caini
38f655e7b8 *: get around the fact that msvc also accepts invalid code O.o 2021-08-21 17:28:30 +02:00
Michele Caini
6f546ffe95 sparse_set: entity ::swap -> ::swap_elements 2021-08-21 17:25:38 +02:00
Michele Caini
ef8211a46e sparse_set: non-copyable allocator-aware container 2021-08-21 17:18:04 +02:00
Michele Caini
52ea5e4074 memory: added pocca/pocma/pocs utility functions 2021-08-21 17:03:07 +02:00
Michele Caini
99621cf08a memory: unfancy -> to_address (waiting for C++20) 2021-08-20 15:28:46 +02:00
Michele Caini
a9ae9ce758 test: code coverage 2021-08-20 00:32:02 +02:00
Michele Caini
fc5a529df8 core:
* added memory.hpp
* added public unfancy function
2021-08-20 00:26:25 +02:00
Michele Caini
bc1081550d storage: minor changes 2021-08-20 00:10:09 +02:00
Michele Caini
e311ab1605 test: try to also please g++-7 2021-08-19 23:32:51 +02:00
Michele Caini
994ade0638 meta_factory: suppress shadow warning 2021-08-19 23:02:28 +02:00
Michele Caini
cf9522bd3b sparse_set: ::clear honors the modality of the set 2021-08-19 17:36:16 +02:00
Michele Caini
7fd1858db0 storage: minor changes 2021-08-19 16:34:03 +02:00
Michele Caini
c41509151b sparse_set:
* (almost) allocator-aware container
* unequal containers not supported
* copying a sparse set isn't supported
2021-08-19 15:50:29 +02:00
Michele Caini
4e12853d4a runtime_view: correctly set the no_tombstone_check member 2021-08-17 11:58:49 +02:00
Michele Caini
e1fadbc9b9 registry: removed ::version/::entity (use entt_traits instead) 2021-08-16 23:30:14 +02:00
Michele Caini
3c4d13d8b9 sparse_set/view:
* added basic_sparse_set<E>::current to return known version of identifiers in set
* sparse arrays also contain updated entity versions, no more unused bits
* basic_sparse_set<E>::contains performs a strict version check
* sparse sets manage correctly foreign entities in all cases now
* no tombstone checks for multi-type views, zero-cost pointer stability model
2021-08-16 17:07:52 +02:00
Michele Caini
732159db07 registry:
* ::current no longer asserts, it returns now tombstone versions for invalid entities
* minor changes to doc and tests
2021-08-16 16:56:11 +02:00
Michele Caini
00894eaccf entity/*: minor changes to doc and tests 2021-08-16 16:52:56 +02:00
Michele Caini
17818178cf entity:
* removed null_t::operator| (use entt_traits<T>::combine instead)
* removed tombstone_t::operator| (use entt_traits<T>::combine instead)
2021-08-14 23:00:02 +02:00
Michele Caini
89c0abfada entity:
* reviewed entt_traits<T>::combine
* added entt_traits::reserved
* use entt_traits<T>::combine where it makes sense to slightly improve perf
2021-08-14 22:26:41 +02:00
Michele Caini
8bbdd92739 entity: added entt_traits<T>::combine 2021-08-14 22:24:43 +02:00
Michele Caini
f724ceb052 entity: cleanup 2021-08-14 16:14:11 +02:00
Michele Caini
b7ac85005f updated TODO 2021-08-13 23:58:57 +02:00
Michele Caini
8ec899a8e7 sigh: make scoped_connection move constructible/assignable (close #760) 2021-08-13 15:35:05 +02:00
Michele Caini
1013c6a50e meta: removed annotation support for meta properties 2021-08-12 19:54:12 +02:00
Michele Caini
2cd5d3bab3 meta: cleanup, minor changes 2021-08-12 19:01:38 +02:00
Michele Caini
d9494960a2 meta:
* removed the possibility of invoking meta_factory::prop multiple times
* further reduced instantiations due to meta properties
2021-08-11 17:11:09 +02:00
Michele Caini
c0eb6590da snapshot: include destroyed list head in the entity count (close #757) 2021-08-11 15:23:46 +02:00
Michele Caini
a9cefcb823 meta: avoid risk of name clashing on meta types and meta data members 2021-08-10 16:07:17 +02:00
Michele Caini
d1ff491010 meta:
* removed meta_type::is_member_object_pointer (no alternative provided)
* removed meta_type::is_member_function_pointer (no alternative provided)
2021-08-10 15:39:26 +02:00
Michele Caini
2aa34f05d3 meta: removed meta_type::is_array (no alternative provided) 2021-08-09 12:47:24 +02:00
Michele Caini
135d6915ee meta, review implicitly generated meta default constructors:
* reduce instantiations and remove their static objects
* these ctors are no longer returned from a direct lookup nor during iterations
2021-08-09 10:07:43 +02:00
Michele Caini
6282195b80 meta:
* removed meta_type::is_integral (no alternative provided)
* removed meta_type::is_floating_point (no alternative provided)
* added meta_type::is_arithmetic, prepare for auto-generated implicit conversions
2021-08-07 20:43:02 +02:00
Michele Caini
adec4f08c6 meta: removed meta_type::is_union (no alternative provided) 2021-08-06 12:07:09 +02:00
Michele Caini
b24fe2f122 test: increase code coverage 2021-08-06 12:06:42 +02:00
Michele Caini
7b35bcb68a meta: use enum-as-bitmask support to get rid of old fashioned enum for meta traits 2021-08-05 10:44:13 +02:00
Michele Caini
4b629045c3 core: added enum-as-bitmask support for enum classes (thanks to @TerensTare for the suggestion) 2021-08-05 10:42:42 +02:00
Michele Caini
c6ba1d05d1 view: avoid polluting global namespace with naked operator| 2021-08-04 16:46:53 +02:00
Michele Caini
3adac90573 meta: removed meta_type::is_function_pointer (no alternative provided) 2021-08-04 16:06:58 +02:00
Michele Caini
0332188b6b meta, get rid of useless info from meta_type to further reduce instantiations:
* removed meta_type::rank (no alternative provided)
* removed meta_type::extent (no alternative provided)
* removed meta_type::rmove_extent (no alternative provided)
* removed meta_type::remove_pointer ((*any).type())
2021-08-03 16:29:19 +02:00
Michele Caini
790a1e2812 view: minor changes to get around an issue of clang 2021-08-02 18:28:48 +02:00
Michele Caini
3bd1e13321 view: removed the foobar alias which was not meant to stay there 2021-08-02 18:24:49 +02:00
Michele Caini
0cd642ca11 view: ::use<T> doesn't modify the view in-place anymore, instead it returns a new view 2021-08-02 18:22:58 +02:00
Michele Caini
d5995998b0 view:
* each<T> doesn't invoke use<T> anymore under the hood
* removed ::traverse function
* minor changes
2021-08-02 18:09:38 +02:00
Michele Caini
62babb9c22 updated TODO 2021-08-01 12:02:43 +02:00
Michele Caini
b98c69521b build_system: add support for c++20 (close #751) 2021-08-01 11:41:31 +02:00
Michele Caini
6cd37b3809 view: get around an issue of msvc when using C++20 2021-08-01 11:21:05 +02:00
Michele Caini
238170d0e2 meta: some changes to make tests compile with C++20 (MSVC) 2021-08-01 11:02:05 +02:00
Michele Caini
6fb5b25530 any: don't compare arrays directly (operator== even deprecated in C++20) 2021-08-01 10:51:48 +02:00
Michele Caini
d63f9e1eff build_system: make ENTT_CXX_STD a cached string variable (not used yet, see #751) 2021-08-01 10:47:14 +02:00
Michele Caini
461865bf3b build_system: make ENTT_ID_TYPE a cached string variable 2021-08-01 10:41:28 +02:00
Michele Caini
e95d9b9cf6 doc: updated links.md 2021-08-01 10:18:21 +02:00
Michele Caini
c52b2a4fa9 entity/*:
* add owned_t utility
* change view def to basic_view<T, get_t<C...>, exclude_t<E...>>
* change group def to basic_group<T, owned_t<O...>, get_t<G...>, exclude_t<E...>>
* remove view dispatcher (reduce instantiations)
* make basic_view::traverse use the local policy
2021-08-01 10:03:45 +02:00
Michele Caini
d0ec5d2da0 dispatcher: mionr changes 2021-08-01 09:58:32 +02:00
Michele Caini
6134058abc type_traits: minor changes (coding style) 2021-08-01 09:58:19 +02:00
Michele Caini
bd4121c260 *: cleanup 2021-07-30 14:27:53 +02:00
Michele Caini
fbab047d7f doc: fixed typo (close #749) 2021-07-30 12:13:13 +02:00
Michele Caini
e216144d07 poly: optimize single function vtables (close #689) 2021-07-29 23:54:10 +02:00
Michele Caini
94d5b11191 any: rvalue any_cast no longer forces by-copy constructor (close #730) 2021-07-29 14:30:16 +02:00
Michele Caini
d3c89da2eb doc: mention vcpkg experimental feature in the readme file (close #743) 2021-07-28 14:52:56 +02:00
Michele Caini
754a8a637e doc: updated list of showcases (close #744) 2021-07-28 14:44:42 +02:00
Michele Caini
ab24a50de7 doc: fixed typo 2021-07-27 18:56:16 +02:00
Michele Caini
8099dde34b view: relax policy constraint if possible 2021-07-27 18:56:14 +02:00
Michele Caini
99198fb07f doc: added a note about the no-policy trick 2021-07-27 18:56:08 +02:00
Michele Caini
18d02a0c95 *: suppress a couple of warnings (shadow/unused) 2021-07-27 18:56:06 +02:00
Michele Caini
e6cac7d5fb group/view: remove references to basic_sparse_set 2021-07-27 18:56:03 +02:00
Michele Caini
d8d6f00fee storage: minor changes + some tests for get/get_as_tuple 2021-07-27 18:56:01 +02:00
Michele Caini
ef5c1a1099 snapshot: minor changes 2021-07-27 18:55:55 +02:00
Michele Caini
2d92d8ee67 storage:
* Removed free function get_as_tuple
* Added get_as_tuple function at storage level
2021-07-27 18:55:52 +02:00
Michele Caini
ae60266477 updated todo list 2021-07-27 18:55:49 +02:00
Michele Caini
88da5261fc component/storage:
* removed ignore_if_empty_v, added ignore_as_empty_v
* removed fake getter requirement for empty storage types
2021-07-27 18:55:46 +02:00
Michele Caini
15efe9f702 sparse_set: make capacity virtual 2021-07-27 18:55:42 +02:00
Michele Caini
badbc5c66e storage: internal review 2021-07-27 18:55:35 +02:00
Michele Caini
d6eea41e13 sparse_set: make reserve virtual 2021-07-27 18:55:31 +02:00
Michele Caini
6a2c54adf5 sparse_set: make shrink_to_fit virtual 2021-07-27 18:55:27 +02:00
Michele Caini
75d4bc7c18 doc: minor changes 2021-07-27 18:55:24 +02:00
Michele Caini
834f7feb27 storage: removed ::sort_n, capture pool and use ::get if needed 2021-07-27 18:55:21 +02:00
Michele Caini
5d2af3f9e1 storage: removed ::sort, capture pool and use ::get if needed 2021-07-27 18:55:19 +02:00
Michele Caini
60e8e4a82a doc: updated list of links (close #723) 2021-07-27 18:54:29 +02:00
Michele Caini
92e23476ab doc: updated list of similar projects (close #731) 2021-07-27 18:54:29 +02:00
Michele Caini
4560fef058 doc: updated list of showcases (close #732) 2021-07-27 18:54:29 +02:00
Michele Caini
c5d6574617 entity: updated fwd decls 2021-07-27 18:54:29 +02:00
Michele Caini
de61a0ca45 view:
* suppress warning for shadow parameter
* adjust deduced type to avoid compile-time errors
2021-07-27 18:54:29 +02:00
Michele Caini
98470caae1 view: suppress warning 2021-07-27 18:54:29 +02:00
Michele Caini
53486c067b view: minor changes 2021-07-27 18:54:29 +02:00
Michele Caini
5b805852fa view: internal iterable_storage class 2021-07-27 18:54:28 +02:00
Michele Caini
94d02eeb4e view: minor changes 2021-07-27 18:54:28 +02:00
Michele Caini
dbfec1b247 view: get rid of filter_to_array, filter is always stored as an array 2021-07-27 18:54:28 +02:00
Michele Caini
a9a1a71145 updated TODO list 2021-07-27 18:54:28 +02:00
Michele Caini
91537bf5f3 sparse_set/storage: try to please the code coverage tool 2021-07-27 18:54:28 +02:00
Michele Caini
1baad08288 storage/sparse_set: ctor review 2021-07-27 18:54:28 +02:00
Michele Caini
1c5b846488 storage: get rid of basic_storage_impl wrapper 2021-07-27 18:54:28 +02:00
Michele Caini
6867760cc4 doc: fixed typo 2021-07-27 18:54:28 +02:00
Michele Caini
516a9209b7 sparse_set/storage: no zero sized allocations on construction 2021-07-27 18:54:28 +02:00
Michele Caini
e352c953dc test: minor changes 2021-07-27 18:54:28 +02:00
Michele Caini
f455846d18 sparse_set/storage: avoid using temporary allocators 2021-07-27 18:54:28 +02:00
Michele Caini
6d45ac942d storage: store aside a compressed allocator to avoid frequent conversions 2021-07-27 18:54:28 +02:00
Michele Caini
48939abafa sparse_set/storage: suppress shadow warnings 2021-07-27 18:54:28 +02:00
Michele Caini
fbc9595f12 sparse_set: used compressed pair to hide the cost of storing allocators 2021-07-27 18:54:28 +02:00
Michele Caini
ed2c714419 compressed_pair: added support for structured binding (with tests) 2021-07-27 18:54:28 +02:00
Michele Caini
ef7731fb8b storage: use underlying allocator 2021-07-27 18:54:28 +02:00
Michele Caini
b5ec03f585 sparse_set:
* added get_allocator
* internal review
2021-07-27 18:54:28 +02:00
Michele Caini
a8b224a364 *: minor explicit names for traits types 2021-07-27 18:54:25 +02:00
Michele Caini
3f3943a233 storage: internal review 2021-07-27 18:53:49 +02:00
Michele Caini
4171b4ae47 resource_handle: add ::use_count (close #727) 2021-07-27 18:53:49 +02:00
Michele Caini
baa4c44632 compressed_pair: minor changes 2021-07-27 18:53:49 +02:00
Hussein Taher
ffed8f37de snapshot: fix warning for discarding a nodiscard (#728) 2021-07-27 18:53:49 +02:00
Michele Caini
8978a0d159 core:
* added trait is_ebco_eligible[_v]
* added class compressed_pair
* tests and doc for compressed_pair
2021-07-27 18:53:49 +02:00
Michele Caini
4d09be0cd3 view: minor changes 2021-07-27 18:53:49 +02:00
Michele Caini
987be01bd6 component: added in_place_delete_v and ignore_if_empty_v 2021-07-27 18:53:49 +02:00
Michele Caini
7f59fc6321 test: minor changes 2021-07-27 18:53:49 +02:00
Michele Caini
feeb122c0d updated todo list 2021-07-27 18:53:49 +02:00
Michele Caini
0754f108c9 sparse_set: fix an issue with assuring pages properly on emplace (close #746) 2021-07-27 18:53:09 +02:00
Michele Caini
ea82f86749 meta: fix precedence issue 2021-07-27 11:39:37 +02:00
Michele Caini
7bd217386a meta: shared simplified visit function 2021-07-27 10:08:20 +02:00
Michele Caini
1f1e02fee1 build system: test id type std::uint64_t on the CI, all platforms 2021-07-26 23:48:26 +02:00
Michele Caini
6d1e4fb3da test: make tests for entity traits work when id_type is std::uint64_t 2021-07-26 23:48:23 +02:00
Michele Caini
9090f84611 test: make tests for hashed string work when id_type is std::uint64_t 2021-07-26 23:48:19 +02:00
Michele Caini
8e5ddba173 sparse set: make vs2017 work (more or less) fine when id_type is std::uint64_t 2021-07-26 23:48:15 +02:00
Michele Caini
86fccb5071 entity: avoid UBs when id type is std::uint64_t (close #745) 2021-07-26 23:48:09 +02:00
Michele Caini
414c3baf15 meta: further reduce size of meta_any's vtable 2021-07-26 17:06:16 +02:00
Michele Caini
bbbddbf617 meta: added internal meta_trait enum to reduce memory usage due to meta node traits 2021-07-26 11:16:00 +02:00
Michele Caini
7638b5a95e meta: suppress warnings, get around visibility issue 2021-07-26 09:55:25 +02:00
Michele Caini
3df6b05c00 doc: fixed typo (close #742) 2021-07-26 09:17:11 +02:00
Michele Caini
8566c58f2b meta: cleanup, prep for hook point 2021-07-23 17:24:41 +02:00
Michele Caini
33ccb71526 meta: add cbegin/cend to meta_range 2021-07-23 15:51:26 +02:00
Michele Caini
2b07b92039 meta (prep for hook func):
* removed meta_type::reset
* added meta_reset overloads
2021-07-23 15:47:40 +02:00
Michele Caini
959bc269e4 test: get rid of inconsistent line (close #741) 2021-07-23 07:53:51 +02:00
Michele Caini
8609068dbf type_traits: try to also please gcc :) 2021-07-22 18:21:22 +02:00
Michele Caini
d6cea80768 core: make is_equality_comparable[_v] work with iterators (close #739) 2021-07-22 18:08:19 +02:00
Michele Caini
0f2d6fb324 meta_any: avoid risky fallthrough in the vtable (close #736) 2021-07-22 17:56:22 +02:00
Michele Caini
f5d303045c meta: make meta_invoke and meta_construct utilities also support lambdas 2021-07-22 17:41:50 +02:00
Michele Caini
ad61b0c84e meta: removed parent link from meta nodes 2021-07-22 17:21:09 +02:00
Michele Caini
18373bb679 meta: meta_any::allow_cast overload for meta types 2021-07-22 16:55:45 +02:00
Michele Caini
c30dfe3bfe meta: add fwd.hpp (see #735) 2021-07-22 16:02:03 +02:00
Michele Caini
4993961c16 *: removed deprecated stuff 2021-07-22 15:20:08 +02:00
Michele Caini
89aece7c28 meta: removed meta_type::is_void 2021-07-21 11:35:35 +02:00
Michele Caini
ec96946513 meta: cleanup/further reduce instantiations 2021-07-21 11:35:35 +02:00
Michele Caini
e49aa0d424 meta: simplified meta_any::try_cast 2021-07-21 11:35:35 +02:00
Michele Caini
b5c929572a meta: possibly reduce number of instantiations due to meta_invoke 2021-07-21 11:35:35 +02:00
Michele Caini
394822aa50 meta: remove internal::meta_visit, further reduce the number of instantiations 2021-07-21 11:35:35 +02:00
Michele Caini
d12ba5e527 meta: reduce lambda instantiations 2021-07-21 11:35:35 +02:00
Michele Caini
ab316bcb2e type_info: use fake vtables 2021-07-21 11:35:35 +02:00
Michele Caini
93fc08df45 meta: template info review 2021-07-21 11:35:35 +02:00
Michele Caini
266dd02ef5 meta: utility review and cleanup 2021-07-21 11:35:35 +02:00
Michele Caini
61c1e359e9 meta: cleanup/review 2021-07-21 11:35:35 +02:00
Michele Caini
32c83986e5 meta: minor changes 2021-07-21 11:35:35 +02:00
Michele Caini
f64af2e69d meta: get rid of useless boolean values in the return types of meta containers 2021-07-21 11:35:35 +02:00
Michele Caini
6e925c7cdd meta: container support review (less instantiations, faster to compile) 2021-07-21 11:35:35 +02:00
Michele Caini
5f50f776c6 meta: remove unnecessary nested alias 2021-07-21 11:35:35 +02:00
Michele Caini
946ccf3db4 meta: reduce instantiations for meta containers 2021-07-21 11:35:35 +02:00
Michele Caini
7a0aea390b meta: minor changes (const correctness) 2021-07-21 11:35:35 +02:00
Michele Caini
ff0e6315f9 doc: fixed typo 2021-07-21 11:35:35 +02:00
Michele Caini
b7fb485349 meta: meta_type::construct should not use base constructors 2021-07-21 11:35:35 +02:00
Michele Caini
45cbe66d5d now working on version 3.9.0 2021-07-21 00:18:12 +02:00
Michele Caini
bc7a6399c1 updated single include file 2021-07-21 00:14:12 +02:00
228 changed files with 92644 additions and 40221 deletions

41
.clang-format Normal file
View File

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

8
.github/FUNDING.yml vendored
View File

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

55
.github/workflows/analyzer.yml vendored Normal file
View File

@@ -0,0 +1,55 @@
name: analyzer
on:
push:
branches:
- master
- wip
jobs:
iwyu:
timeout-minutes: 30
env:
IWYU: 0.18
LLVM: 14
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v2
- name: Install llvm/clang
# see: https://apt.llvm.org/
run: |
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-$LLVM main"
sudo apt update
sudo apt remove -y "llvm*"
sudo apt remove -y "libclang-dev*"
sudo apt remove -y "clang*"
sudo apt install -y llvm-$LLVM-dev
sudo apt install -y libclang-$LLVM-dev
sudo apt install -y clang-$LLVM
- name: Compile iwyu
# see: https://github.com/include-what-you-use/include-what-you-use
working-directory: build
run: |
git clone https://github.com/include-what-you-use/include-what-you-use.git --branch $IWYU --depth 1
mkdir include-what-you-use/build
cd include-what-you-use/build
cmake -DCMAKE_C_COMPILER=clang-$LLVM -DCMAKE_CXX_COMPILER=clang++-$LLVM -DCMAKE_INSTALL_PREFIX=./ ..
make -j4
bin/include-what-you-use --version
- name: Compile tests
working-directory: build
run: |
export PATH=$PATH:${GITHUB_WORKSPACE}/build/include-what-you-use/build/bin
cmake -DENTT_BUILD_TESTING=ON \
-DENTT_BUILD_BENCHMARK=ON \
-DENTT_BUILD_EXAMPLE=ON \
-DENTT_BUILD_LIB=ON \
-DENTT_BUILD_SNAPSHOT=ON \
-DCMAKE_CXX_INCLUDE_WHAT_YOU_USE="include-what-you-use;-Xiwyu;--mapping_file=${GITHUB_WORKSPACE}/entt.imp;-Xiwyu;--no_fwd_decls;-Xiwyu;--verbose=1" ..
make -j4

View File

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

View File

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

View File

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

View File

@@ -11,10 +11,12 @@ ceeac
ColinH
corystegel
Croydon
cschreib
cugone
dbacchet
dBagrat
djarek
DNKpp
DonKult
drglove
eliasdaler
@@ -31,6 +33,7 @@ Lawrencemm
markand
mhammerc
Milerius
Minimonium
morbo84
m-waka
netpoetica
@@ -39,6 +42,7 @@ Oortonaut
Paolo-Oliverio
pgruenbacher
prowolf
Qix-
stefanofiorentino
suVrik
szunhammer
@@ -46,6 +50,7 @@ The5-1
vblanco20-1
willtunnels
WizardIke
WoLfulus
w1th0utnam3
xissburg
zaucy

View File

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

View File

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

View File

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

View File

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

53
TODO
View File

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

View File

@@ -1,5 +1,5 @@
prefix=@CMAKE_INSTALL_PREFIX@
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
includedir=@EnTT_PKGCONFIG_INCLUDEDIR@
Name: EnTT
Description: Gaming meets modern C++

View File

@@ -0,0 +1,23 @@
# This module provides function for joining paths
# known from most languages
#
# SPDX-License-Identifier: (MIT OR CC0-1.0)
# Copyright 2020 Jan Tojnar
# https://github.com/jtojnar/cmake-snips
#
# Modelled after Pythons os.path.join
# https://docs.python.org/3.7/library/os.path.html#os.path.join
# Windows not supported
function(join_paths joined_path first_path_segment)
set(temp_path "${first_path_segment}")
foreach(current_segment IN LISTS ARGN)
if(NOT ("${current_segment}" STREQUAL ""))
if(IS_ABSOLUTE "${current_segment}")
set(temp_path "${current_segment}")
else()
set(temp_path "${temp_path}/${current_segment}")
endif()
endif()
endforeach()
set(${joined_path} "${temp_path}" PARENT_SCOPE)
endfunction()

View File

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

View File

@@ -1,4 +1,4 @@
# Doxyfile 1.9.1
# Doxyfile 1.9.4
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
@@ -12,6 +12,15 @@
# For lists, items can also be appended using:
# TAG += value [value, ...]
# Values that contain spaces should be placed between quotes (\" \").
#
# Note:
#
# Use doxygen to compare the used configuration file with the template
# configuration file:
# doxygen -x [configFile]
# Use doxygen to compare the used configuration file with the template
# configuration file without replacing the environment variables:
# doxygen -x_noenv [configFile]
#---------------------------------------------------------------------------
# Project related configuration options
@@ -60,16 +69,28 @@ PROJECT_LOGO =
OUTPUT_DIRECTORY = @DOXY_OUTPUT_DIRECTORY@
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
# directories (in 2 levels) under the output directory of each output format and
# will distribute the generated files over these directories. Enabling this
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096
# sub-directories (in 2 levels) under the output directory of each output format
# and will distribute the generated files over these directories. Enabling this
# option can be useful when feeding doxygen a huge amount of source files, where
# putting all generated files in the same directory would otherwise causes
# performance problems for the file system.
# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to
# control the number of sub-directories.
# The default value is: NO.
CREATE_SUBDIRS = NO
# Controls the number of sub-directories that will be created when
# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every
# level increment doubles the number of directories, resulting in 4096
# directories at level 8 which is the default and also the maximum value. The
# sub-directories are organized in 2 levels, the first level always has a fixed
# numer of 16 directories.
# Minimum value: 0, maximum value: 8, default value: 8.
# This tag requires that the tag CREATE_SUBDIRS is set to YES.
CREATE_SUBDIRS_LEVEL = 8
# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
# characters to appear in the names of generated files. If set to NO, non-ASCII
# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
@@ -81,26 +102,18 @@ ALLOW_UNICODE_NAMES = NO
# The OUTPUT_LANGUAGE tag is used to specify the language in which all
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all constant output in the proper language.
# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
# Ukrainian and Vietnamese.
# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian,
# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English
# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek,
# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with
# English messages), Korean, Korean-en (Korean with English messages), Latvian,
# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese,
# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish,
# Swedish, Turkish, Ukrainian and Vietnamese.
# The default value is: English.
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.
@@ -248,16 +261,16 @@ TAB_SIZE = 4
# the documentation. An alias has the form:
# name=value
# For example adding
# "sideeffect=@par Side Effects:\n"
# "sideeffect=@par Side Effects:^^"
# 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 (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 \\})
# "Side Effects:". Note that you cannot put \n's in the value part of an alias
# to insert 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 =
@@ -302,8 +315,8 @@ OPTIMIZE_OUTPUT_SLICE = NO
# 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,
# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL,
# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice,
# VHDL, 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). For instance to make doxygen treat .inc files
@@ -450,13 +463,13 @@ TYPEDEF_HIDES_STRUCT = NO
LOOKUP_CACHE_SIZE = 0
# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use
# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use
# during processing. When set to 0 doxygen will based this on the number of
# cores available in the system. You can set it explicitly to a value larger
# than 0 to get more control over the balance between CPU load and processing
# speed. At this moment only the input processing can be done using multiple
# threads. Since this is still an experimental feature the default is set to 1,
# which efficively disables parallel processing. Please report any issues you
# which effectively disables parallel processing. Please report any issues you
# encounter. Generating dot graphs in parallel is controlled by the
# DOT_NUM_THREADS setting.
# Minimum value: 0, maximum value: 32, default value: 1.
@@ -575,7 +588,7 @@ INTERNAL_DOCS = NO
# filesystem is case sensitive (i.e. it supports files in the same directory
# whose names only differ in casing), the option must be set to YES to properly
# deal with such files in case they appear in the input. For filesystems that
# are not case sensitive the option should be be set to NO to properly deal with
# are not case sensitive the option should be set to NO to properly deal with
# output files written for symbols that only differ in casing, such as for two
# classes, one named CLASS and the other named Class, and to also support
# references to files without having to specify the exact matching casing. On
@@ -600,6 +613,12 @@ HIDE_SCOPE_NAMES = NO
HIDE_COMPOUND_REFERENCE= NO
# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class
# will show which file needs to be included to use the class.
# The default value is: YES.
SHOW_HEADERFILE = YES
# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
# the files that are included by a file in the documentation of that file.
# The default value is: YES.
@@ -757,7 +776,8 @@ FILE_VERSION_FILTER =
# output files in an output format independent way. To create the layout file
# that represents doxygen's defaults, run doxygen with the -l option. You can
# optionally specify a file name after the option, if omitted DoxygenLayout.xml
# will be used as the name of the layout file.
# will be used as the name of the layout file. See also section "Changing the
# layout of pages" for information.
#
# Note that if you run doxygen from a directory containing a file called
# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
@@ -803,18 +823,26 @@ WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES
# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
# potential errors in the documentation, such as not documenting some parameters
# in a documented function, or documenting parameters that don't exist or using
# markup commands wrongly.
# potential errors in the documentation, such as documenting some parameters in
# a documented function twice, or documenting parameters that don't exist or
# using markup commands wrongly.
# The default value is: YES.
WARN_IF_DOC_ERROR = YES
# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete
# function parameter documentation. If set to NO, doxygen will accept that some
# parameters have no documentation without warning.
# The default value is: YES.
WARN_IF_INCOMPLETE_DOC = 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. If
# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
# value. If set to NO, doxygen will only warn about wrong parameter
# documentation, but not about the absence of documentation. If EXTRACT_ALL is
# set to YES then this flag will automatically be disabled. See also
# WARN_IF_INCOMPLETE_DOC
# The default value is: NO.
WARN_NO_PARAMDOC = YES
@@ -834,13 +862,27 @@ WARN_AS_ERROR = NO
# and the warning text. Optionally the format may contain $version, which will
# be replaced by the version of the file (if it could be obtained via
# FILE_VERSION_FILTER)
# See also: WARN_LINE_FORMAT
# The default value is: $file:$line: $text.
WARN_FORMAT = "$file:$line: $text"
# In the $text part of the WARN_FORMAT command it is possible that a reference
# to a more specific place is given. To make it easier to jump to this place
# (outside of doxygen) the user can define a custom "cut" / "paste" string.
# Example:
# WARN_LINE_FORMAT = "'vi $file +$line'"
# See also: WARN_FORMAT
# The default value is: at line $line of file $file.
WARN_LINE_FORMAT = "at line $line of file $file"
# The WARN_LOGFILE tag can be used to specify a file to which warning and error
# messages should be written. If left blank the output is written to standard
# error (stderr).
# error (stderr). In case the file specified cannot be opened for writing the
# warning and error messages are written to standard error. When as file - is
# specified the warning and error messages are written to standard output
# (stdout).
WARN_LOGFILE =
@@ -880,10 +922,10 @@ INPUT_ENCODING = UTF-8
#
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl,
# *.ucf, *.qsf and *.ice.
# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,
# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C
# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
# *.vhdl, *.ucf, *.qsf and *.ice.
FILE_PATTERNS = *.h \
*.hpp \
@@ -925,7 +967,7 @@ EXCLUDE_PATTERNS =
# (namespaces, classes, functions, etc.) that should be excluded from the
# output. The symbol name can be a fully qualified name, a word, or if the
# wildcard * is used, a substring. Examples: ANamespace, AClass,
# AClass::ANamespace, ANamespace::*Test
# ANamespace::AClass, ANamespace::*Test
#
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories use the pattern */test/*
@@ -1111,9 +1153,11 @@ VERBATIM_HEADERS = YES
CLANG_ASSISTED_PARSING = NO
# If clang assisted parsing is enabled and the CLANG_ADD_INC_PATHS tag is set to
# YES then doxygen will add the directory of each input to the include path.
# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS
# tag is set to YES then doxygen will add the directory of each input to the
# include path.
# The default value is: YES.
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
CLANG_ADD_INC_PATHS = YES
@@ -1248,7 +1292,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
# this color. Hue is specified as an angle on a color-wheel, see
# 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.
@@ -1258,7 +1302,7 @@ HTML_EXTRA_FILES =
HTML_COLORSTYLE_HUE = 220
# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
# in the HTML output. For a value of 0 the output will use grayscales only. A
# in the HTML output. For a value of 0 the output will use gray-scales only. A
# value of 255 will produce the most vivid colors.
# Minimum value: 0, maximum value: 255, default value: 100.
# This tag requires that the tag GENERATE_HTML is set to YES.
@@ -1340,6 +1384,13 @@ GENERATE_DOCSET = NO
DOCSET_FEEDNAME = "Doxygen generated docs"
# This tag determines the URL of the docset feed. A documentation feed provides
# an umbrella under which multiple documentation sets from a single provider
# (such as a company or product suite) can be grouped.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_FEEDURL =
# This tag specifies a string that should uniquely identify the documentation
# set bundle. This should be a reverse domain-name style string, e.g.
# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
@@ -1365,8 +1416,12 @@ DOCSET_PUBLISHER_NAME = Publisher
# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
# (see:
# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows.
# on Windows. In the beginning of 2021 Microsoft took the original page, with
# a.o. the download links, offline the HTML help workshop was already many years
# in maintenance mode). You can download the HTML help workshop from the web
# archives at Installation executable (see:
# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo
# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe).
#
# The HTML Help Workshop contains a compiler that can convert all HTML output
# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
@@ -1525,16 +1580,28 @@ DISABLE_INDEX = NO
# to work a browser that supports JavaScript, DHTML, CSS and frames is required
# (i.e. any modern browser). Windows users are probably better off using the
# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
# further fine-tune the look of the index. As an example, the default style
# sheet generated by doxygen has an example that shows how to put an image at
# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
# the same information as the tab index, you could consider setting
# DISABLE_INDEX to YES when enabling this option.
# further fine tune the look of the index (see "Fine-tuning the output"). As an
# example, the default style sheet generated by doxygen has an example that
# shows how to put an image at the root of the tree instead of the PROJECT_NAME.
# Since the tree basically has the same information as the tab index, you could
# consider setting DISABLE_INDEX to YES when enabling this option.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_TREEVIEW = NO
# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the
# FULL_SIDEBAR option determines if the side bar is limited to only the treeview
# area (value NO) or if it should extend to the full height of the window (value
# YES). Setting this to YES gives a layout similar to
# https://docs.readthedocs.io with more room for contents, but less room for the
# project logo, title, and description. If either GENERATE_TREEVIEW or
# DISABLE_INDEX is set to NO, this option has no effect.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
FULL_SIDEBAR = NO
# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
# doxygen will group on one line in the generated HTML documentation.
#
@@ -1559,6 +1626,13 @@ TREEVIEW_WIDTH = 250
EXT_LINKS_IN_WINDOW = NO
# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email
# addresses.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
OBFUSCATE_EMAILS = YES
# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
@@ -1607,11 +1681,29 @@ FORMULA_MACROFILE =
USE_MATHJAX = NO
# With MATHJAX_VERSION it is possible to specify the MathJax version to be used.
# Note that the different versions of MathJax have different requirements with
# regards to the different settings, so it is possible that also other MathJax
# settings have to be changed when switching between the different MathJax
# versions.
# Possible values are: MathJax_2 and MathJax_3.
# The default value is: MathJax_2.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_VERSION = MathJax_2
# When MathJax is enabled you can set the default output format to be used for
# the MathJax output. See the MathJax site (see:
# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details.
# the MathJax output. For more details about the output format see MathJax
# version 2 (see:
# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3
# (see:
# http://docs.mathjax.org/en/latest/web/components/output.html).
# Possible values are: HTML-CSS (which is slower, but has the best
# compatibility), NativeMML (i.e. MathML) and SVG.
# compatibility. This is the name for Mathjax version 2, for MathJax version 3
# this will be translated into chtml), NativeMML (i.e. MathML. Only supported
# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This
# is the name for Mathjax version 3, for MathJax version 2 this will be
# translated into HTML-CSS) and SVG.
# The default value is: HTML-CSS.
# This tag requires that the tag USE_MATHJAX is set to YES.
@@ -1624,15 +1716,21 @@ 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 https://www.mathjax.org before deployment.
# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2.
# MathJax from https://www.mathjax.org before deployment. The default value is:
# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2
# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3
# This tag requires that the tag USE_MATHJAX is set to YES.
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
# for MathJax version 2 (see
# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):
# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
# For example for MathJax version 3 (see
# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):
# MATHJAX_EXTENSIONS = ams
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_EXTENSIONS =
@@ -1812,29 +1910,31 @@ PAPER_TYPE = a4
EXTRA_PACKAGES =
# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
# generated LaTeX document. The header should contain everything until the first
# chapter. If it is left blank doxygen will generate a standard header. See
# section "Doxygen usage" for information on how to let doxygen write the
# default header to a separate file.
# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for
# the generated LaTeX document. The header should contain everything until the
# first chapter. If it is left blank doxygen will generate a standard header. It
# is highly recommended to start with a default header using
# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty
# and then modify the file new_header.tex. See also section "Doxygen usage" for
# information on how to generate the default header that doxygen normally uses.
#
# Note: Only use a user-defined header if you know what you are doing! The
# following commands have a special meaning inside the header: $title,
# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
# string, for the replacement values of the other commands the user is referred
# to HTML_HEADER.
# Note: Only use a user-defined header if you know what you are doing!
# Note: The header is subject to change so you typically have to regenerate the
# default header when upgrading to a newer version of doxygen. The following
# commands have a special meaning inside the header (and footer): For a
# description of the possible markers and block names see the documentation.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HEADER =
# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
# generated LaTeX document. The footer should contain everything after the last
# chapter. If it is left blank doxygen will generate a standard footer. See
# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for
# the generated LaTeX document. The footer should contain everything after the
# last chapter. If it is left blank doxygen will generate a standard footer. See
# LATEX_HEADER for more information on how to generate a default footer and what
# special commands can be used inside the footer.
#
# Note: Only use a user-defined footer if you know what you are doing!
# special commands can be used inside the footer. See also section "Doxygen
# usage" for information on how to generate the default footer that doxygen
# normally uses. Note: Only use a user-defined footer if you know what you are
# doing!
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_FOOTER =
@@ -1879,8 +1979,7 @@ USE_PDFLATEX = YES
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
# command to the generated LaTeX files. This will instruct LaTeX to keep running
# if errors occur, instead of asking the user for help. This option is also used
# when generating formulas in HTML.
# if errors occur, instead of asking the user for help.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
@@ -1893,16 +1992,6 @@ LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = NO
# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
# code with syntax highlighting in the LaTeX output.
#
# Note that which sources are shown also depends on other settings such as
# SOURCE_BROWSER.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
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
# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
@@ -1983,16 +2072,6 @@ RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
# with syntax highlighting in the RTF output.
#
# Note that which sources are shown also depends on other settings such as
# SOURCE_BROWSER.
# The default value is: NO.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_SOURCE_CODE = NO
#---------------------------------------------------------------------------
# Configuration options related to the man page output
#---------------------------------------------------------------------------
@@ -2089,15 +2168,6 @@ GENERATE_DOCBOOK = NO
DOCBOOK_OUTPUT = docbook
# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
# program listings (including syntax highlighting and cross-referencing
# information) to the DOCBOOK output. Note that enabling this will significantly
# increase the size of the DOCBOOK output.
# The default value is: NO.
# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
DOCBOOK_PROGRAMLISTING = NO
#---------------------------------------------------------------------------
# Configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
@@ -2184,7 +2254,8 @@ SEARCH_INCLUDES = YES
# The INCLUDE_PATH tag can be used to specify one or more directories that
# contain include files that are not input files but should be processed by the
# preprocessor.
# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of
# RECURSIVE has no effect here.
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
INCLUDE_PATH =
@@ -2276,15 +2347,6 @@ EXTERNAL_PAGES = YES
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
# NO turns the diagrams off. Note that this option also works with HAVE_DOT
# disabled, but it is recommended to install and use dot, since it yields more
# powerful graphs.
# The default value is: YES.
CLASS_DIAGRAMS = YES
# 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.
@@ -2303,7 +2365,7 @@ HIDE_UNDOC_RELATIONS = YES
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
# Bell Labs. The other options in this section have no effect if this option is
# set to NO
# The default value is: YES.
# The default value is: NO.
HAVE_DOT = YES
@@ -2341,11 +2403,14 @@ DOT_FONTSIZE = 10
DOT_FONTPATH =
# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
# each documented class showing the direct and indirect inheritance relations.
# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a
# graph for each documented class showing the direct and indirect inheritance
# relations. In case HAVE_DOT is set as well dot will be used to draw the graph,
# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set
# to TEXT the direct and indirect inheritance relations will be shown as texts /
# links.
# Possible values are: NO, YES, TEXT and GRAPH.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
CLASS_GRAPH = YES
@@ -2359,7 +2424,8 @@ CLASS_GRAPH = YES
COLLABORATION_GRAPH = YES
# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
# groups, showing the direct groups dependencies.
# groups, showing the direct groups dependencies. See also the chapter Grouping
# in the manual.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
@@ -2474,6 +2540,13 @@ GRAPHICAL_HIERARCHY = YES
DIRECTORY_GRAPH = YES
# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels
# of child directories generated in directory dependency graphs by dot.
# Minimum value: 1, maximum value: 25, default value: 1.
# This tag requires that the tag DIRECTORY_GRAPH is set to YES.
DIR_GRAPH_MAX_DEPTH = 1
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
# generated by dot. For an explanation of the image formats see the section
# output formats in the documentation of the dot tool (Graphviz (see:
@@ -2481,9 +2554,7 @@ DIRECTORY_GRAPH = YES
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
# to make the SVG files visible in IE 9+ (other browsers do not have this
# requirement).
# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo,
# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
# png:gdiplus:gdiplus.
# The default value is: png.
@@ -2529,10 +2600,10 @@ MSCFILE_DIRS =
DIAFILE_DIRS =
# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
# path where java can find the plantuml.jar file. If left blank, it is assumed
# PlantUML is not used or called during a preprocessing step. Doxygen will
# generate a warning when it encounters a \startuml command in this case and
# will not generate output for the diagram.
# path where java can find the plantuml.jar file or to the filename of jar file
# to be used. If left blank, it is assumed PlantUML is not used or called during
# a preprocessing step. Doxygen will generate a warning when it encounters a
# \startuml command in this case and will not generate output for the diagram.
PLANTUML_JAR_PATH =
@@ -2594,6 +2665,8 @@ DOT_MULTI_TARGETS = NO
# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
# explaining the meaning of the various boxes and arrows in the dot generated
# graphs.
# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal
# graphical representation for inheritance and collaboration diagrams is used.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
@@ -2602,8 +2675,8 @@ GENERATE_LEGEND = YES
# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate
# files that are used to generate the various graphs.
#
# Note: This setting is not only used for dot files but also for msc and
# plantuml temporary files.
# Note: This setting is not only used for dot files but also for msc temporary
# files.
# The default value is: YES.
DOT_CLEANUP = YES

View File

@@ -7,12 +7,13 @@
* [Introduction](#introduction)
* [Definitions](#definitions)
* [ENTT_NOEXCEPTION](#entt_noexcept)
* [ENTT_NOEXCEPTION](#entt_noexception)
* [ENTT_USE_ATOMIC](#entt_use_atomic)
* [ENTT_ID_TYPE](#entt_id_type)
* [ENTT_SPARSE_PAGE](#entt_sparse_page)
* [ENTT_PACKED_PAGE](#entt_packed_page)
* [ENTT_ASSERT](#entt_assert)
* [ENTT_ASSERT_CONSTEXPR](#entt_assert_constexpr)
* [ENTT_DISABLE_ASSERT](#entt_disable_assert)
* [ENTT_NO_ETO](#entt_no_eto)
* [ENTT_STANDARD_CPP](#entt_standard_cpp)
@@ -23,8 +24,9 @@
# Introduction
`EnTT` doesn't offer many hooks for customization but it certainly offers
some.<br/>
`EnTT` has become almost completely customizable over time, in many
respects. These variables are just one of the many ways to customize how it
works.<br/>
In the vast majority of cases, users will have no interest in changing the
default parameters. For all other cases, the list of possible configurations
with which it's possible to adjust the behavior of the library at runtime can be
@@ -40,17 +42,17 @@ will remain stable over time unlike the options below.
## ENTT_NOEXCEPTION
This parameter can be used to switch off exception handling in `EnTT`.<br/>
To do this, simply define the variable without assigning any value to it. This
is roughly equivalent to setting the compiler flag `-ff-noexceptions`.
Define this variable without assigning any value to it to turn off exception
handling in `EnTT`.<br/>
This is roughly equivalent to setting the compiler flag `-fno-exceptions` but is
also limited to this library only.
## ENTT_USE_ATOMIC
In general, `EnTT` doesn't offer primitives to support multi-threading. Many of
the features can be split over multiple threads without any explicit control and
the user is the only one who knows if and when a synchronization point is
required.<br/>
However, some features aren't easily accessible to users and can be made
the user is the one who knows if a synchronization point is required.<br/>
However, some features aren't easily accessible to users and are made
thread-safe by means of this definition.
## ENTT_ID_TYPE
@@ -70,9 +72,9 @@ power of 2.
## ENTT_PACKED_PAGE
Similar to sparse arrays, packed arrays of components are paginated as well. In
However, int this case the aim isn't to reduce memory usage but to have pointer
stability upon component creation.<br/>
As it happens with sparse arrays, packed arrays are also paginated. However, in
this case the aim isn't to reduce memory usage but to have pointer stability
upon component creation.<br/>
Default size of pages (that is, the number of elements they contain) is 1024 but
users can adjust it if appropriate. In all case, the chosen value **must** be a
power of 2.
@@ -83,21 +85,29 @@ For performance reasons, `EnTT` doesn't use exceptions or any other control
structures. In fact, it offers many features that result in undefined behavior
if not used correctly.<br/>
To get around this, the library relies on a lot of asserts for the purpose of
detecting errors in debug builds. By default, it uses `assert` internally, but
users are allowed to overwrite its behavior by setting this variable.
detecting errors in debug builds. By default, it uses `assert` internally. Users
are allowed to overwrite its behavior by setting this variable.
### ENTT_ASSERT_CONSTEXPR
Usually, an assert within a `constexpr` function isn't a big deal. However, in
case of extreme customizations, it might be useful to differentiate.<br/>
For this purpose, `EnTT` introduces an admittedly badly named variable to make
the job easier in this regard. By default, this variable forwards its arguments
to `ENTT_ASSERT`.
### ENTT_DISABLE_ASSERT
Assertions may in turn affect performance to an extent when enabled. Whether
`ENTT_ASSERT` is redefined or not, all asserts can be disabled at once by means
of this definition.<br/>
Note that `ENTT_DISABLE_ASSERT` takes precedence over the redefinition of
`ENTT_ASSERT` and is therefore meant to disable all controls no matter what.
`ENTT_ASSERT` and `ENTT_ASSERT_CONSTEXPR` are redefined or not, all asserts can
be disabled at once by means of this definition.<br/>
Note that `ENTT_DISABLE_ASSERT` takes precedence over the redefinition of the
other variables and is therefore meant to disable all controls no matter what.
## ENTT_NO_ETO
In order to reduce memory consumption and increase performance, empty types are
never stored by the ECS module of `EnTT`.<br/>
never instantiated nor stored by the ECS module of `EnTT`.<br/>
Use this variable to treat these types like all others and therefore to create a
dedicated storage for them.
@@ -105,7 +115,7 @@ dedicated storage for them.
`EnTT` mixes non-standard language features with others that are perfectly
compliant to offer some of its functionalities.<br/>
This definition will prevent the library from using non-standard techniques,
that is, functionalities that aren't fully compliant with the standard C++.<br/>
This definition prevents the library from using non-standard techniques, that
is, functionalities that aren't fully compliant with the standard C++.<br/>
While there are no known portability issues at the time of this writing, this
should make the library fully portable anyway if needed.

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

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -20,12 +20,13 @@
# Introduction
This is a constantly updated section where I'll try to put the answers to the
This is a constantly updated section where I'm trying to put the answers to the
most frequently asked questions.<br/>
If you don't find your answer here, there are two cases: nobody has done it yet
or this section needs updating. In both cases, try to
[open a new issue](https://github.com/skypjack/entt/issues/new) or enter the
[gitter channel](https://gitter.im/skypjack/entt) and ask your question.
or this section needs updating. In both cases, you can
[open a new issue](https://github.com/skypjack/entt/issues/new) or enter either
the [gitter channel](https://gitter.im/skypjack/entt) or the
[discord server](https://discord.gg/5BjPWBd) to ask for help.<br/>
Probably someone already has an answer for you and we can then integrate this
part of the documentation.
@@ -45,14 +46,14 @@ lot, achieving good results in many cases.
First of all, there are two things to do in a Windows project:
* Disable the [`/JMC`](https://docs.microsoft.com/cpp/build/reference/jmc)
option (_Just My Code_ debugging), available starting in Visual Studio 2017
option (_Just My Code_ debugging), available starting with Visual Studio 2017
version 15.8.
* 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_ASSERT` should be redefined to disable internal checks
made by `EnTT` in debug:
Moreover, set the `ENTT_DISABLE_ASSERT` variable or redefine the `ENTT_ASSERT`
macro to disable internal debug checks in `EnTT`:
```cpp
#define ENTT_ASSERT(...) ((void)0)
@@ -61,22 +62,22 @@ made by `EnTT` in debug:
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`
or preferably `O1`.
With these changes, debug performance should increase enough in most cases. If
you want something more, you can also switch to an optimization level `O0` or
preferably `O1`.
## How can I represent hierarchies with my components?
This is one of the first questions that anyone makes when starting to work with
the entity-component-system architectural pattern.<br/>
There are several approaches to the problem and whats the best one depends
mainly on the real problem one is facing. In all cases, how to do it doesn't
strictly depend on the library in use, but the latter can certainly allow or
not different techniques depending on how the data are laid out.
There are several approaches to the problem and the best one depends mainly on
the real problem one is facing. In all cases, how to do it doesn't strictly
depend on the library in use, but the latter certainly allows or not different
techniques depending on how the data are laid out.
I tried to describe some of the techniques that fit well with the model of
`EnTT`. [Here](https://skypjack.github.io/2019-06-25-ecs-baf-part-4/) is the
first post of a series that tries to explore the problem. More will probably
I tried to describe some of the approaches that fit well with the model of
`EnTT`. [This](https://skypjack.github.io/2019-06-25-ecs-baf-part-4/) is the
first post of a series that tries to _explore_ the problem. More will probably
come in future.<br/>
In addition, `EnTT` also offers the possibility to create stable storage types
and therefore have pointer stability for one, all or some components. This is by
@@ -90,6 +91,7 @@ Custom entity identifiers are definitely a good idea in two cases at least:
* If `std::uint32_t` isn't large enough for your purposes, since this is the
underlying type of `entt::entity`.
* If you want to avoid conflicts when using multiple registries.
Identifiers can be defined through enum classes and class types that define an
@@ -105,7 +107,7 @@ There is no limit to the number of identifiers that can be defined.
## Warning C4307: integral constant overflow
According to [this](https://github.com/skypjack/entt/issues/121) issue, using a
hashed string under VS could generate a warning.<br/>
hashed string under VS (toolset v141) could generate a warning.<br/>
First of all, I want to reassure you: it's expected and harmless. However, it
can be annoying.
@@ -136,7 +138,7 @@ errors during compilation.
It's a pretty big problem but fortunately it's not a problem of `EnTT` and there
is a fairly simple solution to it.<br/>
It consists in defining the `NOMINMAX` macro before to include any other header
It consists in defining the `NOMINMAX` macro before including any other header
so as to get rid of the extra definitions:
```cpp
@@ -149,15 +151,14 @@ more details.
## The standard and the non-copyable types
`EnTT` uses internally the trait `std::is_copy_constructible_v` to check if a
component is actually copyable. This trait doesn't check if an object can
actually be copied but only verifies if there is a copy constructor
available.<br/>
This can lead to surprising results due to some idiosyncrasies of the standard
mainly related to the need to guarantee backward compatibility.
component is actually copyable. However, this trait doesn't really check whether
a type is actually copyable. Instead, it just checks that a suitable copy
constructor and copy operator exist.<br/>
This can lead to surprising results due to some idiosyncrasies of the standard.
For example, `std::vector` defines a copy constructor no matter if its value
type is copyable or not. As a result, `std::is_copy_constructible_v` is true
for the following specialization:
For example, `std::vector` defines a copy constructor that is conditionally
enabled depending on whether the value type is copyable or not. As a result,
`std::is_copy_constructible_v` returns true for the following specialization:
```cpp
struct type {
@@ -165,21 +166,30 @@ struct type {
};
```
When trying to assign an instance of this type to an entity in the ECS part,
this may trigger a compilation error because we cannot really make a copy of
it.<br/>
As a workaround, users can mark the type explicitly as non-copyable:
However, the copy constructor is effectively disabled upon specialization.
Therefore, trying to assign an instance of this type to an entity may trigger a
compilation error.<br/>
As a workaround, users can mark the type explicitly as non-copyable. This also
suppresses the implicit generation of the move constructor and operator, which
will therefore have to be defaulted accordingly:
```cpp
struct type {
type(const type &) = delete;
type(type &&) = default;
type & operator=(const type &) = delete;
type & operator=(type &&) = default;
std::vector<std::unique_ptr<action>> vec;
};
```
Unfortunately, this will also disable aggregate initialization.
Note that aggregate initialization is also disabled as a consequence.<br/>
Fortunately, this type of trick is quite rare. The bad news is that there is no
way to deal with it at the library level, this being due to the design of the
language. On the other hand, the fact that the language itself also offers a way
to mitigate the problem makes it manageable.
## Which functions trigger which signals
@@ -189,7 +199,9 @@ If this isn't clear, below you can find a _vademecum_ for this purpose:
* `on_created` is invoked when a component is first added (neither modified nor
replaced) to an entity.
* `on_update` is called whenever an existing component is modified or replaced.
* `on_destroyed` is called when a component is explicitly or implicitly removed
from an entity.

299
docs/md/graph.md Normal file
View File

@@ -0,0 +1,299 @@
# Crash Course: graph
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [Data structures](#data-structures)
* [Adjacency matrix](#adjacency-matrix)
* [Graphviz dot language](#graphviz-dot-language)
* [Flow builder](#flow-builder)
* [Tasks and resources](#tasks-and-resources)
* [Fake resources and order of execution](#fake-resources-and-order-of-execution)
* [Sync points](#sync-points)
* [Execution graph](#execution-graph)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
`EnTT` doesn't aim to offer everything one needs to work with graphs. Therefore,
anyone looking for this in the _graph_ submodule will be disappointed.<br/>
Quite the opposite is true. This submodule is minimal and contains only the data
structures and algorithms strictly necessary for the development of some tools
such as the _flow builder_.
# Data structures
As anticipated in the introduction, the aim isn't to offer all possible data
structures suitable for representing and working with graphs. Many will likely
be added or refined over time, however I want to discourage anyone expecting
tight scheduling on the subject.<br/>
The data structures presented in this section are mainly useful for the
development and support of some tools which are also part of the same submodule.
## Adjacency matrix
The adjacency matrix is designed to represent either a directed or an undirected
graph:
```cpp
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{};
```
The `directed_tag` type _creates_ the graph as directed. There is also an
`undirected_tag` counterpart which creates it as undirected.<br/>
The interface deviates slightly from the typical double indexing of C and offers
an API that is perhaps more familiar to a C++ programmer. Therefore, the access
and modification of an element will take place via the `contains`, `insert` and
`erase` functions rather than a double call to an `operator[]`:
```cpp
if(adjacency_matrix.contains(0u, 1u)) {
adjacency_matrix.erase(0u, 1u);
} else {
adjacency_matrix.insert(0u, 1u);
}
```
Both `insert` and` erase` are idempotent functions which have no effect if the
element already exists or has already been deleted.<br/>
The first one returns an `std::pair` containing the iterator to the element and
a boolean value indicating whether the element has been inserted or was already
present. The second one instead returns the number of deleted elements (0 or 1).
An adjacency matrix must be initialized with the number of elements (vertices)
when constructing it but can also be resized later using the `resize` function:
```cpp
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
```
To visit all vertices, the class offers a function named `vertices` that returns
an iterable object suitable for the purpose:
```cpp
for(auto &&vertex: adjacency_matrix.vertices()) {
// ...
}
```
Note that the same result can be obtained with the following snippet, since the
vertices are unsigned integral values:
```cpp
for(auto last = adjacency_matrix.size(), pos = {}; pos < last; ++pos) {
// ...
}
```
As for visiting the edges, a few functions are available.<br/>
When the purpose is to visit all the edges of a given adjacency matrix, the
`edges` function returns an iterable object that can be used to get them as
pairs of vertices:
```cpp
for(auto [lhs, rhs]: adjacency_matrix.edges()) {
// ...
}
```
On the other hand, if the goal is to visit all the in- or out-edges of a given
vertex, the `in_edges` and `out_edges` functions are meant for that:
```cpp
for(auto [lhs, rhs]: adjacency_matrix.out_edges(3u)) {
// ...
}
```
As might be expected, these functions expect the vertex to visit (that is, to
return the in- or out-edges for) as an argument.<br/>
Finally, the adjacency matrix is an allocator-aware container and offers most of
the functionality one would expect from this type of containers, such as `clear`
or 'get_allocator` and so on.
## Graphviz dot language
As it's one of the most popular formats, the library offers minimal support for
converting a graph to a Graphviz dot snippet.<br/>
The simplest way is to pass both an output stream and a graph to the `dot`
function:
```cpp
std::ostringstream output{};
entt::dot(output, adjacency_matrix);
```
However, there is also the option of providing a callback to which the vertices
are passed and which can be used to add (`dot`) properties to the output from
time to time:
```cpp
std::ostringstream output{};
entt::dot(output, adjacency_matrix, [](auto &output, auto vertex) {
out << "label=\"v\"" << vertex << ",shape=\"box\"";
});
```
This second mode is particularly convenient when the user wants to associate
data managed externally to the graph being converted.
# Flow builder
A flow builder is used to create execution graphs from tasks and resources.<br/>
The implementation is as generic as possible and doesn't bind to any other part
of the library.
This class is designed as a sort of _state machine_ to which a specific task is
attached for which the resources accessed in read-only or read-write mode are
specified.<br/>
Most of the functions in the API also return the flow builder itself, according
to what is the common sense API when it comes to builder classes.
Once all tasks have been registered and resources assigned to them, an execution
graph in the form of an adjacency matrix is returned to the user.<br/>
This graph contains all the tasks assigned to the flow builder in the form of
_vertices_. The _vertex_ itself can be used as an index to get the identifier
passed during registration.
## Tasks and resources
Although these terms are used extensively in the documentation, the flow builder
has no real concept of tasks and resources.<br/>
This class works mainly with _identifiers_, that is, values of type `id_type`.
That is, both tasks and resources are identified by integral values.<br/>
This allows not to couple the class itself to the rest of the library or to any
particular data structure. On the other hand, it requires the user to keep track
of the association between identifiers and operations or actual data.
Once a flow builder has been created (which requires no constructor arguments),
the first thing to do is to bind a task. This will indicate to the builder who
intends to consume the resources that will be specified immediately after:
```cpp
entt::flow builder{};
builder.bind("task_1"_hs);
```
Note that the example uses the `EnTT` hashed string to generate an identifier
for the task.<br/>
Indeed, the use of `id_type` as an identifier type is not by accident. In fact,
it matches well with the internal hashed string class. Moreover, it's also the
same type returned by the hash function of the internal RTTI system, in case the
user wants to rely on that.<br/>
However, being an integral value, it leaves the user full freedom to rely on his
own tools if he deems it necessary.
Once a task has been associated with the flow builder, it can be assigned
read-only or read-write resources, as appropriate:
```cpp
builder
.bind("task_1"_hs)
.ro("resource_1"_hs)
.ro("resource_2"_hs)
.bind("task_2"_hs)
.rw("resource_2"_hs)
```
As mentioned, many functions return the builder itself and it's therefore easy
to concatenate the different calls.<br/>
Also in the case of resources, these are identified by numeric values of type
`id_type`. As above, the choice is not entirely random. This goes well with the
tools offered by the library while leaving room for maximum flexibility.
Finally, both the `ro` and` rw` functions also offer an overload that accepts a
pair of iterators, so that one can pass a range of resources in one go.
## Fake resources and order of execution
The flow builder doesn't offer the ability to specify when a task should execute
before or after another task.<br/>
In fact, the order of _registration_ on the resources also determines the order
in which the tasks are processed during the generation of the execution graph.
However, there is a way to force the execution order of two processes.<br/>
Briefly, since accessing a resource in opposite modes requires sequential rather
than parallel scheduling, it's possible to make use of fake resources to force
the order execution:
```cpp
builder
.bind("task_1"_hs)
.ro("resource_1"_hs)
.rw("fake"_hs)
.bind("task_2"_hs)
.ro("resource_2"_hs)
.ro("fake"_hs)
.bind("task_3"_hs)
.ro("resource_2"_hs)
.ro("fake"_hs)
```
This snippet forces the execution of `task_2` and `task_3` **after** `task_1`.
This is due to the fact that the latter sets a read-write requirement on a fake
resource that the other tasks also want to access in read-only mode.<br/>
Similarly, it's possible to force a task to run after a certain group:
```cpp
builder
.bind("task_1"_hs)
.ro("resource_1"_hs)
.ro("fake"_hs)
.bind("task_2"_hs)
.ro("resource_1"_hs)
.ro("fake"_hs)
.bind("task_3"_hs)
.ro("resource_2"_hs)
.rw("fake"_hs)
```
In this case, since there are a number of processes that want to read a specific
resource, they will do so in parallel by forcing `task_3` to run after all the
others tasks.
## Sync points
Sometimes it's useful to assign the role of _sync point_ to a node.<br/>
Whether it accesses new resources or is simply a watershed, the procedure for
assigning this role to a vertex is always the same: first it's tied to the flow
builder, then the `sync` function is invoked:
```cpp
builder.bind("sync_point"_hs).sync();
```
The choice to assign an _identity_ to this type of nodes lies in the fact that,
more often than not, they also perform operations on resources.<br/>
If this isn't the case, it will still be possible to create no-op vertices to
which empty tasks are assigned.
## Execution graph
Once both the resources and their consumers have been properly registered, the
purpose of this tool is to generate an execution graph that takes into account
all specified constraints to return the best scheduling for the vertices:
```cpp
entt::adjacency_matrix<entt::directed_tag> graph = builder.graph();
```
The search for the main vertices, that is those without in-edges, is usually the
first thing required:
```cpp
for(auto &&vertex: graph) {
if(auto in_edges = graph.in_edges(vertex); in_edges.begin() == in_edges.end()) {
// ...
}
}
```
Starting from them, using the other functions appropriately (such as `out_edges`
to retrieve the children of a given task or `edges` to access their identifiers)
it will be possible to instantiate an execution graph.

View File

@@ -6,7 +6,7 @@
# Table of Contents
* [Working across boundaries](#working-across-boundaries)
* [The EnTT way](#the-entt-way)
* [Smooth until proven otherwise](#smooth-until-proven-otherwise)
* [Meta context](#meta-context)
* [Memory management](#memory-management)
<!--
@@ -19,88 +19,60 @@
general and on GNU/Linux when default visibility was set to hidden. The
limitation was mainly due to a custom utility used to assign unique, sequential
identifiers with different types.<br/>
Fortunately, nowadays using `EnTT` across boundaries is easier. However, use in
standalone applications is favored and user intervention is otherwise required.
Fortunately, nowadays using `EnTT` across boundaries is much easier.
## The EnTT way
## Smooth until proven otherwise
Many classes in `EnTT` make extensive use of type erasure for their purposes.
This isn't a problem in itself (in fact, it's the basis of an API so convenient
This isn't a problem on itself (in fact, it's the basis of an API so convenient
to use). However, a way is needed to recognize the objects whose type has been
erased on the other side of a boundary.<br/>
The `type_hash` class template is how identifiers are generated and thus made
available to the rest of the library. The `type_seq` class template makes all
types _indexable_ instead, so as to speed up the lookup.
available to the rest of the library. In general, this class doesn't arouse much
interest. The only exception is when a conflict between identifiers occurs
(definitely uncommon though) or when the default solution proposed by `EnTT`
isn't suitable for the user's purposes.<br/>
The section dedicated to `type_info` contains all the details to get around the
issue in a concise and elegant way. Please refer to the specific documentation.
In general, these classes don't arouse much interest. The only exceptions are:
* When a conflict between identifiers occurs (definitely uncommon though) or
when the default solution proposed by `EnTT` isn't suitable for the user's
purposes.<br/>
The section dedicated to `type_info` contains all the details to get around
the problem in a concise and elegant way. Please refer to the specific
documentation.
* When working with linked libraries that also export all required symbols.<br/>
Compile definitions `ENTT_API_EXPORT` and `ENTT_API_IMPORT` should be passed
respectively where there is a need to import or export the symbols defined by
`EnTT`, so as to make everything work nicely across boundaries.
* When working with plugins or shared libraries that don't export any symbol. In
this case, `type_seq` confuses the other classes by giving potentially wrong
information to them.<br/>
To avoid problems, it's required to provide a custom generator. Briefly, it's
necessary to specialize the `type_seq` class and make it point to a context
that is also shared between the main application and the dynamically loaded
libraries or plugins.<br/>
This will make the type system available to the whole application, not just to
a particular tool such as the registry or the dispatcher. It means that a call
to `type_seq::value()` will return the same identifier for the same type from
both sides of a boundary and can be used reliably for any purpose.
When working with linked libraries, compile definitions `ENTT_API_EXPORT` and
`ENTT_API_IMPORT` can be used where there is a need to import or export symbols,
so as to make everything work nicely across boundaries.<br/>
On the other hand, everything should run smoothly when working with plugins or
shared libraries that don't export any symbols.
For anyone who needs more details, the test suite contains multiple examples
covering the most common cases (see the `lib` directory for all details).<br/>
It goes without saying that it's impossible to cover all the possible cases.
It goes without saying that it's impossible to cover **all** possible cases.
However, what is offered should hopefully serve as a basis for all of them.
## Meta context
The runtime reflection system deserves a special mention when it comes to using
it across boundaries.<br/>
Since it's linked already 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.
Since it's linked already to a static context to which the elements are attached
and different contexts don't relate to each other, they must be _shared_ to
allow the use of meta types across boundaries.
Sharing a context is trivial though. First of all, the local one must be
acquired in the main space:
Fortunately, sharing a context is also trivial to do. First of all, the local
one is acquired in the main space:
```cpp
entt::meta_ctx ctx{};
auto handle = entt::locator<entt::meta_ctx>::handle();
```
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:
Then, it's passed to the receiving space that sets it as its default context,
thus discarding or storing aside the local one:
```cpp
entt::meta_ctx::bind(ctx);
entt::locator<entt::meta_ctx>::reset(handle);
```
From now on, both spaces will refer to the same context and on it will be
attached the new visible meta types, no matter where they are created.<br/>
A context can also be reset and then associated again locally as:
```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.
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.
From now on, both spaces refer to the same context and on it are attached all
new meta types, no matter where they are created.<br/>
Note that resetting the main context doesn't also propagate changes across
boundaries. In other words, resetting a context results in the decoupling of the
two sides and therefore a divergence in the contents.
## Memory Management

View File

@@ -20,14 +20,24 @@ I hope this list can grow much more in the future:
* [Minecraft Earth](https://www.minecraft.net/en-us/about-earth) by
[Mojang](https://mojang.com/): an augmented reality game for mobile, that
lets users bring Minecraft into the real world.
* [Ember Sword](https://embersword.com/): a modern Free-to-Play MMORPG with a
player-driven economy, a classless combat system, and scarce, tradable
cosmetic collectibles.
* Apparently [Diablo II: Resurrected](https://diablo2.blizzard.com/) by
[Blizzard](https://www.blizzard.com/): monsters, heroes, items, spells, all
resurrected. Thanks unknown insider.
* [Apparently](https://www.youtube.com/watch?v=P8xvOA3ikrQ&t=1105s)
[Call of Duty: Vanguard](https://www.callofduty.com/vanguard) by
[Sledgehammer Games](https://www.sledgehammergames.com/): I can neither
confirm nor deny but there is a license I know in the credits.
* Apparently [D&D Dark Alliance](https://darkalliance.wizards.com) by
[Wizards of the Coast](https://company.wizards.com): your party, their
funeral.
* [TiltedOnline](https://github.com/tiltedphoques/TiltedOnline) by
[Tilted Phoques](https://github.com/tiltedphoques): Skyrim and Fallout 4 mod
to play online.
* [Antkeeper](https://github.com/antkeeper/antkeeper-source): an ant colony
simulation [game](https://antkeeper.com/).
* [War of Rights](https://store.steampowered.com/app/424030/War_of_Rights/): a
multiplayer game set during the perilous days of the American Civil War, by
Campfire Games.
* [Openblack](https://github.com/openblack/openblack): open source
reimplementation of the game _Black & White_ (2001).
* [Land of the Rair](https://github.com/LandOfTheRair/core2): the new backend
@@ -44,7 +54,7 @@ I hope this list can grow much more in the future:
puzzler with logic gates and other cool stuff.
[Check it out](https://indi-kernick.itch.io/the-machine-web-version).
* [EnTTPong](https://github.com/DomRe/EnttPong): a basic game made to showcase
different parts of EnTT and C++17.
different parts of `EnTT` and C++17.
* [Randballs](https://github.com/gale93/randballs): simple `SFML` and `EnTT`
playground.
* [EnTT Tower Defense](https://github.com/Daivuk/tddod): a data oriented tower
@@ -70,8 +80,28 @@ I hope this list can grow much more in the future:
arcade game about shooting dirty rocks in space, inspired by Asteroids.
* [Wanderer](https://github.com/albin-johansson/wanderer): a 2D exploration
based indie game.
* [Spelunky® Classic remake](https://github.com/dbeef/spelunky-psp): A truly
* [Spelunky® Classic remake](https://github.com/dbeef/spelunky-psp): a truly
multiplatform experience with a rewrite from scratch.
* [CubbyTower](https://github.com/utilForever/CubbyTower): a simple tower
defense game using C++ with Entity Component System (ECS).
* [Runeterra](https://github.com/utilForever/Runeterra): Legends of Runeterra
simulator using C++ with some reinforcement learning.
* [Black Sun](https://store.steampowered.com/app/1670930/Black_Sun/): fly your
space ship through a large 2D open world.
* [PokeMaster](https://github.com/utilForever/PokeMaster): Pokemon Battle
simulator using C++ with some reinforcement learning.
* [HomeHearth](https://youtu.be/GrEWl8npL9Y): choose your hero, protect the
town, before it's too late.
* [City Builder Game](https://github.com/PhiGei2000/CityBuilderGame): a simple
city-building game using C++ and OpenGL.
* [BattleSub](https://github.com/bfeldpw/battlesub): two player 2D submarine
game with some fluid dynamics.
* [Crimson Rush](https://github.com/WilKam01/LuaCGame): a dungeon-crawler and
rougelike inspired game about exploring and surviving as long as possible.
* [Space Fight](https://github.com/cholushkin/SpaceFight): one screen
multi-player arcade shooter game prototype.
* [Confetti Party](https://github.com/hexerei/entt-confetti): C++ sample
application as a starting point using `EnTT` and `SDL2`.
* Engines and the like:
* [Aether Engine](https://hadean.com/spatial-simulation/)
@@ -92,6 +122,9 @@ I hope this list can grow much more in the future:
vrooooommm.
* [Antara Gaming SDK](https://github.com/KomodoPlatform/antara-gaming-sdk):
the Komodo Gaming Software Development Kit.
* [XVP](https://ravingbots.com/xvp-expansive-vehicle-physics-for-unreal-engine/):
[_eXpansive Vehicle Physics_](https://github.com/raving-bots/xvp/wiki/Plugin-integration-guide)
plugin for Unreal Engine.
* [Apparently](https://teamwisp.github.io/credits/)
[Wisp](https://teamwisp.github.io/product/) by
[Team Wisp](https://teamwisp.github.io/): an advanced real-time ray tracing
@@ -116,6 +149,25 @@ I hope this list can grow much more in the future:
framework in C++17 for backend development.
* [Unity/EnTT](https://github.com/TongTungGiang/unity-entt): tech demo of a
native simulation layer using `EnTT` and `Unity` as a rendering engine.
* [OverEngine](https://github.com/OverShifted/OverEngine): an over-engineered
game engine.
* [Electro](https://github.com/Electro-Technologies/Electro): high performance
3D game engine with a high emphasis on rendering.
* [Kawaii](https://github.com/Mathieu-Lala/Kawaii_Engine): a modern data
oriented game engine.
* [Becketron](https://github.com/Doctor-Foxling/Becketron): a game engine
written mostly in C++.
* [Spatial Engine](https://github.com/luizgabriel/Spatial.Engine): a
cross-platform engine created on top of google's filament rendering engine.
* [Kaguya](https://github.com/KaiH0717/Kaguya): D3D12 Rendering Engine.
* [OpenAWE](https://github.com/OpenAWE-Project/OpenAWE): open implementation
of the Alan Wake Engine.
* [Nazara Engine](https://github.com/DigitalPulseSoftware/NazaraEngine): fast,
cross-platform, object-oriented API to help in daily developer life.
* [Billy Engine](https://github.com/billy4479/BillyEngine): some kind of a 2D
engine based on `SDL2` and `EnTT`.
* [Ducktape](https://github.com/DucktapeEngine/Ducktape): an open source C++
2D & 3D game engine that focuses on being fast and powerful.
* Articles, videos and blog posts:
* [Some posts](https://skypjack.github.io/tags/#entt) on my personal
@@ -163,6 +215,9 @@ I hope this list can grow much more in the future:
project retrospect by [Eric Hildebrand](https://www.erichildebrand.net/).
* [EnTT Entity Component System Gaming Library](https://gamefromscratch.com/entt-entity-component-system-gaming-library/):
`EnTT` on GameFromScratch.com.
* [Custom C++ server for UE5](https://youtu.be/fbXZVNCOvjM) optimized for
MMO(RPG)s and its [follow-up](https://youtu.be/yGlZeopx2hU) episode about
player bots and full external ECS: a series definitely worth looking at.
* Any Other Business:
* [ArcGIS Runtime SDKs](https://developers.arcgis.com/arcgis-runtime/) by
@@ -180,6 +235,8 @@ I hope this list can grow much more in the future:
controller emulator and renderer.
* [Ragdoll](https://ragdolldynamics.com/): real-time physics for Autodesk Maya
2020.
* [Project Lagrange](https://github.com/adobe/lagrange): a robust geometry
processing library by [Adobe](https://github.com/adobe).
* [AtomicDEX](https://github.com/KomodoPlatform/atomicDEX-Desktop): a secure
wallet and non-custodial decentralized exchange rolled into one application.
* [Apparently](https://www.linkedin.com/in/skypjack/)

View File

@@ -7,69 +7,82 @@
* [Introduction](#introduction)
* [Service locator](#service-locator)
* [Opaque handles](#opaque-handles)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
Usually service locators are tightly bound to the services they expose and it's
hard to define a general purpose solution. This template based implementation
tries to fill the gap and to get rid of the burden of defining a different
specific locator for each application.<br/>
This class is tiny, partially unsafe and thus risky to use. Moreover it doesn't
fit probably most of the scenarios in which a service locator is required. Look
at it as a small tool that can sometimes be useful if users know how to handle
it.
Usually, service locators are tightly bound to the services they expose and it's
hard to define a general purpose solution.<br/>
This tiny class tries to fill the gap and gets rid of the burden of defining a
different specific locator for each application.
# Service locator
The API is straightforward. The basic idea is that services are implemented by
means of interfaces and rely on polymorphism.<br/>
The locator is instantiated with the base type of the service if any and a
concrete implementation is provided along with all the parameters required to
initialize it. As an example:
The service locator API tries to mimic that of `std::optional` and adds some
extra functionalities on top of it such as allocator support.<br/>
There are a couple of functions to set up a service, namely `emplace` and
`allocate_emplace`:
```cpp
// the service has no base type, a locator is used to treat it as a kind of singleton
entt::service_locator<my_service>::set(params...);
// sets up an opaque service
entt::service_locator<audio_interface>::set<audio_implementation>(params...);
// resets (destroys) the service
entt::service_locator<audio_interface>::reset();
entt::locator<interface>::emplace<service>(argument);
entt::locator<interface>::allocate_emplace<service>(allocator, argument);
```
The locator can also be queried to know if an active service is currently set
and to retrieve it if necessary (either as a pointer or as a reference):
The difference is that the latter expects an allocator as the first argument and
uses it to allocate the service itself.<br/>
Once a service is set up, it's retrieved using the `value` function:
```cpp
// no service currently set
auto empty = entt::service_locator<audio_interface>::empty();
// gets a (possibly empty) shared pointer to the service ...
std::shared_ptr<audio_interface> ptr = entt::service_locator<audio_interface>::get();
// ... or a reference, but it's undefined behaviour if the service isn't set yet
audio_interface &ref = entt::service_locator<audio_interface>::ref();
interface &service = entt::locator<interface>::value();
```
A common use is to wrap the different locators in a container class, creating
aliases for the various services:
Since the service may not be set (and therefore this function may result in an
undefined behavior), the `has_value` and `value_or` functions are also available
to test a service locator and to get a fallback service in case there is none:
```cpp
struct locator {
using camera = entt::service_locator<camera_interface>;
using audio = entt::service_locator<audio_interface>;
// ...
};
// ...
void init() {
locator::camera::set<camera_null>();
locator::audio::set<audio_implementation>(params...);
if(entt::locator<interface>::has_value()) {
// ...
}
interface &service = entt::locator<interface>::value_or<fallback_impl>(argument);
```
All arguments are used only if necessary, that is, if a service doesn't already
exist and therefore the fallback service is constructed and returned. In all
other cases, they are discarded.<br/>
Finally, to reset a service, use the `reset` function.
## Opaque handles
Sometimes it's useful to _transfer_ a copy of a service to another locator. For
example, when working across boundaries it's common to _share_ a service with a
dynamically loaded module.<br/>
Options aren't much in this case. Among these is the possibility of _exporting_
services and assigning them to a different locator.
This is what the `handle` and `reset` functions are meant for.<br/>
The former returns an opaque object useful for _exporting_ (or rather, obtaining
a reference to) a service. The latter also accepts an optional argument to a
handle which then allows users to reset a service by initializing it with an
opaque handle:
```cpp
auto handle = entt::locator<interface>::handle();
entt::locator<interface>::reset(handle);
```
It's worth noting that it's possible to get handles for uninitialized services
and use them with other locators. Of course, all a user will get is to have an
uninitialized service elsewhere as well.
Note that exporting a service allows users to _share_ the object currently set
in a locator. Replacing it won't replace the element even where a service has
been configured with a handle to the previous item.<br/>
In other words, if an audio service is replaced with a null object to silence an
application and the original service was shared, this operation won't propagate
to the other locators. Therefore, a module that share the ownership of the
original audio service is still able to emit sounds.

View File

@@ -13,11 +13,14 @@
* [Container support](#container-support)
* [Pointer-like types](#pointer-like-types)
* [Template information](#template-information)
* [Automatic conversions](#automatic-conversions)
* [Implicitly generated default constructor](#implicitly-generated-default-constructor)
* [From void to any](#from-void-to-any)
* [Policies: the more, the less](#policies-the-more-the-less)
* [Named constants and enums](#named-constants-and-enums)
* [Properties and meta objects](#properties-and-meta-objects)
* [Unregister types](#unregister-types)
* [Meta context](#meta-context)
<!--
@endcond TURN_OFF_DOXYGEN
-->
@@ -25,7 +28,7 @@
# Introduction
Reflection (or rather, its lack) is a trending topic in the C++ world and a tool
that can unlock a lot of interesting feature in the specific case of `EnTT`. I
that can unlock a lot of interesting features in the specific case of `EnTT`. I
looked for a third-party library that met my needs on the subject, but I always
came across some details that I didn't like: macros, being intrusive, too many
allocations, and so on.<br/>
@@ -41,22 +44,22 @@ 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/>
This means that users can assign any type of identifier to the meta objects, as
long as they are numeric. It doesn't matter if they are generated at runtime, at
long as they're numeric. It doesn't matter if they're generated at runtime, at
compile-time or with custom functions.
That being said, the examples in the following sections are all based on the
`hashed_string` class as provided by this library. Therefore, where an
identifier is required, it's likely that an user defined literal is used as
identifier is required, it's likely that a user defined literal is used as
follows:
```cpp
auto factory = entt::meta<my_type>().type("reflected_type"_hs);
```
For what it's worth, this is likely completely equivalent to:
For what it's worth, this is completely equivalent to:
```cpp
auto factory = entt::meta<my_type>().type(42);
auto factory = entt::meta<my_type>().type(42u);
```
Obviously, human-readable identifiers are more convenient to use and highly
@@ -74,20 +77,17 @@ type to reflect as a template parameter:
auto factory = entt::meta<my_type>();
```
This isn't enough to _export_ the given type and make it visible though.<br/>
The returned value is a factory object to use to continue building the meta
type. In order to make the type _visible_, users can assign it an identifier:
type.
By default, a meta type is associated with the identifier returned by the
runtime type identification system built-in in `EnTT`.<br/>
However, it's also possible to assign custom identifiers to meta types:
```cpp
auto factory = entt::meta<my_type>().type("reflected_type"_hs);
```
Or use the default one, that is, the built-in identifier for the given type:
```cpp
auto factory = entt::meta<my_type>().type();
```
Identifiers are important because users can retrieve meta types at runtime by
searching for them by _name_ other than by type.<br/>
On the other hand, there are cases in which users can be interested in adding
@@ -109,9 +109,10 @@ decorated version of it. This object can be used to add the following:
entt::meta<my_type>().ctor<int, char>().ctor<&factory>();
```
* _Destructors_. Free functions can be set as destructors of reflected types.
The purpose is to give users the ability to free up resources that require
special treatment before an object is actually destroyed.<br/>
* _Destructors_. Free functions and member functions can be used as destructors
of reflected types. The purpose is to give users the ability to free up
resources that require special treatment before an object is actually
destroyed.<br/>
Use the `dtor` member function for this purpose:
```cpp
@@ -146,6 +147,12 @@ decorated version of it. This object can be used to add the following:
entt::meta<my_type>().data<nullptr, &my_type::data_member>("member"_hs);
```
Multiple setters are also supported by means of a `value_list` object:
```cpp
entt::meta<my_type>().data<entt::value_list<&from_int, &from_string>, &my_type::data_member>("member"_hs);
```
Refer to the inline documentation for all the details.
* _Member functions_. Both real member functions of the underlying type and free
@@ -249,38 +256,25 @@ auto by_id = entt::resolve("reflected_type"_hs);
auto by_type_id = entt::resolve(entt::type_id<my_type>());
```
There exits also an overload of the `resolve` function to use to iterate all the
reflected types at once. It returns an iterable object that can be used in a
There exists also an overload of the `resolve` function to use to iterate all
the reflected types at once. It returns an iterable object that can be used in a
range-for loop:
```cpp
for(auto type: entt::resolve()) {
for(auto &&[id, type]: entt::resolve()) {
// ...
}
```
In all cases, the returned value is an instance of `meta_type`. This kind of
objects offer an API to know their _runtime identifiers_, to iterate all the
meta objects associated with them and even to build instances of the underlying
type.<br/>
In all cases, the returned value is an instance of `meta_type` (possibly with
its id). This kind of objects offer an API to know their _runtime identifiers_,
to iterate all the meta objects associated with them and even to build instances
of the underlying type.<br/>
Refer to the inline documentation for all the details.
The meta objects that compose a meta type are accessed in the following ways:
Meta data members and functions are accessed by name among the other things:
* _Meta constructors_. They are accessed by types of arguments:
```cpp
auto ctor = entt::resolve<my_type>().ctor<int, char>();
```
The returned type is `meta_ctor` and may be invalid if there is no constructor
that accepts the supplied arguments or at least some types from which they are
derived or to which they can be converted.<br/>
A meta constructor offers an API to know the number of its arguments and their
expected meta types. Furthermor, it's possible to invoke it and therefore to
construct new instances of the underlying type.
* _Meta data_. They are accessed by _name_:
* Meta data members:
```cpp
auto data = entt::resolve<my_type>().data("member"_hs);
@@ -292,7 +286,7 @@ The meta objects that compose a meta type are accessed in the following ways:
know if it's a const or a static one), to get the meta type of the variable
and to set or get the contained value.
* _Meta functions_. They are accessed by _name_:
* Meta function members:
```cpp
auto func = entt::resolve<my_type>().func("member"_hs);
@@ -306,16 +300,7 @@ The meta objects that compose a meta type are accessed in the following ways:
addition, a meta function object can be used to invoke the underlying function
and then get the return value in the form of a `meta_any` object.
* _Meta bases_. They are accessed through the _name_ of the base types:
```cpp
auto base = entt::resolve<derived_type>().base("base"_hs);
```
The returned type is `meta_type` and may be invalid if there is no meta base
object associated with the given identifier.
All the objects thus obtained as well as the meta types can be explicitly
All the meta objects thus obtained as well as the meta types can be explicitly
converted to a boolean value to check if they are valid:
```cpp
@@ -324,16 +309,17 @@ if(auto func = entt::resolve<my_type>().func("member"_hs); func) {
}
```
Furthermore, all them are also returned by specific overloads that provide the
caller with iterable ranges of top-level elements. As an example:
Furthermore, all them (and a few more, like meta basis) are returned by a bunch
of overloads that provide the caller with iterable ranges of top-level elements.
As an example:
```cpp
for(auto data = entt::resolve<my_type>().data()) {
for(auto &&[id, type]: entt::resolve<my_type>().base()) {
// ...
}
```
A meta type can be used to `construct` actual instances of the underlying
A meta type can also 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 then returns a `meta_any` object that may
@@ -363,13 +349,15 @@ meta system in many cases.
To make a container be recognized as such by the meta system, users are required
to provide specializations for either the `meta_sequence_container_traits` class
or the `meta_associative_container_traits` class, according with the actual type
or the `meta_associative_container_traits` class, according to the actual type
of the container.<br/>
`EnTT` already exports the specializations for some common classes. In
particular:
* `std::vector` and `std::array` are exported as _sequence containers_.
* `std::map`, `std::set` and their unordered counterparts are exported as
* `std::vector`, `std::array`, `std::deque` and `std::list` (but not
`std::forward_list`) are supported as _sequence containers_.
* `std::map`, `std::set` and their unordered counterparts are supported as
_associative containers_.
It's important to include the header file `container.hpp` to make these
@@ -421,7 +409,7 @@ to case. In particular:
```
* The `resize` member function allows to resize the wrapped container and
returns true in case of succes:
returns true in case of success:
```cpp
const bool ok = view.resize(3u);
@@ -495,7 +483,11 @@ to case. In particular:
The function returns instances of `meta_any` that directly refer to the actual
elements. Modifying the returned object will then directly modify the element
inside the container.
inside the container.<br/>
Depending on the underlying sequence container, this operation may not be as
efficient. For example, in the case of an `std::list`, a positional access
translates to a linear visit of the list itself (probably not what the user
expects).
Similarly, also the interface of the `meta_associative_container` proxy object
is the same for all types of associative containers. However, there are some
@@ -664,7 +656,7 @@ regardless of the pointed type, no user intervention is required.
Meta types also provide a minimal set of information about the nature of the
original type in case it's a class template.<br/>
By default, this works out of the box and requires no user action. However, it's
important to include the header file `template.hpp` to make these information
important to include the header file `template.hpp` to make this information
available to the compiler when needed.
Meta template information are easily found:
@@ -715,6 +707,56 @@ correspondence between real types and meta types.<br/>
Therefore, the specialization will be used as is and the information it contains
will be associated with the appropriate type when required.
## Automatic conversions
In C++, there are a number of conversions allowed between arithmetic types that
make it convenient to work with this kind of data.<br/>
If this were to be translated into explicit registrations with the reflection
system, it would result in a long series of instructions such as the following:
```cpp
entt::meta<int>()
.conv<bool>()
.conv<char>()
// ...
.conv<double>();
```
Repeated for each type eligible to undergo this type of conversions. This is
both error-prone and repetitive.<br/>
Similarly, the language allows users to silently convert unscoped enums to their
underlying types and offers what it takes to do the same for scoped enums. It
would result in the following if it were to be done explicitly:
```cpp
entt::meta<my_enum>()
.conv<std::underlying_type_t<my_enum>>();
```
Fortunately, all of this can also be avoided. `EnTT` offers implicit support for
these types of conversions:
```cpp
entt::meta_any any{42};
any.allow_cast<double>();
double value = any.cast<double>();
```
With no need for registration, the conversion takes place automatically under
the hood. The same goes for a call to `allow_cast` involving a meta type:
```cpp
entt::meta_type type = entt::resolve<int>();
entt::meta_any any{my_enum::a_value};
any.allow_cast(type);
int value = any.cast<int>();
```
This should make working with arithmetic types and scoped or unscoped enums as
easy as it is in C++.<br/>
It's also worth noting that it's still possible to set up conversion functions
manually and these will always be preferred over the automatic ones.
## Implicitly generated default constructor
In many cases, it's useful to be able to create objects of default constructible
@@ -726,14 +768,7 @@ them.
For this reason and only for default constructible types, default constructors
are automatically defined and associated with their meta types, whether they are
explicitly or implicitly generated.<br/>
Therefore, it won't be necessary to do this in order to construct an integer
from its meta type:
```cpp
entt::meta<int>().ctor<>();
```
Instead, just do this:
Therefore, this is all is needed to construct an integer from its meta type:
```cpp
entt::resolve<int>().construct();
@@ -742,11 +777,26 @@ entt::resolve<int>().construct();
Where the meta type can be for example the one returned from a meta container,
useful for building keys without knowing or having to register the actual types.
In all cases, when users register custom defaul constructors, they are preferred
both during searches and when the `construct` member function is invoked.<br/>
However, the implicitly generated default constructor will always be returned,
either if one is not explicitly specified or if all constructors are iterated
for some reason (in this case, it will always be the last element).
In all cases, when users register default constructors, they are preferred both
during searches and when the `construct` member function is invoked.
## From void to any
Sometimes all a user has is an opaque pointer to an object of a known meta type.
It would be handy in this case to be able to construct a `meta_any` object from
them.<br/>
For this purpose, the `meta_type` class offers a `from_void` member function
designed to convert an opaque pointer into a `meta_any`:
```cpp
entt::meta_any any = entt::resolve(id).from_void(element);
```
It goes without saying that it's not possible to do a check on the actual type.
Therefore, this call can be considered as a _static cast_ with all the problems
and undefined behaviors of the case following errors.<br/>
On the other hand, the ability to construct a `meta_any` from an opaque pointer
opens the door to some pretty interesting uses that are worth exploring.
## Policies: the more, the less
@@ -771,9 +821,11 @@ There are a few alternatives available at the moment:
* The _as-void_ policy, associated with the type `entt::as_void_t`.<br/>
Its purpose is to discard the return value of a meta object, whatever it is,
thus making it appear as if its type were `void`:
```cpp
entt::meta<my_type>().func<&my_type::member_function, entt::as_void_t>("member"_hs);
```
If the use with functions is obvious, it must be said that it's also possible
to use this policy with constructors and data members. In the first case, the
constructor will be invoked but the returned wrapper will actually be empty.
@@ -785,9 +837,11 @@ There are a few alternatives available at the moment:
Accessing the object contained in the wrapper for which the _reference_ was
requested will make it possible to directly access the instance used to
initialize the wrapper itself:
```cpp
entt::meta<my_type>().data<&my_type::data_member, entt::as_ref_t>("member"_hs);
```
These policies work with constructors (for example, when objects are taken
from an external container rather than created on demand), data members and
functions in general.<br/>
@@ -842,7 +896,7 @@ Sometimes (for example, when it comes to creating an editor) it might be useful
to attach properties to the meta objects created. Fortunately, this is possible
for most of them.<br/>
For the meta objects that support properties, the member functions of the
factory used for registering them will return a decorated version of the factory
factory used for registering them will return an extended version of the factory
itself. The latter can be used to attach properties to the last created meta
object.<br/>
Apparently, it's more difficult to say than to do:
@@ -851,73 +905,28 @@ Apparently, it's more difficult to say than to do:
entt::meta<my_type>().type("reflected_type"_hs).prop("tooltip"_hs, "message");
```
Properties are always in the key/value form. There are no restrictions on the
type of the key or value, as long as they are copy constructible objects.<br/>
Multiple formats are supported when it comes to defining a property:
* Properties as key/value pairs:
```cpp
entt::meta<my_type>().type("reflected_type"_hs).prop("tooltip"_hs, "message");
```
* Properties as `std::pair`s:
```cpp
entt::meta<my_type>().type("reflected_type"_hs).prop(std::make_pair("tooltip"_hs, "message"));
```
* Key only properties:
```cpp
entt::meta<my_type>().type("reflected_type"_hs).prop(my_enum::key_only);
```
* Properties as `std::tuple`s:
```cpp
entt::meta<my_type>().type("reflected_type"_hs).prop(std::make_tuple(std::make_pair("tooltip"_hs, "message"), my_enum::key_only));
```
A tuple contains one or more properties. All of them are treated individually.
* Annotations:
```cpp
entt::meta<my_type>().type("reflected_type"_hs).prop(&property_generator);
```
An annotation is an invocable object that returns one or more properties. All
of them are treated individually.
It's possible to invoke the `prop` function several times if needed, one for
each property to associate with the last meta object created:
Properties are always in the key/value form. The key is a numeric identifier,
mostly similar to the identifier used to register meta objects. There are no
restrictions on the type of the value instead, as long as it's movable.<br/>
Key only properties are also supported out of the box:
```cpp
entt::meta<my_type>()
.type("reflected_type"_hs)
.prop(entt::hashed_string{"Name"}, "Reflected Type")
.data<&my_type::data_member>("member"_hs)
.prop(std::make_pair("tooltip"_hs, "Member"))
.prop(my_enum::a_value, 42);
entt::meta<my_type>().type("reflected_type"_hs).prop(my_enum::key_only);
```
Alternatively, the `props` function is available to associate several properties
at a time. However, in this case properties in the key/value form aren't
allowed, since they would be interpreted as two different properties rather than
a single one.
To attach multiple properties to a meta object, it's possible to invoke `prop`
more than once.<br/>
It's also possible to invoke `prop` at different times, as long as the factory
is reset to the meta object of interest.
The meta objects for which properties are supported are currently the meta
types, meta constructors, meta data and meta functions. It's not possible to
attach properties to other types of meta objects and the factory returned as a
result of their construction won't allow such an operation.
These types offer a couple of member functions named `prop` to iterate all
The meta objects for which properties are supported are currently meta types,
meta data and meta functions.<br/>
These types also offer a couple of member functions named `prop` to iterate all
properties at once or to search a specific property by key:
```cpp
// iterate all properties of a meta type
for(auto prop: entt::resolve<my_type>().prop()) {
for(auto &&[id, prop]: entt::resolve<my_type>().prop()) {
// ...
}
@@ -926,23 +935,89 @@ auto prop = entt::resolve<my_type>().prop("tooltip"_hs);
```
Meta properties are objects having a fairly poor interface, all in all. They
only provide the `key` and the `value` member functions to be used to retrieve
the key and the value contained in the form of `meta_any` objects, respectively.
only provide the `value` member function to retrieve the contained value in the
form of a `meta_any` object.
## Unregister types
A type registered with the reflection system can also be unregistered. This
means unregistering all its data members, member functions, conversion functions
and so on. However, base classes aren't unregistered as well, since they don't
necessarily depend on it. Similarly, implicitly generated types (as an example,
the meta types implicitly generated for function parameters when needed) aren't
unregistered.<br/>
necessarily depend on it.<br/>
Roughly speaking, unregistering a type means disconnecting all associated meta
objects from it and making its identifier no longer visible. The underlying node
will remain available though, as if it were implicitly generated:
objects from it and making its identifier no longer available:
```cpp
entt::resolve<my_type>().reset();
entt::meta_reset<my_type>();
```
The type can be re-registered later with a completely different name and form.
It's also possible to reset types by their unique identifiers:
```cpp
entt::meta_reset("my_type"_hs);
```
Finally, there exists a non-template overload of the `meta_reset` function that
doesn't accept arguments and resets all meta types at once:
```cpp
entt::meta_reset();
```
A type can be re-registered later with a completely different name and form.
## Meta context
All meta types and their parts are created at runtime and stored in a default
_context_. This can be reached via a service locator as:
```cpp
auto &&context = entt::locator<entt::meta_context>::value_or();
```
By itself, a context is an opaque object that the user cannot do much with.
However, users can replace an existing context with another at any time:
```cpp
entt::meta_context other{};
auto &&context = entt::locator<entt::meta_context>::value_or();
std::swap(context, other);
```
This can be useful for testing purposes or to define multiple contexts with
different meta objects to be used as appropriate.
If _replacing_ the default context isn't enough, `EnTT` also offers the ability
to use multiple and externally managed contexts with the runtime reflection
system.<br/>
For example, to create new meta types within a context other than the default
one, simply pass it as an argument to the `meta` call:
```cpp
entt::meta_ctx context{};
auto factory = entt::meta<my_type>(context).type("reflected_type"_hs);
```
By doing so, the new meta type won't be available in the default context but
will be usable by passing around the new context when needed, such as when
creating a new `meta_any` object:
```cpp
entt::meta_any any{context, std::in_place_type<my_type>};
```
Similarly, to search for meta types in a context other than the default one, it
will be necessary to pass it to the `resolve` function:
```cpp
entt::meta_type type = entt::resolve(context, "reflected_type"_hs)
```
More generally, when using externally managed contexts, it's always required to
provide the system with the context to use, at least at the _entry point_.<br/>
For example, once the `meta_type` instant is obtained, it's no longer necessary
to pass the context around as the meta type takes the information with it and
eventually propagates it to all its parts.<br/>
On the other hand, it's necessary to instruct the library on where meta types
are to be fetched when `meta_any`s and `meta_handle`s are constructed, a factory
created or a meta type resolved.

View File

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

View File

@@ -98,7 +98,7 @@ private:
Lambdas and functors can't be used directly with a scheduler for they are not
properly defined processes with managed life cycles.<br/>
This class helps in filling the gap and turning lambdas and functors into
full featured processes usable by a scheduler.
full-featured processes usable by a scheduler.
The function call operator has a signature similar to the one of the `update`
function of a process but for the fact that it receives two extra arguments to

View File

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

View File

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

View File

@@ -11,6 +11,7 @@
* [Lambda support](#lambda-support)
* [Signals](#signals)
* [Event dispatcher](#event-dispatcher)
* [Named queues](#named-queues)
* [Event emitter](#event-emitter)
<!--
@endcond TURN_OFF_DOXYGEN
@@ -18,27 +19,27 @@
# Introduction
Signals are usually a core part of games and software architectures in
general.<br/>
Roughly speaking, they help to decouple the various parts of a system while
allowing them to communicate with each other somehow.
Signals are more often than not a core part of games and software architectures
in general.<br/>
They help to decouple the various parts of a system while allowing them to
communicate with each other somehow.
The so called _modern C++_ comes with a tool that can be useful in these terms,
The so called _modern C++_ comes with a tool that can be useful in this regard,
the `std::function`. As an example, it can be used to create delegates.<br/>
However, there is no guarantee that an `std::function` does not perform
However, there is no guarantee that an `std::function` doesn't perform
allocations under the hood and this could be problematic sometimes. Furthermore,
it solves a problem but may not adapt well to other requirements that may arise
from time to time.
In case that the flexibility and power of an `std::function` isn't required or
if the price to pay for them is too high,` EnTT` offers a complete set of
if the price to pay for them is too high, `EnTT` offers a complete set of
lightweight classes to solve the same and many other problems.
# Delegate
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/>
free functions and member functions provided along with an instance on which to
invoke them.<br/>
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
@@ -51,15 +52,13 @@ delegates:
entt::delegate<int(int)> delegate{};
```
All what is needed to create an instance is to specify the type of the function
the delegate will _contain_, that is the signature of the free function or the
member one wants to assign to it.
What is needed to create an instance is to specify the type of the function the
delegate _accepts_, that is the signature of the functions it models.<br/>
However, attempting to use an empty delegate by invoking its function call
operator results in undefined behavior or most likely a crash.
Attempting to use an empty delegate by invoking its function call operator
results in undefined behavior or most likely a crash. Before to use a delegate,
it must be initialized.<br/>
There exists a bunch of overloads of the `connect` member function to do that.
As an example of use:
There exist a few overloads of the `connect` member function to initialize a
delegate:
```cpp
int f(int i) { return i; }
@@ -76,7 +75,7 @@ my_struct instance;
delegate.connect<&my_struct::f>(instance);
```
The delegate class accepts also data members, if needed. In this case, the
The delegate class also accepts data members, if needed. In this case, the
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.
@@ -93,14 +92,11 @@ delegate.connect<&g>(c);
delegate(42);
```
The function `g` will be invoked with a reference to `c` and `42`. However, the
The function `g` is invoked with a reference to `c` and `42`. However, the
function type of the delegate is still `void(int)`. This is also the signature
of its function call operator.
Another interesting aspect of the delegate class is that it accepts also
functions with a list of parameters that is shorter than that of the function
type used to specialize the delegate itself.<br/>
The following code is therefore perfectly valid:
of its function call operator.<br/>
Another interesting aspect of the delegate class is that it accepts functions
with a list of parameters that is shorter than that of its function type:
```cpp
void g() { /* ... */ }
@@ -139,7 +135,7 @@ already shown in the examples above:
auto ret = delegate(42);
```
In all cases, the listeners don't have to strictly follow the signature of the
In all cases, listeners don't have to strictly follow the signature of the
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.
@@ -157,7 +153,7 @@ my_struct instance;
delegate(instance, 42);
```
In this case, it's not possible to deduce the function type since the first
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.
@@ -165,9 +161,9 @@ Therefore, the function type must be declared explicitly for unbound members.
## Runtime arguments
The `delegate` class is meant to be used primarily with template arguments.
However, as a consequence of its design, it can also offer minimal support for
However, as a consequence of its design, it also offers minimal support for
runtime arguments.<br/>
When used in this modality, some feature aren't supported though. In particular:
When used like this, some features aren't supported though. In particular:
* Curried functions aren't accepted.
* Functions with an argument list that differs from that of the delegate aren't
@@ -208,7 +204,7 @@ their nuances. The reason is pretty simple: a `delegate` isn't a drop-in
replacement for an `std::function`. Instead, it tries to overcome the problems
with the latter.<br/>
That being said, non-capturing lambda functions are supported, even though some
feature aren't available in this case.
features aren't available in this case.
This is a logical consequence of the support for connecting functions at
runtime. Therefore, lambda functions undergo the same rules and
@@ -246,16 +242,17 @@ Signals make use of delegates internally and therefore they undergo the same
rules and offer similar functionalities. It may be a good idea to consult the
documentation of the `delegate` class for further information.
A signal handler can be used as a private data member without exposing any
_publish_ functionality to the clients of a class. The basic idea is to impose a
clear separation between the signal itself and the `sink` class, that is a tool
to be used to connect and disconnect listeners on the fly.
A signal handler is can be used as a private data member without exposing any
_publish_ functionality to the clients of a class.<br/>
The basic idea is to impose a clear separation between the signal itself and the
`sink` class, that is a tool to be used to connect and disconnect listeners on
the fly.
The API of a signal handler is straightforward. If a collector is supplied to
the signal when something is published, all the values returned by the listeners
can be literally _collected_ and used later by the caller. Otherwise, the class
the signal when something is published, all the values returned by its listeners
are literally _collected_ and used later by the caller. Otherwise, the class
works just like a plain signal that emits events from time to time.<br/>
To create instances of signal handlers it is sufficient to provide the type of
To create instances of signal handlers it's sufficient to provide the type of
function to which they refer:
```cpp
@@ -299,13 +296,13 @@ sink.disconnect(instance);
sink.disconnect();
```
As shown above, the listeners don't have to strictly follow the signature of the
As shown above, listeners don't have to strictly follow the signature of the
signal. As long as a listener can be invoked with the given arguments to yield a
result that is convertible to the given return type, everything works just
fine.<br/>
It's also possible to connect a listener before other listeners already
contained by the signal. The `before` function returns a `sink` object correctly
initialized for the purpose that can be used to connect one or more listeners in
It's also possible to connect a listener before other elements already contained
by the signal. The `before` function returns a `sink` object that is correctly
initialized for the purpose and can be used to connect one or more listeners in
order and in the desired position:
```cpp
@@ -314,20 +311,19 @@ sink.before<&foo>().connect<&listener::bar>(instance);
In all cases, the `connect` member function returns by default a `connection`
object to be used as an alternative to break a connection by means of its
`release` member function. A `scoped_connection` can also be created from a
connection. In this case, the link is broken automatically as soon as the object
goes out of scope.
`release` member function.<br/>
A `scoped_connection` can also be created from a connection. In this case, the
link is broken automatically as soon as the object goes out of scope.
Once listeners are attached (or even if there are no listeners at all), events
and data in general can be published through a signal by means of the `publish`
and data in general are published through a signal by means of the `publish`
member function:
```cpp
signal.publish(42, 'c');
```
To collect data, the `collect` member function should be used instead. Below is
a minimal example to show how to use it:
To collect data, the `collect` member function is used instead:
```cpp
int f() { return 0; }
@@ -361,7 +357,7 @@ case:
struct my_collector {
std::vector<int> vec{};
bool operator()(int v) noexcept {
bool operator()(int v) {
vec.push_back(v);
return true;
}
@@ -375,23 +371,20 @@ signal.collect(std::ref(collector));
# Event dispatcher
The event dispatcher class is designed so as to be used in a loop. It allows
users both to trigger immediate events or to queue events to be published all
together once per tick.<br/>
This class shares part of its API with the one of the signal handler, but it
doesn't require that all the types of events are specified when declared:
The event dispatcher class allows users to trigger immediate events or to queue
and publish them all together later.<br/>
This class lazily instantiates its queues. Therefore, it's not necessary to
_announce_ the event types in advance:
```cpp
// define a general purpose dispatcher
entt::dispatcher dispatcher{};
```
In order to register an instance of a class to a dispatcher, its type must
expose one or more member functions the arguments of which are such that `E &`
can be converted to them for each type of event `E`, no matter what the return
value is.<br/>
The name of the member function aimed to receive the event must be provided to
the `connect` member function of the sink in charge for the specific event:
A listener registered with a dispatcher is such that its type offers one or more
member functions that take arguments of type `Event &` for any type of event,
regardless of the return value.<br/>
These functions are linked directly via `connect` to a _sink_:
```cpp
struct an_event { int value; };
@@ -409,8 +402,10 @@ dispatcher.sink<an_event>().connect<&listener::receive>(listener);
dispatcher.sink<another_event>().connect<&listener::method>(listener);
```
The `disconnect` member function follows the same pattern and can be used to
remove one listener at a time or all of them at once:
Note that connecting listeners within event handlers can result in undefined
behavior.<br/>
The `disconnect` member function is used to remove one listener at a time or all
of them at once:
```cpp
dispatcher.sink<an_event>().disconnect<&listener::receive>(listener);
@@ -418,14 +413,10 @@ dispatcher.sink<another_event>().disconnect(listener);
```
The `trigger` member function serves the purpose of sending an immediate event
to all the listeners registered so far. It offers a convenient approach that
relieves users from having to create the event itself. Instead, it's enough to
specify the type of event and provide all the parameters required to construct
it.<br/>
As an example:
to all the listeners registered so far:
```cpp
dispatcher.trigger<an_event>(42);
dispatcher.trigger(an_event{42});
dispatcher.trigger<another_event>();
```
@@ -434,16 +425,14 @@ method can be used to push around urgent messages like an _is terminating_
notification on a mobile app.
On the other hand, the `enqueue` member function queues messages together and
allows to maintain control over the moment they are sent to listeners. The
signature of this method is more or less the same of `trigger`:
helps to maintain control over the moment they are sent to listeners:
```cpp
dispatcher.enqueue<an_event>(42);
dispatcher.enqueue<another_event>();
dispatcher.enqueue(another_event{});
```
Events are stored aside until the `update` member function is invoked, then all
the messages that are still pending are sent to the listeners at once:
Events are stored aside until the `update` member function is invoked:
```cpp
// emits all the events of the given type at once
@@ -456,6 +445,30 @@ dispatcher.update();
This way users can embed the dispatcher in a loop and literally dispatch events
once per tick to their systems.
## Named queues
All queues within a dispatcher are associated by default with an event type and
then retrieved from it.<br/>
However, it's possible to create queues with different _names_ (and therefore
also multiple queues for a single type). In fact, more or less all functions
also take an additional parameter. As an example:
```cpp
dispatcher.sink<an_event>("custom"_hs).connect<&listener::receive>(listener);
```
In this case, the term _name_ is misused as these are actual numeric identifiers
of type `id_type`.<br/>
An exception to this rule is the `enqueue` function. There is no additional
parameter for it but rather a different function:
```cpp
dispatcher.enqueue_hint<an_event>("custom"_hs, 42);
```
This is mainly due to the template argument deduction rules and unfortunately
there is no real (elegant) way to avoid it.
# Event emitter
A general purpose event emitter thought mainly for those cases where it comes to
@@ -464,8 +477,7 @@ Originally designed to fit the requirements of
[`uvw`](https://github.com/skypjack/uvw) (a wrapper for `libuv` written in
modern C++), it was adapted later to be included in this library.
To create a custom emitter type, derived classes must inherit directly from the
base class as:
To create an emitter type, derived classes must inherit from the base as:
```cpp
struct my_emitter: emitter<my_emitter> {
@@ -473,18 +485,10 @@ struct my_emitter: emitter<my_emitter> {
}
```
The full list of accepted types of events isn't required. Handlers are created
internally on the fly and thus each type of event is accepted by default.
Whenever an event is published, an emitter provides the listeners with a
reference to itself along with a reference to the event. Therefore listeners
have an handy way to work with it without incurring in the need of capturing a
reference to the emitter itself.<br/>
In addition, an opaque object is returned each time a connection is established
between an emitter and a listener, allowing the caller to disconnect them at a
later time.<br/>
The opaque object used to handle connections is both movable and copyable. On
the other side, an event emitter is movable but not copyable by default.
Handlers for the different events are created internally on the fly. It's not
required to specify in advance the full list of accepted events.<br/>
Moreover, whenever an event is published, an emitter also passes a reference
to itself to its listeners.
To create new instances of an emitter, no arguments are required:
@@ -492,90 +496,54 @@ To create new instances of an emitter, no arguments are required:
my_emitter emitter{};
```
Listeners must be movable and callable objects (free functions, lambdas,
functors, `std::function`s, whatever) whose function type is compatible with:
Listeners are movable and callable objects (free functions, lambdas, functors,
`std::function`s, whatever) whose function type is compatible with:
```cpp
void(Event &, my_emitter &)
void(Type &, my_emitter &)
```
Where `Event` is the type of event they want to listen.<br/>
There are two ways to attach a listener to an event emitter that differ
slightly from each other:
* To register a long-lived listener, use the `on` member function. It is meant
to register a listener designed to be invoked more than once for the given
event type.<br/>
As an example:
```cpp
auto conn = emitter.on<my_event>([](const my_event &event, my_emitter &emitter) {
// ...
});
```
The connection object can be freely discarded. Otherwise, it can be used later
to disconnect the listener if required.
* To register a short-lived listener, use the `once` member function. It is
meant to register a listener designed to be invoked only once for the given
event type. The listener is automatically disconnected after the first
invocation.<br/>
As an example:
```cpp
auto conn = emitter.once<my_event>([](const my_event &event, my_emitter &emitter) {
// ...
});
```
The connection object can be freely discarded. Otherwise, it can be used later
to disconnect the listener if required.
In both cases, the connection object can be used with the `erase` member
function:
Where `Type` is the type of event they want to receive.<br/>
To attach a listener to an emitter, there exists the `on` member function:
```cpp
emitter.erase(conn);
emitter.on<my_event>([](const my_event &event, my_emitter &emitter) {
// ...
});
```
There are also two member functions to use either to disconnect all the
listeners for a given type of event or to clear the emitter:
Similarly, the `reset` member function is used to disconnect listeners given a
type while `clear` is used to disconnect all listeners at once:
```cpp
// removes all the listener for the specific event
emitter.clear<my_event>();
// resets the listener for my_event
emitter.erase<my_event>();
// removes all the listeners registered so far
emitter.clear();
// resets all listeners
emitter.clear()
```
To send an event to all the listeners that are interested in it, the `publish`
member function offers a convenient approach that relieves users from having to
create the event:
To send an event to the listener registered on a given type, the `publish`
function is the way to go:
```cpp
struct my_event { int i; };
// ...
emitter.publish<my_event>(42);
emitter.publish(my_event{42});
```
Finally, the `empty` member function tests if there exists at least either a
listener registered with the event emitter or to a given type of event:
Finally, the `empty` member function tests if there exists at least a listener
registered with the event emitter while `contains` is used to check if a given
event type is associated with a valid listener:
```cpp
bool empty;
// checks if there is any listener registered for the specific event
empty = emitter.empty<my_event>();
// checks it there are listeners registered with the event emitter
empty = emitter.empty();
if(emitter.contains<my_event>()) {
// ...
}
```
In general, the event emitter is a handy tool when the derived classes _wrap_
asynchronous operations, because it introduces a _nice-to-have_ model based on
events and listeners that kindly hides the complexity behind the scenes. However
it is not limited to such uses.
This class introduces a _nice-to-have_ model based on events and listeners.<br/>
More in general, it's a handy tool when the derived classes _wrap_ asynchronous
operations but it's not limited to such uses.

34
entt.imp Normal file
View File

@@ -0,0 +1,34 @@
[
{ "include": [ "@<gtest/internal/.*>", "private", "<gtest/gtest.h>", "public" ] },
{ "include": [ "@<gtest/gtest-.*>", "private", "<gtest/gtest.h>", "public" ] },
{ "include": [ "@[\"<].*/container/fwd.hpp[\">]", "private", "<entt/container/dense_map.hpp>", "public" ] },
{ "include": [ "@[\"<].*/container/fwd.hpp[\">]", "private", "<entt/container/dense_set.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd.hpp[\">]", "private", "<entt/core/any.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd.hpp[\">]", "private", "<entt/core/family.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd.hpp[\">]", "private", "<entt/core/hashed_string.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd.hpp[\">]", "private", "<entt/core/ident.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd.hpp[\">]", "private", "<entt/core/monostate.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd.hpp[\">]", "private", "<entt/core/type_info.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd.hpp[\">]", "private", "<entt/core/type_traits.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/entity.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/group.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/handle.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/helper.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/observer.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/organizer.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/registry.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/runtime_view.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/snapshot.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/sparse_set.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/storage.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/view.hpp>", "public" ] },
{ "include": [ "@[\"<].*/meta/fwd.hpp[\">]", "private", "<entt/meta/meta.hpp>", "public" ] },
{ "include": [ "@[\"<].*/poly/fwd.hpp[\">]", "private", "<entt/poly/poly.hpp>", "public" ] },
{ "include": [ "@[\"<].*/resource/fwd.hpp[\">]", "private", "<entt/resource/cache.hpp>", "public" ] },
{ "include": [ "@[\"<].*/resource/fwd.hpp[\">]", "private", "<entt/resource/loader.hpp>", "public" ] },
{ "include": [ "@[\"<].*/resource/fwd.hpp[\">]", "private", "<entt/resource/resource.hpp>", "public" ] },
{ "include": [ "@[\"<].*/signal/fwd.hpp[\">]", "private", "<entt/signal/delegate.hpp>", "public" ] },
{ "include": [ "@[\"<].*/signal/fwd.hpp[\">]", "private", "<entt/signal/dispatcher.hpp>", "public" ] },
{ "include": [ "@[\"<].*/signal/fwd.hpp[\">]", "private", "<entt/signal/emitter.hpp>", "public" ] },
{ "include": [ "@[\"<].*/signal/fwd.hpp[\">]", "private", "<entt/signal/sigh.hpp>", "public" ] }
]

View File

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

View File

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

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

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

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

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

19
natvis/entt/graph.natvis Normal file
View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="entt::adjacency_matrix&lt;*&gt;">
<DisplayString>{{ size={ vert } }}</DisplayString>
<Expand>
<CustomListItems>
<Variable Name="pos" InitialValue="0" />
<Variable Name="last" InitialValue="vert * vert"/>
<Loop>
<Break Condition="pos == last"/>
<If Condition="matrix[pos] != 0u">
<Item Name="{pos / vert}">pos % vert</Item>
</If>
<Exec>++pos</Exec>
</Loop>
</CustomListItems>
</Expand>
</Type>
</AutoVisualizer>

View File

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

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

@@ -0,0 +1,121 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="entt::internal::meta_base_node">
<DisplayString>{{}}</DisplayString>
</Type>
<Type Name="entt::internal::meta_conv_node">
<DisplayString>{{}}</DisplayString>
</Type>
<Type Name="entt::internal::meta_ctor_node">
<DisplayString>{{ arity={ arity } }}</DisplayString>
</Type>
<Type Name="entt::internal::meta_data_node">
<Intrinsic Name="has_property" Expression="!!(traits &amp; property)">
<Parameter Name="property" Type="int"/>
</Intrinsic>
<DisplayString>{{ arity={ arity } }}</DisplayString>
<Expand>
<Item Name="[arity]">arity</Item>
<Item Name="[is_const]">has_property(entt::internal::meta_traits::is_const)</Item>
<Item Name="[is_static]">has_property(entt::internal::meta_traits::is_static)</Item>
<Item Name="[prop]">prop</Item>
</Expand>
</Type>
<Type Name="entt::internal::meta_func_node" >
<Intrinsic Name="has_property" Expression="!!(traits &amp; property)">
<Parameter Name="property" Type="int"/>
</Intrinsic>
<DisplayString>{{ arity={ arity } }}</DisplayString>
<Expand>
<Item Name="[is_const]">has_property(entt::internal::meta_traits::is_const)</Item>
<Item Name="[is_static]">has_property(entt::internal::meta_traits::is_static)</Item>
<Item Name="[next]" Condition="next != nullptr">*next</Item>
<Item Name="[prop]">prop</Item>
</Expand>
</Type>
<Type Name="entt::internal::meta_prop_node">
<DisplayString>{ value }</DisplayString>
</Type>
<Type Name="entt::internal::meta_template_node">
<DisplayString>{{ arity={ arity } }}</DisplayString>
</Type>
<Type Name="entt::internal::meta_type_node">
<Intrinsic Name="has_property" Expression="!!(traits &amp; property)">
<Parameter Name="property" Type="int"/>
</Intrinsic>
<DisplayString Condition="info != nullptr">{{ type={ info->alias,na } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[id]">id</Item>
<Item Name="[sizeof]">size_of</Item>
<Item Name="[is_arithmetic]">has_property(entt::internal::meta_traits::is_arithmetic)</Item>
<Item Name="[is_integral]">has_property(entt::internal::meta_traits::is_integral)</Item>
<Item Name="[is_signed]">has_property(entt::internal::meta_traits::is_signed)</Item>
<Item Name="[is_array]">has_property(entt::internal::meta_traits::is_array)</Item>
<Item Name="[is_enum]">has_property(entt::internal::meta_traits::is_enum)</Item>
<Item Name="[is_class]">has_property(entt::internal::meta_traits::is_class)</Item>
<Item Name="[is_meta_pointer_like]">has_property(entt::internal::meta_traits::is_meta_pointer_like)</Item>
<Item Name="[is_meta_sequence_container]">has_property(entt::internal::meta_traits::is_meta_sequence_container)</Item>
<Item Name="[is_meta_associative_container]">has_property(entt::internal::meta_traits::is_meta_associative_container)</Item>
<Item Name="[default_constructor]">default_constructor != nullptr</Item>
<Item Name="[conversion_helper]">conversion_helper != nullptr</Item>
<Item Name="[from_void]">from_void != nullptr</Item>
<Item Name="[template_info]">templ</Item>
<Item Name="[details]" Condition="details != nullptr">*details</Item>
</Expand>
</Type>
<Type Name="entt::meta_any">
<DisplayString Condition="node.info != nullptr">{{ type={ node.info->alias,na }, policy={ storage.mode,en } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<ExpandedItem>node</ExpandedItem>
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
</Expand>
</Type>
<Type Name="entt::meta_handle">
<DisplayString>{ any }</DisplayString>
</Type>
<Type Name="entt::meta_associative_container">
<DisplayString>{ storage }</DisplayString>
<Expand>
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
</Expand>
</Type>
<Type Name="entt::meta_sequence_container">
<DisplayString>{ storage }</DisplayString>
<Expand>
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
</Expand>
</Type>
<Type Name="entt::meta_data">
<DisplayString Condition="node != nullptr">{ *node }</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
</Expand>
</Type>
<Type Name="entt::meta_func">
<DisplayString Condition="node != nullptr">{ *node }</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
</Expand>
</Type>
<Type Name="entt::meta_prop">
<DisplayString Condition="node != nullptr">{ *node }</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
</Expand>
</Type>
<Type Name="entt::meta_type">
<DisplayString>{ node }</DisplayString>
<Expand>
<ExpandedItem>node</ExpandedItem>
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
</Expand>
</Type>
</AutoVisualizer>

View File

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

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

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

View File

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

View File

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

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

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

File diff suppressed because it is too large Load Diff

View File

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

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

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@@ -1,9 +1,7 @@
#ifndef ENTT_CORE_ANY_HPP
#define ENTT_CORE_ANY_HPP
#include <cstddef>
#include <functional>
#include <memory>
#include <type_traits>
#include <utility>
@@ -13,9 +11,37 @@
#include "type_info.hpp"
#include "type_traits.hpp"
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
enum class any_operation : std::uint8_t {
copy,
move,
transfer,
assign,
destroy,
compare,
get
};
enum class any_policy : std::uint8_t {
owner,
ref,
cref
};
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief A SBO friendly, type-safe container for single values of any type.
@@ -24,116 +50,108 @@ namespace entt {
*/
template<std::size_t Len, std::size_t Align>
class basic_any {
enum class operation: std::uint8_t { COPY, MOVE, DTOR, COMP, ADDR, CADDR, TYPE };
enum class policy: std::uint8_t { OWNER, REF, CREF };
using operation = internal::any_operation;
using policy = internal::any_policy;
using vtable_type = const void *(const operation, const basic_any &, const void *);
using storage_type = std::aligned_storage_t<Len + !Len, Align>;
using vtable_type = const void *(const operation, const basic_any &, void *);
struct storage_type {
alignas(Align) std::byte data[Len + !Len];
};
template<typename Type>
static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v<Type>;
static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len &&std::is_nothrow_move_constructible_v<Type>;
template<typename Type>
[[nodiscard]] static constexpr policy type_to_policy() {
if constexpr(std::is_lvalue_reference_v<Type>) {
if constexpr(std::is_const_v<std::remove_reference_t<Type>>) {
return policy::CREF;
static const void *basic_vtable(const operation op, const basic_any &value, const void *other) {
static_assert(!std::is_same_v<Type, void> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
const Type *element = nullptr;
if constexpr(in_situ<Type>) {
element = value.owner() ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance);
} else {
element = static_cast<const Type *>(value.instance);
}
switch(op) {
case operation::copy:
if constexpr(std::is_copy_constructible_v<Type>) {
static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*element);
}
break;
case operation::move:
if constexpr(in_situ<Type>) {
if(value.owner()) {
return new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(element))};
}
}
return (static_cast<basic_any *>(const_cast<void *>(other))->instance = std::exchange(const_cast<basic_any &>(value).instance, nullptr));
case operation::transfer:
if constexpr(std::is_move_assignable_v<Type>) {
*const_cast<Type *>(element) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
return other;
}
[[fallthrough]];
case operation::assign:
if constexpr(std::is_copy_assignable_v<Type>) {
*const_cast<Type *>(element) = *static_cast<const Type *>(other);
return other;
}
break;
case operation::destroy:
if constexpr(in_situ<Type>) {
element->~Type();
} else if constexpr(std::is_array_v<Type>) {
delete[] element;
} else {
return policy::REF;
delete element;
}
} else {
return policy::OWNER;
}
}
template<typename Type>
[[nodiscard]] static bool compare(const void *lhs, const void *rhs) {
if constexpr(!std::is_function_v<Type> && is_equality_comparable_v<Type>) {
return *static_cast<const Type *>(lhs) == *static_cast<const Type *>(rhs);
} else {
return lhs == rhs;
}
}
template<typename Type>
static const void * basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &from, [[maybe_unused]] void *to) {
static_assert(std::is_same_v<std::remove_reference_t<std::remove_const_t<Type>>, Type>, "Invalid type");
if constexpr(!std::is_void_v<Type>) {
const Type *instance = (in_situ<Type> && from.mode == policy::OWNER)
? ENTT_LAUNDER(reinterpret_cast<const Type *>(&from.storage))
: static_cast<const Type *>(from.instance);
switch(op) {
case operation::COPY:
if constexpr(std::is_copy_constructible_v<Type>) {
static_cast<basic_any *>(to)->emplace<Type>(*instance);
}
break;
case operation::MOVE:
if constexpr(in_situ<Type>) {
if(from.mode == policy::OWNER) {
return new (&static_cast<basic_any *>(to)->storage) Type{std::move(*const_cast<Type *>(instance))};
}
}
return (static_cast<basic_any *>(to)->instance = std::exchange(const_cast<basic_any &>(from).instance, nullptr));
case operation::DTOR:
if(from.mode == policy::OWNER) {
if constexpr(in_situ<Type>) {
instance->~Type();
} else if constexpr(std::is_array_v<Type>) {
delete[] instance;
} else {
delete instance;
}
}
break;
case operation::COMP:
return compare<Type>(instance, (*static_cast<const basic_any **>(to))->data()) ? to : nullptr;
case operation::ADDR:
if(from.mode == policy::CREF) {
return nullptr;
}
[[fallthrough]];
case operation::CADDR:
return instance;
case operation::TYPE:
*static_cast<type_info *>(to) = type_id<Type>();
break;
break;
case operation::compare:
if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) {
return *element == *static_cast<const Type *>(other) ? other : nullptr;
} else {
return (element == other) ? other : nullptr;
}
case operation::get:
return element;
}
return nullptr;
}
template<typename Type, typename... Args>
void initialize([[maybe_unused]] Args &&... args) {
void initialize([[maybe_unused]] Args &&...args) {
info = &type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
if constexpr(!std::is_void_v<Type>) {
vtable = basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>;
if constexpr(std::is_lvalue_reference_v<Type>) {
static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v<Args> && ...), "Invalid arguments");
mode = std::is_const_v<std::remove_reference_t<Type>> ? policy::cref : policy::ref;
instance = (std::addressof(args), ...);
} else if constexpr(in_situ<Type>) {
if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) {
new (&storage) Type{std::forward<Args>(args)...};
} else if constexpr(in_situ<std::remove_cv_t<std::remove_reference_t<Type>>>) {
if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
new(&storage) std::remove_cv_t<std::remove_reference_t<Type>>{std::forward<Args>(args)...};
} else {
new (&storage) Type(std::forward<Args>(args)...);
new(&storage) std::remove_cv_t<std::remove_reference_t<Type>>(std::forward<Args>(args)...);
}
} else {
if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) {
instance = new Type{std::forward<Args>(args)...};
if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
instance = new std::remove_cv_t<std::remove_reference_t<Type>>{std::forward<Args>(args)...};
} else {
instance = new Type(std::forward<Args>(args)...);
instance = new std::remove_cv_t<std::remove_reference_t<Type>>(std::forward<Args>(args)...);
}
}
}
}
basic_any(const basic_any &other, const policy pol) ENTT_NOEXCEPT
basic_any(const basic_any &other, const policy pol) noexcept
: instance{other.data()},
info{other.info},
vtable{other.vtable},
mode{pol}
{}
mode{pol} {}
public:
/*! @brief Size of the internal storage. */
@@ -142,11 +160,8 @@ public:
static constexpr auto alignment = Align;
/*! @brief Default constructor. */
basic_any() ENTT_NOEXCEPT
: instance{},
vtable{&basic_vtable<void>},
mode{policy::OWNER}
{}
constexpr basic_any() noexcept
: basic_any{std::in_place_type<void>} {}
/**
* @brief Constructs a wrapper by directly initializing the new object.
@@ -155,27 +170,14 @@ public:
* @param args Parameters to use to construct the instance.
*/
template<typename Type, typename... Args>
explicit basic_any(std::in_place_type_t<Type>, Args &&... args)
explicit basic_any(std::in_place_type_t<Type>, Args &&...args)
: instance{},
vtable{&basic_vtable<std::remove_const_t<std::remove_reference_t<Type>>>},
mode{type_to_policy<Type>()}
{
info{},
vtable{},
mode{policy::owner} {
initialize<Type>(std::forward<Args>(args)...);
}
/**
* @brief Constructs a wrapper that holds an unmanaged object.
* @tparam Type Type of object to use to initialize the wrapper.
* @param value An instance of an object to use to initialize the wrapper.
*/
template<typename Type>
basic_any(std::reference_wrapper<Type> value) ENTT_NOEXCEPT
: basic_any{}
{
// invokes deprecated assignment operator (and avoids issues with vs2017)
*this = value;
}
/**
* @brief Constructs a wrapper from a given value.
* @tparam Type Type of object to use to initialize the wrapper.
@@ -183,40 +185,38 @@ public:
*/
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
basic_any(Type &&value)
: instance{},
vtable{&basic_vtable<std::decay_t<Type>>},
mode{policy::OWNER}
{
initialize<std::decay_t<Type>>(std::forward<Type>(value));
}
: basic_any{std::in_place_type<std::decay_t<Type>>, std::forward<Type>(value)} {}
/**
* @brief Copy constructor.
* @param other The instance to copy from.
*/
basic_any(const basic_any &other)
: instance{},
vtable{&basic_vtable<void>},
mode{policy::OWNER}
{
other.vtable(operation::COPY, other, this);
: basic_any{} {
if(other.vtable) {
other.vtable(operation::copy, other, this);
}
}
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
basic_any(basic_any &&other) ENTT_NOEXCEPT
basic_any(basic_any &&other) noexcept
: instance{},
info{other.info},
vtable{other.vtable},
mode{other.mode}
{
vtable(operation::MOVE, other, this);
mode{other.mode} {
if(other.vtable) {
other.vtable(operation::move, other, this);
}
}
/*! @brief Frees the internal storage, whatever it means. */
~basic_any() {
vtable(operation::DTOR, *this, nullptr);
if(vtable && owner()) {
vtable(operation::destroy, *this, nullptr);
}
}
/**
@@ -224,9 +224,13 @@ public:
* @param other The instance to copy from.
* @return This any object.
*/
basic_any & operator=(const basic_any &other) {
basic_any &operator=(const basic_any &other) {
reset();
other.vtable(operation::COPY, other, this);
if(other.vtable) {
other.vtable(operation::copy, other, this);
}
return *this;
}
@@ -235,23 +239,16 @@ public:
* @param other The instance to move from.
* @return This any object.
*/
basic_any & operator=(basic_any &&other) ENTT_NOEXCEPT {
std::exchange(vtable, other.vtable)(operation::DTOR, *this, nullptr);
other.vtable(operation::MOVE, other, this);
mode = other.mode;
return *this;
}
basic_any &operator=(basic_any &&other) noexcept {
reset();
if(other.vtable) {
other.vtable(operation::move, other, this);
info = other.info;
vtable = other.vtable;
mode = other.mode;
}
/**
* @brief Value assignment operator.
* @tparam Type Type of object to use to initialize the wrapper.
* @param value An instance of an object to use to initialize the wrapper.
* @return This any object.
*/
template<typename Type>
[[deprecated("Use std::in_place_type<T &>, entt::make_any<T &>, emplace<Type &> or forward_as_any instead")]]
basic_any & operator=(std::reference_wrapper<Type> value) ENTT_NOEXCEPT {
emplace<Type &>(value.get());
return *this;
}
@@ -269,26 +266,45 @@ public:
}
/**
* @brief Returns the type of the contained object.
* @return The type of the contained object, if any.
* @brief Returns the object type if any, `type_id<void>()` otherwise.
* @return The object type if any, `type_id<void>()` otherwise.
*/
[[nodiscard]] type_info type() const ENTT_NOEXCEPT {
type_info info{};
vtable(operation::TYPE, *this, &info);
return info;
[[nodiscard]] const type_info &type() const noexcept {
return *info;
}
/**
* @brief Returns an opaque pointer to the contained instance.
* @return An opaque pointer the contained instance, if any.
*/
[[nodiscard]] const void * data() const ENTT_NOEXCEPT {
return vtable(operation::CADDR, *this, nullptr);
[[nodiscard]] const void *data() const noexcept {
return vtable ? vtable(operation::get, *this, nullptr) : nullptr;
}
/*! @copydoc data */
[[nodiscard]] void * data() ENTT_NOEXCEPT {
return const_cast<void *>(vtable(operation::ADDR, *this, nullptr));
/**
* @brief Returns an opaque pointer to the contained instance.
* @param req Expected type.
* @return An opaque pointer the contained instance, if any.
*/
[[nodiscard]] const void *data(const type_info &req) const noexcept {
return *info == req ? data() : nullptr;
}
/**
* @brief Returns an opaque pointer to the contained instance.
* @return An opaque pointer the contained instance, if any.
*/
[[nodiscard]] void *data() noexcept {
return mode == policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data());
}
/**
* @brief Returns an opaque pointer to the contained instance.
* @param req Expected type.
* @return An opaque pointer the contained instance, if any.
*/
[[nodiscard]] void *data(const type_info &req) noexcept {
return mode == policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data(req));
}
/**
@@ -298,24 +314,56 @@ public:
* @param args Parameters to use to construct the instance.
*/
template<typename Type, typename... Args>
void emplace(Args &&... args) {
std::exchange(vtable, &basic_vtable<std::remove_const_t<std::remove_reference_t<Type>>>)(operation::DTOR, *this, nullptr);
mode = type_to_policy<Type>();
void emplace(Args &&...args) {
reset();
initialize<Type>(std::forward<Args>(args)...);
}
/**
* @brief Assigns a value to the contained object without replacing it.
* @param other The value to assign to the contained object.
* @return True in case of success, false otherwise.
*/
bool assign(const basic_any &other) {
if(vtable && mode != policy::cref && *info == *other.info) {
return (vtable(operation::assign, *this, other.data()) != nullptr);
}
return false;
}
/*! @copydoc assign */
bool assign(basic_any &&other) {
if(vtable && mode != policy::cref && *info == *other.info) {
if(auto *val = other.data(); val) {
return (vtable(operation::transfer, *this, val) != nullptr);
} else {
return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr);
}
}
return false;
}
/*! @brief Destroys contained object */
void reset() {
std::exchange(vtable, &basic_vtable<void>)(operation::DTOR, *this, nullptr);
mode = policy::OWNER;
if(vtable && owner()) {
vtable(operation::destroy, *this, nullptr);
}
// unnecessary but it helps to detect nasty bugs
ENTT_ASSERT((instance = nullptr) == nullptr, "");
info = &type_id<void>();
vtable = nullptr;
mode = policy::owner;
}
/**
* @brief Returns false if a wrapper is empty, true otherwise.
* @return False if the wrapper is empty, true otherwise.
*/
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
return !(vtable(operation::CADDR, *this, nullptr) == nullptr);
[[nodiscard]] explicit operator bool() const noexcept {
return vtable != nullptr;
}
/**
@@ -323,53 +371,54 @@ public:
* @param other Wrapper with which to compare.
* @return False if the two objects differ in their content, true otherwise.
*/
bool operator==(const basic_any &other) const ENTT_NOEXCEPT {
const basic_any *trampoline = &other;
return type() == other.type() && (vtable(operation::COMP, *this, &trampoline) || !other.data());
[[nodiscard]] bool operator==(const basic_any &other) const noexcept {
if(vtable && *info == *other.info) {
return (vtable(operation::compare, *this, other.data()) != nullptr);
}
return (!vtable && !other.vtable);
}
/**
* @brief Checks if two wrappers differ in their content.
* @param other Wrapper with which to compare.
* @return True if the two objects differ in their content, false otherwise.
*/
[[nodiscard]] bool operator!=(const basic_any &other) const noexcept {
return !(*this == other);
}
/**
* @brief Aliasing constructor.
* @return A wrapper that shares a reference to an unmanaged object.
*/
[[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT {
return basic_any{*this, (mode == policy::CREF ? policy::CREF : policy::REF)};
[[nodiscard]] basic_any as_ref() noexcept {
return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)};
}
/*! @copydoc as_ref */
[[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT {
return basic_any{*this, policy::CREF};
[[nodiscard]] basic_any as_ref() const noexcept {
return basic_any{*this, policy::cref};
}
/**
* @brief Returns true if a wrapper owns its object, false otherwise.
* @return True if the wrapper owns its object, false otherwise.
*/
[[nodiscard]] bool owner() const ENTT_NOEXCEPT {
return (mode == policy::OWNER);
[[nodiscard]] bool owner() const noexcept {
return (mode == policy::owner);
}
private:
union { const void *instance; storage_type storage; };
union {
const void *instance;
storage_type storage;
};
const type_info *info;
vtable_type *vtable;
policy mode;
};
/**
* @brief Checks if two wrappers differ in their content.
* @tparam Len Size of the storage reserved for the small buffer optimization.
* @tparam Align Alignment requirement.
* @param lhs A wrapper, either empty or not.
* @param rhs A wrapper, either empty or not.
* @return True if the two wrappers differ in their content, false otherwise.
*/
template<std::size_t Len, std::size_t Align>
[[nodiscard]] inline bool operator!=(const basic_any<Len, Align> &lhs, const basic_any<Len, Align> &rhs) ENTT_NOEXCEPT {
return !(lhs == rhs);
}
/**
* @brief Performs type-safe access to the contained object.
* @tparam Type Type to which conversion is required.
@@ -379,48 +428,56 @@ template<std::size_t Len, std::size_t Align>
* @return The element converted to the requested type.
*/
template<typename Type, std::size_t Len, std::size_t Align>
Type any_cast(const basic_any<Len, Align> &data) ENTT_NOEXCEPT {
const auto * const instance = any_cast<std::remove_reference_t<Type>>(&data);
Type any_cast(const basic_any<Len, Align> &data) noexcept {
const auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
ENTT_ASSERT(instance, "Invalid instance");
return static_cast<Type>(*instance);
}
/*! @copydoc any_cast */
template<typename Type, std::size_t Len, std::size_t Align>
Type any_cast(basic_any<Len, Align> &data) ENTT_NOEXCEPT {
Type any_cast(basic_any<Len, Align> &data) noexcept {
// forces const on non-reference types to make them work also with wrappers for const references
auto * const instance = any_cast<std::remove_reference_t<const Type>>(&data);
auto *const instance = any_cast<std::remove_reference_t<const Type>>(&data);
ENTT_ASSERT(instance, "Invalid instance");
return static_cast<Type>(*instance);
}
/*! @copydoc any_cast */
template<typename Type, std::size_t Len, std::size_t Align>
Type any_cast(basic_any<Len, Align> &&data) noexcept {
if constexpr(std::is_copy_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
if(auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); instance) {
return static_cast<Type>(std::move(*instance));
} else {
return any_cast<Type>(data);
}
} else {
auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
ENTT_ASSERT(instance, "Invalid instance");
return static_cast<Type>(std::move(*instance));
}
}
/*! @copydoc any_cast */
template<typename Type, std::size_t Len, std::size_t Align>
Type any_cast(basic_any<Len, Align> &&data) ENTT_NOEXCEPT {
// forces const on non-reference types to make them work also with wrappers for const references
auto * const instance = any_cast<std::remove_reference_t<const Type>>(&data);
ENTT_ASSERT(instance, "Invalid instance");
return static_cast<Type>(std::move(*instance));
const Type *any_cast(const basic_any<Len, Align> *data) noexcept {
const auto &info = type_id<std::remove_cv_t<Type>>();
return static_cast<const Type *>(data->data(info));
}
/*! @copydoc any_cast */
template<typename Type, std::size_t Len, std::size_t Align>
const Type * any_cast(const basic_any<Len, Align> *data) ENTT_NOEXCEPT {
return (data->type() == type_id<Type>() ? static_cast<const Type *>(data->data()) : nullptr);
Type *any_cast(basic_any<Len, Align> *data) noexcept {
if constexpr(std::is_const_v<Type>) {
// last attempt to make wrappers for const references return their values
return any_cast<Type>(&std::as_const(*data));
} else {
const auto &info = type_id<std::remove_cv_t<Type>>();
return static_cast<Type *>(data->data(info));
}
}
/*! @copydoc any_cast */
template<typename Type, std::size_t Len, std::size_t Align>
Type * any_cast(basic_any<Len, Align> *data) ENTT_NOEXCEPT {
// last attempt to make wrappers for const references return their values
return (data->type() == type_id<Type>() ? static_cast<Type *>(static_cast<constness_as_t<basic_any<Len, Align>, Type> *>(data)->data()) : nullptr);
}
/**
* @brief Constructs a wrapper from a given type, passing it all arguments.
* @tparam Type Type of object to use to initialize the wrapper.
@@ -431,11 +488,10 @@ Type * any_cast(basic_any<Len, Align> *data) ENTT_NOEXCEPT {
* @return A properly initialized wrapper for an object of the given type.
*/
template<typename Type, std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename... Args>
basic_any<Len, Align> make_any(Args &&... args) {
basic_any<Len, Align> make_any(Args &&...args) {
return basic_any<Len, Align>{std::in_place_type<Type>, std::forward<Args>(args)...};
}
/**
* @brief Forwards its argument and avoids copies for lvalue references.
* @tparam Len Size of the storage reserved for the small buffer optimization.
@@ -446,11 +502,9 @@ basic_any<Len, Align> make_any(Args &&... args) {
*/
template<std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename Type>
basic_any<Len, Align> forward_as_any(Type &&value) {
return basic_any<Len, Align>{std::in_place_type<std::conditional_t<std::is_rvalue_reference_v<Type>, std::decay_t<Type>, Type>>, std::forward<Type>(value)};
}
return basic_any<Len, Align>{std::in_place_type<Type &&>, std::forward<Type>(value)};
}
} // namespace entt
#endif

View File

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

View File

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

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

@@ -0,0 +1,97 @@
#ifndef ENTT_CORE_ENUM_HPP
#define ENTT_CORE_ENUM_HPP
#include <type_traits>
namespace entt {
/**
* @brief Enable bitmask support for enum classes.
* @tparam Type The enum type for which to enable bitmask support.
*/
template<typename Type, typename = void>
struct enum_as_bitmask: std::false_type {};
/*! @copydoc enum_as_bitmask */
template<typename Type>
struct enum_as_bitmask<Type, std::void_t<decltype(Type::_entt_enum_as_bitmask)>>: std::is_enum<Type> {};
/**
* @brief Helper variable template.
* @tparam Type The enum class type for which to enable bitmask support.
*/
template<typename Type>
inline constexpr bool enum_as_bitmask_v = enum_as_bitmask<Type>::value;
} // namespace entt
/**
* @brief Operator available for enums for which bitmask support is enabled.
* @tparam Type Enum class type.
* @param lhs The first value to use.
* @param rhs The second value to use.
* @return The result of invoking the operator on the underlying types of the
* two values provided.
*/
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
operator|(const Type lhs, const Type rhs) noexcept {
return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) | static_cast<std::underlying_type_t<Type>>(rhs));
}
/*! @copydoc operator| */
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
operator&(const Type lhs, const Type rhs) noexcept {
return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) & static_cast<std::underlying_type_t<Type>>(rhs));
}
/*! @copydoc operator| */
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
operator^(const Type lhs, const Type rhs) noexcept {
return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) ^ static_cast<std::underlying_type_t<Type>>(rhs));
}
/**
* @brief Operator available for enums for which bitmask support is enabled.
* @tparam Type Enum class type.
* @param value The value to use.
* @return The result of invoking the operator on the underlying types of the
* value provided.
*/
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
operator~(const Type value) noexcept {
return static_cast<Type>(~static_cast<std::underlying_type_t<Type>>(value));
}
/*! @copydoc operator~ */
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, bool>
operator!(const Type value) noexcept {
return !static_cast<std::underlying_type_t<Type>>(value);
}
/*! @copydoc operator| */
template<typename Type>
constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &>
operator|=(Type &lhs, const Type rhs) noexcept {
return (lhs = (lhs | rhs));
}
/*! @copydoc operator| */
template<typename Type>
constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &>
operator&=(Type &lhs, const Type rhs) noexcept {
return (lhs = (lhs & rhs));
}
/*! @copydoc operator| */
template<typename Type>
constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &>
operator^=(Type &lhs, const Type rhs) noexcept {
return (lhs = (lhs ^ rhs));
}
#endif

View File

@@ -1,14 +1,11 @@
#ifndef ENTT_CORE_FAMILY_HPP
#define ENTT_CORE_FAMILY_HPP
#include "../config/config.h"
#include "fwd.hpp"
namespace entt {
/**
* @brief Dynamic identifier generator.
*
@@ -22,16 +19,14 @@ class family {
public:
/*! @brief Unsigned integer type. */
using family_type = id_type;
using value_type = id_type;
/*! @brief Statically generated unique identifier for the given type. */
template<typename... Type>
// at the time I'm writing, clang crashes during compilation if auto is used instead of family_type
inline static const family_type type = identifier++;
inline static const value_type value = identifier++;
};
}
} // namespace entt
#endif

View File

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

View File

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

View File

@@ -1,64 +1,35 @@
#ifndef ENTT_CORE_IDENT_HPP
#define ENTT_CORE_IDENT_HPP
#include <cstddef>
#include <utility>
#include <type_traits>
#include "../config/config.h"
#include <utility>
#include "fwd.hpp"
#include "type_traits.hpp"
namespace entt {
/**
* @brief Types identifiers.
*
* Variable template used to generate identifiers at compile-time for the given
* types. Use the `get` member function to know what's the identifier associated
* to the specific type.
*
* @note
* Identifiers are constant expression and can be used in any context where such
* an expression is required. As an example:
* @code{.cpp}
* using id = entt::identifier<a_type, another_type>;
*
* switch(a_type_identifier) {
* case id::type<a_type>:
* // ...
* break;
* case id::type<another_type>:
* // ...
* break;
* default:
* // ...
* }
* @endcode
*
* @tparam Types List of types for which to generate identifiers.
* @brief Type integral identifiers.
* @tparam Type List of types for which to generate identifiers.
*/
template<typename... Types>
class identifier {
template<typename Type, std::size_t... Index>
[[nodiscard]] static constexpr id_type get(std::index_sequence<Index...>) {
static_assert(std::disjunction_v<std::is_same<Type, Types>...>, "Invalid type");
return (0 + ... + (std::is_same_v<Type, type_list_element_t<Index, type_list<std::decay_t<Types>...>>> ? id_type{Index} : id_type{}));
template<typename... Type>
class ident {
template<typename Curr, std::size_t... Index>
[[nodiscard]] static constexpr id_type get(std::index_sequence<Index...>) noexcept {
static_assert((std::is_same_v<Curr, Type> || ...), "Invalid type");
return (0 + ... + (std::is_same_v<Curr, type_list_element_t<Index, type_list<std::decay_t<Type>...>>> ? id_type{Index} : id_type{}));
}
public:
/*! @brief Unsigned integer type. */
using identifier_type = id_type;
using value_type = id_type;
/*! @brief Statically generated unique identifier for the given type. */
template<typename Type>
static constexpr identifier_type type = get<std::decay_t<Type>>(std::index_sequence_for<Types...>{});
template<typename Curr>
static constexpr value_type value = get<std::decay_t<Curr>>(std::index_sequence_for<Type...>{});
};
}
} // namespace entt
#endif

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

@@ -0,0 +1,197 @@
#ifndef ENTT_CORE_ITERATOR_HPP
#define ENTT_CORE_ITERATOR_HPP
#include <iterator>
#include <memory>
#include <type_traits>
#include <utility>
namespace entt {
/**
* @brief Helper type to use as pointer with input iterators.
* @tparam Type of wrapped value.
*/
template<typename Type>
struct input_iterator_pointer final {
/*! @brief Value type. */
using value_type = Type;
/*! @brief Pointer type. */
using pointer = Type *;
/*! @brief Reference type. */
using reference = Type &;
/**
* @brief Constructs a proxy object by move.
* @param val Value to use to initialize the proxy object.
*/
constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v<value_type>)
: value{std::move(val)} {}
/**
* @brief Access operator for accessing wrapped values.
* @return A pointer to the wrapped value.
*/
[[nodiscard]] constexpr pointer operator->() noexcept {
return std::addressof(value);
}
/**
* @brief Dereference operator for accessing wrapped values.
* @return A reference to the wrapped value.
*/
[[nodiscard]] constexpr reference operator*() noexcept {
return value;
}
private:
Type value;
};
/**
* @brief Plain iota iterator (waiting for C++20).
* @tparam Type Value type.
*/
template<typename Type>
class iota_iterator final {
static_assert(std::is_integral_v<Type>, "Not an integral type");
public:
/*! @brief Value type, likely an integral one. */
using value_type = Type;
/*! @brief Invalid pointer type. */
using pointer = void;
/*! @brief Non-reference type, same as value type. */
using reference = value_type;
/*! @brief Difference type. */
using difference_type = std::ptrdiff_t;
/*! @brief Iterator category. */
using iterator_category = std::input_iterator_tag;
/*! @brief Default constructor. */
constexpr iota_iterator() noexcept
: current{} {}
/**
* @brief Constructs an iota iterator from a given value.
* @param init The initial value assigned to the iota iterator.
*/
constexpr iota_iterator(const value_type init) noexcept
: current{init} {}
/**
* @brief Pre-increment operator.
* @return This iota iterator.
*/
constexpr iota_iterator &operator++() noexcept {
return ++current, *this;
}
/**
* @brief Post-increment operator.
* @return This iota iterator.
*/
constexpr iota_iterator operator++(int) noexcept {
iota_iterator orig = *this;
return ++(*this), orig;
}
/**
* @brief Dereference operator.
* @return The underlying value.
*/
[[nodiscard]] constexpr reference operator*() const noexcept {
return current;
}
private:
value_type current;
};
/**
* @brief Comparison operator.
* @tparam Type Value type of the iota iterator.
* @param lhs A properly initialized iota iterator.
* @param rhs A properly initialized iota iterator.
* @return True if the two iterators are identical, false otherwise.
*/
template<typename Type>
[[nodiscard]] constexpr bool operator==(const iota_iterator<Type> &lhs, const iota_iterator<Type> &rhs) noexcept {
return *lhs == *rhs;
}
/**
* @brief Comparison operator.
* @tparam Type Value type of the iota iterator.
* @param lhs A properly initialized iota iterator.
* @param rhs A properly initialized iota iterator.
* @return True if the two iterators differ, false otherwise.
*/
template<typename Type>
[[nodiscard]] constexpr bool operator!=(const iota_iterator<Type> &lhs, const iota_iterator<Type> &rhs) noexcept {
return !(lhs == rhs);
}
/**
* @brief Utility class to create an iterable object from a pair of iterators.
* @tparam It Type of iterator.
* @tparam Sentinel Type of sentinel.
*/
template<typename It, typename Sentinel = It>
struct iterable_adaptor final {
/*! @brief Value type. */
using value_type = typename std::iterator_traits<It>::value_type;
/*! @brief Iterator type. */
using iterator = It;
/*! @brief Sentinel type. */
using sentinel = Sentinel;
/*! @brief Default constructor. */
constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v<iterator> &&std::is_nothrow_default_constructible_v<sentinel>)
: first{},
last{} {}
/**
* @brief Creates an iterable object from a pair of iterators.
* @param from Begin iterator.
* @param to End iterator.
*/
constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v<iterator> &&std::is_nothrow_move_constructible_v<sentinel>)
: first{std::move(from)},
last{std::move(to)} {}
/**
* @brief Returns an iterator to the beginning.
* @return An iterator to the first element of the range.
*/
[[nodiscard]] constexpr iterator begin() const noexcept {
return first;
}
/**
* @brief Returns an iterator to the end.
* @return An iterator to the element following the last element of the
* range.
*/
[[nodiscard]] constexpr sentinel end() const noexcept {
return last;
}
/*! @copydoc begin */
[[nodiscard]] constexpr iterator cbegin() const noexcept {
return begin();
}
/*! @copydoc end */
[[nodiscard]] constexpr sentinel cend() const noexcept {
return end();
}
private:
It first;
Sentinel last;
};
} // namespace entt
#endif

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

@@ -0,0 +1,289 @@
#ifndef ENTT_CORE_MEMORY_HPP
#define ENTT_CORE_MEMORY_HPP
#include <cstddef>
#include <limits>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include "../config/config.h"
namespace entt {
/**
* @brief Checks whether a value is a power of two or not.
* @param value A value that may or may not be a power of two.
* @return True if the value is a power of two, false otherwise.
*/
[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept {
return value && ((value & (value - 1)) == 0);
}
/**
* @brief Computes the smallest power of two greater than or equal to a value.
* @param value The value to use.
* @return The smallest power of two greater than or equal to the given value.
*/
[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept {
ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded");
std::size_t curr = value - (value != 0u);
for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) {
curr |= curr >> next;
}
return ++curr;
}
/**
* @brief Fast module utility function (powers of two only).
* @param value A value for which to calculate the modulus.
* @param mod _Modulus_, it must be a power of two.
* @return The common remainder.
*/
[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept {
ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two");
return value & (mod - 1u);
}
/**
* @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20).
* @tparam Type Pointer type.
* @param ptr Fancy or raw pointer.
* @return A raw pointer that represents the address of the original pointer.
*/
template<typename Type>
[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept {
if constexpr(std::is_pointer_v<std::decay_t<Type>>) {
return ptr;
} else {
return to_address(std::forward<Type>(ptr).operator->());
}
}
/**
* @brief Utility function to design allocation-aware containers.
* @tparam Allocator Type of allocator.
* @param lhs A valid allocator.
* @param rhs Another valid allocator.
*/
template<typename Allocator>
constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept {
if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) {
lhs = rhs;
}
}
/**
* @brief Utility function to design allocation-aware containers.
* @tparam Allocator Type of allocator.
* @param lhs A valid allocator.
* @param rhs Another valid allocator.
*/
template<typename Allocator>
constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept {
if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) {
lhs = std::move(rhs);
}
}
/**
* @brief Utility function to design allocation-aware containers.
* @tparam Allocator Type of allocator.
* @param lhs A valid allocator.
* @param rhs Another valid allocator.
*/
template<typename Allocator>
constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept {
if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) {
using std::swap;
swap(lhs, rhs);
} else {
ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers");
}
}
/**
* @brief Deleter for allocator-aware unique pointers (waiting for C++20).
* @tparam Args Types of arguments to use to construct the object.
*/
template<typename Allocator>
struct allocation_deleter: private Allocator {
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Pointer type. */
using pointer = typename std::allocator_traits<Allocator>::pointer;
/**
* @brief Inherited constructors.
* @param alloc The allocator to use.
*/
constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v<allocator_type>)
: Allocator{alloc} {}
/**
* @brief Destroys the pointed object and deallocates its memory.
* @param ptr A valid pointer to an object of the given type.
*/
constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v<typename allocator_type::value_type>) {
using alloc_traits = typename std::allocator_traits<Allocator>;
alloc_traits::destroy(*this, to_address(ptr));
alloc_traits::deallocate(*this, ptr, 1u);
}
};
/**
* @brief Allows `std::unique_ptr` to use allocators (waiting for C++20).
* @tparam Type Type of object to allocate for and to construct.
* @tparam Allocator Type of allocator used to manage memory and elements.
* @tparam Args Types of arguments to use to construct the object.
* @param allocator The allocator to use.
* @param args Parameters to use to construct the object.
* @return A properly initialized unique pointer with a custom deleter.
*/
template<typename Type, typename Allocator, typename... Args>
ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) {
static_assert(!std::is_array_v<Type>, "Array types are not supported");
using alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<Type>;
using allocator_type = typename alloc_traits::allocator_type;
allocator_type alloc{allocator};
auto ptr = alloc_traits::allocate(alloc, 1u);
ENTT_TRY {
alloc_traits::construct(alloc, to_address(ptr), std::forward<Args>(args)...);
}
ENTT_CATCH {
alloc_traits::deallocate(alloc, ptr, 1u);
ENTT_THROW;
}
return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc};
}
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename Type>
struct uses_allocator_construction {
template<typename Allocator, typename... Params>
static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept {
if constexpr(!std::uses_allocator_v<Type, Allocator> && std::is_constructible_v<Type, Params...>) {
return std::forward_as_tuple(std::forward<Params>(params)...);
} else {
static_assert(std::uses_allocator_v<Type, Allocator>, "Ill-formed request");
if constexpr(std::is_constructible_v<Type, std::allocator_arg_t, const Allocator &, Params...>) {
return std::tuple<std::allocator_arg_t, const Allocator &, Params &&...>{std::allocator_arg, allocator, std::forward<Params>(params)...};
} else {
static_assert(std::is_constructible_v<Type, Params..., const Allocator &>, "Ill-formed request");
return std::forward_as_tuple(std::forward<Params>(params)..., allocator);
}
}
}
};
template<typename Type, typename Other>
struct uses_allocator_construction<std::pair<Type, Other>> {
using type = std::pair<Type, Other>;
template<typename Allocator, typename First, typename Second>
static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept {
return std::make_tuple(
std::piecewise_construct,
std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Type>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<First>(first)),
std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Other>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<Second>(second)));
}
template<typename Allocator>
static constexpr auto args(const Allocator &allocator) noexcept {
return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{});
}
template<typename Allocator, typename First, typename Second>
static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept {
return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward<First>(first)), std::forward_as_tuple(std::forward<Second>(second)));
}
template<typename Allocator, typename First, typename Second>
static constexpr auto args(const Allocator &allocator, const std::pair<First, Second> &value) noexcept {
return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second));
}
template<typename Allocator, typename First, typename Second>
static constexpr auto args(const Allocator &allocator, std::pair<First, Second> &&value) noexcept {
return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second)));
}
};
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Uses-allocator construction utility (waiting for C++20).
*
* Primarily intended for internal use. Prepares the argument list needed to
* create an object of a given type by means of uses-allocator construction.
*
* @tparam Type Type to return arguments for.
* @tparam Allocator Type of allocator used to manage memory and elements.
* @tparam Args Types of arguments to use to construct the object.
* @param allocator The allocator to use.
* @param args Parameters to use to construct the object.
* @return The arguments needed to create an object of the given type.
*/
template<typename Type, typename Allocator, typename... Args>
constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept {
return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...);
}
/**
* @brief Uses-allocator construction utility (waiting for C++20).
*
* Primarily intended for internal use. Creates an object of a given type by
* means of uses-allocator construction.
*
* @tparam Type Type of object to create.
* @tparam Allocator Type of allocator used to manage memory and elements.
* @tparam Args Types of arguments to use to construct the object.
* @param allocator The allocator to use.
* @param args Parameters to use to construct the object.
* @return A newly created object of the given type.
*/
template<typename Type, typename Allocator, typename... Args>
constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) {
return std::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
}
/**
* @brief Uses-allocator construction utility (waiting for C++20).
*
* Primarily intended for internal use. Creates an object of a given type by
* means of uses-allocator construction at an uninitialized memory location.
*
* @tparam Type Type of object to create.
* @tparam Allocator Type of allocator used to manage memory and elements.
* @tparam Args Types of arguments to use to construct the object.
* @param value Memory location in which to place the object.
* @param allocator The allocator to use.
* @param args Parameters to use to construct the object.
* @return A pointer to the newly created object of the given type.
*/
template<typename Type, typename Allocator, typename... Args>
constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) {
return std::apply([value](auto &&...curr) { return new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
}
} // namespace entt
#endif

View File

@@ -1,14 +1,11 @@
#ifndef ENTT_CORE_MONOSTATE_HPP
#define ENTT_CORE_MONOSTATE_HPP
#include "../config/config.h"
#include "fwd.hpp"
namespace entt {
/**
* @brief Minimal implementation of the monostate pattern.
*
@@ -28,7 +25,7 @@ struct monostate {
* @param val User data to assign to the given key.
*/
template<typename Type>
void operator=(Type val) const ENTT_NOEXCEPT {
void operator=(Type val) const noexcept {
value<Type> = val;
}
@@ -38,7 +35,7 @@ struct monostate {
* @return Stored value, if any.
*/
template<typename Type>
operator Type() const ENTT_NOEXCEPT {
operator Type() const noexcept {
return value<Type>;
}
@@ -47,7 +44,6 @@ private:
inline static ENTT_MAYBE_ATOMIC(Type) value{};
};
/**
* @brief Helper variable template.
* @tparam Value Value used to differentiate between different variables.
@@ -55,8 +51,6 @@ private:
template<id_type Value>
inline monostate<Value> monostate_v = {};
}
} // namespace entt
#endif

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

@@ -0,0 +1,103 @@
#ifndef ENTT_CORE_TUPLE_HPP
#define ENTT_CORE_TUPLE_HPP
#include <tuple>
#include <type_traits>
#include <utility>
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename>
struct is_tuple_impl: std::false_type {};
template<typename... Args>
struct is_tuple_impl<std::tuple<Args...>>: std::true_type {};
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Provides the member constant `value` to true if a given type is a
* tuple, false otherwise.
* @tparam Type The type to test.
*/
template<typename Type>
struct is_tuple: internal::is_tuple_impl<std::remove_cv_t<Type>> {};
/**
* @brief Helper variable template.
* @tparam Type The type to test.
*/
template<typename Type>
inline constexpr bool is_tuple_v = is_tuple<Type>::value;
/**
* @brief Utility function to unwrap tuples of a single element.
* @tparam Type Tuple type of any sizes.
* @param value A tuple object of the given type.
* @return The tuple itself if it contains more than one element, the first
* element otherwise.
*/
template<typename Type>
constexpr decltype(auto) unwrap_tuple(Type &&value) noexcept {
if constexpr(std::tuple_size_v<std::remove_reference_t<Type>> == 1u) {
return std::get<0>(std::forward<Type>(value));
} else {
return std::forward<Type>(value);
}
}
/**
* @brief Utility class to forward-and-apply tuple objects.
* @tparam Func Type of underlying invocable object.
*/
template<typename Func>
struct forward_apply: private Func {
/**
* @brief Constructs a forward-and-apply object.
* @tparam Args Types of arguments to use to construct the new instance.
* @param args Parameters to use to construct the instance.
*/
template<class... Args>
constexpr forward_apply(Args &&...args) noexcept(std::is_nothrow_constructible_v<Func, Args...>)
: Func{std::forward<Args>(args)...} {}
/**
* @brief Forwards and applies the arguments with the underlying function.
* @tparam Type Tuple-like type to forward to the underlying function.
* @param args Parameters to forward to the underlying function.
* @return Return value of the underlying function, if any.
*/
template<class Type>
constexpr decltype(auto) operator()(Type &&args) noexcept(noexcept(std::apply(std::declval<Func &>(), args))) {
return std::apply(static_cast<Func &>(*this), std::forward<Type>(args));
}
/*! @copydoc operator()() */
template<class Type>
constexpr decltype(auto) operator()(Type &&args) const noexcept(noexcept(std::apply(std::declval<const Func &>(), args))) {
return std::apply(static_cast<const Func &>(*this), std::forward<Type>(args));
}
};
/**
* @brief Deduction guide.
* @tparam Func Type of underlying invocable object.
*/
template<typename Func>
forward_apply(Func) -> forward_apply<std::remove_reference_t<std::remove_cv_t<Func>>>;
} // namespace entt
#endif

View File

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

View File

@@ -1,7 +1,6 @@
#ifndef ENTT_CORE_TYPE_TRAITS_HPP
#define ENTT_CORE_TYPE_TRAITS_HPP
#include <cstddef>
#include <iterator>
#include <type_traits>
@@ -9,10 +8,8 @@
#include "../config/config.h"
#include "fwd.hpp"
namespace entt {
/**
* @brief Utility class to disambiguate overloaded functions.
* @tparam N Number of choices available.
@@ -20,17 +17,13 @@ namespace entt {
template<std::size_t N>
struct choice_t
// Unfortunately, doxygen cannot parse such a construct.
/*! @cond TURN_OFF_DOXYGEN */
: choice_t<N-1>
/*! @endcond */
: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
{};
/*! @copybrief choice_t */
template<>
struct choice_t<0> {};
/**
* @brief Variable template for the choice trick.
* @tparam N Number of choices available.
@@ -38,7 +31,6 @@ struct choice_t<0> {};
template<std::size_t N>
inline constexpr choice_t<N> choice{};
/**
* @brief Identity type trait.
*
@@ -53,7 +45,6 @@ struct type_identity {
using type = Type;
};
/**
* @brief Helper type.
* @tparam Type A type.
@@ -61,7 +52,6 @@ struct type_identity {
template<typename Type>
using type_identity_t = typename type_identity<Type>::type;
/**
* @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
* @tparam Type The type of which to return the size.
@@ -70,30 +60,25 @@ using type_identity_t = typename type_identity<Type>::type;
template<typename Type, typename = void>
struct size_of: std::integral_constant<std::size_t, 0u> {};
/*! @copydoc size_of */
template<typename Type>
struct size_of<Type, std::void_t<decltype(sizeof(Type))>>
: std::integral_constant<std::size_t, sizeof(Type)>
{};
: std::integral_constant<std::size_t, sizeof(Type)> {};
/**
* @brief Helper variable template.
* @tparam Type The type of which to return the size.
*/
template<class Type>
template<typename Type>
inline constexpr std::size_t size_of_v = size_of<Type>::value;
/**
* @brief Using declaration to be used to _repeat_ the same type a number of
* times equal to the size of a given parameter pack.
* @tparam Type A type to repeat.
*/
template<typename Type, typename>
using unpack_as_t = Type;
using unpack_as_type = Type;
/**
* @brief Helper variable template to be used to _repeat_ the same value a
@@ -101,8 +86,7 @@ using unpack_as_t = Type;
* @tparam Value A value to repeat.
*/
template<auto Value, typename>
inline constexpr auto unpack_as_v = Value;
inline constexpr auto unpack_as_value = Value;
/**
* @brief Wraps a static constant.
@@ -111,7 +95,6 @@ inline constexpr auto unpack_as_v = Value;
template<auto Value>
using integral_constant = std::integral_constant<decltype(Value), Value>;
/**
* @brief Alias template to facilitate the creation of named values.
* @tparam Value A constant value at least convertible to `id_type`.
@@ -119,7 +102,6 @@ using integral_constant = std::integral_constant<decltype(Value), Value>;
template<id_type Value>
using tag = integral_constant<Value>;
/**
* @brief A class to use to push around lists of types, nothing more.
* @tparam Type Types provided by the type list.
@@ -132,36 +114,31 @@ struct type_list {
static constexpr auto size = sizeof...(Type);
};
/*! @brief Primary template isn't defined on purpose. */
template<std::size_t, typename>
struct type_list_element;
/**
* @brief Provides compile-time indexed access to the types of a type list.
* @tparam Index Index of the type to return.
* @tparam Type First type provided by the type list.
* @tparam First First type provided by the type list.
* @tparam Other Other types provided by the type list.
*/
template<std::size_t Index, typename Type, typename... Other>
struct type_list_element<Index, type_list<Type, Other...>>
: type_list_element<Index - 1u, type_list<Other...>>
{};
template<std::size_t Index, typename First, typename... Other>
struct type_list_element<Index, type_list<First, Other...>>
: type_list_element<Index - 1u, type_list<Other...>> {};
/**
* @brief Provides compile-time indexed access to the types of a type list.
* @tparam Type First type provided by the type list.
* @tparam First First type provided by the type list.
* @tparam Other Other types provided by the type list.
*/
template<typename Type, typename... Other>
struct type_list_element<0u, type_list<Type, Other...>> {
template<typename First, typename... Other>
struct type_list_element<0u, type_list<First, Other...>> {
/*! @brief Searched type. */
using type = Type;
using type = First;
};
/**
* @brief Helper type.
* @tparam Index Index of the type to return.
@@ -170,6 +147,57 @@ struct type_list_element<0u, type_list<Type, Other...>> {
template<std::size_t Index, typename List>
using type_list_element_t = typename type_list_element<Index, List>::type;
/*! @brief Primary template isn't defined on purpose. */
template<typename, typename>
struct type_list_index;
/**
* @brief Provides compile-time type access to the types of a type list.
* @tparam Type Type to look for and for which to return the index.
* @tparam First First type provided by the type list.
* @tparam Other Other types provided by the type list.
*/
template<typename Type, typename First, typename... Other>
struct type_list_index<Type, type_list<First, Other...>> {
/*! @brief Unsigned integer type. */
using value_type = std::size_t;
/*! @brief Compile-time position of the given type in the sublist. */
static constexpr value_type value = 1u + type_list_index<Type, type_list<Other...>>::value;
};
/**
* @brief Provides compile-time type access to the types of a type list.
* @tparam Type Type to look for and for which to return the index.
* @tparam Other Other types provided by the type list.
*/
template<typename Type, typename... Other>
struct type_list_index<Type, type_list<Type, Other...>> {
static_assert(type_list_index<Type, type_list<Other...>>::value == sizeof...(Other), "Non-unique type");
/*! @brief Unsigned integer type. */
using value_type = std::size_t;
/*! @brief Compile-time position of the given type in the sublist. */
static constexpr value_type value = 0u;
};
/**
* @brief Provides compile-time type access to the types of a type list.
* @tparam Type Type to look for and for which to return the index.
*/
template<typename Type>
struct type_list_index<Type, type_list<>> {
/*! @brief Unsigned integer type. */
using value_type = std::size_t;
/*! @brief Compile-time position of the given type in the sublist. */
static constexpr value_type value = 0u;
};
/**
* @brief Helper variable template.
* @tparam List Type list.
* @tparam Type Type to look for and for which to return the index.
*/
template<typename Type, typename List>
inline constexpr std::size_t type_list_index_v = type_list_index<Type, List>::value;
/**
* @brief Concatenates multiple type lists.
@@ -178,14 +206,14 @@ using type_list_element_t = typename type_list_element<Index, List>::type;
* @return A type list composed by the types of both the type lists.
*/
template<typename... Type, typename... Other>
constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) { return {}; }
constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) {
return {};
}
/*! @brief Primary template isn't defined on purpose. */
template<typename...>
struct type_list_cat;
/*! @brief Concatenates multiple type lists. */
template<>
struct type_list_cat<> {
@@ -193,7 +221,6 @@ struct type_list_cat<> {
using type = type_list<>;
};
/**
* @brief Concatenates multiple type lists.
* @tparam Type Types provided by the first type list.
@@ -206,7 +233,6 @@ struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> {
using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type;
};
/**
* @brief Concatenates multiple type lists.
* @tparam Type Types provided by the type list.
@@ -217,7 +243,6 @@ struct type_list_cat<type_list<Type...>> {
using type = type_list<Type...>;
};
/**
* @brief Helper type.
* @tparam List Type lists to concatenate.
@@ -225,12 +250,10 @@ struct type_list_cat<type_list<Type...>> {
template<typename... List>
using type_list_cat_t = typename type_list_cat<List...>::type;
/*! @brief Primary template isn't defined on purpose. */
template<typename>
struct type_list_unique;
/**
* @brief Removes duplicates types from a type list.
* @tparam Type One of the types provided by the given type list.
@@ -240,13 +263,11 @@ template<typename Type, typename... Other>
struct type_list_unique<type_list<Type, Other...>> {
/*! @brief A type list without duplicate types. */
using type = std::conditional_t<
std::disjunction_v<std::is_same<Type, Other>...>,
(std::is_same_v<Type, Other> || ...),
typename type_list_unique<type_list<Other...>>::type,
type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>
>;
type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>;
};
/*! @brief Removes duplicates types from a type list. */
template<>
struct type_list_unique<type_list<>> {
@@ -254,7 +275,6 @@ struct type_list_unique<type_list<>> {
using type = type_list<>;
};
/**
* @brief Helper type.
* @tparam Type A type list.
@@ -262,7 +282,6 @@ struct type_list_unique<type_list<>> {
template<typename Type>
using type_list_unique_t = typename type_list_unique<Type>::type;
/**
* @brief Provides the member constant `value` to true if a type list contains a
* given type, false otherwise.
@@ -272,7 +291,6 @@ using type_list_unique_t = typename type_list_unique<Type>::type;
template<typename List, typename Type>
struct type_list_contains;
/**
* @copybrief type_list_contains
* @tparam Type Types provided by the type list.
@@ -281,21 +299,18 @@ struct type_list_contains;
template<typename... Type, typename Other>
struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
/**
* @brief Helper variable template.
* @tparam List Type list.
* @tparam Type Type to look for.
*/
template<class List, typename Type>
template<typename List, typename Type>
inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value;
/*! @brief Primary template isn't defined on purpose. */
template<typename...>
struct type_list_diff;
/**
* @brief Computes the difference between two type lists.
* @tparam Type Types provided by the first type list.
@@ -307,7 +322,6 @@ struct type_list_diff<type_list<Type...>, type_list<Other...>> {
using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>;
};
/**
* @brief Helper type.
* @tparam List Type lists between which to compute the difference.
@@ -315,6 +329,28 @@ struct type_list_diff<type_list<Type...>, type_list<Other...>> {
template<typename... List>
using type_list_diff_t = typename type_list_diff<List...>::type;
/*! @brief Primary template isn't defined on purpose. */
template<typename, template<typename...> class>
struct type_list_transform;
/**
* @brief Applies a given _function_ to a type list and generate a new list.
* @tparam Type Types provided by the type list.
* @tparam Op Unary operation as template class with a type member named `type`.
*/
template<typename... Type, template<typename...> class Op>
struct type_list_transform<type_list<Type...>, Op> {
/*! @brief Resulting type list after applying the transform function. */
using type = type_list<typename Op<Type>::type...>;
};
/**
* @brief Helper type.
* @tparam List Type list.
* @tparam Op Unary operation as template class with a type member named `type`.
*/
template<typename List, template<typename...> class Op>
using type_list_transform_t = typename type_list_transform<List, Op>::type;
/**
* @brief A class to use to push around lists of constant values, nothing more.
@@ -328,12 +364,10 @@ struct value_list {
static constexpr auto size = sizeof...(Value);
};
/*! @brief Primary template isn't defined on purpose. */
template<std::size_t, typename>
struct value_list_element;
/**
* @brief Provides compile-time indexed access to the values of a value list.
* @tparam Index Index of the value to return.
@@ -342,9 +376,7 @@ struct value_list_element;
*/
template<std::size_t Index, auto Value, auto... Other>
struct value_list_element<Index, value_list<Value, Other...>>
: value_list_element<Index - 1u, value_list<Other...>>
{};
: value_list_element<Index - 1u, value_list<Other...>> {};
/**
* @brief Provides compile-time indexed access to the types of a type list.
@@ -357,7 +389,6 @@ struct value_list_element<0u, value_list<Value, Other...>> {
static constexpr auto value = Value;
};
/**
* @brief Helper type.
* @tparam Index Index of the value to return.
@@ -366,7 +397,6 @@ struct value_list_element<0u, value_list<Value, Other...>> {
template<std::size_t Index, typename List>
inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
/**
* @brief Concatenates multiple value lists.
* @tparam Value Values provided by the first value list.
@@ -374,14 +404,14 @@ inline constexpr auto value_list_element_v = value_list_element<Index, List>::va
* @return A value list composed by the values of both the value lists.
*/
template<auto... Value, auto... Other>
constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) { return {}; }
constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) {
return {};
}
/*! @brief Primary template isn't defined on purpose. */
template<typename...>
struct value_list_cat;
/*! @brief Concatenates multiple value lists. */
template<>
struct value_list_cat<> {
@@ -389,7 +419,6 @@ struct value_list_cat<> {
using type = value_list<>;
};
/**
* @brief Concatenates multiple value lists.
* @tparam Value Values provided by the first value list.
@@ -402,7 +431,6 @@ struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> {
using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type;
};
/**
* @brief Concatenates multiple value lists.
* @tparam Value Values provided by the value list.
@@ -413,7 +441,6 @@ struct value_list_cat<value_list<Value...>> {
using type = value_list<Value...>;
};
/**
* @brief Helper type.
* @tparam List Value lists to concatenate.
@@ -421,74 +448,10 @@ struct value_list_cat<value_list<Value...>> {
template<typename... List>
using value_list_cat_t = typename value_list_cat<List...>::type;
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename>
[[nodiscard]] constexpr bool is_equality_comparable(...) { return false; }
template<typename Type>
[[nodiscard]] constexpr auto is_equality_comparable(choice_t<0>)
-> decltype(std::declval<Type>() == std::declval<Type>()) { return true; }
template<typename Type>
[[nodiscard]] constexpr auto is_equality_comparable(choice_t<1>)
-> decltype(std::declval<typename Type::value_type>(), std::declval<Type>() == std::declval<Type>()) {
if constexpr(std::is_same_v<typename Type::value_type, Type>) {
return is_equality_comparable<Type>(choice<0>);
} else {
return is_equality_comparable<typename Type::value_type>(choice<2>);
}
}
template<typename Type>
[[nodiscard]] constexpr auto is_equality_comparable(choice_t<2>)
-> decltype(std::declval<typename Type::mapped_type>(), std::declval<Type>() == std::declval<Type>()) {
return is_equality_comparable<typename Type::key_type>(choice<2>) && is_equality_comparable<typename Type::mapped_type>(choice<2>);
}
}
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Provides the member constant `value` to true if a given type is
* equality comparable, false otherwise.
* @tparam Type The type to test.
*/
template<typename Type, typename = void>
struct is_equality_comparable: std::bool_constant<internal::is_equality_comparable<Type>(choice<2>)> {};
/**
* @brief Helper variable template.
* @tparam Type The type to test.
*/
template<class Type>
inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value;
/*! @brief Same as std::is_invocable, but with tuples. */
template<typename, typename>
struct is_applicable: std::false_type {};
/**
* @copybrief is_applicable
* @tparam Func A valid function type.
@@ -498,7 +461,6 @@ struct is_applicable: std::false_type {};
template<typename Func, template<typename...> class Tuple, typename... Args>
struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
/**
* @copybrief is_applicable
* @tparam Func A valid function type.
@@ -508,7 +470,6 @@ struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
template<typename Func, template<typename...> class Tuple, typename... Args>
struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {};
/**
* @brief Helper variable template.
* @tparam Func A valid function type.
@@ -517,12 +478,10 @@ struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args..
template<typename Func, typename Args>
inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value;
/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */
template<typename, typename, typename>
struct is_applicable_r: std::false_type {};
/**
* @copybrief is_applicable_r
* @tparam Ret The type to which the return type of the function should be
@@ -533,7 +492,6 @@ struct is_applicable_r: std::false_type {};
template<typename Ret, typename Func, typename... Args>
struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {};
/**
* @brief Helper variable template.
* @tparam Ret The type to which the return type of the function should be
@@ -544,7 +502,6 @@ struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret,
template<typename Ret, typename Func, typename Args>
inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value;
/**
* @brief Provides the member constant `value` to true if a given type is
* complete, false otherwise.
@@ -553,12 +510,10 @@ inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::valu
template<typename Type, typename = void>
struct is_complete: std::false_type {};
/*! @copydoc is_complete */
template<typename Type>
struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {};
/**
* @brief Helper variable template.
* @tparam Type The type to test.
@@ -566,7 +521,6 @@ struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {}
template<typename Type>
inline constexpr bool is_complete_v = is_complete<Type>::value;
/**
* @brief Provides the member constant `value` to true if a given type is an
* iterator, false otherwise.
@@ -575,13 +529,30 @@ inline constexpr bool is_complete_v = is_complete<Type>::value;
template<typename Type, typename = void>
struct is_iterator: std::false_type {};
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename, typename = void>
struct has_iterator_category: std::false_type {};
template<typename Type>
struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @copydoc is_iterator */
template<typename Type>
struct is_iterator<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>
: std::true_type
{};
struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_pointer_t<Type>>, void>>>
: internal::has_iterator_category<Type> {};
/**
* @brief Helper variable template.
@@ -590,39 +561,110 @@ struct is_iterator<Type, std::void_t<typename std::iterator_traits<Type>::iterat
template<typename Type>
inline constexpr bool is_iterator_v = is_iterator<Type>::value;
/**
* @brief Provides the member constant `value` to true if a given type is of the
* required iterator type, false otherwise.
* @tparam Type The type to test.
* @tparam It Required iterator type.
* @brief Provides the member constant `value` to true if a given type is both
* an empty and non-final class, false otherwise.
* @tparam Type The type to test
*/
template<typename Type, typename It, typename = void>
struct is_iterator_type: std::false_type {};
/*! @copydoc is_iterator_type */
template<typename Type, typename It>
struct is_iterator_type<Type, It, std::enable_if_t<is_iterator_v<Type> && std::is_same_v<Type, It>>>
: std::true_type
{};
/*! @copydoc is_iterator_type */
template<typename Type, typename It>
struct is_iterator_type<Type, It, std::enable_if_t<!std::is_same_v<Type, It>, std::void_t<typename It::iterator_type>>>
: is_iterator_type<Type, typename It::iterator_type>
{};
template<typename Type>
struct is_ebco_eligible
: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
/**
* @brief Helper variable template.
* @tparam Type The type to test.
* @tparam It Required iterator type.
*/
template<typename Type, typename It>
inline constexpr bool is_iterator_type_v = is_iterator_type<Type, It>::value;
template<typename Type>
inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value;
/**
* @brief Provides the member constant `value` to true if `Type::is_transparent`
* is valid and denotes a type, false otherwise.
* @tparam Type The type to test.
*/
template<typename Type, typename = void>
struct is_transparent: std::false_type {};
/*! @copydoc is_transparent */
template<typename Type>
struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {};
/**
* @brief Helper variable template.
* @tparam Type The type to test.
*/
template<typename Type>
inline constexpr bool is_transparent_v = is_transparent<Type>::value;
/**
* @brief Provides the member constant `value` to true if a given type is
* equality comparable, false otherwise.
* @tparam Type The type to test.
*/
template<typename Type, typename = void>
struct is_equality_comparable: std::false_type {};
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename, typename = void>
struct has_tuple_size_value: std::false_type {};
template<typename Type>
struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
template<typename Type, std::size_t... Index>
[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
}
template<typename>
[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
return true;
}
template<typename Type>
[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
if constexpr(is_iterator_v<Type>) {
return true;
} else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
return maybe_equality_comparable<Type>(choice<0>);
} else {
return is_equality_comparable<typename Type::value_type>::value;
}
}
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_cv_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
if constexpr(has_tuple_size_value<Type>::value) {
return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
} else {
return maybe_equality_comparable<Type>(choice<1>);
}
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @copydoc is_equality_comparable */
template<typename Type>
struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
/**
* @brief Helper variable template.
* @tparam Type The type to test.
*/
template<typename Type>
inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value;
/**
* @brief Transcribes the constness of a type to another type.
@@ -635,15 +677,13 @@ struct constness_as {
using type = std::remove_const_t<To>;
};
/*! @copydoc constness_as */
template<typename To, typename From>
struct constness_as<To, const From> {
/*! @brief The type resulting from the transcription of the constness. */
using type = std::add_const_t<To>;
using type = const To;
};
/**
* @brief Alias template to facilitate the transcription of the constness.
* @tparam To The type to which to transcribe the constness.
@@ -652,7 +692,6 @@ struct constness_as<To, const From> {
template<typename To, typename From>
using constness_as_t = typename constness_as<To, From>::type;
/**
* @brief Extracts the class of a non-static member object or function.
* @tparam Member A pointer to a non-static member object or function.
@@ -662,20 +701,19 @@ class member_class {
static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function");
template<typename Class, typename Ret, typename... Args>
static Class * clazz(Ret(Class:: *)(Args...));
static Class *clazz(Ret (Class::*)(Args...));
template<typename Class, typename Ret, typename... Args>
static Class * clazz(Ret(Class:: *)(Args...) const);
static Class *clazz(Ret (Class::*)(Args...) const);
template<typename Class, typename Type>
static Class * clazz(Type Class:: *);
static Class *clazz(Type Class::*);
public:
/*! @brief The class of the given non-static member object or function. */
using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>;
};
/**
* @brief Helper type.
* @tparam Member A pointer to a non-static member object or function.
@@ -683,8 +721,38 @@ public:
template<typename Member>
using member_class_t = typename member_class<Member>::type;
/**
* @brief Extracts the n-th argument of a given function or member function.
* @tparam Index The index of the argument to extract.
* @tparam Candidate A valid function, member function or data member.
*/
template<std::size_t Index, auto Candidate>
class nth_argument {
template<typename Ret, typename... Args>
static constexpr type_list<Args...> pick_up(Ret (*)(Args...));
}
template<typename Ret, typename Class, typename... Args>
static constexpr type_list<Args...> pick_up(Ret (Class ::*)(Args...));
template<typename Ret, typename Class, typename... Args>
static constexpr type_list<Args...> pick_up(Ret (Class ::*)(Args...) const);
template<typename Type, typename Class>
static constexpr type_list<Type> pick_up(Type Class ::*);
public:
/*! @brief N-th argument of the given function or member function. */
using type = type_list_element_t<Index, decltype(pick_up(Candidate))>;
};
/**
* @brief Helper type.
* @tparam Index The index of the argument to extract.
* @tparam Candidate A valid function, member function or data member.
*/
template<std::size_t Index, auto Candidate>
using nth_argument_t = typename nth_argument<Index, Candidate>::type;
} // namespace entt
#endif

View File

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

View File

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

View File

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

View File

@@ -1,147 +1,206 @@
#ifndef ENTT_ENTITY_FWD_HPP
#define ENTT_ENTITY_FWD_HPP
#include <memory>
#include <type_traits>
#include "../core/fwd.hpp"
#include "../core/type_traits.hpp"
namespace entt {
/*! @brief Default entity identifier. */
enum class entity : id_type {};
template<typename Entity, typename = std::allocator<Entity>>
template<typename Entity = entity, typename = std::allocator<Entity>>
class basic_sparse_set;
template<typename Type, typename = entity, typename = std::allocator<Type>, typename = void>
class basic_storage;
template<typename, typename Type, typename = std::allocator<Type>>
struct basic_storage;
template<typename Type>
class sigh_storage_mixin;
/**
* @brief Provides a common way to define storage types.
* @tparam Type Storage value type.
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Type, typename Entity = entity, typename Allocator = std::allocator<Type>, typename = void>
struct storage_type {
/*! @brief Type-to-storage conversion result. */
using type = sigh_storage_mixin<basic_storage<Type, Entity, Allocator>>;
};
template<typename>
/**
* @brief Helper type.
* @tparam Args Arguments to forward.
*/
template<typename... Args>
using storage_type_t = typename storage_type<Args...>::type;
/**
* Type-to-storage conversion utility that preserves constness.
* @tparam Type Storage value type, eventually const.
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Type, typename Entity = entity, typename Allocator = std::allocator<std::remove_const_t<Type>>>
struct storage_for {
/*! @brief Type-to-storage conversion result. */
using type = constness_as_t<storage_type_t<std::remove_const_t<Type>, Entity, Allocator>, Type>;
};
/**
* @brief Helper type.
* @tparam Args Arguments to forward.
*/
template<typename... Args>
using storage_for_t = typename storage_for<Args...>::type;
template<typename Entity = entity, typename = std::allocator<Entity>>
class basic_registry;
template<typename, typename, typename = void>
class basic_view;
template<typename...>
struct basic_view;
template<typename>
template<typename Type, typename = std::allocator<Type *>>
class basic_runtime_view;
template<typename...>
template<typename, typename, typename>
class basic_group;
template<typename>
class basic_observer;
template<typename>
class basic_organizer;
template<typename, typename...>
struct basic_handle;
template<typename>
class basic_snapshot;
template<typename>
class basic_snapshot_loader;
template<typename>
class basic_continuous_loader;
/**
* @brief Alias for exclusion lists.
* @tparam Type List of types.
*/
template<typename... Type>
using exclude_t = type_list<Type...>;
/*! @brief Default entity identifier. */
enum class entity: id_type {};
/**
* @brief Variable template for exclusion lists.
* @tparam Type List of types.
*/
template<typename... Type>
inline constexpr exclude_t<Type...> exclude{};
/**
* @brief Alias for lists of observed components.
* @tparam Type List of types.
*/
template<typename... Type>
using get_t = type_list<Type...>;
/**
* @brief Variable template for lists of observed components.
* @tparam Type List of types.
*/
template<typename... Type>
inline constexpr get_t<Type...> get{};
/**
* @brief Alias for lists of owned components.
* @tparam Type List of types.
*/
template<typename... Type>
using owned_t = type_list<Type...>;
/**
* @brief Variable template for lists of owned components.
* @tparam Type List of types.
*/
template<typename... Type>
inline constexpr owned_t<Type...> owned{};
/*! @brief Alias declaration for the most common use case. */
using sparse_set = basic_sparse_set<entity>;
using sparse_set = basic_sparse_set<>;
/**
* @brief Alias declaration for the most common use case.
* @tparam Type Type of objects assigned to the entities.
*/
template<typename Type>
using storage = basic_storage<Type>;
/*! @brief Alias declaration for the most common use case. */
using registry = basic_registry<>;
/*! @brief Alias declaration for the most common use case. */
using observer = basic_observer<registry>;
/*! @brief Alias declaration for the most common use case. */
using organizer = basic_organizer<registry>;
/*! @brief Alias declaration for the most common use case. */
using handle = basic_handle<registry>;
/*! @brief Alias declaration for the most common use case. */
using const_handle = basic_handle<const registry>;
/**
* @brief Alias declaration for the most common use case.
* @tparam Args Other template parameters.
*/
template<typename... Args>
using storage = basic_storage<entity, Args...>;
/*! @brief Alias declaration for the most common use case. */
using registry = basic_registry<entity>;
/*! @brief Alias declaration for the most common use case. */
using observer = basic_observer<entity>;
/*! @brief Alias declaration for the most common use case. */
using organizer = basic_organizer<entity>;
/*! @brief Alias declaration for the most common use case. */
using handle = basic_handle<entity>;
/*! @brief Alias declaration for the most common use case. */
using const_handle = basic_handle<const entity>;
using handle_view = basic_handle<registry, Args...>;
/**
* @brief Alias declaration for the most common use case.
* @tparam Args Other template parameters.
*/
template<typename... Args>
using handle_view = basic_handle<entity, Args...>;
using const_handle_view = basic_handle<const registry, Args...>;
/*! @brief Alias declaration for the most common use case. */
using snapshot = basic_snapshot<registry>;
/*! @brief Alias declaration for the most common use case. */
using snapshot_loader = basic_snapshot_loader<registry>;
/*! @brief Alias declaration for the most common use case. */
using continuous_loader = basic_continuous_loader<registry>;
/**
* @brief Alias declaration for the most common use case.
* @tparam Args Other template parameters.
* @tparam Get Types of storage iterated by the view.
* @tparam Exclude Types of storage used to filter the view.
*/
template<typename... Args>
using const_handle_view = basic_handle<const entity, Args...>;
template<typename Get, typename Exclude = exclude_t<>>
using view = basic_view<type_list_transform_t<Get, storage_for>, type_list_transform_t<Exclude, storage_for>>;
/*! @brief Alias declaration for the most common use case. */
using snapshot = basic_snapshot<entity>;
using runtime_view = basic_runtime_view<sparse_set>;
/*! @brief Alias declaration for the most common use case. */
using snapshot_loader = basic_snapshot_loader<entity>;
/*! @brief Alias declaration for the most common use case. */
using continuous_loader = basic_continuous_loader<entity>;
using const_runtime_view = basic_runtime_view<const sparse_set>;
/**
* @brief Alias declaration for the most common use case.
* @tparam Args Other template parameters.
* @tparam Owned Types of storage _owned_ by the group.
* @tparam Get Types of storage _observed_ by the group.
* @tparam Exclude Types of storage used to filter the group.
*/
template<typename... Args>
using view = basic_view<entity, Args...>;
/*! @brief Alias declaration for the most common use case. */
using runtime_view = basic_runtime_view<entity>;
/**
* @brief Alias declaration for the most common use case.
* @tparam Args Other template parameters.
*/
template<typename... Args>
using group = basic_group<entity, Args...>;
}
template<typename Owned, typename Get, typename Exclude>
using group = basic_group<type_list_transform_t<Owned, storage_for>, type_list_transform_t<Get, storage_for>, type_list_transform_t<Exclude, storage_for>>;
} // namespace entt
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,31 +1,107 @@
#ifndef ENTT_ENTITY_HANDLE_HPP
#define ENTT_ENTITY_HANDLE_HPP
#include <iterator>
#include <tuple>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/iterator.hpp"
#include "../core/type_traits.hpp"
#include "entity.hpp"
#include "fwd.hpp"
#include "registry.hpp"
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename It>
class handle_storage_iterator final {
template<typename Other>
friend class handle_storage_iterator;
using underlying_type = std::remove_reference_t<typename It::value_type::second_type>;
using entity_type = typename underlying_type::entity_type;
public:
using value_type = typename std::iterator_traits<It>::value_type;
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
constexpr handle_storage_iterator() noexcept
: entt{null},
it{},
last{} {}
constexpr handle_storage_iterator(entity_type value, It from, It to) noexcept
: entt{value},
it{from},
last{to} {
while(it != last && !it->second.contains(entt)) { ++it; }
}
constexpr handle_storage_iterator &operator++() noexcept {
while(++it != last && !it->second.contains(entt)) {}
return *this;
}
constexpr handle_storage_iterator operator++(int) noexcept {
handle_storage_iterator orig = *this;
return ++(*this), orig;
}
[[nodiscard]] constexpr reference operator*() const noexcept {
return *it;
}
[[nodiscard]] constexpr pointer operator->() const noexcept {
return operator*();
}
template<typename ILhs, typename IRhs>
friend constexpr bool operator==(const handle_storage_iterator<ILhs> &, const handle_storage_iterator<IRhs> &) noexcept;
private:
entity_type entt;
It it;
It last;
};
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator==(const handle_storage_iterator<ILhs> &lhs, const handle_storage_iterator<IRhs> &rhs) noexcept {
return lhs.it == rhs.it;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator!=(const handle_storage_iterator<ILhs> &lhs, const handle_storage_iterator<IRhs> &rhs) noexcept {
return !(lhs == rhs);
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Non-owning handle to an entity.
*
* Tiny wrapper around a registry and an entity.
*
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Type Types to which to restrict the scope of a handle.
* @tparam Registry Basic registry type.
* @tparam Scope Types to which to restrict the scope of a handle.
*/
template<typename Entity, typename... Type>
template<typename Registry, typename... Scope>
struct basic_handle {
/*! @brief Type of registry accepted by the handle. */
using registry_type = constness_as_t<basic_registry<std::remove_const_t<Entity>>, Entity>;
using registry_type = Registry;
/*! @brief Underlying entity identifier. */
using entity_type = typename registry_type::entity_type;
/*! @brief Underlying version type. */
@@ -34,29 +110,33 @@ struct basic_handle {
using size_type = typename registry_type::size_type;
/*! @brief Constructs an invalid handle. */
basic_handle() ENTT_NOEXCEPT
: reg{}, entt{null}
{}
basic_handle() noexcept
: reg{},
entt{null} {}
/**
* @brief Constructs a handle from a given registry and entity.
* @param ref An instance of the registry class.
* @param value An entity identifier.
* @param value A valid identifier.
*/
basic_handle(registry_type &ref, entity_type value) ENTT_NOEXCEPT
: reg{&ref}, entt{value}
{}
basic_handle(registry_type &ref, entity_type value) noexcept
: reg{&ref},
entt{value} {}
/**
* @brief Compares two handles.
* @tparam Args Template parameters of the handle with which to compare.
* @param other Handle with which to compare.
* @return True if both handles refer to the same registry and the same
* entity, false otherwise.
* @brief Returns an iterable object to use to _visit_ a handle.
*
* The iterable object returns a pair that contains the name and a reference
* to the current storage.<br/>
* Returned storage are those that contain the entity associated with the
* handle.
*
* @return An iterable object to use to _visit_ the handle.
*/
template<typename... Args>
[[nodiscard]] bool operator==(const basic_handle<Args...> &other) const ENTT_NOEXCEPT {
return reg == other.registry() && entt == other.entity();
[[nodiscard]] auto storage() const noexcept {
auto iterable = reg->storage();
using iterator_type = internal::handle_storage_iterator<typename decltype(iterable)::iterator>;
return iterable_adaptor{iterator_type{entt, iterable.begin(), iterable.end()}, iterator_type{entt, iterable.end(), iterable.end()}};
}
/**
@@ -67,21 +147,18 @@ struct basic_handle {
* entity.
*/
template<typename Other, typename... Args>
operator basic_handle<Other, Args...>() const ENTT_NOEXCEPT {
static_assert(
(std::is_same_v<Other, Entity> || std::is_same_v<std::remove_const_t<Other>, Entity>)
&& (sizeof...(Type) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Type)) && ... && (type_list_contains_v<type_list<Type...>, Args>))),
"Invalid conversion between different handles"
);
operator basic_handle<Other, Args...>() const noexcept {
static_assert(std::is_same_v<Other, Registry> || std::is_same_v<std::remove_const_t<Other>, Registry>, "Invalid conversion between different handles");
static_assert((sizeof...(Scope) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Scope)) && ... && (type_list_contains_v<type_list<Scope...>, Args>))), "Invalid conversion between different handles");
return reg ? basic_handle<Other, Args...>{*reg, entt} : basic_handle<Other, Args...>{};
}
/**
* @brief Converts a handle to its underlying entity.
* @return An entity identifier.
* @return The contained identifier.
*/
[[nodiscard]] operator entity_type() const ENTT_NOEXCEPT {
[[nodiscard]] operator entity_type() const noexcept {
return entity();
}
@@ -89,7 +166,7 @@ struct basic_handle {
* @brief Checks if a handle refers to non-null registry pointer and entity.
* @return True if the handle refers to non-null registry and entity, false otherwise.
*/
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
[[nodiscard]] explicit operator bool() const noexcept {
return reg && reg->valid(entt);
}
@@ -105,7 +182,7 @@ struct basic_handle {
* @brief Returns a pointer to the underlying registry, if any.
* @return A pointer to the underlying registry, if any.
*/
[[nodiscard]] registry_type * registry() const ENTT_NOEXCEPT {
[[nodiscard]] registry_type *registry() const noexcept {
return reg;
}
@@ -113,21 +190,17 @@ struct basic_handle {
* @brief Returns the entity associated with a handle.
* @return The entity associated with the handle.
*/
[[nodiscard]] entity_type entity() const ENTT_NOEXCEPT {
[[nodiscard]] entity_type entity() const noexcept {
return entt;
}
/**
* @brief Destroys the entity associated with a handle.
* @sa basic_registry::destroy
*/
/*! @brief Destroys the entity associated with a handle. */
void destroy() {
reg->destroy(entt);
}
/**
* @brief Destroys the entity associated with a handle.
* @sa basic_registry::destroy
* @param version A desired version upon destruction.
*/
void destroy(const version_type version) {
@@ -136,103 +209,79 @@ struct basic_handle {
/**
* @brief Assigns the given component to a handle.
* @sa basic_registry::emplace
* @tparam Component Type of component to create.
* @tparam Args Types of arguments to use to construct the component.
* @param args Parameters to use to initialize the component.
* @return A reference to the newly created component.
*/
template<typename Component, typename... Args>
decltype(auto) emplace(Args &&... args) const {
static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
decltype(auto) emplace(Args &&...args) const {
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
return reg->template emplace<Component>(entt, std::forward<Args>(args)...);
}
/**
* @brief Assigns or replaces the given component for a handle.
* @sa basic_registry::emplace_or_replace
* @tparam Component Type of component to assign or replace.
* @tparam Args Types of arguments to use to construct the component.
* @param args Parameters to use to initialize the component.
* @return A reference to the newly created component.
*/
template<typename Component, typename... Args>
decltype(auto) emplace_or_replace(Args &&... args) const {
static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
decltype(auto) emplace_or_replace(Args &&...args) const {
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
return reg->template emplace_or_replace<Component>(entt, std::forward<Args>(args)...);
}
/**
* @brief Patches the given component for a handle.
* @sa basic_registry::patch
* @tparam Component Type of component to patch.
* @tparam Func Types of the function objects to invoke.
* @param func Valid function objects.
* @return A reference to the patched component.
*/
template<typename Component, typename... Func>
decltype(auto) patch(Func &&... func) const {
static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
decltype(auto) patch(Func &&...func) const {
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
return reg->template patch<Component>(entt, std::forward<Func>(func)...);
}
/**
* @brief Replaces the given component for a handle.
* @sa basic_registry::replace
* @tparam Component Type of component to replace.
* @tparam Args Types of arguments to use to construct the component.
* @param args Parameters to use to initialize the component.
* @return A reference to the component being replaced.
*/
template<typename Component, typename... Args>
decltype(auto) replace(Args &&... args) const {
static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
decltype(auto) replace(Args &&...args) const {
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
return reg->template replace<Component>(entt, std::forward<Args>(args)...);
}
/**
* @brief Removes the given components from a handle.
* @sa basic_registry::remove
* @tparam Component Types of components to remove.
* @return The number of components actually removed.
*/
template<typename... Component>
size_type remove() const {
static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type");
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Component> && ...), "Invalid type");
return reg->template remove<Component...>(entt);
}
/**
* @brief Erases the given components from a handle.
* @sa basic_registry::erase
* @tparam Component Types of components to erase.
*/
template<typename... Component>
void erase() const {
static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type");
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Component> && ...), "Invalid type");
reg->template erase<Component...>(entt);
}
/*! @copydoc remove */
template<typename... Component>
[[deprecated("Use ::remove instead")]]
size_type remove_if_exists() const {
return remove<Component...>();
}
/**
* @brief Removes all the components from a handle and makes it orphaned.
* @sa basic_registry::remove_all
*/
[[deprecated("No longer supported")]]
void remove_all() const {
static_assert(sizeof...(Type) == 0, "Invalid operation");
reg->remove_all(entt);
}
/**
* @brief Checks if a handle has all the given components.
* @sa basic_registry::all_of
* @tparam Component Components for which to perform the check.
* @return True if the handle has all the components, false otherwise.
*/
@@ -243,7 +292,6 @@ struct basic_handle {
/**
* @brief Checks if a handle has at least one of the given components.
* @sa basic_registry::any_of
* @tparam Component Components for which to perform the check.
* @return True if the handle has at least one of the given components,
* false otherwise.
@@ -255,39 +303,36 @@ struct basic_handle {
/**
* @brief Returns references to the given components for a handle.
* @sa basic_registry::get
* @tparam Component Types of components to get.
* @return References to the components owned by the handle.
*/
template<typename... Component>
[[nodiscard]] decltype(auto) get() const {
static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type");
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Component> && ...), "Invalid type");
return reg->template get<Component...>(entt);
}
/**
* @brief Returns a reference to the given component for a handle.
* @sa basic_registry::get_or_emplace
* @tparam Component Type of component to get.
* @tparam Args Types of arguments to use to construct the component.
* @param args Parameters to use to initialize the component.
* @return Reference to the component owned by the handle.
*/
template<typename Component, typename... Args>
[[nodiscard]] decltype(auto) get_or_emplace(Args &&... args) const {
static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
[[nodiscard]] decltype(auto) get_or_emplace(Args &&...args) const {
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
return reg->template get_or_emplace<Component>(entt, std::forward<Args>(args)...);
}
/**
* @brief Returns pointers to the given components for a handle.
* @sa basic_registry::try_get
* @tparam Component Types of components to get.
* @return Pointers to the components owned by the handle.
*/
template<typename... Component>
[[nodiscard]] auto try_get() const {
static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type");
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Component> && ...), "Invalid type");
return reg->template try_get<Component...>(entt);
}
@@ -299,57 +344,39 @@ struct basic_handle {
return reg->orphan(entt);
}
/**
* @brief Visits a handle and returns the types for its components.
* @sa basic_registry::visit
* @tparam Func Type of the function object to invoke.
* @param func A valid function object.
*/
template<typename Func>
void visit(Func &&func) const {
reg->visit(entt, std::forward<Func>(func));
}
private:
registry_type *reg;
entity_type entt;
};
/**
* @brief Compares two handles.
* @tparam Args Scope of the first handle.
* @tparam Other Scope of the second handle.
* @param lhs A valid handle.
* @param rhs A valid handle.
* @return True if both handles refer to the same registry and the same
* entity, false otherwise.
*/
template<typename... Args, typename... Other>
[[nodiscard]] bool operator==(const basic_handle<Args...> &lhs, const basic_handle<Other...> &rhs) noexcept {
return lhs.registry() == rhs.registry() && lhs.entity() == rhs.entity();
}
/**
* @brief Compares two handles.
* @tparam Type A valid entity type (see entt_traits for more details).
* @tparam Other A valid entity type (see entt_traits for more details).
* @tparam Args Scope of the first handle.
* @tparam Other Scope of the second handle.
* @param lhs A valid handle.
* @param rhs A valid handle.
* @return False if both handles refer to the same registry and the same
* entity, true otherwise.
*/
template<typename Type, typename Other>
bool operator!=(const basic_handle<Type> &lhs, const basic_handle<Other> &rhs) ENTT_NOEXCEPT {
template<typename... Args, typename... Other>
[[nodiscard]] bool operator!=(const basic_handle<Args...> &lhs, const basic_handle<Other...> &rhs) noexcept {
return !(lhs == rhs);
}
/**
* @brief Deduction guide.
* @tparam Entity A valid entity type (see entt_traits for more details).
*/
template<typename Entity>
basic_handle(basic_registry<Entity> &, Entity)
-> basic_handle<Entity>;
/**
* @brief Deduction guide.
* @tparam Entity A valid entity type (see entt_traits for more details).
*/
template<typename Entity>
basic_handle(const basic_registry<Entity> &, Entity)
-> basic_handle<const Entity>;
}
} // namespace entt
#endif

View File

@@ -1,139 +1,116 @@
#ifndef ENTT_ENTITY_HELPER_HPP
#define ENTT_ENTITY_HELPER_HPP
#include <memory>
#include <type_traits>
#include "../config/config.h"
#include "../core/fwd.hpp"
#include "../core/type_traits.hpp"
#include "../signal/delegate.hpp"
#include "registry.hpp"
#include "component.hpp"
#include "fwd.hpp"
#include "group.hpp"
#include "view.hpp"
namespace entt {
/**
* @brief Converts a registry to a view.
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Registry Basic registry type.
*/
template<typename Entity>
struct as_view {
/*! @brief Underlying entity identifier. */
using entity_type = std::remove_const_t<Entity>;
template<typename Registry>
class as_view {
template<typename... Get, typename... Exclude>
auto dispatch(get_t<Get...>, exclude_t<Exclude...>) const {
return reg.template view<constness_as_t<typename Get::value_type, Get>...>(exclude_t<constness_as_t<typename Exclude::value_type, Exclude>...>{});
}
public:
/*! @brief Type of registry to convert. */
using registry_type = constness_as_t<basic_registry<entity_type>, Entity>;
using registry_type = Registry;
/*! @brief Underlying entity identifier. */
using entity_type = std::remove_const_t<typename registry_type::entity_type>;
/**
* @brief Constructs a converter for a given registry.
* @param source A valid reference to a registry.
*/
as_view(registry_type &source) ENTT_NOEXCEPT: reg{source} {}
as_view(registry_type &source) noexcept
: reg{source} {}
/**
* @brief Conversion function from a registry to a view.
* @tparam Exclude Types of components used to filter the view.
* @tparam Component Type of components used to construct the view.
* @tparam Get Type of storage used to construct the view.
* @tparam Exclude Types of storage used to filter the view.
* @return A newly created view.
*/
template<typename Exclude, typename... Component>
operator basic_view<entity_type, Exclude, Component...>() const {
return reg.template view<Component...>(Exclude{});
template<typename Get, typename Exclude>
operator basic_view<Get, Exclude>() const {
return dispatch(Get{}, Exclude{});
}
private:
registry_type &reg;
};
/**
* @brief Deduction guide.
* @tparam Entity A valid entity type (see entt_traits for more details).
*/
template<typename Entity>
as_view(basic_registry<Entity> &) -> as_view<Entity>;
/**
* @brief Deduction guide.
* @tparam Entity A valid entity type (see entt_traits for more details).
*/
template<typename Entity>
as_view(const basic_registry<Entity> &) -> as_view<const Entity>;
/**
* @brief Converts a registry to a group.
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Registry Basic registry type.
*/
template<typename Entity>
struct as_group {
/*! @brief Underlying entity identifier. */
using entity_type = std::remove_const_t<Entity>;
template<typename Registry>
class as_group {
template<typename... Owned, typename... Get, typename... Exclude>
auto dispatch(owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>) const {
if constexpr(std::is_const_v<registry_type>) {
return reg.template group_if_exists<typename Owned::value_type...>(get_t<typename Get::value_type...>{}, exclude_t<typename Exclude::value_type...>{});
} else {
return reg.template group<constness_as_t<typename Owned::value_type, Owned>...>(get_t<constness_as_t<typename Get::value_type, Get>...>{}, exclude_t<constness_as_t<typename Exclude::value_type, Exclude>...>{});
}
}
public:
/*! @brief Type of registry to convert. */
using registry_type = constness_as_t<basic_registry<entity_type>, Entity>;
using registry_type = Registry;
/*! @brief Underlying entity identifier. */
using entity_type = std::remove_const_t<typename registry_type::entity_type>;
/**
* @brief Constructs a converter for a given registry.
* @param source A valid reference to a registry.
*/
as_group(registry_type &source) ENTT_NOEXCEPT: reg{source} {}
as_group(registry_type &source) noexcept
: reg{source} {}
/**
* @brief Conversion function from a registry to a group.
* @tparam Exclude Types of components used to filter the group.
* @tparam Get Types of components observed by the group.
* @tparam Owned Types of components owned by the group.
* @tparam Owned Types of _owned_ by the group.
* @tparam Get Types of storage _observed_ by the group.
* @tparam Exclude Types of storage used to filter the group.
* @return A newly created group.
*/
template<typename Exclude, typename Get, typename... Owned>
operator basic_group<entity_type, Exclude, Get, Owned...>() const {
if constexpr(std::is_const_v<registry_type>) {
return reg.template group_if_exists<Owned...>(Get{}, Exclude{});
} else {
return reg.template group<Owned...>(Get{}, Exclude{});
}
template<typename Owned, typename Get, typename Exclude>
operator basic_group<Owned, Get, Exclude>() const {
return dispatch(Owned{}, Get{}, Exclude{});
}
private:
registry_type &reg;
};
/**
* @brief Deduction guide.
* @tparam Entity A valid entity type (see entt_traits for more details).
*/
template<typename Entity>
as_group(basic_registry<Entity> &) -> as_group<Entity>;
/**
* @brief Deduction guide.
* @tparam Entity A valid entity type (see entt_traits for more details).
*/
template<typename Entity>
as_group(const basic_registry<Entity> &) -> as_group<const Entity>;
/**
* @brief Helper to create a listener that directly invokes a member function.
* @tparam Member Member function to invoke on a component of the given type.
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Registry Basic registry type.
* @param reg A registry that contains the given entity and its components.
* @param entt Entity from which to get the component.
*/
template<auto Member, typename Entity = entity>
void invoke(basic_registry<Entity> &reg, const Entity entt) {
template<auto Member, typename Registry = std::decay_t<nth_argument_t<0u, Member>>>
void invoke(Registry &reg, const typename Registry::entity_type entt) {
static_assert(std::is_member_function_pointer_v<decltype(Member)>, "Invalid pointer to non-static member function");
delegate<void(basic_registry<Entity> &, const Entity)> func;
delegate<void(Registry &, const typename Registry::entity_type)> func;
func.template connect<Member>(reg.template get<member_class_t<decltype(Member)>>(entt));
func(reg, entt);
}
/**
* @brief Returns the entity associated with a given component.
*
@@ -141,28 +118,27 @@ void invoke(basic_registry<Entity> &reg, const Entity entt) {
* Currently, this function only works correctly with the default pool as it
* makes assumptions about how the components are laid out.
*
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Registry Basic registry type.
* @tparam Component Type of component.
* @param reg A registry that contains the given entity and its components.
* @param instance A valid component instance.
* @return The entity associated with the given component.
*/
template<typename Entity, typename Component>
Entity to_entity(const basic_registry<Entity> &reg, const Component &instance) {
const auto view = reg.template view<const Component>();
template<typename Registry, typename Component>
typename Registry::entity_type to_entity(const Registry &reg, const Component &instance) {
const auto &storage = reg.template storage<Component>();
const typename Registry::base_type &base = storage;
const auto *addr = std::addressof(instance);
for(auto it = view.rbegin(), last = view.rend(); it < last; it += ENTT_PACKED_PAGE) {
if(const auto dist = (addr - std::addressof(view.template get<const Component>(*it))); dist >= 0 && dist < ENTT_PACKED_PAGE) {
for(auto it = base.rbegin(), last = base.rend(); it < last; it += component_traits<Component>::page_size) {
if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < static_cast<decltype(dist)>(component_traits<Component>::page_size)) {
return *(it + dist);
}
}
return entt::null;
}
return null;
}
} // namespace entt
#endif

View File

@@ -1,30 +1,22 @@
#ifndef ENTT_ENTITY_OBSERVER_HPP
#define ENTT_ENTITY_OBSERVER_HPP
#include <limits>
#include <cstddef>
#include <cstdint>
#include <utility>
#include <limits>
#include <type_traits>
#include "../config/config.h"
#include <utility>
#include "../core/type_traits.hpp"
#include "../signal/delegate.hpp"
#include "registry.hpp"
#include "storage.hpp"
#include "utility.hpp"
#include "entity.hpp"
#include "fwd.hpp"
#include "storage.hpp"
namespace entt {
/*! @brief Grouping matcher. */
template<typename...>
struct matcher {};
/**
* @brief Collector.
*
@@ -34,7 +26,6 @@ struct matcher {};
template<typename...>
struct basic_collector;
/**
* @brief Collector.
*
@@ -52,7 +43,7 @@ struct basic_collector<> {
* @return The updated collector.
*/
template<typename... AllOf, typename... NoneOf>
static constexpr auto group(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT {
static constexpr auto group(exclude_t<NoneOf...> = {}) noexcept {
return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>>{};
}
@@ -62,7 +53,7 @@ struct basic_collector<> {
* @return The updated collector.
*/
template<typename AnyOf>
static constexpr auto update() ENTT_NOEXCEPT {
static constexpr auto update() noexcept {
return basic_collector<matcher<type_list<>, type_list<>, AnyOf>>{};
}
};
@@ -87,7 +78,7 @@ struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule
* @return The updated collector.
*/
template<typename... AllOf, typename... NoneOf>
static constexpr auto group(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT {
static constexpr auto group(exclude_t<NoneOf...> = {}) noexcept {
return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>, current_type, Other...>{};
}
@@ -97,7 +88,7 @@ struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule
* @return The updated collector.
*/
template<typename AnyOf>
static constexpr auto update() ENTT_NOEXCEPT {
static constexpr auto update() noexcept {
return basic_collector<matcher<type_list<>, type_list<>, AnyOf>, current_type, Other...>{};
}
@@ -108,17 +99,15 @@ struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule
* @return The updated collector.
*/
template<typename... AllOf, typename... NoneOf>
static constexpr auto where(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT {
static constexpr auto where(exclude_t<NoneOf...> = {}) noexcept {
using extended_type = matcher<type_list<Reject..., NoneOf...>, type_list<Require..., AllOf...>, Rule...>;
return basic_collector<extended_type, Other...>{};
}
};
/*! @brief Variable template used to ease the definition of collectors. */
inline constexpr basic_collector<> collector{};
/**
* @brief Observer.
*
@@ -166,11 +155,11 @@ inline constexpr basic_collector<> collector{};
* from the registry before being destroyed to avoid crashes due to dangling
* pointers.
*
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Registry Basic registry type.
*/
template<typename Entity>
class basic_observer {
using payload_type = std::uint32_t;
template<typename Registry>
class basic_observer: private basic_storage<std::uint32_t, typename Registry::entity_type> {
using base_type = basic_storage<std::uint32_t, typename Registry::entity_type>;
template<typename>
struct matcher_handler;
@@ -178,32 +167,32 @@ 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, basic_registry<Entity> &reg, const Entity entt) {
static void maybe_valid_if(basic_observer &obs, Registry &reg, const typename Registry::entity_type entt) {
if(reg.template all_of<Require...>(entt) && !reg.template any_of<Reject...>(entt)) {
if(!obs.storage.contains(entt)) {
obs.storage.emplace(entt);
if(!obs.contains(entt)) {
obs.emplace(entt);
}
obs.storage.get(entt) |= (1 << Index);
obs.get(entt) |= (1 << Index);
}
}
template<std::size_t Index>
static void discard_if(basic_observer &obs, basic_registry<Entity> &, const Entity entt) {
if(obs.storage.contains(entt) && !(obs.storage.get(entt) &= (~(1 << Index)))) {
obs.storage.erase(entt);
static void discard_if(basic_observer &obs, Registry &, const typename Registry::entity_type entt) {
if(obs.contains(entt) && !(obs.get(entt) &= (~(1 << Index)))) {
obs.erase(entt);
}
}
template<std::size_t Index>
static void connect(basic_observer &obs, basic_registry<Entity> &reg) {
static void connect(basic_observer &obs, Registry &reg) {
(reg.template on_destroy<Require>().template connect<&discard_if<Index>>(obs), ...);
(reg.template on_construct<Reject>().template connect<&discard_if<Index>>(obs), ...);
reg.template on_update<AnyOf>().template connect<&maybe_valid_if<Index>>(obs);
reg.template on_destroy<AnyOf>().template connect<&discard_if<Index>>(obs);
}
static void disconnect(basic_observer &obs, basic_registry<Entity> &reg) {
static void disconnect(basic_observer &obs, Registry &reg) {
(reg.template on_destroy<Require>().disconnect(obs), ...);
(reg.template on_construct<Reject>().disconnect(obs), ...);
reg.template on_update<AnyOf>().disconnect(obs);
@@ -214,32 +203,33 @@ 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, typename... Ignore>
static void maybe_valid_if(basic_observer &obs, basic_registry<Entity> &reg, const Entity entt) {
if([&reg, entt]() {
static void maybe_valid_if(basic_observer &obs, Registry &reg, const typename Registry::entity_type entt) {
auto condition = [&reg, entt]() {
if constexpr(sizeof...(Ignore) == 0) {
return reg.template all_of<AllOf..., Require...>(entt) && !reg.template any_of<NoneOf..., Reject...>(entt);
} else {
return reg.template all_of<AllOf..., Require...>(entt) && ((std::is_same_v<Ignore..., NoneOf> || !reg.template any_of<NoneOf>(entt)) && ...) && !reg.template any_of<Reject...>(entt);
}
}())
{
if(!obs.storage.contains(entt)) {
obs.storage.emplace(entt);
};
if(condition()) {
if(!obs.contains(entt)) {
obs.emplace(entt);
}
obs.storage.get(entt) |= (1 << Index);
obs.get(entt) |= (1 << Index);
}
}
template<std::size_t Index>
static void discard_if(basic_observer &obs, basic_registry<Entity> &, const Entity entt) {
if(obs.storage.contains(entt) && !(obs.storage.get(entt) &= (~(1 << Index)))) {
obs.storage.erase(entt);
static void discard_if(basic_observer &obs, Registry &, const typename Registry::entity_type entt) {
if(obs.contains(entt) && !(obs.get(entt) &= (~(1 << Index)))) {
obs.erase(entt);
}
}
template<std::size_t Index>
static void connect(basic_observer &obs, basic_registry<Entity> &reg) {
static void connect(basic_observer &obs, Registry &reg) {
(reg.template on_destroy<Require>().template connect<&discard_if<Index>>(obs), ...);
(reg.template on_construct<Reject>().template connect<&discard_if<Index>>(obs), ...);
(reg.template on_construct<AllOf>().template connect<&maybe_valid_if<Index>>(obs), ...);
@@ -248,7 +238,7 @@ class basic_observer {
(reg.template on_construct<NoneOf>().template connect<&discard_if<Index>>(obs), ...);
}
static void disconnect(basic_observer &obs, basic_registry<Entity> &reg) {
static void disconnect(basic_observer &obs, Registry &reg) {
(reg.template on_destroy<Require>().disconnect(obs), ...);
(reg.template on_construct<Reject>().disconnect(obs), ...);
(reg.template on_construct<AllOf>().disconnect(obs), ...);
@@ -259,30 +249,30 @@ class basic_observer {
};
template<typename... Matcher>
static void disconnect(basic_registry<Entity> &reg, basic_observer &obs) {
static void disconnect(Registry &reg, basic_observer &obs) {
(matcher_handler<Matcher>::disconnect(obs, reg), ...);
}
template<typename... Matcher, std::size_t... Index>
void connect(basic_registry<Entity> &reg, std::index_sequence<Index...>) {
static_assert(sizeof...(Matcher) < std::numeric_limits<payload_type>::digits, "Too many matchers");
void connect(Registry &reg, std::index_sequence<Index...>) {
static_assert(sizeof...(Matcher) < std::numeric_limits<typename base_type::value_type>::digits, "Too many matchers");
(matcher_handler<Matcher>::template connect<Index>(*this, reg), ...);
release.template connect<&basic_observer::disconnect<Matcher...>>(reg);
}
public:
/*! Basic registry type. */
using registry_type = Registry;
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
using entity_type = typename registry_type::entity_type;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Random access iterator type. */
using iterator = typename basic_sparse_set<Entity>::iterator;
using iterator = typename registry_type::base_type::iterator;
/*! @brief Default constructor. */
basic_observer()
: release{},
storage{}
{}
: release{} {}
/*! @brief Default copy constructor, deleted on purpose. */
basic_observer(const basic_observer &) = delete;
@@ -295,9 +285,8 @@ public:
* @param reg A valid reference to a registry.
*/
template<typename... Matcher>
basic_observer(basic_registry<entity_type> &reg, basic_collector<Matcher...>)
: basic_observer{}
{
basic_observer(registry_type &reg, basic_collector<Matcher...>)
: basic_observer{} {
connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{});
}
@@ -308,13 +297,13 @@ public:
* @brief Default copy assignment operator, deleted on purpose.
* @return This observer.
*/
basic_observer & operator=(const basic_observer &) = delete;
basic_observer &operator=(const basic_observer &) = delete;
/**
* @brief Default move assignment operator, deleted on purpose.
* @return This observer.
*/
basic_observer & operator=(basic_observer &&) = delete;
basic_observer &operator=(basic_observer &&) = delete;
/**
* @brief Connects an observer to a given registry.
@@ -322,10 +311,10 @@ public:
* @param reg A valid reference to a registry.
*/
template<typename... Matcher>
void connect(basic_registry<entity_type> &reg, basic_collector<Matcher...>) {
void connect(registry_type &reg, basic_collector<Matcher...>) {
disconnect();
connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{});
storage.clear();
base_type::clear();
}
/*! @brief Disconnects an observer from the registry it keeps track of. */
@@ -340,16 +329,16 @@ public:
* @brief Returns the number of elements in an observer.
* @return Number of elements.
*/
[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
return storage.size();
[[nodiscard]] size_type size() const noexcept {
return base_type::size();
}
/**
* @brief Checks whether an observer is empty.
* @return True if the observer is empty, false otherwise.
*/
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
return storage.empty();
[[nodiscard]] bool empty() const noexcept {
return base_type::empty();
}
/**
@@ -364,8 +353,8 @@ public:
*
* @return A pointer to the array of entities.
*/
[[nodiscard]] const entity_type * data() const ENTT_NOEXCEPT {
return storage.data();
[[nodiscard]] const entity_type *data() const noexcept {
return base_type::data();
}
/**
@@ -376,8 +365,8 @@ public:
*
* @return An iterator to the first entity of the observer.
*/
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
return storage.basic_sparse_set<entity_type>::begin();
[[nodiscard]] iterator begin() const noexcept {
return base_type::base_type::begin();
}
/**
@@ -390,13 +379,13 @@ public:
* @return An iterator to the entity following the last entity of the
* observer.
*/
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
return storage.basic_sparse_set<entity_type>::end();
[[nodiscard]] iterator end() const noexcept {
return base_type::base_type::end();
}
/*! @brief Clears the underlying container. */
void clear() ENTT_NOEXCEPT {
storage.clear();
void clear() noexcept {
base_type::clear();
}
/**
@@ -436,11 +425,8 @@ public:
private:
delegate<void(basic_observer &)> release;
basic_storage<entity_type, payload_type> storage;
};
}
} // namespace entt
#endif

View File

@@ -1,115 +1,98 @@
#ifndef ENTT_ENTITY_ORGANIZER_HPP
#define ENTT_ENTITY_ORGANIZER_HPP
#include <cstddef>
#include <algorithm>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>
#include "../core/type_info.hpp"
#include "../core/type_traits.hpp"
#include "../core/utility.hpp"
#include "../graph/adjacency_matrix.hpp"
#include "../graph/flow.hpp"
#include "fwd.hpp"
#include "helper.hpp"
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename>
struct is_view: std::false_type {};
template<typename Entity, typename... Exclude, typename... Component>
struct is_view<basic_view<Entity, exclude_t<Exclude...>, Component...>>: std::true_type {};
template<typename... Args>
struct is_view<basic_view<Args...>>: std::true_type {};
template<typename Type>
inline constexpr bool is_view_v = is_view<Type>::value;
template<typename Type, typename Override>
struct unpack_type {
using ro = std::conditional_t<
type_list_contains_v<Override, std::add_const_t<Type>> || (std::is_const_v<Type> && !type_list_contains_v<Override, std::remove_const_t<Type>>),
type_list_contains_v<Override, const Type> || (std::is_const_v<Type> && !type_list_contains_v<Override, std::remove_const_t<Type>>),
type_list<std::remove_const_t<Type>>,
type_list<>
>;
type_list<>>;
using rw = std::conditional_t<
type_list_contains_v<Override, std::remove_const_t<Type>> || (!std::is_const_v<Type> && !type_list_contains_v<Override, std::add_const_t<Type>>),
type_list_contains_v<Override, std::remove_const_t<Type>> || (!std::is_const_v<Type> && !type_list_contains_v<Override, const Type>),
type_list<Type>,
type_list<>
>;
type_list<>>;
};
template<typename Entity, typename... Override>
struct unpack_type<basic_registry<Entity>, type_list<Override...>> {
template<typename... Args, typename... Override>
struct unpack_type<basic_registry<Args...>, type_list<Override...>> {
using ro = type_list<>;
using rw = type_list<>;
};
template<typename Entity, typename... Override>
struct unpack_type<const basic_registry<Entity>, type_list<Override...>>
: unpack_type<basic_registry<Entity>, type_list<Override...>>
{};
template<typename... Args, typename... Override>
struct unpack_type<const basic_registry<Args...>, type_list<Override...>>
: unpack_type<basic_registry<Args...>, type_list<Override...>> {};
template<typename Entity, typename... Exclude, typename... Component, typename... Override>
struct unpack_type<basic_view<Entity, exclude_t<Exclude...>, Component...>, type_list<Override...>> {
using ro = type_list_cat_t<type_list<Exclude...>, typename unpack_type<Component, type_list<Override...>>::ro...>;
using rw = type_list_cat_t<typename unpack_type<Component, type_list<Override...>>::rw...>;
template<typename... Get, typename... Exclude, typename... Override>
struct unpack_type<basic_view<get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>> {
using ro = type_list_cat_t<type_list<typename Exclude::value_type...>, typename unpack_type<constness_as_t<typename Get::value_type, Get>, type_list<Override...>>::ro...>;
using rw = type_list_cat_t<typename unpack_type<constness_as_t<typename Get::value_type, Get>, type_list<Override...>>::rw...>;
};
template<typename Entity, typename... Exclude, typename... Component, typename... Override>
struct unpack_type<const basic_view<Entity, exclude_t<Exclude...>, Component...>, type_list<Override...>>
: unpack_type<basic_view<Entity, exclude_t<Exclude...>, Component...>, type_list<Override...>>
{};
template<typename... Get, typename... Exclude, typename... Override>
struct unpack_type<const basic_view<get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>>
: unpack_type<basic_view<get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>> {};
template<typename, typename>
struct resource;
struct resource_traits;
template<typename... Args, typename... Req>
struct resource<type_list<Args...>, type_list<Req...>> {
struct resource_traits<type_list<Args...>, type_list<Req...>> {
using args = type_list<std::remove_const_t<Args>...>;
using ro = type_list_cat_t<typename unpack_type<Args, type_list<Req...>>::ro..., typename unpack_type<Req, type_list<>>::ro...>;
using rw = type_list_cat_t<typename unpack_type<Args, type_list<Req...>>::rw..., typename unpack_type<Req, type_list<>>::rw...>;
};
template<typename... Req, typename Ret, typename... Args>
resource<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> free_function_to_resource(Ret(*)(Args...));
resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> free_function_to_resource_traits(Ret (*)(Args...));
template<typename... Req, typename Ret, typename Type, typename... Args>
resource<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource(Ret(*)(Type &, Args...));
resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (*)(Type &, Args...));
template<typename... Req, typename Ret, typename Class, typename... Args>
resource<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource(Ret(Class:: *)(Args...));
resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...));
template<typename... Req, typename Ret, typename Class, typename... Args>
resource<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource(Ret(Class:: *)(Args...) const);
template<typename... Req>
resource<type_list<>, type_list<Req...>> to_resource();
}
resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...) const);
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Utility class for creating a static task graph.
*
@@ -119,13 +102,13 @@ resource<type_list<>, type_list<Req...>> to_resource();
* goal of the tool. Instead, they are returned to the user in the form of a
* graph that allows for safe execution.
*
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Registry Basic registry type.
*/
template<typename Entity>
template<typename Registry>
class basic_organizer final {
using callback_type = void(const void *, entt::basic_registry<Entity> &);
using prepare_type = void(entt::basic_registry<Entity> &);
using dependency_type = std::size_t(const bool, type_info *, const std::size_t);
using callback_type = void(const void *, Registry &);
using prepare_type = void(Registry &);
using dependency_type = std::size_t(const bool, const type_info **, const std::size_t);
struct vertex_data final {
std::size_t ro_count{};
@@ -135,117 +118,54 @@ class basic_organizer final {
callback_type *callback{};
dependency_type *dependency;
prepare_type *prepare{};
type_info info{};
const type_info *info{};
};
template<typename Type>
[[nodiscard]] static decltype(auto) extract(basic_registry<Entity> &reg) {
if constexpr(std::is_same_v<Type, basic_registry<Entity>>) {
[[nodiscard]] static decltype(auto) extract(Registry &reg) {
if constexpr(std::is_same_v<Type, Registry>) {
return reg;
} else if constexpr(internal::is_view_v<Type>) {
return as_view{reg};
} else {
return reg.template ctx_or_set<std::remove_reference_t<Type>>();
return reg.ctx().template emplace<std::remove_reference_t<Type>>();
}
}
template<typename... Args>
[[nodiscard]] static auto to_args(basic_registry<Entity> &reg, type_list<Args...>) {
[[nodiscard]] static auto to_args(Registry &reg, type_list<Args...>) {
return std::tuple<decltype(extract<Args>(reg))...>(extract<Args>(reg)...);
}
template<typename... Type>
static std::size_t fill_dependencies(type_list<Type...>, [[maybe_unused]] type_info *buffer, [[maybe_unused]] const std::size_t count) {
static std::size_t fill_dependencies(type_list<Type...>, [[maybe_unused]] const type_info **buffer, [[maybe_unused]] const std::size_t count) {
if constexpr(sizeof...(Type) == 0u) {
return {};
} else {
type_info info[sizeof...(Type)]{type_id<Type>()...};
const auto length = (std::min)(count, sizeof...(Type));
std::copy_n(info, length, buffer);
const type_info *info[sizeof...(Type)]{&type_id<Type>()...};
const auto length = count < sizeof...(Type) ? count : sizeof...(Type);
for(std::size_t pos{}; pos < length; ++pos) {
buffer[pos] = info[pos];
}
return length;
}
}
template<typename... RO, typename... RW>
void track_dependencies(std::size_t index, const bool requires_registry, type_list<RO...>, type_list<RW...>) {
dependencies[type_hash<basic_registry<Entity>>::value()].emplace_back(index, requires_registry || (sizeof...(RO) + sizeof...(RW) == 0u));
(dependencies[type_hash<RO>::value()].emplace_back(index, false), ...);
(dependencies[type_hash<RW>::value()].emplace_back(index, true), ...);
}
[[nodiscard]] std::vector<bool> adjacency_matrix() {
const auto length = vertices.size();
std::vector<bool> edges(length * length, false);
// creates the ajacency matrix
for(const auto &deps: dependencies) {
const auto last = deps.second.cend();
auto it = deps.second.cbegin();
while(it != last) {
if(it->second) {
// rw item
if(auto curr = it++; it != last) {
if(it->second) {
edges[curr->first * length + it->first] = true;
} else {
if(const auto next = std::find_if(it, last, [](const auto &elem) { return elem.second; }); next != last) {
for(; it != next; ++it) {
edges[curr->first * length + it->first] = true;
edges[it->first * length + next->first] = true;
}
} else {
for(; it != next; ++it) {
edges[curr->first * length + it->first] = true;
}
}
}
}
} else {
// ro item, possibly only on first iteration
if(const auto next = std::find_if(it, last, [](const auto &elem) { return elem.second; }); next != last) {
for(; it != next; ++it) {
edges[it->first * length + next->first] = true;
}
} else {
it = last;
}
}
}
}
// computes the transitive closure
for(std::size_t vk{}; vk < length; ++vk) {
for(std::size_t vi{}; vi < length; ++vi) {
for(std::size_t vj{}; vj < length; ++vj) {
edges[vi * length + vj] = edges[vi * length + vj] || (edges[vi * length + vk] && edges[vk * length + vj]);
}
}
}
// applies the transitive reduction
for(std::size_t vert{}; vert < length; ++vert) {
edges[vert * length + vert] = false;
}
for(std::size_t vj{}; vj < length; ++vj) {
for(std::size_t vi{}; vi < length; ++vi) {
if(edges[vi * length + vj]) {
for(std::size_t vk{}; vk < length; ++vk) {
if(edges[vj * length + vk]) {
edges[vi * length + vk] = false;
}
}
}
}
}
return edges;
builder.bind(static_cast<id_type>(index));
builder.set(type_hash<Registry>::value(), requires_registry || (sizeof...(RO) + sizeof...(RW) == 0u));
(builder.ro(type_hash<RO>::value()), ...);
(builder.rw(type_hash<RW>::value()), ...);
}
public:
/*! Basic registry type. */
using registry_type = Registry;
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
using entity_type = typename registry_type::entity_type;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Raw task function type. */
@@ -262,8 +182,7 @@ public:
vertex(const bool vtype, vertex_data data, std::vector<std::size_t> edges)
: is_top_level{vtype},
node{std::move(data)},
reachable{std::move(edges)}
{}
reachable{std::move(edges)} {}
/**
* @brief Fills a buffer with the type info objects for the writable
@@ -272,7 +191,7 @@ public:
* @param length The length of the user-supplied buffer.
* @return The number of type info objects written to the buffer.
*/
size_type ro_dependency(type_info *buffer, const std::size_t length) const ENTT_NOEXCEPT {
size_type ro_dependency(const type_info **buffer, const std::size_t length) const noexcept {
return node.dependency(false, buffer, length);
}
@@ -283,7 +202,7 @@ public:
* @param length The length of the user-supplied buffer.
* @return The number of type info objects written to the buffer.
*/
size_type rw_dependency(type_info *buffer, const std::size_t length) const ENTT_NOEXCEPT {
size_type rw_dependency(const type_info **buffer, const std::size_t length) const noexcept {
return node.dependency(true, buffer, length);
}
@@ -291,7 +210,7 @@ public:
* @brief Returns the number of read-only resources of a vertex.
* @return The number of read-only resources of the vertex.
*/
size_type ro_count() const ENTT_NOEXCEPT {
size_type ro_count() const noexcept {
return node.ro_count;
}
@@ -299,7 +218,7 @@ public:
* @brief Returns the number of writable resources of a vertex.
* @return The number of writable resources of the vertex.
*/
size_type rw_count() const ENTT_NOEXCEPT {
size_type rw_count() const noexcept {
return node.rw_count;
}
@@ -307,7 +226,7 @@ public:
* @brief Checks if a vertex is also a top-level one.
* @return True if the vertex is a top-level one, false otherwise.
*/
bool top_level() const ENTT_NOEXCEPT {
bool top_level() const noexcept {
return is_top_level;
}
@@ -315,15 +234,15 @@ public:
* @brief Returns a type info object associated with a vertex.
* @return A properly initialized type info object.
*/
type_info info() const ENTT_NOEXCEPT {
return node.info;
const type_info &info() const noexcept {
return *node.info;
}
/**
* @brief Returns a user defined name associated with a vertex, if any.
* @return The user defined name associated with the vertex, if any.
*/
const char * name() const ENTT_NOEXCEPT {
const char *name() const noexcept {
return node.name;
}
@@ -331,7 +250,7 @@ public:
* @brief Returns the function associated with a vertex.
* @return The function associated with the vertex.
*/
function_type * callback() const ENTT_NOEXCEPT {
function_type *callback() const noexcept {
return node.callback;
}
@@ -339,7 +258,7 @@ public:
* @brief Returns the payload associated with a vertex, if any.
* @return The payload associated with the vertex, if any.
*/
const void * data() const ENTT_NOEXCEPT {
const void *data() const noexcept {
return node.payload;
}
@@ -347,7 +266,7 @@ public:
* @brief Returns the list of nodes reachable from a given vertex.
* @return The list of nodes reachable from the vertex.
*/
const std::vector<std::size_t> & children() const ENTT_NOEXCEPT {
const std::vector<std::size_t> &children() const noexcept {
return reachable;
}
@@ -356,7 +275,7 @@ public:
* are properly instantiated before using them.
* @param reg A valid registry.
*/
void prepare(basic_registry<entity_type> &reg) const {
void prepare(registry_type &reg) const {
node.prepare ? node.prepare(reg) : void();
}
@@ -374,25 +293,25 @@ public:
*/
template<auto Candidate, typename... Req>
void emplace(const char *name = nullptr) {
using resource_type = decltype(internal::free_function_to_resource<Req...>(Candidate));
constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, basic_registry<entity_type>>;
using resource_type = decltype(internal::free_function_to_resource_traits<Req...>(Candidate));
constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, registry_type>;
callback_type *callback = +[](const void *, basic_registry<entity_type> &reg) {
callback_type *callback = +[](const void *, registry_type &reg) {
std::apply(Candidate, to_args(reg, typename resource_type::args{}));
};
track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{});
vertices.push_back({
vertex_data vdata{
resource_type::ro::size,
resource_type::rw::size,
name,
nullptr,
callback,
+[](const bool rw, type_info *buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); },
+[](basic_registry<entity_type> &reg) { void(to_args(reg, typename resource_type::args{})); },
type_id<std::integral_constant<decltype(Candidate), Candidate>>()
});
+[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); },
+[](registry_type &reg) { void(to_args(reg, typename resource_type::args{})); },
&type_id<std::integral_constant<decltype(Candidate), Candidate>>()};
track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{});
vertices.push_back(std::move(vdata));
}
/**
@@ -406,30 +325,26 @@ public:
*/
template<auto Candidate, typename... Req, typename Type>
void emplace(Type &value_or_instance, const char *name = nullptr) {
using resource_type = decltype(internal::constrained_function_to_resource<Req...>(Candidate));
constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, basic_registry<entity_type>>;
using resource_type = decltype(internal::constrained_function_to_resource_traits<Req...>(Candidate));
constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, registry_type>;
callback_type *callback = +[](const void *payload, basic_registry<entity_type> &reg) {
callback_type *callback = +[](const void *payload, registry_type &reg) {
Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
std::apply(Candidate, std::tuple_cat(std::forward_as_tuple(*curr), to_args(reg, typename resource_type::args{})));
};
track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{});
vertices.push_back({
vertex_data vdata{
resource_type::ro::size,
resource_type::rw::size,
name,
&value_or_instance,
callback,
+[](const bool rw, type_info *buffer, const std::size_t length) {
return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length);
},
+[](basic_registry<entity_type> &reg) {
void(to_args(reg, typename resource_type::args{}));
},
type_id<std::integral_constant<decltype(Candidate), Candidate>>()
});
+[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); },
+[](registry_type &reg) { void(to_args(reg, typename resource_type::args{})); },
&type_id<std::integral_constant<decltype(Candidate), Candidate>>()};
track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{});
vertices.push_back(std::move(vdata));
}
/**
@@ -442,21 +357,20 @@ public:
*/
template<typename... Req>
void emplace(function_type *func, const void *payload = nullptr, const char *name = nullptr) {
using resource_type = internal::resource<type_list<>, type_list<Req...>>;
using resource_type = internal::resource_traits<type_list<>, type_list<Req...>>;
track_dependencies(vertices.size(), true, typename resource_type::ro{}, typename resource_type::rw{});
vertices.push_back({
vertex_data vdata{
resource_type::ro::size,
resource_type::rw::size,
name,
payload,
func,
+[](const bool rw, type_info *buffer, const std::size_t length) {
return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length);
},
+[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); },
nullptr,
type_info{}
});
&type_id<void>()};
vertices.push_back(std::move(vdata));
}
/**
@@ -464,28 +378,19 @@ public:
* @return The adjacency list of the task graph.
*/
std::vector<vertex> graph() {
const auto edges = adjacency_matrix();
// creates the adjacency list
std::vector<vertex> adjacency_list{};
adjacency_list.reserve(vertices.size());
auto adjacency_matrix = builder.graph();
for(std::size_t col{}, length = vertices.size(); col < length; ++col) {
for(auto curr: adjacency_matrix.vertices()) {
const auto iterable = adjacency_matrix.in_edges(curr);
std::vector<std::size_t> reachable{};
const auto row = col * length;
bool is_top_level = true;
for(std::size_t next{}; next < length; ++next) {
if(edges[row + next]) {
reachable.push_back(next);
}
for(auto &&edge: adjacency_matrix.out_edges(curr)) {
reachable.push_back(edge.second);
}
for(std::size_t next{}; next < length && is_top_level; ++next) {
is_top_level = !edges[next * length + col];
}
adjacency_list.emplace_back(is_top_level, vertices[col], std::move(reachable));
adjacency_list.emplace_back(iterable.cbegin() == iterable.cend(), vertices[curr], std::move(reachable));
}
return adjacency_list;
@@ -493,17 +398,15 @@ public:
/*! @brief Erases all elements from a container. */
void clear() {
dependencies.clear();
builder.clear();
vertices.clear();
}
private:
std::unordered_map<entt::id_type, std::vector<std::pair<std::size_t, bool>>> dependencies;
std::vector<vertex_data> vertices;
flow builder;
};
}
} // namespace entt
#endif

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,23 +1,108 @@
#ifndef ENTT_ENTITY_RUNTIME_VIEW_HPP
#define ENTT_ENTITY_RUNTIME_VIEW_HPP
#include <iterator>
#include <vector>
#include <utility>
#include <algorithm>
#include <type_traits>
#include "../config/config.h"
#include <cstddef>
#include <iterator>
#include <utility>
#include <vector>
#include "entity.hpp"
#include "sparse_set.hpp"
#include "fwd.hpp"
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename Set>
class runtime_view_iterator final {
using iterator_type = typename Set::iterator;
[[nodiscard]] bool valid() const {
return (!tombstone_check || *it != tombstone)
&& std::all_of(++pools->begin(), pools->end(), [entt = *it](const auto *curr) { return curr->contains(entt); })
&& std::none_of(filter->cbegin(), filter->cend(), [entt = *it](const auto *curr) { return curr && curr->contains(entt); });
}
public:
using difference_type = typename iterator_type::difference_type;
using value_type = typename iterator_type::value_type;
using pointer = typename iterator_type::pointer;
using reference = typename iterator_type::reference;
using iterator_category = std::bidirectional_iterator_tag;
constexpr runtime_view_iterator() noexcept
: pools{},
filter{},
it{},
tombstone_check{} {}
runtime_view_iterator(const std::vector<Set *> &cpools, const std::vector<Set *> &ignore, iterator_type curr) noexcept
: pools{&cpools},
filter{&ignore},
it{curr},
tombstone_check{pools->size() == 1u && (*pools)[0u]->policy() == deletion_policy::in_place} {
if(it != (*pools)[0]->end() && !valid()) {
++(*this);
}
}
runtime_view_iterator &operator++() {
while(++it != (*pools)[0]->end() && !valid()) {}
return *this;
}
runtime_view_iterator operator++(int) {
runtime_view_iterator orig = *this;
return ++(*this), orig;
}
runtime_view_iterator &operator--() {
while(--it != (*pools)[0]->begin() && !valid()) {}
return *this;
}
runtime_view_iterator operator--(int) {
runtime_view_iterator orig = *this;
return operator--(), orig;
}
[[nodiscard]] pointer operator->() const noexcept {
return it.operator->();
}
[[nodiscard]] reference operator*() const noexcept {
return *operator->();
}
[[nodiscard]] constexpr bool operator==(const runtime_view_iterator &other) const noexcept {
return it == other.it;
}
[[nodiscard]] constexpr bool operator!=(const runtime_view_iterator &other) const noexcept {
return !(*this == other);
}
private:
const std::vector<Set *> *pools;
const std::vector<Set *> *filter;
iterator_type it;
bool tombstone_check;
};
} // namespace internal
/**
* @brief Runtime view.
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Generic runtime view.
*
* Runtime views iterate over those entities that have at least all the given
* components in their bags. During initialization, a runtime view looks at the
@@ -52,116 +137,122 @@ namespace entt {
* Lifetime of a view must not overcome that of the registry that generated it.
* In any other case, attempting to use a view results in undefined behavior.
*
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Type Common base type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Entity>
class basic_runtime_view final {
using basic_common_type = basic_sparse_set<Entity>;
using underlying_iterator = typename basic_common_type::iterator;
class view_iterator final {
[[nodiscard]] bool valid() const {
const auto entt = *it;
return (!stable_storage || (entt != tombstone))
&& std::all_of(pools->begin()++, pools->end(), [entt](const auto *curr) { return curr->contains(entt); })
&& std::none_of(filter->cbegin(), filter->cend(), [entt](const auto *curr) { return curr && curr->contains(entt); });
}
public:
using difference_type = typename underlying_iterator::difference_type;
using value_type = typename underlying_iterator::value_type;
using pointer = typename underlying_iterator::pointer;
using reference = typename underlying_iterator::reference;
using iterator_category = std::bidirectional_iterator_tag;
view_iterator() ENTT_NOEXCEPT = default;
view_iterator(const std::vector<const basic_common_type *> &cpools, const std::vector<const basic_common_type *> &ignore, underlying_iterator curr) ENTT_NOEXCEPT
: pools{&cpools},
filter{&ignore},
it{curr},
stable_storage{std::any_of(pools->cbegin(), pools->cend(), [](const basic_common_type *cpool) { return (cpool->policy() == deletion_policy::in_place); })}
{
if(it != (*pools)[0]->end() && !valid()) {
++(*this);
}
}
view_iterator & operator++() {
while(++it != (*pools)[0]->end() && !valid());
return *this;
}
view_iterator operator++(int) {
view_iterator orig = *this;
return ++(*this), orig;
}
view_iterator & operator--() ENTT_NOEXCEPT {
while(--it != (*pools)[0]->begin() && !valid());
return *this;
}
view_iterator operator--(int) ENTT_NOEXCEPT {
view_iterator orig = *this;
return operator--(), orig;
}
[[nodiscard]] bool operator==(const view_iterator &other) const ENTT_NOEXCEPT {
return other.it == it;
}
[[nodiscard]] bool operator!=(const view_iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
[[nodiscard]] pointer operator->() const {
return it.operator->();
}
[[nodiscard]] reference operator*() const {
return *operator->();
}
private:
const std::vector<const basic_common_type *> *pools;
const std::vector<const basic_common_type *> *filter;
underlying_iterator it;
bool stable_storage;
};
[[nodiscard]] bool valid() const {
return !pools.empty() && pools.front();
}
template<typename Type, typename Allocator>
class basic_runtime_view {
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, Type *>, "Invalid value type");
using container_type = std::vector<Type *, Allocator>;
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
using entity_type = typename Type::entity_type;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Common type among all storage types. */
using base_type = Type;
/*! @brief Bidirectional iterator type. */
using iterator = view_iterator;
using iterator = internal::runtime_view_iterator<base_type>;
/*! @brief Default constructor to use to create empty, invalid views. */
basic_runtime_view() ENTT_NOEXCEPT
: pools{},
filter{}
{}
basic_runtime_view() noexcept
: basic_runtime_view{allocator_type{}} {}
/**
* @brief Constructs a runtime view from a set of storage classes.
* @param cpools The storage for the types to iterate.
* @param epools The storage for the types used to filter the view.
* @brief Constructs an empty, invalid view with a given allocator.
* @param allocator The allocator to use.
*/
basic_runtime_view(std::vector<const basic_common_type *> cpools, std::vector<const basic_common_type *> epools) ENTT_NOEXCEPT
: pools{std::move(cpools)},
filter{std::move(epools)}
{
// brings the best candidate (if any) on front of the vector
std::rotate(pools.begin(), std::min_element(pools.begin(), pools.end(), [](const auto *lhs, const auto *rhs) {
return (!lhs && rhs) || (lhs && rhs && lhs->size() < rhs->size());
}), pools.end());
explicit basic_runtime_view(const allocator_type &allocator)
: pools{allocator},
filter{allocator} {}
/*! @brief Default copy constructor. */
basic_runtime_view(const basic_runtime_view &) = default;
/**
* @brief Allocator-extended copy constructor.
* @param other The instance to copy from.
* @param allocator The allocator to use.
*/
basic_runtime_view(const basic_runtime_view &other, const allocator_type &allocator)
: pools{other.pools, allocator},
filter{other.filter, allocator} {}
/*! @brief Default move constructor. */
basic_runtime_view(basic_runtime_view &&) noexcept(std::is_nothrow_move_constructible_v<container_type>) = default;
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
basic_runtime_view(basic_runtime_view &&other, const allocator_type &allocator)
: pools{std::move(other.pools), allocator},
filter{std::move(other.filter), allocator} {}
/**
* @brief Default copy assignment operator.
* @return This container.
*/
basic_runtime_view &operator=(const basic_runtime_view &) = default;
/**
* @brief Default move assignment operator.
* @return This container.
*/
basic_runtime_view &operator=(basic_runtime_view &&) noexcept(std::is_nothrow_move_assignable_v<container_type>) = default;
/**
* @brief Exchanges the contents with those of a given view.
* @param other View to exchange the content with.
*/
void swap(basic_runtime_view &other) {
using std::swap;
swap(pools, other.pools);
swap(filter, other.filter);
}
/**
* @brief Returns the associated allocator.
* @return The associated allocator.
*/
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
return pools.get_allocator();
}
/*! @brief Clears the view. */
void clear() {
pools.clear();
filter.clear();
}
/**
* @brief Appends an opaque storage object to a runtime view.
* @param base An opaque reference to a storage object.
* @return This runtime view.
*/
basic_runtime_view &iterate(base_type &base) {
if(pools.empty() || !(base.size() < pools[0u]->size())) {
pools.push_back(&base);
} else {
pools.push_back(std::exchange(pools[0u], &base));
}
return *this;
}
/**
* @brief Adds an opaque storage object as a filter of a runtime view.
* @param base An opaque reference to a storage object.
* @return This runtime view.
*/
basic_runtime_view &exclude(base_type &base) {
filter.push_back(&base);
return *this;
}
/**
@@ -169,7 +260,7 @@ public:
* @return Estimated number of entities iterated by the view.
*/
[[nodiscard]] size_type size_hint() const {
return valid() ? pools.front()->size() : size_type{};
return pools.empty() ? size_type{} : pools.front()->size();
}
/**
@@ -183,7 +274,7 @@ public:
* @return An iterator to the first entity that has the given components.
*/
[[nodiscard]] iterator begin() const {
return valid() ? iterator{pools, filter, pools[0]->begin()} : iterator{};
return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->begin()};
}
/**
@@ -198,17 +289,18 @@ public:
* given components.
*/
[[nodiscard]] iterator end() const {
return valid() ? iterator{pools, filter, pools[0]->end()} : iterator{};
return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->end()};
}
/**
* @brief Checks if a view contains an entity.
* @param entt A valid entity identifier.
* @param entt A valid identifier.
* @return True if the view contains the given entity, false otherwise.
*/
[[nodiscard]] bool contains(const entity_type entt) const {
return valid() && std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *curr) { return curr->contains(entt); })
&& std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); });
return !pools.empty()
&& std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *curr) { return curr->contains(entt); })
&& std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); });
}
/**
@@ -234,12 +326,10 @@ public:
}
private:
std::vector<const basic_common_type *> pools;
std::vector<const basic_common_type *> filter;
container_type pools;
container_type filter;
};
}
} // namespace entt
#endif

View File

@@ -1,25 +1,23 @@
#ifndef ENTT_ENTITY_SNAPSHOT_HPP
#define ENTT_ENTITY_SNAPSHOT_HPP
#include <array>
#include <cstddef>
#include <iterator>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../container/dense_map.hpp"
#include "../core/type_traits.hpp"
#include "component.hpp"
#include "entity.hpp"
#include "fwd.hpp"
#include "registry.hpp"
#include "view.hpp"
namespace entt {
/**
* @brief Utility class to create snapshots from a registry.
*
@@ -28,16 +26,16 @@ namespace entt {
* This type can be used in both cases if provided with a correctly configured
* output archive.
*
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Registry Basic registry type.
*/
template<typename Entity>
template<typename Registry>
class basic_snapshot {
using traits_type = entt_traits<Entity>;
using entity_traits = entt_traits<typename Registry::entity_type>;
template<typename Component, typename Archive, typename It>
void get(Archive &archive, std::size_t sz, It first, It last) const {
const auto view = reg->template view<std::add_const_t<Component>>();
archive(typename traits_type::entity_type(sz));
const auto view = reg->template view<const Component>();
archive(typename entity_traits::entity_type(sz));
while(first != last) {
const auto entt = *(first++);
@@ -55,29 +53,30 @@ class basic_snapshot {
while(begin != last) {
const auto entt = *(begin++);
((reg->template all_of<Component>(entt) ? ++size[Index] : size[Index]), ...);
((reg->template all_of<Component>(entt) ? ++size[Index] : 0u), ...);
}
(get<Component>(archive, size[Index], first, last), ...);
}
public:
/*! Basic registry type. */
using registry_type = Registry;
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
using entity_type = typename registry_type::entity_type;
/**
* @brief Constructs an instance that is bound to a given registry.
* @param source A valid reference to a registry.
*/
basic_snapshot(const basic_registry<entity_type> &source) ENTT_NOEXCEPT
: reg{&source}
{}
basic_snapshot(const registry_type &source) noexcept
: reg{&source} {}
/*! @brief Default move constructor. */
basic_snapshot(basic_snapshot &&) = default;
basic_snapshot(basic_snapshot &&) noexcept = default;
/*! @brief Default move assignment operator. @return This snapshot. */
basic_snapshot & operator=(basic_snapshot &&) = default;
basic_snapshot &operator=(basic_snapshot &&) noexcept = default;
/**
* @brief Puts aside all the entities from the underlying registry.
@@ -90,17 +89,16 @@ public:
* @return An object of this type to continue creating the snapshot.
*/
template<typename Archive>
const basic_snapshot & entities(Archive &archive) const {
const basic_snapshot &entities(Archive &archive) const {
const auto sz = reg->size();
archive(typename traits_type::entity_type(sz));
archive(typename entity_traits::entity_type(sz + 1u));
archive(reg->released());
for(auto first = reg->data(), last = first + sz; first != last; ++first) {
archive(*first);
}
archive(reg->released());
return *this;
}
@@ -116,10 +114,10 @@ public:
* @return An object of this type to continue creating the snapshot.
*/
template<typename... Component, typename Archive>
const basic_snapshot & component(Archive &archive) const {
const basic_snapshot &component(Archive &archive) const {
if constexpr(sizeof...(Component) == 1u) {
const auto view = reg->template view<const Component...>();
(component<Component>(archive, view.data(), view.data() + view.size()), ...);
(component<Component>(archive, view.rbegin(), view.rend()), ...);
return *this;
} else {
(component<Component>(archive), ...);
@@ -142,16 +140,15 @@ public:
* @return An object of this type to continue creating the snapshot.
*/
template<typename... Component, typename Archive, typename It>
const basic_snapshot & component(Archive &archive, It first, It last) const {
const basic_snapshot &component(Archive &archive, It first, It last) const {
component<Component...>(archive, first, last, std::index_sequence_for<Component...>{});
return *this;
}
private:
const basic_registry<entity_type> *reg;
const registry_type *reg;
};
/**
* @brief Utility class to restore a snapshot as a whole.
*
@@ -160,58 +157,59 @@ private:
* originally had.<br/>
* An example of use is the implementation of a save/restore utility.
*
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Registry Basic registry type.
*/
template<typename Entity>
template<typename Registry>
class basic_snapshot_loader {
using traits_type = entt_traits<Entity>;
using entity_traits = entt_traits<typename Registry::entity_type>;
template<typename Type, typename Archive>
template<typename Component, typename Archive>
void assign(Archive &archive) const {
typename traits_type::entity_type length{};
typename entity_traits::entity_type length{};
entity_type entt;
archive(length);
entity_type entt{};
if constexpr(std::tuple_size_v<decltype(reg->template view<Type>().get({}))> == 0) {
if constexpr(ignore_as_empty_v<Component>) {
while(length--) {
archive(entt);
const auto entity = reg->valid(entt) ? entt : reg->create(entt);
ENTT_ASSERT(entity == entt, "Entity not available for use");
reg->template emplace<Type>(entity);
reg->template emplace<Component>(entt);
}
} else {
Type instance{};
Component instance;
while(length--) {
archive(entt, instance);
const auto entity = reg->valid(entt) ? entt : reg->create(entt);
ENTT_ASSERT(entity == entt, "Entity not available for use");
reg->template emplace<Type>(entity, std::move(instance));
reg->template emplace<Component>(entt, std::move(instance));
}
}
}
public:
/*! Basic registry type. */
using registry_type = Registry;
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
using entity_type = typename registry_type::entity_type;
/**
* @brief Constructs an instance that is bound to a given registry.
* @param source A valid reference to a registry.
*/
basic_snapshot_loader(basic_registry<entity_type> &source) ENTT_NOEXCEPT
: reg{&source}
{
basic_snapshot_loader(registry_type &source) noexcept
: reg{&source} {
// restoring a snapshot as a whole requires a clean registry
ENTT_ASSERT(reg->empty(), "Registry must be empty");
}
/*! @brief Default move constructor. */
basic_snapshot_loader(basic_snapshot_loader &&) = default;
basic_snapshot_loader(basic_snapshot_loader &&) noexcept = default;
/*! @brief Default move assignment operator. @return This loader. */
basic_snapshot_loader & operator=(basic_snapshot_loader &&) = default;
basic_snapshot_loader &operator=(basic_snapshot_loader &&) noexcept = default;
/**
* @brief Restores entities that were in use during serialization.
@@ -224,20 +222,17 @@ public:
* @return A valid loader to continue restoring data.
*/
template<typename Archive>
const basic_snapshot_loader & entities(Archive &archive) const {
typename traits_type::entity_type length{};
const basic_snapshot_loader &entities(Archive &archive) const {
typename entity_traits::entity_type length{};
archive(length);
std::vector<entity_type> all(length);
for(decltype(length) pos{}; pos < length; ++pos) {
for(std::size_t pos{}; pos < length; ++pos) {
archive(all[pos]);
}
entity_type destroyed;
archive(destroyed);
reg->assign(all.cbegin(), all.cend(), destroyed);
reg->assign(++all.cbegin(), all.cend(), all[0u]);
return *this;
}
@@ -256,7 +251,7 @@ public:
* @return A valid loader to continue restoring data.
*/
template<typename... Component, typename Archive>
const basic_snapshot_loader & component(Archive &archive) const {
const basic_snapshot_loader &component(Archive &archive) const {
(assign<Component>(archive), ...);
return *this;
}
@@ -271,19 +266,20 @@ public:
*
* @return A valid loader to continue restoring data.
*/
const basic_snapshot_loader & orphans() const {
reg->orphans([this](const auto entt) {
reg->release(entt);
const basic_snapshot_loader &orphans() const {
reg->each([this](const auto entt) {
if(reg->orphan(entt)) {
reg->release(entt);
}
});
return *this;
}
private:
basic_registry<entity_type> *reg;
registry_type *reg;
};
/**
* @brief Utility class for _continuous loading_.
*
@@ -298,13 +294,13 @@ private:
* the requirement of transferring somehow parts of the representation side to
* side.
*
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Registry Basic registry type.
*/
template<typename Entity>
template<typename Registry>
class basic_continuous_loader {
using traits_type = entt_traits<Entity>;
using entity_traits = entt_traits<typename Registry::entity_type>;
void destroy(Entity entt) {
void destroy(typename Registry::entity_type entt) {
if(const auto it = remloc.find(entt); it == remloc.cend()) {
const auto local = reg->create();
remloc.emplace(entt, std::make_pair(local, true));
@@ -312,7 +308,7 @@ class basic_continuous_loader {
}
}
void restore(Entity entt) {
void restore(typename Registry::entity_type entt) {
const auto it = remloc.find(entt);
if(it == remloc.cend()) {
@@ -329,8 +325,7 @@ class basic_continuous_loader {
}
template<typename Container>
auto update(int, Container &container)
-> decltype(typename Container::mapped_type{}, void()) {
auto update(int, Container &container) -> decltype(typename Container::mapped_type{}, void()) {
// map like container
Container other;
@@ -348,12 +343,12 @@ class basic_continuous_loader {
}
}
std::swap(container, other);
using std::swap;
swap(container, other);
}
template<typename Container>
auto update(char, Container &container)
-> decltype(typename Container::value_type{}, void()) {
auto update(char, Container &container) -> decltype(typename Container::value_type{}, void()) {
// vector like container
static_assert(std::is_same_v<typename Container::value_type, entity_type>, "Invalid value type");
@@ -362,9 +357,9 @@ class basic_continuous_loader {
}
}
template<typename Other, typename Type, typename Member>
void update([[maybe_unused]] Other &instance, [[maybe_unused]] Member Type:: *member) {
if constexpr(!std::is_same_v<Other, Type>) {
template<typename Component, typename Other, typename Member>
void update([[maybe_unused]] Component &instance, [[maybe_unused]] Member Other::*member) {
if constexpr(!std::is_same_v<Component, Other>) {
return;
} else if constexpr(std::is_same_v<Member, entity_type>) {
instance.*member = map(instance.*member);
@@ -385,48 +380,49 @@ class basic_continuous_loader {
}
}
template<typename Other, typename Archive, typename... Type, typename... Member>
void assign(Archive &archive, [[maybe_unused]] Member Type:: *... member) {
typename traits_type::entity_type length{};
template<typename Component, typename Archive, typename... Other, typename... Member>
void assign(Archive &archive, [[maybe_unused]] Member Other::*...member) {
typename entity_traits::entity_type length{};
entity_type entt;
archive(length);
entity_type entt{};
if constexpr(std::tuple_size_v<decltype(reg->template view<Other>().get({}))> == 0) {
if constexpr(ignore_as_empty_v<Component>) {
while(length--) {
archive(entt);
restore(entt);
reg->template emplace_or_replace<Other>(map(entt));
reg->template emplace_or_replace<Component>(map(entt));
}
} else {
Other instance{};
Component instance;
while(length--) {
archive(entt, instance);
(update(instance, member), ...);
restore(entt);
reg->template emplace_or_replace<Other>(map(entt), std::move(instance));
reg->template emplace_or_replace<Component>(map(entt), std::move(instance));
}
}
}
public:
/*! Basic registry type. */
using registry_type = Registry;
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
using entity_type = typename registry_type::entity_type;
/**
* @brief Constructs an instance that is bound to a given registry.
* @param source A valid reference to a registry.
*/
basic_continuous_loader(basic_registry<entity_type> &source) ENTT_NOEXCEPT
: reg{&source}
{}
basic_continuous_loader(registry_type &source) noexcept
: reg{&source} {}
/*! @brief Default move constructor. */
basic_continuous_loader(basic_continuous_loader &&) = default;
/*! @brief Default move assignment operator. @return This loader. */
basic_continuous_loader & operator=(basic_continuous_loader &&) = default;
basic_continuous_loader &operator=(basic_continuous_loader &&) = default;
/**
* @brief Restores entities that were in use during serialization.
@@ -439,25 +435,24 @@ public:
* @return A non-const reference to this loader.
*/
template<typename Archive>
basic_continuous_loader & entities(Archive &archive) {
typename traits_type::entity_type length{};
basic_continuous_loader &entities(Archive &archive) {
typename entity_traits::entity_type length{};
entity_type entt{};
archive(length);
// discards the head of the list of destroyed entities
archive(entt);
for(decltype(length) pos{}; pos < length; ++pos) {
for(std::size_t pos{}, last = length - 1u; pos < last; ++pos) {
archive(entt);
if(const auto entity = traits_type::to_entity(entt); entity == pos) {
if(const auto entity = entity_traits::to_entity(entt); entity == pos) {
restore(entt);
} else {
destroy(entt);
}
}
// discards the head of the list of destroyed entities
archive(entt);
return *this;
}
@@ -474,14 +469,14 @@ public:
*
* @tparam Component Type of component to restore.
* @tparam Archive Type of input archive.
* @tparam Type Types of components to update with local counterparts.
* @tparam Other Types of components to update with local counterparts.
* @tparam Member Types of members to update with their local counterparts.
* @param archive A valid reference to an input archive.
* @param member Members to update with their local counterparts.
* @return A non-const reference to this loader.
*/
template<typename... Component, typename Archive, typename... Type, typename... Member>
basic_continuous_loader & component(Archive &archive, Member Type:: *... member) {
template<typename... Component, typename Archive, typename... Other, typename... Member>
basic_continuous_loader &component(Archive &archive, Member Other::*...member) {
(remove_if_exists<Component>(), ...);
(assign<Component>(archive, member...), ...);
return *this;
@@ -495,7 +490,7 @@ public:
*
* @return A non-const reference to this loader.
*/
basic_continuous_loader & shrink() {
basic_continuous_loader &shrink() {
auto it = remloc.begin();
while(it != remloc.cend()) {
@@ -527,9 +522,11 @@ public:
*
* @return A non-const reference to this loader.
*/
basic_continuous_loader & orphans() {
reg->orphans([this](const auto entt) {
reg->release(entt);
basic_continuous_loader &orphans() {
reg->each([this](const auto entt) {
if(reg->orphan(entt)) {
reg->release(entt);
}
});
return *this;
@@ -537,19 +534,19 @@ public:
/**
* @brief Tests if a loader knows about a given entity.
* @param entt An entity identifier.
* @param entt A valid identifier.
* @return True if `entity` is managed by the loader, false otherwise.
*/
[[nodiscard]] bool contains(entity_type entt) const ENTT_NOEXCEPT {
[[nodiscard]] bool contains(entity_type entt) const noexcept {
return (remloc.find(entt) != remloc.cend());
}
/**
* @brief Returns the identifier to which an entity refers.
* @param entt An entity identifier.
* @param entt A valid identifier.
* @return The local identifier if any, the null entity otherwise.
*/
[[nodiscard]] entity_type map(entity_type entt) const ENTT_NOEXCEPT {
[[nodiscard]] entity_type map(entity_type entt) const noexcept {
const auto it = remloc.find(entt);
entity_type other = null;
@@ -561,12 +558,10 @@ public:
}
private:
std::unordered_map<entity_type, std::pair<entity_type, bool>> remloc;
basic_registry<entity_type> *reg;
dense_map<entity_type, std::pair<entity_type, bool>> remloc;
registry_type *reg;
};
}
} // namespace entt
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,236 @@
#ifndef ENTT_ENTITY_SIGH_STORAGE_MIXIN_HPP
#define ENTT_ENTITY_SIGH_STORAGE_MIXIN_HPP
#include <utility>
#include "../config/config.h"
#include "../core/any.hpp"
#include "../signal/sigh.hpp"
#include "fwd.hpp"
namespace entt {
/**
* @brief Mixin type used to add signal support to storage types.
*
* The function type of a listener is equivalent to:
*
* @code{.cpp}
* void(basic_registry<entity_type> &, entity_type);
* @endcode
*
* This applies to all signals made available.
*
* @tparam Type The type of the underlying storage.
*/
template<typename Type>
class sigh_storage_mixin final: public Type {
using basic_registry_type = basic_registry<typename Type::entity_type, typename Type::base_type::allocator_type>;
using sigh_type = sigh<void(basic_registry_type &, const typename Type::entity_type), typename Type::allocator_type>;
using basic_iterator = typename Type::basic_iterator;
void pop(basic_iterator first, basic_iterator last) override {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
for(; first != last; ++first) {
const auto entt = *first;
destruction.publish(*owner, entt);
const auto it = Type::find(entt);
Type::pop(it, it + 1u);
}
}
basic_iterator try_emplace(const typename basic_registry_type::entity_type entt, const bool force_back, const void *value) final {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
Type::try_emplace(entt, force_back, value);
construction.publish(*owner, entt);
return Type::find(entt);
}
public:
/*! @brief Allocator type. */
using allocator_type = typename Type::allocator_type;
/*! @brief Underlying entity identifier. */
using entity_type = typename Type::entity_type;
/*! @brief Expected registry type. */
using registry_type = basic_registry_type;
/*! @brief Default constructor. */
sigh_storage_mixin()
: sigh_storage_mixin{allocator_type{}} {}
/**
* @brief Constructs an empty storage with a given allocator.
* @param allocator The allocator to use.
*/
explicit sigh_storage_mixin(const allocator_type &allocator)
: Type{allocator},
owner{},
construction{allocator},
destruction{allocator},
update{allocator} {}
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
sigh_storage_mixin(sigh_storage_mixin &&other) noexcept
: Type{std::move(other)},
owner{other.owner},
construction{std::move(other.construction)},
destruction{std::move(other.destruction)},
update{std::move(other.update)} {}
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
sigh_storage_mixin(sigh_storage_mixin &&other, const allocator_type &allocator) noexcept
: Type{std::move(other), allocator},
owner{other.owner},
construction{std::move(other.construction), allocator},
destruction{std::move(other.destruction), allocator},
update{std::move(other.update), allocator} {}
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This storage.
*/
sigh_storage_mixin &operator=(sigh_storage_mixin &&other) noexcept {
Type::operator=(std::move(other));
owner = other.owner;
construction = std::move(other.construction);
destruction = std::move(other.destruction);
update = std::move(other.update);
return *this;
}
/**
* @brief Exchanges the contents with those of a given storage.
* @param other Storage to exchange the content with.
*/
void swap(sigh_storage_mixin &other) {
using std::swap;
Type::swap(other);
swap(owner, other.owner);
swap(construction, other.construction);
swap(destruction, other.destruction);
swap(update, other.update);
}
/**
* @brief Returns a sink object.
*
* The sink returned by this function can be used to receive notifications
* whenever a new instance is created and assigned to an entity.<br/>
* Listeners are invoked after the object has been assigned to the entity.
*
* @sa sink
*
* @return A temporary sink object.
*/
[[nodiscard]] auto on_construct() noexcept {
return sink{construction};
}
/**
* @brief Returns a sink object.
*
* The sink returned by this function can be used to receive notifications
* whenever an instance is explicitly updated.<br/>
* Listeners are invoked after the object has been updated.
*
* @sa sink
*
* @return A temporary sink object.
*/
[[nodiscard]] auto on_update() noexcept {
return sink{update};
}
/**
* @brief Returns a sink object.
*
* The sink returned by this function can be used to receive notifications
* whenever an instance is removed from an entity and thus destroyed.<br/>
* Listeners are invoked before the object has been removed from the entity.
*
* @sa sink
*
* @return A temporary sink object.
*/
[[nodiscard]] auto on_destroy() noexcept {
return sink{destruction};
}
/**
* @brief Assigns entities to a storage.
* @tparam Args Types of arguments to use to construct the object.
* @param entt A valid identifier.
* @param args Parameters to use to initialize the object.
* @return A reference to the newly created object.
*/
template<typename... Args>
decltype(auto) emplace(const entity_type entt, Args &&...args) {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
Type::emplace(entt, std::forward<Args>(args)...);
construction.publish(*owner, entt);
return this->get(entt);
}
/**
* @brief Patches the given instance for an entity.
* @tparam Func Types of the function objects to invoke.
* @param entt A valid identifier.
* @param func Valid function objects.
* @return A reference to the patched instance.
*/
template<typename... Func>
decltype(auto) patch(const entity_type entt, Func &&...func) {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
Type::patch(entt, std::forward<Func>(func)...);
update.publish(*owner, entt);
return this->get(entt);
}
/**
* @brief Assigns entities to a storage.
* @tparam It Type of input iterator.
* @tparam Args Types of arguments to use to construct the objects assigned
* to the entities.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @param args Parameters to use to initialize the objects assigned to the
* entities.
*/
template<typename It, typename... Args>
void insert(It first, It last, Args &&...args) {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
Type::insert(first, last, std::forward<Args>(args)...);
for(auto it = construction.empty() ? last : first; it != last; ++it) {
construction.publish(*owner, *it);
}
}
/**
* @brief Forwards variables to derived classes, if any.
* @param value A variable wrapped in an opaque container.
*/
void bind(any value) noexcept final {
auto *reg = any_cast<basic_registry_type>(&value);
owner = reg ? reg : owner;
Type::bind(std::move(value));
}
private:
basic_registry_type *owner;
sigh_type construction;
sigh_type destruction;
sigh_type update;
};
} // namespace entt
#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>
inline constexpr exclude_t<Type...> exclude{};
/**
* @brief Alias for lists of observed components.
* @tparam Type List of types.
*/
template<typename... Type>
struct get_t: type_list<Type...>{};
/**
* @brief Variable template for lists of observed components.
* @tparam Type List of types.
*/
template<typename... Type>
inline constexpr get_t<Type...> get{};
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,21 @@
// IWYU pragma: begin_exports
#include "config/config.h"
#include "config/macro.h"
#include "config/version.h"
#include "container/dense_map.hpp"
#include "container/dense_set.hpp"
#include "core/algorithm.hpp"
#include "core/any.hpp"
#include "core/attribute.h"
#include "core/compressed_pair.hpp"
#include "core/enum.hpp"
#include "core/family.hpp"
#include "core/hashed_string.hpp"
#include "core/ident.hpp"
#include "core/iterator.hpp"
#include "core/memory.hpp"
#include "core/monostate.hpp"
#include "core/tuple.hpp"
#include "core/type_info.hpp"
#include "core/type_traits.hpp"
#include "core/utility.hpp"
@@ -16,18 +26,20 @@
#include "entity/helper.hpp"
#include "entity/observer.hpp"
#include "entity/organizer.hpp"
#include "entity/poly_storage.hpp"
#include "entity/registry.hpp"
#include "entity/runtime_view.hpp"
#include "entity/snapshot.hpp"
#include "entity/sparse_set.hpp"
#include "entity/storage.hpp"
#include "entity/utility.hpp"
#include "entity/storage_mixin.hpp"
#include "entity/view.hpp"
#include "graph/adjacency_matrix.hpp"
#include "graph/dot.hpp"
#include "graph/flow.hpp"
#include "locator/locator.hpp"
#include "meta/adl_pointer.hpp"
#include "meta/container.hpp"
#include "meta/ctx.hpp"
#include "meta/context.hpp"
#include "meta/factory.hpp"
#include "meta/meta.hpp"
#include "meta/node.hpp"
@@ -43,9 +55,10 @@
#include "process/process.hpp"
#include "process/scheduler.hpp"
#include "resource/cache.hpp"
#include "resource/handle.hpp"
#include "resource/loader.hpp"
#include "resource/resource.hpp"
#include "signal/delegate.hpp"
#include "signal/dispatcher.hpp"
#include "signal/emitter.hpp"
#include "signal/sigh.hpp"
// IWYU pragma: end_exports

View File

@@ -1,5 +1,11 @@
// IWYU pragma: begin_exports
#include "container/fwd.hpp"
#include "core/fwd.hpp"
#include "entity/fwd.hpp"
#include "graph/fwd.hpp"
#include "meta/fwd.hpp"
#include "poly/fwd.hpp"
#include "process/fwd.hpp"
#include "resource/fwd.hpp"
#include "signal/fwd.hpp"
// IWYU pragma: end_exports

View File

@@ -0,0 +1,348 @@
#ifndef ENTT_GRAPH_ADJACENCY_MATRIX_HPP
#define ENTT_GRAPH_ADJACENCY_MATRIX_HPP
#include <cstddef>
#include <iterator>
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../core/iterator.hpp"
#include "fwd.hpp"
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename It>
class edge_iterator {
using size_type = std::size_t;
public:
using value_type = std::pair<size_type, size_type>;
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
constexpr edge_iterator() noexcept
: it{},
vert{},
pos{},
last{},
offset{} {}
constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept
: it{std::move(base)},
vert{vertices},
pos{from},
last{to},
offset{step} {
for(; pos != last && !it[pos]; pos += offset) {}
}
constexpr edge_iterator &operator++() noexcept {
for(pos += offset; pos != last && !it[pos]; pos += offset) {}
return *this;
}
constexpr edge_iterator operator++(int) noexcept {
edge_iterator orig = *this;
return ++(*this), orig;
}
[[nodiscard]] constexpr reference operator*() const noexcept {
return *operator->();
}
[[nodiscard]] constexpr pointer operator->() const noexcept {
return std::make_pair<size_type>(pos / vert, pos % vert);
}
template<typename Type>
friend constexpr bool operator==(const edge_iterator<Type> &, const edge_iterator<Type> &) noexcept;
private:
It it;
size_type vert;
size_type pos;
size_type last;
size_type offset{};
};
template<typename Container>
[[nodiscard]] inline constexpr bool operator==(const edge_iterator<Container> &lhs, const edge_iterator<Container> &rhs) noexcept {
return lhs.pos == rhs.pos;
}
template<typename Container>
[[nodiscard]] inline constexpr bool operator!=(const edge_iterator<Container> &lhs, const edge_iterator<Container> &rhs) noexcept {
return !(lhs == rhs);
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Basic implementation of a directed adjacency matrix.
* @tparam Category Either a directed or undirected category tag.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Category, typename Allocator>
class adjacency_matrix {
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_base_of_v<directed_tag, Category>, "Invalid graph category");
static_assert(std::is_same_v<typename alloc_traits::value_type, std::size_t>, "Invalid value type");
using container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Vertex type. */
using vertex_type = size_type;
/*! @brief Edge type. */
using edge_type = std::pair<vertex_type, vertex_type>;
/*! @brief Vertex iterator type. */
using vertex_iterator = iota_iterator<vertex_type>;
/*! @brief Edge iterator type. */
using edge_iterator = internal::edge_iterator<typename container_type::const_iterator>;
/*! @brief Out edge iterator type. */
using out_edge_iterator = edge_iterator;
/*! @brief In edge iterator type. */
using in_edge_iterator = edge_iterator;
/*! @brief Graph category tag. */
using graph_category = Category;
/*! @brief Default constructor. */
adjacency_matrix() noexcept(noexcept(allocator_type{}))
: adjacency_matrix{0u} {}
/**
* @brief Constructs an empty container with a given allocator.
* @param allocator The allocator to use.
*/
explicit adjacency_matrix(const allocator_type &allocator) noexcept
: adjacency_matrix{0u, allocator} {}
/**
* @brief Constructs an empty container with a given allocator and user
* supplied number of vertices.
* @param vertices Number of vertices.
* @param allocator The allocator to use.
*/
adjacency_matrix(const size_type vertices, const allocator_type &allocator = allocator_type{})
: matrix{vertices * vertices, allocator},
vert{vertices} {}
/**
* @brief Copy constructor.
* @param other The instance to copy from.
*/
adjacency_matrix(const adjacency_matrix &other)
: adjacency_matrix{other, other.get_allocator()} {}
/**
* @brief Allocator-extended copy constructor.
* @param other The instance to copy from.
* @param allocator The allocator to use.
*/
adjacency_matrix(const adjacency_matrix &other, const allocator_type &allocator)
: matrix{other.matrix, allocator},
vert{other.vert} {}
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
adjacency_matrix(adjacency_matrix &&other) noexcept
: adjacency_matrix{std::move(other), other.get_allocator()} {}
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator)
: matrix{std::move(other.matrix), allocator},
vert{std::exchange(other.vert, 0u)} {}
/**
* @brief Default copy assignment operator.
* @param other The instance to copy from.
* @return This container.
*/
adjacency_matrix &operator=(const adjacency_matrix &other) {
matrix = other.matrix;
vert = other.vert;
return *this;
}
/**
* @brief Default move assignment operator.
* @param other The instance to move from.
* @return This container.
*/
adjacency_matrix &operator=(adjacency_matrix &&other) noexcept {
matrix = std::move(other.matrix);
vert = std::exchange(other.vert, 0u);
return *this;
}
/**
* @brief Returns the associated allocator.
* @return The associated allocator.
*/
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
return matrix.get_allocator();
}
/*! @brief Clears the adjacency matrix. */
void clear() noexcept {
matrix.clear();
vert = {};
}
/**
* @brief Exchanges the contents with those of a given adjacency matrix.
* @param other Adjacency matrix to exchange the content with.
*/
void swap(adjacency_matrix &other) {
using std::swap;
swap(matrix, other.matrix);
swap(vert, other.vert);
}
/**
* @brief Returns the number of vertices.
* @return The number of vertices.
*/
[[nodiscard]] size_type size() const noexcept {
return vert;
}
/**
* @brief Returns an iterable object to visit all vertices of a matrix.
* @return An iterable object to visit all vertices of a matrix.
*/
[[nodiscard]] iterable_adaptor<vertex_iterator> vertices() const noexcept {
return {0u, vert};
}
/**
* @brief Returns an iterable object to visit all edges of a matrix.
* @return An iterable object to visit all edges of a matrix.
*/
[[nodiscard]] iterable_adaptor<edge_iterator> edges() const noexcept {
const auto it = matrix.cbegin();
const auto sz = matrix.size();
return {{it, vert, 0u, sz, 1u}, {it, vert, sz, sz, 1u}};
}
/**
* @brief Returns an iterable object to visit all out edges of a vertex.
* @param vertex The vertex of which to return all out edges.
* @return An iterable object to visit all out edges of a vertex.
*/
[[nodiscard]] iterable_adaptor<out_edge_iterator> out_edges(const vertex_type vertex) const noexcept {
const auto it = matrix.cbegin();
const auto from = vertex * vert;
const auto to = vertex * vert + vert;
return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}};
}
/**
* @brief Returns an iterable object to visit all in edges of a vertex.
* @param vertex The vertex of which to return all in edges.
* @return An iterable object to visit all in edges of a vertex.
*/
[[nodiscard]] iterable_adaptor<in_edge_iterator> in_edges(const vertex_type vertex) const noexcept {
const auto it = matrix.cbegin();
const auto from = vertex;
const auto to = vert * (vert - 1u) + vertex;
return {{it, vert, from, to, vert}, {it, vert, to, to, vert}};
}
/**
* @brief Resizes an adjacency matrix.
* @param vertices The new number of vertices.
*/
void resize(const size_type vertices) {
adjacency_matrix other{vertices, get_allocator()};
for(auto [lhs, rhs]: edges()) {
other.insert(lhs, rhs);
}
other.swap(*this);
}
/**
* @brief Inserts an edge into the adjacency matrix, if it does not exist.
* @param lhs The left hand vertex of the edge.
* @param rhs The right hand vertex of the edge.
* @return A pair consisting of an iterator to the inserted element (or to
* the element that prevented the insertion) and a bool denoting whether the
* insertion took place.
*/
std::pair<edge_iterator, bool> insert(const vertex_type lhs, const vertex_type rhs) {
const auto pos = lhs * vert + rhs;
if constexpr(std::is_same_v<graph_category, undirected_tag>) {
const auto rev = rhs * vert + lhs;
ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong");
matrix[rev] = 1u;
}
const auto inserted = !std::exchange(matrix[pos], 1u);
return {edge_iterator{matrix.cbegin(), vert, pos, matrix.size(), 1u}, inserted};
}
/**
* @brief Removes the edge associated with a pair of given vertices.
* @param lhs The left hand vertex of the edge.
* @param rhs The right hand vertex of the edge.
* @return Number of elements removed (either 0 or 1).
*/
size_type erase(const vertex_type lhs, const vertex_type rhs) {
const auto pos = lhs * vert + rhs;
if constexpr(std::is_same_v<graph_category, undirected_tag>) {
const auto rev = rhs * vert + lhs;
ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong");
matrix[rev] = 0u;
}
return std::exchange(matrix[pos], 0u);
}
/**
* @brief Checks if an adjacency matrix contains a given edge.
* @param lhs The left hand vertex of the edge.
* @param rhs The right hand vertex of the edge.
* @return True if there is such an edge, false otherwise.
*/
[[nodiscard]] bool contains(const vertex_type lhs, const vertex_type rhs) const {
const auto pos = lhs * vert + rhs;
return pos < matrix.size() && matrix[pos];
}
private:
container_type matrix;
size_type vert;
};
} // namespace entt
#endif

58
src/entt/graph/dot.hpp Normal file
View File

@@ -0,0 +1,58 @@
#ifndef ENTT_GRAPH_DOT_HPP
#define ENTT_GRAPH_DOT_HPP
#include <ostream>
#include <type_traits>
#include "fwd.hpp"
namespace entt {
/**
* @brief Outputs a graph in dot format.
* @tparam Graph Graph type, valid as long as it exposes edges and vertices.
* @tparam Writer Vertex decorator type.
* @param out A standard output stream.
* @param graph The graph to output.
* @param writer Vertex decorator object.
*/
template<typename Graph, typename Writer>
void dot(std::ostream &out, const Graph &graph, Writer writer) {
static_assert(std::is_base_of_v<directed_tag, typename Graph::graph_category>, "Invalid graph category");
if constexpr(std::is_same_v<typename Graph::graph_category, undirected_tag>) {
out << "graph{";
} else {
out << "digraph{";
}
for(auto &&vertex: graph.vertices()) {
out << vertex << "[";
writer(out, vertex);
out << "];";
}
for(auto [lhs, rhs]: graph.edges()) {
if constexpr(std::is_same_v<typename Graph::graph_category, undirected_tag>) {
out << lhs << "--" << rhs << ";";
} else {
out << lhs << "->" << rhs << ";";
}
}
out << "}";
}
/**
* @brief Outputs a graph in dot format.
* @tparam Graph Graph type, valid as long as it exposes edges and vertices.
* @param out A standard output stream.
* @param graph The graph to output.
*/
template<typename Graph>
void dot(std::ostream &out, const Graph &graph) {
return dot(out, graph, [](auto &&...) {});
}
} // namespace entt
#endif

327
src/entt/graph/flow.hpp Normal file
View File

@@ -0,0 +1,327 @@
#ifndef ENTT_GRAPH_FLOW_HPP
#define ENTT_GRAPH_FLOW_HPP
#include <algorithm>
#include <cstddef>
#include <functional>
#include <iterator>
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../container/dense_map.hpp"
#include "../container/dense_set.hpp"
#include "../core/compressed_pair.hpp"
#include "../core/fwd.hpp"
#include "../core/iterator.hpp"
#include "../core/utility.hpp"
#include "adjacency_matrix.hpp"
#include "fwd.hpp"
namespace entt {
/**
* @brief Utility class for creating task graphs.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Allocator>
class basic_flow {
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, id_type>, "Invalid value type");
using task_container_type = dense_set<id_type, identity, std::equal_to<id_type>, typename alloc_traits::template rebind_alloc<id_type>>;
using ro_rw_container_type = std::vector<std::pair<std::size_t, bool>, typename alloc_traits::template rebind_alloc<std::pair<std::size_t, bool>>>;
using deps_container_type = dense_map<id_type, ro_rw_container_type, identity, std::equal_to<id_type>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, ro_rw_container_type>>>;
void emplace(const id_type res, const bool is_rw) {
ENTT_ASSERT(index.first() < vertices.size(), "Invalid node");
if(!deps.contains(res) && sync_on != vertices.size()) {
deps[res].emplace_back(sync_on, true);
}
deps[res].emplace_back(index.first(), is_rw);
}
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Iterable task list. */
using iterable = iterable_adaptor<typename task_container_type::const_iterator>;
/*! @brief Default constructor. */
basic_flow()
: basic_flow{allocator_type{}} {}
/**
* @brief Constructs a flow builder with a given allocator.
* @param allocator The allocator to use.
*/
explicit basic_flow(const allocator_type &allocator)
: index{0u, allocator},
vertices{},
deps{},
sync_on{} {}
/*! @brief Default copy constructor. */
basic_flow(const basic_flow &) = default;
/**
* @brief Allocator-extended copy constructor.
* @param other The instance to copy from.
* @param allocator The allocator to use.
*/
basic_flow(const basic_flow &other, const allocator_type &allocator)
: index{other.index.first(), allocator},
vertices{other.vertices, allocator},
deps{other.deps, allocator},
sync_on{other.sync_on} {}
/*! @brief Default move constructor. */
basic_flow(basic_flow &&) noexcept = default;
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
basic_flow(basic_flow &&other, const allocator_type &allocator)
: index{other.index.first(), allocator},
vertices{std::move(other.vertices), allocator},
deps{std::move(other.deps), allocator},
sync_on{other.sync_on} {}
/**
* @brief Default copy assignment operator.
* @return This flow builder.
*/
basic_flow &operator=(const basic_flow &) = default;
/**
* @brief Default move assignment operator.
* @return This flow builder.
*/
basic_flow &operator=(basic_flow &&) noexcept = default;
/**
* @brief Returns the associated allocator.
* @return The associated allocator.
*/
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
return allocator_type{index.second()};
}
/**
* @brief Returns the identifier at specified location.
* @param pos Position of the identifier to return.
* @return The requested identifier.
*/
[[nodiscard]] id_type operator[](const size_type pos) const {
return vertices.cbegin()[pos];
}
/*! @brief Clears the flow builder. */
void clear() noexcept {
index.first() = sync_on = {};
vertices.clear();
deps.clear();
}
/**
* @brief Exchanges the contents with those of a given flow builder.
* @param other Flow builder to exchange the content with.
*/
void swap(basic_flow &other) {
using std::swap;
std::swap(index, other.index);
std::swap(vertices, other.vertices);
std::swap(deps, other.deps);
std::swap(sync_on, other.sync_on);
}
/**
* @brief Returns the number of tasks.
* @return The number of tasks.
*/
[[nodiscard]] size_type size() const noexcept {
return vertices.size();
}
/**
* @brief Binds a task to a flow builder.
* @param value Task identifier.
* @return This flow builder.
*/
basic_flow &bind(const id_type value) {
sync_on += (sync_on == vertices.size());
const auto it = vertices.emplace(value).first;
index.first() = size_type(it - vertices.begin());
return *this;
}
/**
* @brief Turns the current task into a sync point.
* @return This flow builder.
*/
basic_flow &sync() {
ENTT_ASSERT(index.first() < vertices.size(), "Invalid node");
sync_on = index.first();
for(const auto &elem: deps) {
elem.second.emplace_back(sync_on, true);
}
return *this;
}
/**
* @brief Assigns a resource to the current task with a given access mode.
* @param res Resource identifier.
* @param is_rw Access mode.
* @return This flow builder.
*/
basic_flow &set(const id_type res, bool is_rw = false) {
emplace(res, is_rw);
return *this;
}
/**
* @brief Assigns a read-only resource to the current task.
* @param res Resource identifier.
* @return This flow builder.
*/
basic_flow &ro(const id_type res) {
emplace(res, false);
return *this;
}
/**
* @brief Assigns a range of read-only resources to the current task.
* @tparam It Type of input iterator.
* @param first An iterator to the first element of the range of elements.
* @param last An iterator past the last element of the range of elements.
* @return This flow builder.
*/
template<typename It>
std::enable_if_t<std::is_same_v<std::remove_const_t<typename std::iterator_traits<It>::value_type>, id_type>, basic_flow &>
ro(It first, It last) {
for(; first != last; ++first) {
emplace(*first, false);
}
return *this;
}
/**
* @brief Assigns a writable resource to the current task.
* @param res Resource identifier.
* @return This flow builder.
*/
basic_flow &rw(const id_type res) {
emplace(res, true);
return *this;
}
/**
* @brief Assigns a range of writable resources to the current task.
* @tparam It Type of input iterator.
* @param first An iterator to the first element of the range of elements.
* @param last An iterator past the last element of the range of elements.
* @return This flow builder.
*/
template<typename It>
std::enable_if_t<std::is_same_v<std::remove_const_t<typename std::iterator_traits<It>::value_type>, id_type>, basic_flow &>
rw(It first, It last) {
for(; first != last; ++first) {
emplace(*first, true);
}
return *this;
}
/**
* @brief Generates a task graph for the current content.
* @return The adjacency matrix of the task graph.
*/
[[nodiscard]] adjacency_matrix<directed_tag> graph() const {
const auto length = vertices.size();
adjacency_matrix<directed_tag> matrix{length};
// creates the adjacency matrix
for(const auto &elem: deps) {
const auto last = elem.second.cend();
auto it = elem.second.cbegin();
while(it != last) {
if(it->second) {
// rw item
if(auto curr = it++; it != last) {
if(it->second) {
matrix.insert(curr->first, it->first);
} else if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) {
for(; it != next; ++it) {
matrix.insert(curr->first, it->first);
matrix.insert(it->first, next->first);
}
} else {
for(; it != next; ++it) {
matrix.insert(curr->first, it->first);
}
}
}
} else {
// ro item (first iteration only)
if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) {
for(; it != next; ++it) {
matrix.insert(it->first, next->first);
}
} else {
it = last;
}
}
}
}
// computes the transitive closure
for(std::size_t vk{}; vk < length; ++vk) {
for(std::size_t vi{}; vi < length; ++vi) {
for(std::size_t vj{}; vj < length; ++vj) {
if(matrix.contains(vi, vk) && matrix.contains(vk, vj)) {
matrix.insert(vi, vj);
}
}
}
}
// applies the transitive reduction
for(std::size_t vert{}; vert < length; ++vert) {
matrix.erase(vert, vert);
}
for(std::size_t vj{}; vj < length; ++vj) {
for(std::size_t vi{}; vi < length; ++vi) {
if(matrix.contains(vi, vj)) {
for(std::size_t vk{}; vk < length; ++vk) {
if(matrix.contains(vj, vk)) {
matrix.erase(vi, vk);
}
}
}
}
}
return matrix;
}
private:
compressed_pair<size_type, allocator_type> index;
task_container_type vertices;
deps_container_type deps;
size_type sync_on;
};
} // namespace entt
#endif

27
src/entt/graph/fwd.hpp Normal file
View File

@@ -0,0 +1,27 @@
#ifndef ENTT_GRAPH_FWD_HPP
#define ENTT_GRAPH_FWD_HPP
#include <cstddef>
#include <memory>
#include "../core/fwd.hpp"
namespace entt {
/*! @brief Undirected graph category tag. */
struct directed_tag {};
/*! @brief Directed graph category tag. */
struct undirected_tag: directed_tag {};
template<typename, typename = std::allocator<std::size_t>>
class adjacency_matrix;
template<typename = std::allocator<id_type>>
class basic_flow;
/*! @brief Alias declaration for the most common use case. */
using flow = basic_flow<>;
} // namespace entt
#endif

View File

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

View File

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

View File

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

67
src/entt/meta/context.hpp Normal file
View File

@@ -0,0 +1,67 @@
#ifndef ENTT_META_CTX_HPP
#define ENTT_META_CTX_HPP
#include "../container/dense_map.hpp"
#include "../core/fwd.hpp"
#include "../core/utility.hpp"
namespace entt {
class meta_ctx;
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
struct meta_type_node;
struct meta_context {
dense_map<id_type, meta_type_node, identity> value{};
static inline meta_context &from(meta_ctx &ctx);
static inline const meta_context &from(const meta_ctx &ctx);
};
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/*! @brief Disambiguation tag for constructors and the like. */
class meta_ctx_arg_t final {};
/*! @brief Constant of type meta_context_arg_t used to disambiguate calls. */
inline constexpr meta_ctx_arg_t meta_ctx_arg{};
/*! @brief Opaque meta context type. */
class meta_ctx: private internal::meta_context {
/*! @brief Attorney idiom like model to access the base class. */
friend struct internal::meta_context;
};
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
inline internal::meta_context &internal::meta_context::from(meta_ctx &ctx) {
return ctx;
}
inline const internal::meta_context &internal::meta_context::from(const meta_ctx &ctx) {
return ctx;
}
/**
* Internal details not to be documented.
* @endcond
*/
} // namespace entt
#endif

View File

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

View File

@@ -1,8 +1,9 @@
#ifndef ENTT_META_FACTORY_HPP
#define ENTT_META_FACTORY_HPP
#include <cstddef>
#include <functional>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
@@ -10,196 +11,138 @@
#include "../core/fwd.hpp"
#include "../core/type_info.hpp"
#include "../core/type_traits.hpp"
#include "../locator/locator.hpp"
#include "context.hpp"
#include "meta.hpp"
#include "node.hpp"
#include "policy.hpp"
#include "range.hpp"
#include "resolve.hpp"
#include "utility.hpp"
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename Node>
[[nodiscard]] bool find_if(const Node *candidate, const Node *node) ENTT_NOEXCEPT {
return node && (node == candidate || find_if(candidate, node->next));
inline decltype(auto) owner(meta_ctx &ctx, const type_info &info) {
auto &&context = internal::meta_context::from(ctx);
ENTT_ASSERT(context.value.contains(info.hash()), "Type not available");
return context.value[info.hash()];
}
inline meta_base_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_base_node node) {
return parent.details->base.insert_or_assign(id, std::move(node)).first->second;
}
template<typename Id, typename Node>
[[nodiscard]] bool find_if_not(const Id id, Node *node, const Node *owner) ENTT_NOEXCEPT {
if constexpr(std::is_pointer_v<Id>) {
return node && ((*node->id == *id && node != owner) || find_if_not(id, node->next, owner));
} else {
return node && ((node->id == id && node != owner) || find_if_not(id, node->next, owner));
inline meta_conv_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_conv_node node) {
return parent.details->conv.insert_or_assign(id, std::move(node)).first->second;
}
inline meta_ctor_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_ctor_node node) {
return parent.details->ctor.insert_or_assign(id, std::move(node)).first->second;
}
inline meta_dtor_node &meta_extend(internal::meta_type_node &parent, meta_dtor_node node) {
return (parent.dtor = std::move(node));
}
inline meta_data_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_data_node node) {
return parent.details->data.insert_or_assign(id, std::move(node)).first->second;
}
inline meta_func_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_func_node node) {
if(auto it = parent.details->func.find(id); it != parent.details->func.end()) {
for(auto *curr = &it->second; curr; curr = curr->next.get()) {
if(curr->invoke == node.invoke) {
node.next = std::move(curr->next);
*curr = std::move(node);
return *curr;
}
}
// locally overloaded function
node.next = std::make_shared<meta_func_node>(std::move(parent.details->func[id]));
}
return parent.details->func.insert_or_assign(id, std::move(node)).first->second;
}
inline meta_prop_node &meta_extend(dense_map<id_type, meta_prop_node, identity> &prop, const id_type id, meta_prop_node node) {
return (prop[id] = std::move(node));
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @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...>
struct meta_factory;
/**
* @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>
struct meta_factory<Type, Spec...>: public meta_factory<Type> {
private:
template<std::size_t Step = 0, std::size_t... Index, typename... Property, typename... Other>
void unpack(std::index_sequence<Index...>, std::tuple<Property...> property, Other &&... other) {
unroll<Step>(choice<3>, std::move(std::get<Index>(property))..., std::forward<Other>(other)...);
}
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>
void assign(Key &&key, meta_any value = {}) {
static meta_any property[2u]{};
static internal::meta_prop_node node{
nullptr,
property[0u],
property[1u]
};
entt::meta_any instance{std::forward<Key>(key)};
ENTT_ASSERT(!internal::find_if_not(&instance, *curr, &node), "Duplicate key");
property[0u] = std::move(instance);
property[1u] = std::move(value);
if(!internal::find_if(&node, *curr)) {
node.next = *curr;
*curr = &node;
}
}
public:
/**
* @brief Constructs an extended factory from a given node.
* @param target The underlying node to which to assign the properties.
*/
meta_factory(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 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 meta_factory<Type, Spec..., Property...>{curr};
}
private:
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>
struct meta_factory<Type> {
class meta_factory {
template<typename Setter, auto Getter, typename Policy, std::size_t... Index>
void data(const id_type id, std::index_sequence<Index...>) noexcept {
using data_type = std::invoke_result_t<decltype(Getter), Type &>;
using args_type = type_list<typename meta_function_helper_t<Type, decltype(value_list_element_v<Index, Setter>)>::args_type...>;
static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
auto &&elem = internal::meta_extend(
internal::owner(*ctx, *info),
id,
internal::meta_data_node{
/* this is never static */
(std::is_member_object_pointer_v<decltype(value_list_element_v<Index, Setter>)> && ... && std::is_const_v<std::remove_reference_t<data_type>>) ? internal::meta_traits::is_const : internal::meta_traits::is_none,
Setter::size,
&internal::resolve<std::remove_cv_t<std::remove_reference_t<data_type>>>,
&meta_arg<type_list<type_list_element_t<type_list_element_t<Index, args_type>::size != 1u, type_list_element_t<Index, args_type>>...>>,
+[](meta_handle instance, meta_any value) { return (meta_setter<Type, value_list_element_v<Index, Setter>>(*instance.operator->(), value.as_ref()) || ...); },
&meta_getter<Type, Getter, Policy>});
bucket = &elem.prop;
}
public:
/*! @brief Default constructor. */
meta_factory() noexcept
: meta_factory{locator<meta_ctx>::value_or()} {}
/**
* @brief Makes a meta type _searchable_.
* @param id Optional unique identifier.
* @return An extended meta factory for the given type.
* @brief Context aware constructor.
* @param area The context into which to construct meta types.
*/
auto type(const id_type id = type_hash<Type>::value()) {
auto * const node = internal::meta_info<Type>::resolve();
meta_factory(meta_ctx &area) noexcept
: ctx{&area},
bucket{},
info{&type_id<Type>()} {
auto &&elem = internal::owner(*ctx, *info);
ENTT_ASSERT(!internal::find_if_not(id, *internal::meta_context::global(), node), "Duplicate identifier");
node->id = id;
if(!internal::find_if(node, *internal::meta_context::global())) {
node->next = *internal::meta_context::global();
*internal::meta_context::global() = node;
if(!elem.details) {
elem.details = std::make_shared<internal::meta_type_descriptor>();
}
return meta_factory<Type, Type>{&node->prop};
bucket = &elem.details->prop;
}
/**
* @brief Assigns a custom unique identifier to a meta type.
* @param id A custom unique identifier.
* @return An extended meta factory for the given type.
*/
auto type(const id_type id) noexcept {
auto &&elem = internal::owner(*ctx, *info);
ENTT_ASSERT(elem.id == id || !resolve(*ctx, id), "Duplicate identifier");
bucket = &elem.details->prop;
elem.id = id;
return *this;
}
/**
@@ -211,25 +154,20 @@ struct meta_factory<Type> {
* @return A meta factory for the parent type.
*/
template<typename Base>
auto base() ENTT_NOEXCEPT {
static_assert(std::is_base_of_v<Base, Type>, "Invalid base type");
auto * const type = internal::meta_info<Type>::resolve();
auto base() noexcept {
static_assert(!std::is_same_v<Type, Base> && std::is_base_of_v<Base, Type>, "Invalid base type");
static internal::meta_base_node node{
type,
nullptr,
&internal::meta_info<Base>::resolve,
[](const void *instance) ENTT_NOEXCEPT -> const void * {
return static_cast<const Base *>(static_cast<const Type *>(instance));
}
};
internal::meta_extend(
internal::owner(*ctx, *info),
type_id<Base>().hash(),
internal::meta_base_node{
&internal::resolve<Base>,
+[](const void *instance) noexcept {
return static_cast<const void *>(static_cast<const Base *>(static_cast<const Type *>(instance)));
}});
if(!internal::find_if(&node, type->base)) {
node.next = type->base;
type->base = &node;
}
return meta_factory<Type>{};
bucket = nullptr;
return *this;
}
/**
@@ -245,48 +183,19 @@ struct meta_factory<Type> {
* @return A meta factory for the parent type.
*/
template<auto Candidate>
std::enable_if_t<std::is_member_function_pointer_v<decltype(Candidate)>, meta_factory<Type>> conv() ENTT_NOEXCEPT {
using conv_type = std::invoke_result_t<decltype(Candidate), Type &>;
auto * const type = internal::meta_info<Type>::resolve();
auto conv() noexcept {
using conv_type = std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(Candidate), Type &>>>;
static internal::meta_conv_node node{
type,
nullptr,
&internal::meta_info<conv_type>::resolve,
[](const void *instance) -> meta_any {
return (static_cast<const Type *>(instance)->*Candidate)();
}
};
internal::meta_extend(
internal::owner(*ctx, *info),
type_id<conv_type>().hash(),
internal::meta_conv_node{
+[](const meta_ctx &area, const void *instance) {
return forward_as_meta(area, std::invoke(Candidate, *static_cast<const Type *>(instance)));
}});
if(!internal::find_if(&node, type->conv)) {
node.next = type->conv;
type->conv = &node;
}
return meta_factory<Type>{};
}
/*! @copydoc conv */
template<auto Candidate>
std::enable_if_t<!std::is_member_function_pointer_v<decltype(Candidate)>, meta_factory<Type>> conv() ENTT_NOEXCEPT {
using conv_type = std::invoke_result_t<decltype(Candidate), Type &>;
auto * const type = internal::meta_info<Type>::resolve();
static internal::meta_conv_node node{
type,
nullptr,
&internal::meta_info<conv_type>::resolve,
[](const void *instance) -> meta_any {
return Candidate(*static_cast<const Type *>(instance));
}
};
if(!internal::find_if(&node, type->conv)) {
node.next = type->conv;
type->conv = &node;
}
return meta_factory<Type>{};
bucket = nullptr;
return *this;
}
/**
@@ -299,25 +208,19 @@ struct meta_factory<Type> {
* @return A meta factory for the parent type.
*/
template<typename To>
auto conv() ENTT_NOEXCEPT {
static_assert(std::is_convertible_v<Type, To>, "Could not convert to the required type");
auto * const type = internal::meta_info<Type>::resolve();
auto conv() noexcept {
using conv_type = std::remove_cv_t<std::remove_reference_t<To>>;
static internal::meta_conv_node node{
type,
nullptr,
&internal::meta_info<To>::resolve,
[](const void *instance) -> meta_any {
return static_cast<To>(*static_cast<const Type *>(instance));
}
};
internal::meta_extend(
internal::owner(*ctx, *info),
type_id<conv_type>().hash(),
internal::meta_conv_node{
+[](const meta_ctx &area, const void *instance) {
return forward_as_meta(area, static_cast<To>(*static_cast<const Type *>(instance)));
}});
if(!internal::find_if(&node, type->conv)) {
node.next = type->conv;
type->conv = &node;
}
return meta_factory<Type>{};
bucket = nullptr;
return *this;
}
/**
@@ -334,30 +237,21 @@ struct meta_factory<Type> {
* @return An extended meta factory for the parent type.
*/
template<auto Candidate, typename Policy = as_is_t>
auto ctor() ENTT_NOEXCEPT {
auto ctor() noexcept {
using descriptor = meta_function_helper_t<Type, decltype(Candidate)>;
static_assert(std::is_same_v<std::decay_t<typename descriptor::return_type>, Type>, "The function doesn't return an object of the required type");
auto * const type = internal::meta_info<Type>::resolve();
static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy");
static_assert(std::is_same_v<std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>, Type>, "The function doesn't return an object of the required type");
static internal::meta_ctor_node node{
type,
nullptr,
nullptr,
descriptor::args_type::size,
[](const typename internal::meta_ctor_node::size_type index) ENTT_NOEXCEPT {
return meta_arg(typename descriptor::args_type{}, index);
},
[](meta_any * const args) {
return meta_invoke<Type, Candidate, Policy>({}, args, std::make_index_sequence<descriptor::args_type::size>{});
}
};
internal::meta_extend(
internal::owner(*ctx, *info),
type_id<typename descriptor::args_type>().hash(),
internal::meta_ctor_node{
descriptor::args_type::size,
&meta_arg<typename descriptor::args_type>,
&meta_construct<Type, Candidate, Policy>});
if(!internal::find_if(&node, type->ctor)) {
node.next = type->ctor;
type->ctor = &node;
}
return meta_factory<Type, std::integral_constant<decltype(Candidate), Candidate>>{&node.prop};
bucket = nullptr;
return *this;
}
/**
@@ -371,41 +265,36 @@ struct meta_factory<Type> {
* @return An extended meta factory for the parent type.
*/
template<typename... Args>
auto ctor() ENTT_NOEXCEPT {
using descriptor = meta_function_helper_t<Type, Type(*)(Args...)>;
auto * const type = internal::meta_info<Type>::resolve();
auto ctor() noexcept {
// default constructor is already implicitly generated, no need for redundancy
if constexpr(sizeof...(Args) != 0u) {
using descriptor = meta_function_helper_t<Type, Type (*)(Args...)>;
static internal::meta_ctor_node node{
type,
nullptr,
nullptr,
descriptor::args_type::size,
[](const typename internal::meta_ctor_node::size_type index) ENTT_NOEXCEPT {
return meta_arg(typename descriptor::args_type{}, index);
},
[](meta_any * const args) {
return meta_construct<Type, Args...>(args, std::make_index_sequence<descriptor::args_type::size>{});
}
};
if(!internal::find_if(&node, type->ctor)) {
node.next = type->ctor;
type->ctor = &node;
internal::meta_extend(
internal::owner(*ctx, *info),
type_id<typename descriptor::args_type>().hash(),
internal::meta_ctor_node{
descriptor::args_type::size,
&meta_arg<typename descriptor::args_type>,
&meta_construct<Type, Args...>});
}
return meta_factory<Type, Type(Args...)>{&node.prop};
bucket = nullptr;
return *this;
}
/**
* @brief Assigns a meta destructor to a meta type.
*
* Free functions can be assigned to meta types in the role of destructors.
* The signature of the function should identical to the following:
* Both free functions and member functions can be assigned to meta types in
* the role of destructors.<br/>
* The signature of a free function should be identical to the following:
*
* @code{.cpp}
* void(Type &);
* @endcode
*
* Member functions should not take arguments instead.<br/>
* The purpose is to give users the ability to free up resources that
* require special treatment before an object is actually destroyed.
*
@@ -413,15 +302,16 @@ struct meta_factory<Type> {
* @return A meta factory for the parent type.
*/
template<auto Func>
auto dtor() ENTT_NOEXCEPT {
auto dtor() noexcept {
static_assert(std::is_invocable_v<decltype(Func), Type &>, "The function doesn't accept an object of the type provided");
auto * const type = internal::meta_info<Type>::resolve();
type->dtor = [](void *instance) {
Func(*static_cast<Type *>(instance));
};
internal::meta_extend(
internal::owner(*ctx, *info),
internal::meta_dtor_node{
+[](void *instance) { std::invoke(Func, *static_cast<Type *>(instance)); }});
return meta_factory<Type>{};
bucket = nullptr;
return *this;
}
/**
@@ -438,35 +328,41 @@ struct meta_factory<Type> {
* @return An extended meta factory for the parent type.
*/
template<auto Data, typename Policy = as_is_t>
auto data(const id_type id) ENTT_NOEXCEPT {
auto data(const id_type id) noexcept {
if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
return data<Data, Data, Policy>(id);
using data_type = std::remove_reference_t<std::invoke_result_t<decltype(Data), Type &>>;
auto &&elem = internal::meta_extend(
internal::owner(*ctx, *info),
id,
internal::meta_data_node{
/* this is never static */
std::is_const_v<data_type> ? internal::meta_traits::is_const : internal::meta_traits::is_none,
1u,
&internal::resolve<std::remove_cv_t<data_type>>,
&meta_arg<type_list<std::remove_cv_t<data_type>>>,
&meta_setter<Type, Data>,
&meta_getter<Type, Data, Policy>});
bucket = &elem.prop;
} else {
using data_type = std::remove_pointer_t<decltype(Data)>;
auto * const type = internal::meta_info<Type>::resolve();
using data_type = std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>;
static internal::meta_data_node node{
{},
type,
nullptr,
nullptr,
std::is_same_v<Type, data_type> || std::is_const_v<data_type>,
true,
&internal::meta_info<data_type>::resolve,
&meta_setter<Type, Data>,
&meta_getter<Type, Data, Policy>
};
auto &&elem = internal::meta_extend(
internal::owner(*ctx, *info),
id,
internal::meta_data_node{
((std::is_same_v<Type, std::remove_cv_t<data_type>> || std::is_const_v<data_type>) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static,
1u,
&internal::resolve<std::remove_cv_t<data_type>>,
&meta_arg<type_list<std::remove_cv_t<data_type>>>,
&meta_setter<Type, Data>,
&meta_getter<Type, Data, Policy>});
ENTT_ASSERT(!internal::find_if_not(id, type->data, &node), "Duplicate identifier");
node.id = id;
if(!internal::find_if(&node, type->data)) {
node.next = type->data;
type->data = &node;
}
return meta_factory<Type, std::integral_constant<decltype(Data), Data>>{&node.prop};
bucket = &elem.prop;
}
return *this;
}
/**
@@ -490,35 +386,70 @@ struct meta_factory<Type> {
* @return An extended meta factory for the parent type.
*/
template<auto Setter, auto Getter, typename Policy = as_is_t>
auto data(const id_type id) ENTT_NOEXCEPT {
using underlying_type = std::remove_reference_t<std::invoke_result_t<decltype(Getter), Type &>>;
auto * const type = internal::meta_info<Type>::resolve();
auto data(const id_type id) noexcept {
using data_type = std::invoke_result_t<decltype(Getter), Type &>;
static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
static internal::meta_data_node node{
{},
type,
nullptr,
nullptr,
std::is_same_v<decltype(Setter), std::nullptr_t> || (std::is_member_object_pointer_v<decltype(Setter)> && std::is_const_v<underlying_type>),
false,
&internal::meta_info<underlying_type>::resolve,
&meta_setter<Type, Setter>,
&meta_getter<Type, Getter, Policy>
};
if constexpr(std::is_same_v<decltype(Setter), std::nullptr_t>) {
auto &&elem = internal::meta_extend(
internal::owner(*ctx, *info),
id,
internal::meta_data_node{
/* this is never static */
internal::meta_traits::is_const,
0u,
&internal::resolve<std::remove_cv_t<std::remove_reference_t<data_type>>>,
&meta_arg<type_list<>>,
&meta_setter<Type, Setter>,
&meta_getter<Type, Getter, Policy>});
ENTT_ASSERT(!internal::find_if_not(id, type->data, &node), "Duplicate identifier");
node.id = id;
bucket = &elem.prop;
} else {
using args_type = typename meta_function_helper_t<Type, decltype(Setter)>::args_type;
if(!internal::find_if(&node, type->data)) {
node.next = type->data;
type->data = &node;
auto &&elem = internal::meta_extend(
internal::owner(*ctx, *info),
id,
internal::meta_data_node{
/* this is never static nor const */
internal::meta_traits::is_none,
1u,
&internal::resolve<std::remove_cv_t<std::remove_reference_t<data_type>>>,
&meta_arg<type_list<type_list_element_t<args_type::size != 1u, args_type>>>,
&meta_setter<Type, Setter>,
&meta_getter<Type, Getter, Policy>});
bucket = &elem.prop;
}
return meta_factory<Type, std::integral_constant<decltype(Setter), Setter>, std::integral_constant<decltype(Getter), Getter>>{&node.prop};
return *this;
}
/**
* @brief Assigns a meta funcion to a meta type.
* @brief Assigns a meta data to a meta type by means of its setters and
* getter.
*
* Multi-setter support for meta data members. All setters are tried in the
* order of definition before returning to the caller.<br/>
* Setters can be either free functions, member functions or a mix of them
* and are provided via a `value_list` type.
*
* @sa data
*
* @tparam Setter The actual functions to use as setters.
* @tparam Getter The actual getter function.
* @tparam Policy Optional policy (no policy set by default).
* @param id Unique identifier.
* @return An extended meta factory for the parent type.
*/
template<typename Setter, auto Getter, typename Policy = as_is_t>
auto data(const id_type id) noexcept {
data<Setter, Getter, Policy>(id, std::make_index_sequence<Setter::size>{});
return *this;
}
/**
* @brief Assigns a meta function to a meta type.
*
* Both member functions and free functions can be assigned to a meta
* type.<br/>
@@ -531,46 +462,81 @@ struct meta_factory<Type> {
* @return An extended meta factory for the parent type.
*/
template<auto Candidate, typename Policy = as_is_t>
auto func(const id_type id) ENTT_NOEXCEPT {
auto func(const id_type id) noexcept {
using descriptor = meta_function_helper_t<Type, decltype(Candidate)>;
auto * const type = internal::meta_info<Type>::resolve();
static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy");
static internal::meta_func_node node{
{},
type,
nullptr,
nullptr,
descriptor::args_type::size,
descriptor::is_const,
descriptor::is_static,
&internal::meta_info<std::conditional_t<std::is_same_v<Policy, as_void_t>, void, typename descriptor::return_type>>::resolve,
[](const typename internal::meta_func_node::size_type index) ENTT_NOEXCEPT {
return meta_arg(typename descriptor::args_type{}, index);
},
[](meta_handle instance, meta_any *args) {
return meta_invoke<Type, Candidate, Policy>(std::move(instance), args, std::make_index_sequence<descriptor::args_type::size>{});
}
};
auto &&elem = internal::meta_extend(
internal::owner(*ctx, *info),
id,
internal::meta_func_node{
(descriptor::is_const ? internal::meta_traits::is_const : internal::meta_traits::is_none) | (descriptor::is_static ? internal::meta_traits::is_static : internal::meta_traits::is_none),
descriptor::args_type::size,
&internal::resolve<std::conditional_t<std::is_same_v<Policy, as_void_t>, void, std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>>>,
&meta_arg<typename descriptor::args_type>,
&meta_invoke<Type, Candidate, Policy>});
for(auto *it = &type->func; *it; it = &(*it)->next) {
if(*it == &node) {
*it = node.next;
break;
}
bucket = &elem.prop;
return *this;
}
/**
* @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 Value Optional type of the property value.
* @param id Property key.
* @param value Optional property value.
* @return An extended meta factory for the given type.
*/
template<typename... Value>
meta_factory prop(id_type id, [[maybe_unused]] Value &&...value) {
ENTT_ASSERT(bucket != nullptr, "Meta object does not support properties");
if constexpr(sizeof...(Value) == 0u) {
internal::meta_extend(
*bucket,
id,
internal::meta_prop_node{
&internal::resolve<void>});
} else {
internal::meta_extend(
*bucket,
id,
internal::meta_prop_node{
&internal::resolve<std::decay_t<Value>>...,
std::make_shared<std::decay_t<Value>>(std::forward<Value>(value))...});
}
internal::meta_func_node **it = &type->func;
for(; *it && (*it)->id != id; it = &(*it)->next);
for(; *it && (*it)->id == id && (*it)->arity < node.arity; it = &(*it)->next);
node.id = id;
node.next = *it;
*it = &node;
return meta_factory<Type, std::integral_constant<decltype(Candidate), Candidate>>{&node.prop};
return *this;
}
private:
meta_ctx *ctx;
dense_map<id_type, internal::meta_prop_node, identity> *bucket;
const type_info *info;
};
/**
* @brief Utility function to use for reflection.
*
* This is the point from which everything starts.<br/>
* By invoking this function with a type that is not yet reflected, a meta type
* is created to which it will be possible to attach meta objects through a
* dedicated factory.
*
* @tparam Type Type to reflect.
* @param ctx The context into which to construct meta types.
* @return A meta factory for the given type.
*/
template<typename Type>
[[nodiscard]] auto meta(meta_ctx &ctx) noexcept {
auto &&context = internal::meta_context::from(ctx);
// make sure the type exists in the context before returning a factory
context.value.try_emplace(type_id<Type>().hash(), internal::resolve<Type>(context));
return meta_factory<Type>{ctx};
}
/**
* @brief Utility function to use for reflection.
@@ -584,14 +550,94 @@ struct meta_factory<Type> {
* @return A meta factory for the given type.
*/
template<typename Type>
[[nodiscard]] auto meta() ENTT_NOEXCEPT {
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};
[[nodiscard]] auto meta() noexcept {
return meta<Type>(locator<meta_ctx>::value_or());
}
/**
* @brief Resets a type and all its parts.
*
* Resets a type and all its data members, member functions and properties, as
* well as its constructors, destructors and conversion functions if any.<br/>
* Base classes aren't reset but the link between the two types is removed.
*
* The type is also removed from the set of searchable types.
*
* @param id Unique identifier.
* @param ctx The context from which to reset meta types.
*/
inline void meta_reset(meta_ctx &ctx, const id_type id) noexcept {
auto &&context = internal::meta_context::from(ctx);
for(auto it = context.value.begin(); it != context.value.end();) {
if(it->second.id == id) {
it = context.value.erase(it);
} else {
++it;
}
}
}
/**
* @brief Resets a type and all its parts.
*
* Resets a type and all its data members, member functions and properties, as
* well as its constructors, destructors and conversion functions if any.<br/>
* Base classes aren't reset but the link between the two types is removed.
*
* The type is also removed from the set of searchable types.
*
* @param id Unique identifier.
*/
inline void meta_reset(const id_type id) noexcept {
meta_reset(locator<meta_ctx>::value_or(), id);
}
/**
* @brief Resets a type and all its parts.
*
* @sa meta_reset
*
* @tparam Type Type to reset.
* @param ctx The context from which to reset meta types.
*/
template<typename Type>
void meta_reset(meta_ctx &ctx) noexcept {
internal::meta_context::from(ctx).value.erase(type_id<Type>().hash());
}
/**
* @brief Resets a type and all its parts.
*
* @sa meta_reset
*
* @tparam Type Type to reset.
*/
template<typename Type>
void meta_reset() noexcept {
meta_reset<Type>(locator<meta_ctx>::value_or());
}
/**
* @brief Resets all meta types.
*
* @sa meta_reset
*
* @param ctx The context from which to reset meta types.
*/
inline void meta_reset(meta_ctx &ctx) noexcept {
internal::meta_context::from(ctx).value.clear();
}
/**
* @brief Resets all meta types.
*
* @sa meta_reset
*/
inline void meta_reset() noexcept {
meta_reset(locator<meta_ctx>::value_or());
}
} // namespace entt
#endif

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

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

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