Compare commits

..

1819 Commits

Author SHA1 Message Date
skypjack
b4e58bdd36 *: updated TODO 2025-11-19 10:10:18 +01:00
skypjack
8375ee0910 update single include file 2025-11-17 09:27:48 +01:00
skypjack
137339f48c build: refine clang-tidy setup for local use 2025-11-15 19:11:57 +01:00
skypjack
713bcd50a3 test: explicit nullptr check 2025-11-15 19:11:54 +01:00
skypjack
03a45d9cca test: clang-tidy friendly tests 2025-11-15 18:51:35 +01:00
skypjack
cffbf89d9b test: refine lib tests 2025-11-15 18:50:57 +01:00
skypjack
f48d16a6e6 meta: linter friendly extra pointer check 2025-11-15 17:52:29 +01:00
skypjack
4905da2637 test: internal changes to make clang-tidy happy 2025-11-15 16:02:36 +01:00
skypjack
558109bbe1 doc: add OneArc to the EnTT in Action list 2025-11-14 17:30:45 +01:00
skypjack
a505d41266 meta: explicit nullptr checks 2025-11-14 17:04:34 +01:00
skypjack
9381900b00 meta: get around bugprone assignment warning 2025-11-14 16:59:14 +01:00
skypjack
f6a3b09ec2 dense set: suppress conversion warning 2025-11-14 16:58:25 +01:00
skypjack
51c7533265 meta: use find_member to slightly reduce symbol size 2025-11-14 16:58:10 +01:00
skypjack
d502bf18da dense map: avoid implicit conversions 2025-11-14 16:10:50 +01:00
skypjack
db263d2ff9 any: reorder ctor warning 2025-11-14 16:10:30 +01:00
skypjack
16fae2d625 doc: cleanup 2025-11-14 15:14:52 +01:00
skypjack
4e3a1a8428 meta: internal changes 2025-11-14 15:14:42 +01:00
skypjack
aec8a6588c meta: cleanup 2025-11-13 18:41:31 +01:00
skypjack
3160d991f7 meta: drop unnecessary remove_reference_t 2025-11-13 18:34:45 +01:00
skypjack
7359018880 dense_map: refine constrained_find (perf) 2025-11-13 12:02:36 +01:00
skypjack
17fb301636 dense_set: refine constrained_find (perf) 2025-11-13 12:02:28 +01:00
skypjack
7dac5af05d meta: skip fetch_node if possible 2025-11-12 15:39:59 +01:00
skypjack
d15e514f10 meta: allow unsetting flags from meta factory 2025-11-12 10:08:31 +01:00
skypjack
fe8320c66f any: plugin-friendly, faster quick check 2025-11-10 14:34:39 +01:00
skypjack
7111549c7c meta:: minor changes 2025-11-10 00:01:54 +01:00
skypjack
e8e5f5ee2b dense_[map|set]: internal changes 2025-11-09 23:38:26 +01:00
skypjack
9caaf569e6 config: drop attribute.h, merge it with config.h 2025-11-07 10:21:02 +01:00
skypjack
03bf0f90b4 meta: minor changes 2025-11-06 00:16:15 +01:00
skypjack
5df7142f51 *: move attribute.h to config 2025-11-05 23:47:16 +01:00
skypjack
4d95793123 meta: minor changes 2025-11-05 12:12:10 +01:00
skypjack
0aa503e69e meta: use the right guard 2025-11-04 15:12:17 +01:00
skypjack
02323e814a meta: missing template keyword 2025-11-04 15:10:59 +01:00
skypjack
440fed990d doc: cleanup 2025-11-04 15:08:37 +01:00
skypjack
c2dcd7257f meta: deprecate const meta_handle::operator-> 2025-11-04 15:08:24 +01:00
skypjack
716e3257b1 meta: refine meta_any::as_ref 2025-11-04 15:01:39 +01:00
skypjack
f781441bcd meta: oops :) 2025-11-04 14:54:37 +01:00
skypjack
3c0aa4d300 meta: minor changes 2025-11-04 14:42:25 +01:00
skypjack
a57e1a971d meta: minor changes 2025-11-04 14:19:57 +01:00
skypjack
dc8da9e42c meta: minor changes 2025-11-04 14:11:44 +01:00
skypjack
7eeb828980 meta: further refine typed allow_cast 2025-11-04 14:06:53 +01:00
skypjack
b0d7490829 meta: replace try_convert to squeeze more perf from allow_cast 2025-11-04 13:34:22 +01:00
skypjack
0d678442a0 cmake: profile prop 2025-11-03 23:37:43 +01:00
skypjack
2dff7c9d55 container: prepare for some optimizations 2025-11-03 18:16:28 +01:00
skypjack
3fc5de2916 doc: meta 2025-11-03 14:32:12 +01:00
skypjack
a572671947 doc: meta 2025-11-03 08:30:04 +01:00
skypjack
2a6f769092 any: internal changes 2025-10-31 18:02:53 +01:00
skypjack
6a25c0df84 *: updated TODO 2025-10-31 17:50:13 +01:00
skypjack
72127d699a meta: avoid invoking twice the same function 2025-10-31 16:19:54 +01:00
skypjack
b52d0aa133 *: updated TODO 2025-10-31 15:35:09 +01:00
skypjack
5d006fefcc meta: drop redundant (wrong) const 2025-10-31 15:00:48 +01:00
skypjack
d7c58d00b4 test: make the linter happy again 2025-10-31 14:44:38 +01:00
skypjack
9198a27c25 meta: avoid resetting nodes on meta_any::emplace if not needed 2025-10-31 14:37:16 +01:00
skypjack
31d5d5ff1c meta: top-level only searches for data and functions 2025-10-31 11:36:26 +01:00
skypjack
f3fa359edf natvis: meta 2025-10-30 12:32:22 +01:00
skypjack
cba31e00ea meta: as_is -> as_value / as_auto -> as_is (breaking) 2025-10-29 17:40:18 +01:00
skypjack
d366a5ed92 meta: as_auto policy 2025-10-28 15:34:45 +01:00
skypjack
d92bea4afd test: non-regression tests for meta_type::lookup constness 2025-10-27 00:01:13 +01:00
skypjack
bbfd458d2d test: more about overloaded meta functions 2025-10-26 23:51:18 +01:00
skypjack
87a114ccc6 test: more about meta_func 2025-10-26 23:37:43 +01:00
skypjack
6852415ffc test: minor changes 2025-10-26 15:11:04 +01:00
skypjack
15f65bd58e core/meta: minor changes 2025-10-26 13:59:09 +01:00
skypjack
54c7c47e3d .*: updated TODO 2025-10-26 13:58:58 +01:00
skypjack
5e6ccdedc8 meta: not going to drop policy anymore, we can use it safely 2025-10-24 10:19:42 +02:00
skypjack
dadf493aca meta: cleanup 2025-10-23 18:39:30 +02:00
skypjack
3e5842a467 meta: try to make the linter happy 2025-10-23 18:32:19 +02:00
skypjack
75ae0a10d0 any: cleanup 2025-10-23 18:19:54 +02:00
skypjack
e87a8e4b63 storage: NOLINT odissey :) 2025-10-23 17:58:55 +02:00
skypjack
41282b0ecc entity: reduce number of NOLINT 2025-10-23 17:28:13 +02:00
skypjack
030095f48d .*: updated TODO 2025-10-23 17:27:47 +02:00
skypjack
e18061bf86 any: avoid vtable calls to release trivially destructible objects (perf) 2025-10-23 11:58:29 +02:00
Michele Caini
38bcbf41a3 any: discard pointless NOLINT 2025-10-22 23:21:27 +02:00
Michele Caini
602626c840 .*: cleanup TODO 2025-10-22 23:17:03 +02:00
skypjack
41e359502d any: avoid vtable calls to get data (perf) 2025-10-22 17:09:41 +02:00
skypjack
8e64f8accf any: info request to get rid of descriptor 2025-10-22 15:58:15 +02:00
skypjack
0afab946da meta: reduce uses of basic_any<...>::policy() 2025-10-21 15:10:52 +02:00
skypjack
20d77024f7 *: updated TODO 2025-10-21 15:10:23 +02:00
skypjack
b1bb9b4b5b test: reduce uses of basic_any<...>::policy() 2025-10-21 15:10:17 +02:00
skypjack
978e003dbc meta: internal changes 2025-10-21 15:09:51 +02:00
skypjack
47c65a20a1 any: clang-tidy docet 2025-10-20 17:20:29 +02:00
skypjack
31d845a5f5 meta: clang-tidy docet 2025-10-20 17:18:26 +02:00
skypjack
03c5661df9 meta: rework internal fake vtable 2025-10-20 15:57:32 +02:00
skypjack
48644e568f meta: cleanup 2025-10-20 15:54:35 +02:00
skypjack
19c0e8029d meta: remove the resolve function from meta_any to reduce the footprint 2025-10-20 15:14:08 +02:00
skypjack
3562bec4a7 meta: reduce internal uses of meta_any::resolve 2025-10-18 23:11:35 +02:00
skypjack
e863e4fe54 meta: reduce internal uses of meta_any::resolve 2025-10-18 12:43:39 +02:00
skypjack
c5d89597fc meta: avoid using meta_any::resolve when comparing 2025-10-18 12:40:33 +02:00
skypjack
091eef0e4c any: void fake vtable for the win 2025-10-18 11:48:32 +02:00
skypjack
96b56ca630 any: try to make all compilers happy again 2025-10-17 16:50:19 +02:00
skypjack
56d78c767a any: no delete for const void * 2025-10-17 16:49:22 +02:00
skypjack
1f57b2795f any: cleanup 2025-10-17 16:46:08 +02:00
skypjack
de054655ab any: doc 2025-10-17 16:41:27 +02:00
skypjack
bee74568e5 *; updated TODO 2025-10-17 08:18:14 +02:00
skypjack
ebb2af1225 any: make ::initialize fully support void 2025-10-17 08:13:41 +02:00
skypjack
1487ad292b any: avoid using vtable ptr with has_value 2025-10-16 23:31:55 +02:00
skypjack
d3b8e92294 any: cleanup 2025-10-16 13:59:08 +02:00
skypjack
ab1d7d0126 any: make vtable void-friendly 2025-10-15 17:48:09 +02:00
skypjack
01b21afca0 any: make in_situ void-friendly 2025-10-15 17:46:25 +02:00
skypjack
ef0056908a core: doc 2025-10-15 11:13:27 +02:00
skypjack
b021c78550 natvis: update basic_any view 2025-10-14 15:23:27 +02:00
skypjack
aedaa7f6a4 any: cleanup 2025-10-13 14:56:36 +02:00
skypjack
e15af7b500 doc: minor changes 2025-10-10 15:11:24 +02:00
skypjack
38b34eb986 any: add comments for things to cleanup :) 2025-10-10 12:58:14 +02:00
skypjack
b4fc627c99 any: use vtable instead of descriptor 2025-10-10 12:54:54 +02:00
skypjack
c9364d3af5 *: updated TODO 2025-10-10 12:49:49 +02:00
skypjack
baa0d2c23c any: use vtable instead of descriptor for early exit 2025-10-10 10:53:25 +02:00
skypjack
dc154a4b8a build: also test toolset v143 for backward compatibility 2025-10-10 10:24:22 +02:00
skypjack
ba0e9fab45 any: reduce direct uses of descriptor 2025-10-10 10:08:44 +02:00
skypjack
4ab5a375e8 *: updated TODO 2025-10-10 10:08:18 +02:00
skypjack
f63f36a411 any: basic_any_storage, prepare for vtable-less optimizations 2025-10-09 15:52:43 +02:00
skypjack
f2b8177aaf any: avoid name clashing if possible 2025-10-09 09:24:18 +02:00
skypjack
53fab33423 core: cleanup headers 2025-10-08 15:41:26 +02:00
skypjack
4a252bdb84 any: minor changes 2025-10-08 14:24:33 +02:00
skypjack
cece4be1ec any: prepare for ebco 2025-10-08 14:20:48 +02:00
skypjack
8f23d743ca *: updated TODO 2025-10-08 12:29:30 +02:00
skypjack
0fa1bc5999 test: cleanup 2025-10-08 12:28:22 +02:00
skypjack
281276e84e meta: rollback some stuff to make everything clang-tidy friendly 2025-10-07 18:52:46 +02:00
skypjack
f73409e9ad meta: safe self-assignment for meta_any 2025-10-07 17:33:45 +02:00
skypjack
740cf006d9 any: safe self-assignment 2025-10-07 17:33:32 +02:00
skypjack
828ecb57e6 any: internal changes 2025-10-07 17:24:22 +02:00
skypjack
8794888c98 meta: cleanup 2025-10-07 17:23:29 +02:00
skypjack
dd7b96fc9e test: make them clang-tidy friendly 2025-10-07 15:59:01 +02:00
skypjack
1116c33ada test: minor changes 2025-10-07 15:30:58 +02:00
skypjack
38fe05db85 meta: minor changes 2025-10-07 12:47:35 +02:00
skypjack
b2d4832842 clang-tidy: typo 2025-10-07 12:37:29 +02:00
skypjack
4c19d2844e clang-tidy: refine config to match my tastes too :) 2025-10-07 11:46:27 +02:00
skypjack
6f2e5090e1 any: [[nodiscard]] 2025-10-07 11:45:20 +02:00
skypjack
25e5123128 meta: remove redundant initializer 2025-10-07 11:23:38 +02:00
skypjack
8c3ca20d19 clang-tidy: I do actually prefer else-after-return style 2025-10-07 11:21:45 +02:00
skypjack
ee7f24be63 meta: no else after return 2025-10-07 11:15:17 +02:00
skypjack
a5f58ef49f meta: clang-tidy 2025-10-07 11:11:01 +02:00
skypjack
ba5c4ba121 meta: use any::has_value (perf) 2025-10-07 09:53:52 +02:00
skypjack
fcf455f7ef any: ::has_value overloads 2025-10-07 09:24:21 +02:00
skypjack
6836d5b024 meta: refine meta_any::assign 2025-10-06 17:07:09 +02:00
skypjack
8142136940 meta: internal changes 2025-10-03 17:42:41 +02:00
skypjack
e76bbb2015 test: code coverage 2025-10-03 17:36:50 +02:00
skypjack
6f57622eff *: updated TODO 2025-10-03 17:32:16 +02:00
skypjack
0c5cab164a any: missing template keywords 2025-10-03 14:21:22 +02:00
skypjack
c67a62de81 *: updated TODO 2025-10-03 14:13:59 +02:00
skypjack
80eba8afb4 any: typed ::data with fallback (perf) 2025-10-03 14:12:46 +02:00
skypjack
0d6615a767 meta: cleanup 2025-10-02 17:05:00 +02:00
skypjack
fc4d18ce44 meta: cleanup 2025-10-02 15:42:18 +02:00
skypjack
ddf28480dc *: internal changes 2025-10-01 13:12:11 +02:00
skypjack
f49f2331ba doc: minor changes 2025-10-01 13:12:00 +02:00
skypjack
6526d67d16 any: internal changes 2025-09-30 15:13:15 +02:00
skypjack
4d90521fcf *: updated TODO 2025-09-30 14:58:05 +02:00
skypjack
9165a1df1e meta: minor changes 2025-09-30 14:57:33 +02:00
skypjack
a1406baf61 meta: cleanup 2025-09-29 18:56:41 +02:00
skypjack
645b3563a9 natvis: meta 2025-09-29 08:38:12 +02:00
skypjack
91eecc1efd sparse set: minor changes 2025-09-29 08:38:03 +02:00
skypjack
0782855880 meta: internal changes 2025-09-28 17:43:34 +02:00
skypjack
b9bfeb9b3d meta: bring meta custom back in place 2025-09-26 16:56:03 +02:00
skypjack
0409aab6bb meta: cleanup 2025-09-26 15:09:10 +02:00
skypjack
947d89844b *: no entt:: namespace 2025-09-26 15:05:39 +02:00
Ted de Munnik
ff8544f97d sparse set: simplify try_emplace iterator computation to improve MSVC codegen (#1286) 2025-09-26 11:20:58 +02:00
skypjack
c777cda857 meta: support {} on static calls 2025-09-25 16:37:29 +02:00
skypjack
a90584f1ce meta: update meta_handle context propagation (with non-regression tests) 2025-09-25 15:44:01 +02:00
skypjack
4e47c535c4 meta: meta_any -> meta_handle 2025-09-24 15:52:32 +02:00
skypjack
84fee2848f tools: update davey 2025-09-24 15:42:37 +02:00
skypjack
42259fb1ce meta: reintroduce meta_handle default ctor for utilities 2025-09-24 15:41:40 +02:00
skypjack
e38e88362f meta: cleanup meta_handle further (breaking change) 2025-09-24 15:30:16 +02:00
skypjack
14be4141fa meta: strip deprecated ctor from meta_handle (breaking change) 2025-09-24 15:29:47 +02:00
skypjack
6a4ca4c4ba tools: update davey 2025-09-24 15:19:51 +02:00
skypjack
71e61b5270 meta: make meta_handle support empty construction from nullptr 2025-09-24 15:14:33 +02:00
skypjack
affe6ca7b8 meta: internal changes 2025-09-24 15:13:03 +02:00
skypjack
f0a07c4530 meta: avoid shadow warnings 2025-09-24 15:01:03 +02:00
skypjack
a003575811 meta: avoid useless moves 2025-09-24 14:58:47 +02:00
skypjack
be3f022445 meta: struct -> class 2025-09-24 14:57:04 +02:00
skypjack
4b5b3fec41 meta: deferred meta handle creation (perf) 2025-09-24 14:55:41 +02:00
skypjack
4debb79a60 meta: improved meta_handle support to meta_any 2025-09-24 13:07:24 +02:00
skypjack
f3b763d973 meta: deprecate ctors that should not exist 2025-09-24 13:06:05 +02:00
skypjack
356024ed6e doc: cleanup 2025-09-24 12:38:57 +02:00
skypjack
e8e62638fc meta: deprecate meta_handle ctor 2025-09-24 12:33:54 +02:00
skypjack
6ecf1f35c0 meta: revert automatic policy (too convenient a feature) 2025-09-24 12:30:21 +02:00
skypjack
05863c6a15 meta: automatic policy (perf, breaking change) 2025-09-24 12:02:56 +02:00
skypjack
1a02b6ad57 meta: refine meta_any move ctor 2025-09-23 15:53:50 +02:00
skypjack
5ee1f3fec1 meta: internal changes 2025-09-23 15:53:18 +02:00
skypjack
f44c9a1c05 meta: deprecate a few functions that should not be there 2025-09-23 09:12:34 +02:00
skypjack
715e5318de meta: refine try_convert/allow_cast 2025-09-22 16:35:11 +02:00
Tobias Backer Dirks
211e5fddc1 doc: add Game link to EnTT In Action (#1284) 2025-09-22 09:15:21 +02:00
skypjack
9347e91db6 meta: stable meta types 2025-09-19 11:30:21 +02:00
skypjack
bebfbada68 meta: refine factory init step 2025-09-19 11:28:26 +02:00
skypjack
c239821d12 meta: keep supporting invalid types 2025-09-19 10:46:28 +02:00
skypjack
293cbe2769 meta: avoid copying meta type nodes 2025-09-19 09:49:33 +02:00
skypjack
3587eaed9a meta: deprecate meta_type::operator bool, meta types are never invalid 2025-09-19 08:54:45 +02:00
skypjack
cb9d8ac3f0 meta: meta_type is void (and safe again) when empty 2025-09-18 17:44:32 +02:00
skypjack
e544755bbc meta: refine meta_any::fetch_node function 2025-09-18 17:33:56 +02:00
skypjack
e2003ff17a natvis: meta_any view 2025-09-18 17:33:16 +02:00
skypjack
6232c93907 meta: a bunch of [[nodiscard]] 2025-09-18 17:26:34 +02:00
skypjack
6153cdcbbe meta: lazy_node -> node 2025-09-18 17:25:21 +02:00
skypjack
89af917748 natvis: updated meta_any/meta_type_node views 2025-09-18 14:25:23 +02:00
skypjack
28466a7316 meta: lighter meta type objects (perf) 2025-09-18 14:11:24 +02:00
skypjack
33efea3f03 meta: avoid moving meta handles again and again (perf) 2025-09-17 09:13:42 +02:00
skypjack
55f1a47fcc meta: lazy_node pointer in meta_any (perf) 2025-09-16 10:44:35 +02:00
skypjack
e9b3eee6d3 meta: static local meta type node fallback 2025-09-15 14:48:15 +02:00
skypjack
b400927f02 meta: missing [[nodiscard]] 2025-09-12 18:06:07 +02:00
skypjack
a6d566ae4a natvis: update meta type and meta type descriptor views 2025-09-12 17:44:13 +02:00
skypjack
93979e13ac meta: typo 2025-09-12 17:43:50 +02:00
skypjack
a77e40093d meta: cleanup 2025-09-12 17:00:24 +02:00
skypjack
695ac44673 meta: prepare for more perf improvements 2025-09-12 16:23:37 +02:00
skypjack
1cc078e4fb test: cleanup 2025-09-12 16:11:00 +02:00
skypjack
94cd89a72a doc: typo 2025-09-12 14:38:58 +02:00
skypjack
5497291c29 meta: avoid copying meta_custom_node as not needed (perf) 2025-09-11 17:54:10 +02:00
skypjack
3c75f7e2fd meta: move custom data to meta type node details (perf) 2025-09-11 17:52:26 +02:00
skypjack
03d8cccc86 meta: use unique_ptr internall for meta_func_node 2025-09-11 17:30:31 +02:00
skypjack
47bfffc7cc meta: avoid moving handles if not needed 2025-09-10 19:09:02 +02:00
skypjack
9795f60bc1 meta: use ctx only for return type 2025-09-10 19:08:31 +02:00
skypjack
b3b48db957 meta: cleanup 2025-09-09 18:21:30 +02:00
skypjack
7d2b4fcf57 meta: internal changes 2025-09-09 17:30:09 +02:00
skypjack
3af8525eb8 natvis: refine meta_any view 2025-09-09 08:27:30 +02:00
skypjack
3306b72f87 natvis: refine basic_any view 2025-09-09 08:27:14 +02:00
skypjack
91a239a277 meta: typo 2025-09-09 08:13:22 +02:00
skypjack
fdaaeea52d meta: lazy initialization for nodes in meta_any 2025-09-08 15:43:01 +02:00
skypjack
d81e57daa1 meta: use fwd ctors internally 2025-09-08 08:39:57 +02:00
skypjack
ed84c6e9ff meta: early exit from allow_cast 2025-09-05 15:34:04 +02:00
skypjack
fe872f6a0d test: cleanup 2025-09-05 15:00:42 +02:00
skypjack
d8aa90234a meta: cleanup internal::try_convert 2025-09-05 13:04:20 +02:00
skypjack
28d8041da6 doc: typo 2025-09-05 12:58:35 +02:00
skypjack
1f53d163b7 meta: prepare internal::try_cast for lazy load on nodes 2025-09-05 12:57:30 +02:00
skypjack
7f38b7668e meta: break pointless meta destructors support for perf reasons (breaking change, use type destructors instead) 2025-09-04 15:05:15 +02:00
skypjack
dc70d97760 *: updated TODO 2025-09-04 15:03:59 +02:00
skypjack
4d0d48f7d9 test: delete meta_drop.cpp 2025-09-04 15:03:42 +02:00
skypjack
4ecaf18b20 doc: update meta, drop reference to meta destructors 2025-09-04 14:57:28 +02:00
skypjack
30a66cf513 bazel: cleanup 2025-09-04 14:57:10 +02:00
skypjack
9bbc8ad014 natvis: remove view for meta_dtor_node 2025-09-04 14:55:35 +02:00
skypjack
626b882f26 meta: reduce uses of node in meta_any 2025-09-03 17:00:22 +02:00
skypjack
9b44fa47d0 meta: guard try_cast to reduce uses of the internal node 2025-09-02 17:53:42 +02:00
skypjack
9170fc850b meta: cleanup 2025-09-02 14:29:39 +02:00
skypjack
973f010d85 meta: early exit when assigning 2025-09-02 14:20:18 +02:00
skypjack
6241543fe2 meta: cleanup 2025-09-02 10:33:31 +02:00
skypjack
5e99452cf3 meta: avoid using node in meta_any if not necessary 2025-09-01 18:41:34 +02:00
skypjack
d058ccd084 meta: avoid using node in meta_any if not necessary 2025-09-01 14:23:26 +02:00
skypjack
7e4694ce69 meta: prepare for lazy node initialization 2025-09-01 13:47:15 +02:00
skypjack
b3b7975290 *: updated TODO 2025-08-29 17:33:57 +02:00
skypjack
5ee0a39616 meta: review allow_cast 2025-08-29 15:24:24 +02:00
skypjack
27b9a25d06 meta: cleanup comparison operator 2025-08-29 11:41:54 +02:00
skypjack
9f5a6260ce meta: node_or_assert for data/func 2025-08-29 09:59:28 +02:00
skypjack
b4b85c8d53 meta: remove unnecessary checks 2025-08-29 09:55:27 +02:00
skypjack
4bc11e4297 natvis: meta 2025-08-29 09:34:32 +02:00
skypjack
ad291d4280 natvis: core 2025-08-29 09:34:26 +02:00
skypjack
f61fd4584a meta: skip unnecessary resolve calls (perf) 2025-08-28 16:56:31 +02:00
skypjack
536dba3048 meta: rollback safe mode for meta data and functions (perf, not necessary) 2025-08-28 13:38:41 +02:00
skypjack
4760a09d61 meta: avoid creating a new shared_ptr if not needed (perf) 2025-08-28 12:53:01 +02:00
skypjack
47ff98e25b storage: quit accepting (and silently discarding) arguments for empty types when invoking ::insert 2025-08-27 12:08:25 +02:00
skypjack
a638df6cae storage: quit accepting (and silently discarding) arguments for empty types when invoking ::emplace 2025-08-26 11:05:01 +02:00
skypjack
4f1626fce9 runtime view: rework/get rid of NOLINT 2025-08-26 10:56:02 +02:00
skypjack
9b3965c2c9 family: rework/get rid of NOLINT 2025-08-26 10:51:45 +02:00
skypjack
536a43be90 storage: add proper swap function to entity specialization - close #1273 2025-08-25 15:18:00 +02:00
Christoph
09b0e7f528 meta: add parentheses around max to prevent conflicts with max macro on Windows (#1278) 2025-08-25 08:59:16 +02:00
skypjack
76b6841c51 organizer: make graph() const - close #1274 2025-08-25 08:57:41 +02:00
skypjack
4507c546c1 meta: reduce instantiations on meta containers 2025-08-07 11:04:27 +02:00
skypjack
add999d105 meta: internal changes 2025-08-06 12:20:57 +02:00
skypjack
9cefbc3c79 meta: reduce size of fake vtable/template functions 2025-08-06 12:20:44 +02:00
skypjack
22347cfbd0 meta: remove pointless check 2025-08-05 14:47:39 +02:00
skypjack
6966eabd25 resource: minor changes 2025-08-05 13:59:23 +02:00
skypjack
a32e004c92 test: a few more 2025-08-05 13:59:09 +02:00
skypjack
49d9385aa4 process: handle_type to make the type opaque for future changes 2025-08-05 13:36:13 +02:00
skypjack
9970ba2284 davey: remove useless macro 2025-08-04 16:33:08 +02:00
skypjack
20cc85011e davey: no more entt:: 2025-08-04 16:21:22 +02:00
skypjack
7f5d4fb8d2 davey: minor changes 2025-08-04 16:19:13 +02:00
skypjack
03e046f880 davey: revert recent changes, move towards multiple tools with shared functions instead 2025-08-04 16:13:52 +02:00
skypjack
436f077e0b *: updated TODO 2025-08-04 16:12:14 +02:00
skypjack
d76cfb06f0 davey: prepare to support editor mode 2025-08-04 15:06:11 +02:00
Aaron Heysse
2bb947f6de doc: fixed variadic template args on a resource loader example (#1272) 2025-08-01 11:55:28 +02:00
skypjack
8a8f8dfd1b test: refine tests for dense_map 2025-07-31 14:49:47 +02:00
skypjack
15b8db6f61 doc: dense map 2025-07-31 14:49:38 +02:00
Terens
05e0a22fb3 dense_map: transparent lookup for at (#1270) 2025-07-31 14:29:44 +02:00
skypjack
ada11c4be1 test: headers cleanup 2025-07-30 14:09:56 +02:00
skypjack
7dbb59adc9 test: increase code coverage 2025-07-30 12:04:57 +02:00
skypjack
02440e17ae test: code coverage 2025-07-30 10:35:11 +02:00
skypjack
fa234af36b gh: update iwyu version 2025-07-30 10:24:36 +02:00
skypjack
40fdd26788 test: make clang-tidy happy 2025-07-30 10:21:10 +02:00
skypjack
6fa668900e natvis: sparse_set/storage/handle 2025-07-29 09:27:16 +02:00
skypjack
17e88e506c test: scheduler - close #1257 2025-07-28 15:10:11 +02:00
skypjack
3bcbea785a test: process 2025-07-28 14:32:42 +02:00
skypjack
3bac3d4bbe process: make inheritance from enable_shared_from_this public 2025-07-28 14:28:54 +02:00
skypjack
abdb293106 doc: a note about shared processes 2025-07-28 14:28:29 +02:00
skypjack
f4b960dc93 process: enable_shared_from_this to allow callers to get valid references 2025-07-28 14:20:38 +02:00
skypjack
71c217da28 *: updated TODO 2025-07-25 18:17:40 +02:00
skypjack
4777ca2980 doc: process 2025-07-25 18:17:20 +02:00
skypjack
4aabca2f0b natvis: process 2025-07-25 18:02:42 +02:00
skypjack
5c31fda9bd process/scheduler: refine API (finally), add allocator support - see #1257 2025-07-25 14:59:39 +02:00
skypjack
eecd1dc636 doc: process (draft) 2025-07-24 18:48:16 +02:00
Michele Caini
998f4b45da doc: process (wip - tbd) 2025-07-23 18:11:14 +02:00
Michele Caini
fb0b7e2aab *: updated TODO 2025-07-23 18:01:07 +02:00
Michele Caini
cbb50c1292 process: prepare migration to allocator-aware model 2025-07-23 16:05:13 +02:00
Michele Caini
fcd265a6ed process: shared_from_this is no longer required 2025-07-23 10:55:42 +02:00
Michele Caini
357ee3f994 scheduler: remove unnecessary template parameter 2025-07-22 10:59:40 +02:00
Michele Caini
e35eed905b process: safety net 2025-07-22 10:58:00 +02:00
Michele Caini
7942648035 process/scheduler: refine API - see #1257 2025-07-22 10:51:15 +02:00
Michele Caini
93dca93616 process: prepare for a more user-friendly attach/then API 2025-07-21 12:51:38 +02:00
Michele Caini
9f10d32b73 meta: move meta_dynamic_extent to fwd.hpp 2025-07-21 12:02:35 +02:00
Michele Caini
21e82abfa7 scheduler: cleanup API 2025-07-18 18:59:08 +02:00
Michele Caini
7b48069bd5 process: process_from utility 2025-07-18 18:58:46 +02:00
Michele Caini
5721e62ed0 *: updated TODO 2025-07-18 18:10:55 +02:00
Michele Caini
e140399725 doc: cleanup 2025-07-17 18:23:45 +02:00
Michele Caini
7f1a2b1887 doc: refine doc for process adaptor 2025-07-17 18:22:35 +02:00
Michele Caini
0fd70878b3 doc: process_adaptor 2025-07-17 18:17:14 +02:00
Michele Caini
06e88ce062 test: prepare to refactor scheduler API 2025-07-17 18:06:08 +02:00
Michele Caini
b5201a56df scheduler: add a plain attach for shared processes - see #1257 2025-07-16 15:26:18 +02:00
Michele Caini
9c05f1d8f3 natvis: process 2025-07-16 15:08:58 +02:00
Michele Caini
093718a1c6 process: add base_type alias to process_adaptor 2025-07-16 15:08:47 +02:00
skypjack
c07cb32ef8 *: updated TODO 2025-07-15 18:56:14 +02:00
skypjack
04da178800 process; process as first argument in process_adaptor 2025-07-15 09:47:55 +02:00
skypjack
5ab8da50cb *: updated TODO 2025-07-15 09:47:31 +02:00
skypjack
da684a4159 process: basic_process_adaptor -> process_adaptor 2025-07-15 09:31:44 +02:00
skypjack
ba20bde250 process: refine basic_process_adaptor implementation to use deduction guides 2025-07-15 09:30:04 +02:00
skypjack
cec550ad11 process: pass directly the base process to callbacks 2025-07-14 18:00:05 +02:00
skypjack
62b56e1b56 process: move basic_process_adaptor design from base class functor to member 2025-07-14 17:20:34 +02:00
skypjack
b045846579 test: minor changes 2025-07-14 16:48:03 +02:00
skypjack
665aa3c4cf *: updated TODO 2025-07-02 16:31:53 +02:00
skypjack
0f49a1fe27 davey: initial doc (despite the work-in-progress nature) 2025-07-02 09:41:20 +02:00
skypjack
ad4ab9dc08 davey: fix entry points for storage types 2025-07-02 09:37:15 +02:00
skypjack
1293990605 davey: comment out internals to avoid warnings due to missing doc 2025-07-01 16:43:26 +02:00
skypjack
c71bf6e8d9 process: avoid unqualified lookup 2025-07-01 16:37:42 +02:00
skypjack
716c67d3a5 process: enable_shared_from_this was actually useful here 2025-07-01 15:32:37 +02:00
skypjack
33b1daafa0 process: refine then return type 2025-06-30 17:32:27 +02:00
skypjack
2532158860 scheduler: process type 2025-06-30 17:32:04 +02:00
skypjack
da65e347b3 doc: typo 2025-06-30 08:35:01 +02:00
skypjack
c940bc8015 process: enable_shared_from_this is no longer required 2025-06-30 08:33:25 +02:00
skypjack
9c4676f4c0 process: merge peek/release, no need for two functions 2025-06-30 08:26:24 +02:00
skypjack
3b64dae13e natvis: process 2025-06-30 08:18:09 +02:00
skypjack
3126b3efbf scheduler: review internals to use peek/release/whatever - see #1257 2025-06-27 11:27:12 +02:00
skypjack
01e12fa011 doc: cleanup 2025-06-27 11:26:28 +02:00
skypjack
1d163ee113 process: then/release/peek for child processes 2025-06-27 09:12:02 +02:00
skypjack
f97ecd3ba4 process: rollback token based ctor, prep to move continuation to process 2025-06-25 19:16:29 +02:00
skypjack
3d66676c1b doc: a note about ENTT_INSTALL - close #1265 2025-06-25 18:13:26 +02:00
skypjack
bf28bab7ba natvis: basic_scheduler 2025-06-25 16:43:53 +02:00
skypjack
a6c2dc89ac scheduler/process: abort immediate param does not propagate anymore 2025-06-24 18:12:27 +02:00
skypjack
e36ff3d50e build: move tools to source dir 2025-06-24 17:41:01 +02:00
skypjack
dd4c36333b natvis: basic_process 2025-06-24 17:12:04 +02:00
skypjack
9e328ce232 build: remove extra > from HEADERS_FILES 2025-06-23 13:53:53 +02:00
skypjack
624d0994ff build: BUILD_INTERFACE vs INSTALL_INTERFACE for headers 2025-06-23 10:38:35 +02:00
skypjack
6ec1ee6072 build: refine cmake natvis to reduce boilerplate 2025-06-23 10:37:57 +02:00
skypjack
5642788046 process: struct vs class decl 2025-06-23 08:48:38 +02:00
skypjack
e8e77bf011 build: minor changes 2025-06-23 08:13:07 +02:00
skypjack
964dc6ec87 build: BUILD_INTERFACE vs INSTALL_INTERFACE 2025-06-20 16:24:41 +02:00
skypjack
a0d738215a build: move natvis files to source dir 2025-06-20 15:17:39 +02:00
skypjack
9ae1181ca7 build: also install natvis files 2025-06-20 15:09:39 +02:00
skypjack
3b89567d77 type_info: wrap ENTT_PRETTY_FUNCTION_PREFIX/SUFFIX with an ifdef 2025-06-20 13:26:13 +02:00
skypjack
09bb10377b process: basic_process_adaptor vs process_adaptor 2025-06-19 17:16:56 +02:00
skypjack
ffbc5519c1 process: start refining process_adaptor 2025-06-19 13:02:12 +02:00
skypjack
c80b634497 process: basic_process vs process 2025-06-18 12:22:41 +02:00
skypjack
db6c6573af scheduler: suppress warnings due to shadow variables 2025-06-17 15:27:11 +02:00
skypjack
7257dd50c6 process: single allocate ::function 2025-06-17 15:26:27 +02:00
skypjack
5f0af48b4c *: updated TODO 2025-06-17 08:16:10 +02:00
skypjack
41ebe0e779 scheduler/process: factory-like model, first draft, work in progress - see #1257 2025-06-17 08:16:05 +02:00
skypjack
0cc95c947e scheduler: further cleanup 2025-06-16 14:49:01 +02:00
skypjack
991f66e1a3 scheduler: cleanup to reduce allocations 2025-06-16 14:42:43 +02:00
skypjack
d75efb9f16 *: updated TODO 2025-06-13 18:31:32 +02:00
skypjack
3575f00bb4 doc: minor changes 2025-06-13 18:31:18 +02:00
skypjack
f532221685 doc: You are Circle 2025-06-13 08:02:30 +02:00
skypjack
f502827e88 tests: update to match the last changes to the process and scheduler classes 2025-06-11 17:11:21 +02:00
skypjack
50c842883f scheduler: init calls are no longer required 2025-06-11 17:10:59 +02:00
skypjack
2f5b25b189 process: minor changes 2025-06-11 17:10:37 +02:00
skypjack
fb0fedf8fc doc: process 2025-06-11 14:10:07 +02:00
skypjack
45e9c12b29 process: remove init, we have constructors already 2025-06-11 14:10:01 +02:00
skypjack
e52957fabb scheduler: reduce process_handler to a single class 2025-06-10 08:04:50 +02:00
skypjack
209f333252 scheduler: keep removing logic from process handler 2025-06-09 14:50:47 +02:00
skypjack
aa5c0d14ed scheduler: prepare to get rid of process handler 2025-06-09 14:46:15 +02:00
skypjack
ca40a3e62f process: move away from the crtp idiom - see #1257 2025-06-06 15:15:20 +02:00
skypjack
8574335fe6 process: make succeed and fail functions public 2025-06-05 15:12:21 +02:00
skypjack
9493756895 test: cleanup 2025-06-05 15:10:52 +02:00
skypjack
df5e57f381 process: make pause/unpause public - see #1257 2025-06-04 19:04:56 +02:00
skypjack
62e05abc77 doc: process 2025-06-03 10:50:37 +02:00
skypjack
d7d63b09b7 registry: context .clear() function - close #1262 2025-06-03 10:49:10 +02:00
skypjack
ae7b3d87ee test: avoid shadow variable warning 2025-05-29 17:49:13 +02:00
skypjack
e2ef08f129 clang-tidy: suppress ok-ish warning 2025-05-29 17:45:10 +02:00
skypjack
8dfa4aa677 test: cleanup 2025-05-29 17:39:29 +02:00
skypjack
31522ce703 *: updated TODO 2025-05-29 14:23:17 +02:00
skypjack
25cad2543d test: reduce NOLINT usage 2025-05-29 14:23:12 +02:00
skypjack
34e50d6550 organizer: a const registry is not a sync point - close #1258 2025-05-28 18:28:22 +02:00
skypjack
b7a06c535d hashed string: suppress linter warning 2025-05-27 15:36:49 +02:00
skypjack
bf5b188b13 test: missing include 2025-05-27 09:20:47 +02:00
skypjack
20af487ea9 test: minor changes 2025-05-27 09:10:22 +02:00
skypjack
d037976bcd hashed string: suppress warnings due to pointer arithmetic 2025-05-27 09:07:34 +02:00
skypjack
330bf8e120 sparse set: avoid warning due to missing [[nodiscard]] 2025-05-27 08:58:23 +02:00
skypjack
7064415265 *: updated TODO 2025-05-27 08:58:05 +02:00
skypjack
df07350a46 gh: typo in a workflow 2025-05-27 08:46:44 +02:00
skypjack
66e80820c9 doc: meta built-in names support 2025-05-26 09:20:09 +02:00
skypjack
5d3173f1b4 doc: updated links 2025-05-26 09:19:52 +02:00
skypjack
3b977186ed runtime view: swap-only policy support - close #1252 2025-05-23 15:03:09 +02:00
skypjack
38caafd999 test: runtime view and storage entity with exclude 2025-05-22 15:35:15 +02:00
skypjack
d7232dcf56 test: runtime view and storage entity 2025-05-22 15:32:09 +02:00
skypjack
825f73df6b test: more on the runtime view 2025-05-22 14:48:50 +02:00
skypjack
696e084b02 davey: refine pointer-like path 2025-05-21 17:40:43 +02:00
skypjack
fcc7306407 *: cleanup 2025-05-21 17:40:35 +02:00
skypjack
1f182a25db doc: minor changes 2025-05-21 16:29:02 +02:00
andre-caldas
995018f1a7 doc: improve documentation on for ENTT_USE_ATOMIC (#1251) 2025-05-21 16:23:23 +02:00
skypjack
a6fa6f6122 doc: auto-binding mechanism for signals 2025-05-19 19:26:22 +02:00
skypjack
1b6462a327 davey: better enum support 2025-05-19 13:38:41 +02:00
skypjack
6054e93ecc *: updated TODO 2025-05-19 13:38:39 +02:00
skypjack
3acf03ae1c testbed: more bindings 2025-05-16 11:46:56 +02:00
skypjack
42f4d28715 testbed: cleanup 2025-05-16 09:43:54 +02:00
skypjack
4ee86d8456 meta: forward declare meta_ctx 2025-05-16 09:43:47 +02:00
skypjack
3b2adc999d davey: davey_data is no longer required 2025-05-15 10:52:21 +02:00
skypjack
87ec44d3ee natvis: meta 2025-05-15 09:41:37 +02:00
skypjack
182a6d5fe4 meta: refine label support implementation 2025-05-14 14:53:02 +02:00
skypjack
49b5b5b2b1 hashed_string: make const char * conversion explicit 2025-05-12 16:05:35 +02:00
skypjack
f984ae4930 hashed_string: break the dependency with string_view 2025-05-12 09:09:35 +02:00
skypjack
e6ef506f2f natvis: label on meta elements 2025-05-09 17:33:29 +02:00
skypjack
2d1ae3d5eb *: updated TODO 2025-05-09 17:23:24 +02:00
skypjack
d775841457 meta: built-in labels for meta data 2025-05-09 17:22:57 +02:00
skypjack
2f60398892 *: updated TODO 2025-05-09 17:20:17 +02:00
skypjack
393a347bad meta: prepare factory for labels on data 2025-05-09 14:57:48 +02:00
skypjack
6a4c594792 doc: cleanup 2025-05-09 09:29:56 +02:00
skypjack
154c16a9d3 meta: built-in labels for meta functions 2025-05-08 16:46:54 +02:00
skypjack
9536039932 meta: refine type label impl and tests 2025-05-08 10:17:49 +02:00
skypjack
cad91fd0d1 *: updated TODO 2025-05-05 18:31:30 +02:00
skypjack
7615e55e96 meta: built-in labels for meta types 2025-05-05 18:31:22 +02:00
skypjack
0f54af1a11 doc: typo 2025-05-05 18:30:16 +02:00
skypjack
a439cf476c meta: prepare for built-in name support 2025-05-05 17:50:24 +02:00
skypjack
121e5a0ca8 hashed_string: minor changes 2025-05-05 17:49:59 +02:00
skypjack
328e53bd7b testbed: use meta info 2025-05-02 14:13:03 +02:00
skypjack
4dd03da2e2 testbed: meta setup function proto 2025-05-02 11:36:45 +02:00
skypjack
ebab67c314 davey: davey_data constructor 2025-05-02 11:10:32 +02:00
skypjack
5ddab2b9d9 testbed: refine a couple of components 2025-05-02 09:12:03 +02:00
skypjack
265f00becf testbed: setup some components to start with... 2025-04-30 16:24:16 +02:00
skypjack
340888e4e2 testbed: bare minimum rendering system 2025-04-30 16:24:01 +02:00
skypjack
f5bbc895b5 testbed: updated components 2025-04-30 16:23:47 +02:00
skypjack
57a6c6b238 testbed: bare minimal input system 2025-04-30 16:06:55 +02:00
skypjack
cb10854bc8 testbed: a bunch of components to start with 2025-04-30 16:06:17 +02:00
skypjack
1796b18284 testbed: setup hud system for later use 2025-04-29 14:48:23 +02:00
skypjack
f505050391 testbed: prepare input system 2025-04-28 17:01:53 +02:00
skypjack
b0afed4bd0 davey: present_view function (with quick test example) 2025-04-24 19:15:36 +02:00
skypjack
292cbd3d95 davey: use right entity type 2025-04-24 19:03:47 +02:00
skypjack
756e861346 davey: internal changes 2025-04-24 17:35:41 +02:00
skypjack
6db98b40a1 davey: cleanup 2025-04-24 17:31:41 +02:00
skypjack
a9852a2d2f testbed: quick check for storage window 2025-04-23 17:49:41 +02:00
skypjack
7302b1544d davey: swap entity and storage in registry view 2025-04-23 17:48:06 +02:00
skypjack
491956a69e davey: typo 2025-04-23 17:46:32 +02:00
skypjack
2b94080ee5 davey: missing template keyword 2025-04-23 09:30:58 +02:00
skypjack
4732bd1bf7 davey: entry point for views 2025-04-23 08:59:06 +02:00
skypjack
2bdb44e3ba davey: typo 2025-04-23 08:54:40 +02:00
skypjack
d805c29b73 davey: minor changes 2025-04-22 19:03:43 +02:00
skypjack
a9187c1f45 davey: rework storage path 2025-04-22 18:56:10 +02:00
skypjack
4e76ae5cc6 davey: refine storage tab 2025-04-22 14:07:04 +02:00
skypjack
54b6ee0f27 davey: view entry point (draft) 2025-04-22 13:56:57 +02:00
skypjack
bf8c83e798 davey: make registry tab bar embeddable 2025-04-17 11:51:16 +02:00
skypjack
4aed49b371 davey: context-less single storage overload 2025-04-17 11:50:18 +02:00
skypjack
c1df295845 davey: break entity_tab dependency on registry class 2025-04-17 11:44:58 +02:00
skypjack
265057be83 davey: break storage_tab dependency on registry class 2025-04-17 11:38:03 +02:00
skypjack
021fbb6c69 davey: break present_entity dependency on registry class 2025-04-17 11:33:22 +02:00
skypjack
843480fdf6 davey: missing template keywords 2025-04-17 11:15:09 +02:00
skypjack
3eb78a175e davey: add missing template keyword 2025-04-16 19:40:31 +02:00
skypjack
de1ae66966 davey: refine specifiers 2025-04-16 19:39:43 +02:00
skypjack
ab9a62bcd8 davey: avoid using deprecated members 2025-04-16 19:31:51 +02:00
skypjack
41d9e44a23 davey: present_element (first draft, read-only) 2025-04-15 19:27:18 +02:00
skypjack
693b85fe40 davey: DAVEY_OR macro (for later uses) 2025-04-15 19:15:25 +02:00
skypjack
7a90e2a93b type_info: rename things for the best 2025-04-14 15:35:34 +02:00
skypjack
11c3ea2fdb type_info: full_type_name returns a non-null name in all cases 2025-04-14 15:32:14 +02:00
skypjack
57d604bcb2 type_info: full_type_name vs stripped_type_name 2025-04-14 15:24:00 +02:00
skypjack
63e38e40dd type_info: stripped_type_name returns empty views if name is not available 2025-04-14 15:20:27 +02:00
skypjack
c66f116449 build: refine cmake for testbed 2025-04-11 18:27:25 +02:00
skypjack
55fb754614 davey: work in progress 2025-04-11 18:16:25 +02:00
skypjack
afe687e9d7 testbed: temp init function for test purposes 2025-04-11 18:16:03 +02:00
Michele Caini
6ca9c77b26 davey: missing includes 2025-04-10 16:59:50 +02:00
Michele Caini
990956980b *: updated TODO 2025-04-10 16:59:42 +02:00
Michele Caini
f7f30bc5b6 davey: work in progress... 2025-04-10 13:01:53 +02:00
Michele Caini
81b8d01e96 testbed: try to use gha-setup-ninja to setup ninja on the CI 2025-04-09 10:46:07 +02:00
Michele Caini
7ecfe45923 meta: rollback factory setup support to get wip in e healthy state (damn MSVC that accepts everything) 2025-04-09 10:30:52 +02:00
Michele Caini
5212f0c44a test: scoped functions 2025-04-09 10:23:31 +02:00
Michele Caini
fa58cedd2e meta: avoid shadow-warnings 2025-04-09 10:20:30 +02:00
Michele Caini
8b1b7a0fc9 test: add missing template keywords 2025-04-09 10:11:45 +02:00
Michele Caini
2a37b972d9 meta: meta_factory::setup function 2025-04-09 09:26:00 +02:00
Michele Caini
df7ebd244b doc: minor changes 2025-04-08 17:13:17 +02:00
Michele Caini
b4b1cf38a6 test: cleanup 2025-04-08 17:12:39 +02:00
Michele Caini
292d57a60b meta: add meta_factory to fwd file 2025-04-08 12:08:27 +02:00
Michele Caini
9d6850636a meta_factory: type -> element_type to avoid collisions 2025-04-08 11:41:38 +02:00
Michele Caini
8879a76e88 meta_factory: expose underlying type 2025-04-07 17:50:56 +02:00
Michele Caini
0fa38bf0e0 testbed: work in progress (if only I had more time these days...) 2025-04-07 17:46:21 +02:00
Michele Caini
36eba38f62 *: updated TODO 2025-04-07 17:45:45 +02:00
Michele Caini
fe8d7d78c4 meta: drop unnecessary static keywords 2025-04-04 12:35:12 +02:00
Michele Caini
ff32d77eba meta: deprecate fixed_size in favor of extent/meta_dynamic_extent 2025-04-04 12:00:15 +02:00
Michele Caini
84f60ea2be build: use ninja generator for testbed windows workflow 2025-04-03 16:05:38 +02:00
Michele Caini
cc6fe33709 build: add ubuntu required packages to testbed workflow 2025-04-03 15:58:47 +02:00
Michele Caini
5b3f04c5ff build: testbed GH workflow (first attempt) 2025-04-03 14:35:47 +02:00
Michele Caini
dc0bf7bdd8 build: fix typo in GH workflow 2025-04-03 14:35:27 +02:00
Michele Caini
13f21dd942 build: tools.yml -> analyzer.yml 2025-04-01 14:30:47 +02:00
Michele Caini
fbe69ae90b tools: davey trauma :) 2025-04-01 14:23:15 +02:00
Michele Caini
dedad6a03e testbed: updated SDL3 version 2025-04-01 11:19:16 +02:00
Michele Caini
86d3d2207a testbed: application skel 2025-03-31 14:41:50 +02:00
Michele Caini
9c39f76774 build: update tests to make them work (again) with cr 2025-03-31 14:31:51 +02:00
Michele Caini
8c04222a40 testbed: app -> application 2025-03-28 11:28:32 +01:00
Michele Caini
d84a18e456 testbed: context class 2025-03-28 11:25:29 +01:00
Michele Caini
e42231fab0 sparse_set: deprecate type() in favor of info() for consistency 2025-03-27 10:23:26 +01:00
Michele Caini
d60efe04fd poly: deprecate type() in favor of info() for consistency 2025-03-26 15:19:15 +01:00
Michele Caini
6e7ca646ec any: deprecate type() in favor of info() for consistency 2025-03-26 15:16:19 +01:00
Michele Caini
cac3e4c62b test: minor changes 2025-03-26 15:15:57 +01:00
Michele Caini
0e323954e4 build: testbed initial setup 2025-03-25 15:44:52 +01:00
Michele Caini
385f5a4379 doc: typo 2025-03-25 11:55:36 +01:00
Michele Caini
e80d20a740 build: prepare testbed executable 2025-03-25 11:43:56 +01:00
Justin Braben
9d4db9738a build: fix delegate compare test in Release mode (#1242) 2025-03-25 11:40:32 +01:00
Michele Caini
f54859bedf build: prepare for tools and testbed 2025-03-21 09:39:53 +01:00
Michele Caini
556ff733d5 meta: drop deprecated utilities 2025-03-21 08:08:45 +01:00
Michele Caini
4027bfff69 meta: drop deprecated accessors on meta_any 2025-03-21 07:51:17 +01:00
Michele Caini
dbcc0baa7b meta: drop the meta<Type> function 2025-03-20 19:02:01 +01:00
Michele Caini
d45c2aca8a meta: drop multi-setter support for meta data 2025-03-20 09:26:25 +01:00
Michele Caini
fd015edb98 meta: drop meta_any_policy 2025-03-20 09:21:38 +01:00
Michele Caini
5ccb46383c storage: drop deprecated functions 2025-03-20 09:20:44 +01:00
Michele Caini
fa6fdc4059 now working on version 3.16.0 2025-03-19 17:09:40 +01:00
Michele Caini
d4014c74dc update single include file 2025-03-19 10:41:43 +01:00
Michele Caini
21ac61abbc helper: suppress a warning from the linter (waiting for C++20) 2025-03-18 14:51:38 +01:00
Michele Caini
9d00adf387 helper: minor changes 2025-03-18 14:33:37 +01:00
Michele Caini
35055e8886 *: updated TODO 2025-03-18 14:33:31 +01:00
Michele Caini
53da07ebd5 update single include file 2025-03-17 17:28:54 +01:00
Michele Caini
41e91688da meta: internal change 2025-03-14 15:49:33 +01:00
Michele Caini
1650d058f6 meta: early exit for typed allow_cast 2025-03-14 12:13:14 +01:00
Michele Caini
4208eaa4d8 entity: make to_entity work with stable types - close #1233 2025-03-12 18:01:49 +01:00
Michele Caini
5ef09c04c6 build: update doxygen 2025-03-11 11:24:26 +01:00
Michele Caini
a1f978da54 doc: entt namespace in-source doc 2025-03-11 09:47:05 +01:00
Michele Caini
090ba4ba06 doc: nvm, doxygen v1.13.2 emits wrong warnings here, but it gets the job done too 2025-03-11 08:08:40 +01:00
Michele Caini
aa039b9a0c storage: make doxygen 1.13 happy, it goes crazy with this ::get otherwise 2025-03-10 14:15:03 +01:00
Michele Caini
d0711fd7da build: do not install doc if not required 2025-03-10 09:29:04 +01:00
Michele Caini
8daacc339f build: self contained subdirs 2025-03-10 09:16:18 +01:00
Michele Caini
43fe07c23f build: coding style 2025-03-10 09:10:25 +01:00
Michele Caini
b9850320c1 build: prepare for tools and testbed 2025-03-10 09:05:21 +01:00
Michele Caini
5f819c1b25 test: cleanup 2025-03-07 17:33:50 +01:00
Michele Caini
91a6837d6a meta: support deducing meta pointer like types directly 2025-03-07 14:54:12 +01:00
Michele Caini
0fafdf65f8 build: prepare 2025-03-06 15:08:00 +01:00
Michele Caini
217bd07634 doc: reserved identifiers 2025-03-06 10:01:32 +01:00
Michele Caini
7e0e3a2196 snapshot: use start_from with entity storage 2025-03-06 10:00:59 +01:00
Michele Caini
be0e896d25 storage: reserved entities support 2025-03-06 09:57:01 +01:00
Michele Caini
0cb9f7c778 doc: typo 2025-03-05 09:09:09 +01:00
Michele Caini
52d3fd851e doc: entity storage (generate vs emplace/insert) 2025-03-04 17:23:14 +01:00
Michele Caini
2427c09ec6 test: updated test names 2025-03-04 15:48:07 +01:00
Michele Caini
6777f8538b *: minor changes, coding style 2025-03-04 15:42:02 +01:00
Michele Caini
4d3e11645b doc: long deserved links 2025-03-03 16:01:11 +01:00
Michele Caini
f2588c94c6 doc: add fennecs to similar projects 2025-03-03 15:57:15 +01:00
Michele Caini
fc01149121 test: registry emplace_or_replace empty - close #1228 2025-03-03 08:49:36 +01:00
Michele Caini
4d1501277d test: registry get_or_emplace empty 2025-03-03 08:48:25 +01:00
Michele Caini
269bb61f91 test: registry emplace empty 2025-03-03 08:47:51 +01:00
Michele Caini
bbf1b9c1ab test: registry emplace aggregate 2025-03-03 08:42:40 +01:00
Michele Caini
82b5bcc282 test: some hints by iwyu 2025-02-28 19:00:03 +01:00
Michele Caini
8c55c09c3f meta: revert rebind meta_any with context(ctx), I prefer the rebinding constructors actually 2025-02-28 18:44:43 +01:00
Michele Caini
e3e84490e5 meta: rebind meta_any with context(ctx) 2025-02-28 08:37:32 +01:00
Michele Caini
da1f56d046 mixin: support custom private/protected registries - close #1222 2025-02-27 09:52:05 +01:00
Michele Caini
3bd8807ec7 *: updated TODO 2025-02-27 09:30:56 +01:00
Michele Caini
da60ec0a9e test: code coverage 2025-02-26 15:45:25 +01:00
Michele Caini
6efb7dc92d meta: get the context from the instance in the meta invoke (and deprecate old API) 2025-02-26 15:15:44 +01:00
Michele Caini
904c14095e meta: make required functions visible 2025-02-26 12:30:52 +01:00
Michele Caini
6cf0883769 meta: get the context from the instance in the meta getter (and deprecate old API) 2025-02-26 10:28:02 +01:00
Michele Caini
2f35d1cd0e meta: internal changes 2025-02-25 18:37:50 +01:00
Michele Caini
91a5f10598 meta: internal changes 2025-02-25 17:33:32 +01:00
Michele Caini
780e3d8e3b test: suppress a false positive by the linter 2025-02-25 15:06:03 +01:00
Michele Caini
350b10ef53 doc: remove reference to multiple-setters support for meta data 2025-02-25 12:32:42 +01:00
Michele Caini
e7e248b526 doc: minor changes 2025-02-25 12:31:07 +01:00
Lucy
8883281848 doc: fix multiple grammar and spelling issues in docs (#1212) 2025-02-25 10:27:31 +01:00
Michele Caini
7784c741c4 meta: cleanup 2025-02-25 10:26:32 +01:00
Michele Caini
46e68362f7 meta: deprecate multi-setters data support to make room for multi-argument data functions 2025-02-24 15:49:57 +01:00
Michele Caini
84c85dc2d2 natvis: update meta views 2025-02-24 15:30:21 +01:00
Michele Caini
0787f13f7f doc: updated copyright 2025-02-23 19:42:31 +01:00
Michele Caini
6b95c614bb view: setup placeholders on view packs - close #1224 2025-02-21 12:36:03 +01:00
Michele Caini
7a7695e603 organizer: group support 2025-02-20 11:30:41 +01:00
Michele Caini
b198b3f087 group: default template parameters 2025-02-19 12:41:58 +01:00
Michele Caini
cf51b5bed5 test: more tests around component_traits 2025-02-19 11:59:43 +01:00
Michele Caini
777ef5a0bd component_traits: entity based model 2025-02-18 14:24:27 +01:00
Michele Caini
f301738bf4 doc: rollback changes about component_traits 2025-02-18 09:09:52 +01:00
Michele Caini
de80e7b910 component_traits: deprecate old version (sort of) to make the migration easier 2025-02-17 11:01:43 +01:00
Michele Caini
dd70a5833a test: rework things a little 2025-02-14 15:31:58 +01:00
Michele Caini
aaf7661f8e test: cleanup 2025-02-14 15:15:17 +01:00
Michele Caini
fdb6214879 sparse_set: test shrink_to_fit and improve corner cases 2025-02-14 15:15:10 +01:00
Michele Caini
2df2dd2d6f bit: handle conversion warnings 2025-02-13 11:48:14 +01:00
Michele Caini
c3853e7a39 dense_map: make tests -Wconversion friendly 2025-02-13 11:42:42 +01:00
Michele Caini
da69a6f19c build: suppress conversion warnings when using external libraries 2025-02-12 15:46:06 +01:00
Michele Caini
65b2e5c671 core: handle conversion warnings 2025-02-12 15:45:23 +01:00
Michele Caini
dfeac358cc view: handle conversion warnings 2025-02-11 10:57:14 +01:00
Michele Caini
52be46965b container: handle conversion warnings 2025-02-11 10:57:04 +01:00
Michele Caini
95c142ad65 entity: handle conversion warnings 2025-02-10 11:09:43 +01:00
Michele Caini
562d25c7ca container: handle conversion warnings 2025-02-10 11:09:39 +01:00
Michele Caini
b10ba4da1c entity: handle conversion warnings 2025-02-10 10:31:32 +01:00
Michele Caini
ba70637eeb entity: handle conversion warnings 2025-02-09 11:45:36 +01:00
Michele Caini
c2226ee1f4 flow: handle conversion warnings 2025-02-09 11:45:26 +01:00
Michele Caini
f17d1801d5 test: handle conversion warnings 2025-02-09 11:45:14 +01:00
Michele Caini
3ef946802b storage: handle conversion warnings 2025-02-09 10:53:53 +01:00
Michele Caini
86757df072 registry: handle conversion warnings 2025-02-07 15:41:06 +01:00
Michele Caini
f0c763b984 container: difference_type 2025-02-07 15:39:13 +01:00
Michele Caini
5fbddb471c signal: handle conversion warnings 2025-02-07 15:27:11 +01:00
Michele Caini
1fddb2c4c9 entity: handle conversion warnings 2025-02-07 15:27:04 +01:00
Michele Caini
8156eeb47e core: handle conversion warnings 2025-02-07 15:26:57 +01:00
Michele Caini
ccb63bfd80 container: handle conversion warnings 2025-02-07 15:26:48 +01:00
Michele Caini
baad567dfa dense_set: handle conversion warnings 2025-02-07 15:26:29 +01:00
Michele Caini
b0bd5f641d dense_map: handle conversion warnings 2025-02-07 15:12:53 +01:00
Michele Caini
6ae6e038d6 dispatcher: handle conversion warnings 2025-02-07 14:03:44 +01:00
Michele Caini
9b2a791878 dense_set: handle conversion warnings 2025-02-07 13:59:49 +01:00
Michele Caini
fc48670219 dense_map: handle conversion warnings 2025-02-07 13:59:42 +01:00
Michele Caini
f7f1e2162f adjacency matrix: minor changes 2025-02-07 13:59:25 +01:00
Michele Caini
9f7be95782 adjacency matrix: handle conversion warning 2025-02-07 10:48:52 +01:00
Michele Caini
1a3e16d7ce *: updated TODO 2025-02-07 10:48:27 +01:00
Michele Caini
ddbfaa2dc6 build: try to enable -Wconversion on the CI (see #1215) 2025-02-06 16:00:53 +01:00
Michele Caini
38fb30e336 test: const correctness 2025-02-05 15:09:01 +01:00
Michele Caini
24446f2f81 meta: [[nodiscard]] 2025-02-05 11:52:22 +01:00
Michele Caini
0d0373e222 test: cleanup 2025-02-05 11:34:27 +01:00
Michele Caini
86c3c4565b registry: ::storage<T> const correctness - close #1217 2025-02-05 11:32:07 +01:00
Michele Caini
12859bb856 meta: limit ::context() to meta_any 2025-02-05 11:26:57 +01:00
Michele Caini
43a7b452c4 meta: drop context holder base class 2025-02-05 10:44:08 +01:00
Michele Caini
f30ca72c69 meta: make meta_type return the underlying context 2025-02-05 10:34:50 +01:00
Michele Caini
041b5e9882 meta: make meta_func return the underlying context 2025-02-04 16:54:05 +01:00
Michele Caini
36c1a58bff meta: make meta_data return the underlying context 2025-02-04 16:53:20 +01:00
Michele Caini
0c73244575 meta: common base class for meta context holders 2025-02-04 15:15:00 +01:00
Michele Caini
8e6bbc4a6c meta: as_ref for void types still returns a valid meta_any 2025-02-03 17:48:09 +01:00
Michele Caini
303801c23b any: typo 2025-01-31 12:35:21 +01:00
Michele Caini
165a048fa1 algorithm: suppress yet another annoying warning 2025-01-31 12:33:59 +01:00
Michele Caini
6eb42cd782 sigh_mixin: const correctness 2025-01-31 12:32:08 +01:00
Michele Caini
d726bd761c view: cannot also please the linter when unpacking like this 2025-01-31 12:27:07 +01:00
Michele Caini
c506fd6f5e test: clang triggers a false positive, but doesn't even want me to suppress it 2025-01-31 12:22:56 +01:00
Michele Caini
39aae24c0a test: minor changes 2025-01-31 12:20:12 +01:00
Michele Caini
8b890993a2 storage: refine from_placeholder function 2025-01-31 08:50:02 +01:00
Michele Caini
0a706ec1c5 build: no need to run tests after clang-tidy 2025-01-30 10:25:06 +01:00
Michele Caini
06d5694f7a test: cleanup 2025-01-30 10:24:51 +01:00
Michele Caini
15be7d0301 signal: review sink API 2025-01-30 09:42:32 +01:00
Michele Caini
e878406824 *: updated TODO 2025-01-30 09:42:16 +01:00
Michele Caini
ee7a33f62f build: update tools workflow 2025-01-29 14:08:01 +01:00
Michele Caini
68c649794c build: update iwyu job 2025-01-29 09:32:15 +01:00
Michele Caini
4686094c09 storage: avoid do-while loops 2025-01-29 09:31:15 +01:00
Michele Caini
476822e6c4 sparse_set: improved shrink_to_fit to also cleanup the sparse array 2025-01-28 15:11:16 +01:00
Michele Caini
7fe1a69d58 *: updated TODO 2025-01-28 14:10:48 +01:00
Michele Caini
7bcb4d739e meta: move lint directive to the right spot 2025-01-27 15:11:24 +01:00
Michele Caini
08b21b17e2 any: I know what I'm doing, I don't want to cast things explicitly and make it harder to read 2025-01-27 13:53:11 +01:00
Michele Caini
9ae5a3d7d4 meta: suppress wrong warning by clang 2025-01-27 13:52:35 +01:00
Michele Caini
0aa6cb8301 type_traits: sizeof(A*) used on purpose 2025-01-27 13:46:19 +01:00
Michele Caini
9aaef992ba meta: suppress wrong warning by clang-tidy 2025-01-27 13:43:20 +01:00
Michele Caini
a5d4611d21 mixin: avoid use-after-move (wrong) warnings 2025-01-27 10:08:08 +01:00
Michele Caini
25dff4bddc sparse_set: suppress a wrong warning by gcc - see #1194 2025-01-24 15:35:02 +01:00
Michele Caini
6936aa951a test: no leak here clang, but thanks for caring 2025-01-24 15:32:24 +01:00
Michele Caini
54adabee28 test: remove useless inline specifier 2025-01-24 15:28:22 +01:00
Michele Caini
85bc3b778a factory: explicit casts 2025-01-24 15:26:12 +01:00
Michele Caini
c38f1349fc type_traits: suppress warnings on blind operations 2025-01-24 14:29:58 +01:00
Michele Caini
f86c325f0b storage/mixin: prefer NOLINTBEGIN/END on the right (wrong) warning 2025-01-24 14:27:09 +01:00
Michele Caini
38551a5332 storage/mixin: avoid use-after-move (wrong) warnings 2025-01-24 10:08:57 +01:00
Michele Caini
00365b26d9 any: suppress a wrong warning by the linter 2025-01-24 09:34:46 +01:00
Michele Caini
1eaff4f3d9 any: explicit casts 2025-01-24 09:34:31 +01:00
Michele Caini
9e0f8149a4 meta: small internal change to suppress a warning 2025-01-24 09:34:03 +01:00
Michele Caini
279166cb8b emitter: updated definition for ::publish 2025-01-24 09:33:30 +01:00
Michele Caini
b6d3c84595 test: const correctness 2025-01-24 08:22:22 +01:00
Michele Caini
0d944f6547 any/meta_any: no leaks here clang, but thanks for asking 2025-01-24 08:22:08 +01:00
Michele Caini
20b99b6b8b clang-tidy: first attempt to disable EnumCastOutOfRange warnings 2025-01-23 11:29:52 +01:00
Michele Caini
5e687893de clang-tidy: feel free to ignore meta_traits 2025-01-23 11:29:21 +01:00
Michele Caini
915207aa6b test: minor changes to enum class types 2025-01-23 11:28:46 +01:00
Michele Caini
9ba4fb867a process: review ::next fallback 2025-01-23 11:25:10 +01:00
Michele Caini
e342f3b419 storage: suppress wrong-ish warning 2025-01-23 11:24:46 +01:00
Michele Caini
3721cf31a4 type_info: suppress wrong-ish warning 2025-01-23 11:23:56 +01:00
Michele Caini
19ca7143c8 any: not-moved-params are moved later on, suppress the warnings 2025-01-23 11:23:14 +01:00
Michele Caini
2aebb12e06 version: no, linter, I really want them to be macros, sorry 2025-01-22 10:19:04 +01:00
Michele Caini
b4dfda501b resource_cache_iterator: const correctness 2025-01-22 10:18:38 +01:00
Michele Caini
eb7c15bee0 view_iterator(s): const correctness 2025-01-22 10:18:25 +01:00
Michele Caini
5636304217 storage_iterator(s): const correctness 2025-01-22 10:18:06 +01:00
Michele Caini
588440b487 sparse_set_iterator: const correctness 2025-01-22 10:17:49 +01:00
Michele Caini
bea0418927 handle_storage_iterator: const correctness 2025-01-22 10:17:34 +01:00
Michele Caini
b229ca3aba test: const correctness 2025-01-22 10:17:19 +01:00
Michele Caini
41b4acb5b5 meta_range_iterator: const correctness 2025-01-22 10:14:26 +01:00
Michele Caini
3b371e9e52 meta: explicit check to avoid warnings due to implicit conversions 2025-01-22 10:14:08 +01:00
Michele Caini
eb1e055941 edge_iterator: const correctness 2025-01-22 10:13:43 +01:00
Michele Caini
93152791da iota_iterator: const correctness 2025-01-22 10:13:27 +01:00
Michele Caini
7a7cd41b8c adjacency_matrix: drop redundant inline 2025-01-21 12:46:52 +01:00
Michele Caini
03f704d767 type_info: drop redundant inline 2025-01-21 12:46:38 +01:00
Michele Caini
399ca817ec container: const correctness 2025-01-21 12:46:26 +01:00
Michele Caini
2fb2ace002 bit: suppress a wrong warning for bugprone-assert-side-effect 2025-01-21 12:45:48 +01:00
Michele Caini
0592c4e4e6 meta: drop redundant member init 2025-01-21 12:19:49 +01:00
Michele Caini
48872e7ef8 any/meta_any: suppress bugprone-casting-through-void as needed 2025-01-21 12:18:57 +01:00
Michele Caini
0d3aaef63f helper: make as_view/as_group copyable 2025-01-21 12:18:21 +01:00
Michele Caini
75bfbd3b0a runtime_view_iterator: const correctness 2025-01-21 11:41:48 +01:00
Michele Caini
27358e6ab8 registry_storage_iterator: const correctness 2025-01-21 11:41:34 +01:00
Michele Caini
af193d8523 group: const correctness 2025-01-21 11:41:16 +01:00
Michele Caini
00b6b5c5f5 storage: internal changes to trick the linter 2025-01-21 11:15:18 +01:00
Michele Caini
6d03a44246 type_info: const correctness 2025-01-21 11:10:44 +01:00
Michele Caini
380afaad8f sparse_set: suppress a warning from the linter, I know what I'm doing here 2025-01-21 11:10:32 +01:00
Michele Caini
7607c0dc4c clang-tidy: refine config for cppcoreguidelines-rvalue-reference-param-not-moved 2025-01-21 11:03:56 +01:00
Michele Caini
cdfb1828dd compressed_pair: noexcept swap 2025-01-21 11:01:12 +01:00
Michele Caini
8bf0f622e6 meta: drop redundant member init 2025-01-21 10:59:04 +01:00
Michele Caini
0e15186d42 meta: drop redundant inline 2025-01-21 10:54:34 +01:00
Michele Caini
09c11f9324 meta: drop redundant member init 2025-01-21 10:53:29 +01:00
Michele Caini
4ac175ca81 meta: drop redundant member init 2025-01-21 10:52:47 +01:00
Michele Caini
6304cf6f1e compressed_pair: drop redundant inline 2025-01-21 10:50:15 +01:00
Michele Caini
8ec65feddd type_info: const correctness 2025-01-21 10:49:48 +01:00
Michele Caini
d99bb004d7 test: const correctness 2025-01-21 10:49:32 +01:00
Michele Caini
50d23e2413 mixin: a comment for the future me 2025-01-21 10:26:35 +01:00
Michele Caini
b0a102b9fb doc: typo 2025-01-21 10:26:04 +01:00
Michele Caini
15c3ac27e9 storage: emplace/insert -> generate, for storage entity 2025-01-20 14:10:00 +01:00
Michele Caini
fae3345f79 storage: shrink_to_fit storage pools 2025-01-20 11:35:55 +01:00
Michele Caini
d1abac2adc config: move ENTT_CONSTEVAL to config.h 2025-01-17 15:31:37 +01:00
Dominic Koepke
12b0a8650d hashed string: apply consteval on appropriate basic_hashed_string related functions (#1203) 2025-01-17 15:18:26 +01:00
Michele Caini
3c5e77e4dd sigh: support uninitialized sink objects 2025-01-16 15:28:04 +01:00
Michele Caini
073bd70c46 *: updated TODO 2025-01-15 15:43:23 +01:00
Michele Caini
65bf5cfe2d meta: allow attaching const values of non-Type type to meta types 2025-01-15 15:43:13 +01:00
Michele Caini
b834148a76 view: internal changes 2025-01-14 13:59:10 +01:00
Michele Caini
15934056da build: remove WORKSPACE file 2025-01-14 12:24:40 +01:00
Michele Caini
a6306e1736 build: ENTT_INSTALL - close #1204 2025-01-13 12:24:49 +01:00
Michele Caini
32fbc8a51f any/meta_any: safe but unspecified state after self move - close #1206
Related Work Items: #1
2025-01-13 12:13:06 +01:00
Michele Caini
6f5e9351ed meta: no context-less meta_any from meta objects as it ought to be 2025-01-10 14:45:43 +01:00
Michele Caini
0b97fa5036 meta: rollback recent changes and use only basic context types internally 2025-01-10 14:44:13 +01:00
Michele Caini
c413ebd628 meta: default context on meta objects to avoid pointless checks 2025-01-10 14:41:39 +01:00
Michele Caini
3de8a04ebc meta: check on key functions rather than on context validity 2025-01-10 14:40:43 +01:00
Michele Caini
521f72067c meta: check the right function 2025-01-10 14:04:25 +01:00
Michele Caini
c158c2d839 meta: get rid of unnecessary checks 2025-01-09 17:37:29 +01:00
Michele Caini
2e201c3a60 meta: minor changes to prepare to cleanup further 2025-01-09 16:05:11 +01:00
Michele Caini
167ec4b310 meta: safer meta_type 2025-01-08 08:49:59 +01:00
Michele Caini
49f4eab570 test: minor changes 2025-01-07 18:16:13 +01:00
Michele Caini
acdd212767 meta: safer empty meta_data/meta_func 2025-01-07 09:14:04 +01:00
Michele Caini
7262858a76 type traits: cleanup 2024-12-29 11:14:10 +01:00
Michele Caini
eccac01899 storage: try to address wrong code coverage results by lcov2 2024-12-23 12:37:42 +01:00
Michele Caini
15fe6fed49 storage: please the code coverage tool 2024-12-23 09:19:23 +01:00
Michele Caini
0d7c437153 gh: try to run lcov2 and gtest together 2024-12-23 08:55:30 +01:00
Michele Caini
fa93eb956a doc: view::operator| for storage types 2024-12-20 10:00:13 +01:00
Michele Caini
47d0a4328f view: join -> operator| 2024-12-20 09:59:55 +01:00
Michele Caini
abbb3bf7ff type_traits: internal changes 2024-12-19 14:06:11 +01:00
Michele Caini
b465e5531e type_traits: refine has_void_element_type 2024-12-19 12:02:53 +01:00
Michele Caini
3ed80499f7 type_traits: rollback recent changes to decouple checks again 2024-12-19 12:01:30 +01:00
Michele Caini
6601ebeb60 type_traits: try to make all compilers happy 2024-12-19 11:54:58 +01:00
Michele Caini
7a59e147d2 view: constrained storage type 2024-12-19 11:41:10 +01:00
Michele Caini
86e558b6ce type_traits: refine is_iterator and is_equality_comparable 2024-12-19 11:38:34 +01:00
Michele Caini
326bb9879d test: minor changes 2024-12-18 17:13:55 +01:00
Michele Caini
60a707f9e4 view: use operator| within join 2024-12-18 17:11:22 +01:00
Michele Caini
5428dfaf69 doc: minor changes 2024-12-18 17:07:27 +01:00
Terens
832ff4afcb view: joining views with storages (#1196) 2024-12-18 16:47:28 +01:00
Michele Caini
da8e4bfd99 meta: standalone meta_data/meta_func 2024-12-17 09:37:58 +01:00
Michele Caini
b5b3106d35 test: ops, fixed the path 2024-12-16 11:53:03 +01:00
Michele Caini
3ae5cbbc3a view: refine fallback mechanism (with non-regression lib tests) 2024-12-16 11:42:44 +01:00
Michele Caini
e343a24966 meta: if it's initialized, it's ok 2024-12-13 14:46:05 +01:00
Michele Caini
c8000f3e87 meta: cleanup (we know it's non-const already) 2024-12-13 14:40:58 +01:00
Paolo Monteverde
5ef4a2f22a meta: avoid pointless lookups in try_cast (#1201) 2024-12-13 14:35:05 +01:00
Michele Caini
abdbddf77f test: try to trick clang 2024-12-12 14:16:44 +01:00
Michele Caini
295bbb209f test: try to please the linter 2024-12-12 14:12:03 +01:00
Michele Caini
ec2443e5c4 meta: no else-after-return statements 2024-12-12 12:38:39 +01:00
Michele Caini
b353f2ed7f meta: explicit checks 2024-12-12 12:38:11 +01:00
Michele Caini
b879c722f4 test: linter changes 2024-12-12 09:24:49 +01:00
Michele Caini
b9c50bc731 test: [[nodiscard]] to make the linter happy 2024-12-12 09:16:38 +01:00
Michele Caini
41c41e9e30 meta: suppress wrong linter warning 2024-12-12 09:15:34 +01:00
Michele Caini
158ff57fdd gh: updated build workflow 2024-12-12 09:12:08 +01:00
Michele Caini
626a8cfb50 test: prepare tests for views across boundaries 2024-12-11 16:34:41 +01:00
Michele Caini
84583d9a40 doc: make doxygen happy again 2024-12-11 16:34:34 +01:00
Michele Caini
73ff634a75 sparse set: I don't even read my own comments sometimes :) 2024-12-10 16:36:39 +01:00
Michele Caini
fe5797e78f sparse set: internal changes 2024-12-10 16:34:44 +01:00
Michele Caini
c3b66bf091 sparse set: prepare for improved shrink_to_fit 2024-12-10 16:34:44 +01:00
Michele Caini
f1356db111 view: internal changes 2024-12-10 16:34:44 +01:00
Michele Caini
2dcc9bd0aa view: internal changes 2024-12-10 16:34:44 +01:00
Michele Caini
18e97db6b4 *: updated TODO 2024-12-10 16:34:44 +01:00
Michele Caini
b971116c21 meta_any: transfer ownership with from_void - close #1179 2024-12-10 16:34:44 +01:00
Michele Caini
adcf929fdf any: cleanup further 2024-12-10 16:34:44 +01:00
Michele Caini
8e792df7f7 any: never initialize info with void 2024-12-10 16:34:44 +01:00
Michele Caini
71f55e7ab1 any: internal changes 2024-12-10 16:34:44 +01:00
Michele Caini
809a8679a8 any/meta: cleanup 2024-12-10 16:34:44 +01:00
Michele Caini
36e2ac5c0b any: internal changes for clarity 2024-12-10 16:34:43 +01:00
Michele Caini
fd4dfbc328 any: minor changes 2024-12-10 16:34:43 +01:00
Michele Caini
5903894032 core: move any_policy to fwd.hpp 2024-12-10 16:34:43 +01:00
Michele Caini
4634130d4c doc: cleanup 2024-12-10 16:34:43 +01:00
Michele Caini
0f06314822 doc: cleanup 2024-12-10 16:34:43 +01:00
Michele Caini
beaec4c5fe meta: minor changes 2024-12-10 16:34:43 +01:00
Michele Caini
b480d8bc14 meta_any: cleanup 2024-12-10 16:34:43 +01:00
Michele Caini
be0d274255 meta: cleanup 2024-12-10 16:34:43 +01:00
Michele Caini
ee204b2f1d meta_any: no void vtable by default 2024-12-10 16:34:43 +01:00
Michele Caini
991b5176da meta: minor changes 2024-12-10 16:34:43 +01:00
Michele Caini
bd53a126bb doc: meta_any 2024-12-10 16:34:43 +01:00
Michele Caini
abc823303c meta_any: support taking ownership of passed in pointers 2024-12-10 16:34:43 +01:00
Michele Caini
9a19f9aa2f test: minor changes 2024-12-10 16:34:43 +01:00
Michele Caini
25e52cc1f8 doc: updated link - see #1185 2024-12-10 16:34:43 +01:00
Michele Caini
364ff53182 doc: updated ref to Friflo ECS - close #1197 2024-12-10 16:34:43 +01:00
Michele Caini
f6e1b774b0 sparse set: internal changes - close #1194 2024-12-10 16:34:08 +01:00
Michele Caini
49ba7ee208 meta: deprecate meta<T>(...) 2024-12-10 16:34:08 +01:00
Michele Caini
42d9628d39 meta: rework internals to work around a (maybe) compiler issue 2024-12-10 16:25:52 +01:00
Michele Caini
f3f9e9a8b9 test: use meta_factory<T> instead of meta<T> 2024-11-29 14:18:41 +01:00
Michele Caini
c3914e0181 *: updated TODO 2024-11-29 14:17:40 +01:00
Michele Caini
95397a8ea2 doc: meta<T> -> meta_factory<T> 2024-11-29 12:10:23 +01:00
Michele Caini
f330c305bb meta: self contained meta_factory, prepare to deprecate meta<T> 2024-11-29 12:08:09 +01:00
Michele Caini
01940a5ee0 test: fixed typo 2024-11-28 17:59:05 +01:00
Michele Caini
60289fa846 test: meta_reset 2024-11-28 15:47:54 +01:00
Michele Caini
d84ebb4b0c test: meta<T>(...) 2024-11-28 09:51:08 +01:00
Michele Caini
9c4bfdef7c test: meta_factory::custom 2024-11-28 09:49:33 +01:00
Michele Caini
4bdda6c0cf test: meta_factory::traits 2024-11-27 17:01:03 +01:00
Michele Caini
388b39dfac test: meta_factory::func 2024-11-27 16:48:06 +01:00
Michele Caini
ede1dba441 test: avoid C-like arrays 2024-11-26 15:31:59 +01:00
Michele Caini
41ccbd6700 test: meta_factory::data 2024-11-26 14:52:55 +01:00
Michele Caini
3c6285fd77 test. meta_factory::data for values 2024-11-25 17:32:53 +01:00
Michele Caini
7baf2aad1e test: meta_factory::dtor 2024-11-25 16:59:45 +01:00
Michele Caini
bf9ff1a725 *: updated TODO 2024-11-25 16:59:29 +01:00
Michele Caini
cadf755d47 test: meta_factory::ctor 2024-11-22 15:22:40 +01:00
Michele Caini
df78fe739f test: internal changes 2024-11-22 14:30:35 +01:00
Michele Caini
550fe8bdeb test: meta_factory::conv 2024-11-22 13:54:30 +01:00
Michele Caini
84543d98c5 test: meta_factory::base 2024-11-22 11:02:47 +01:00
Michele Caini
5a502466aa test: meta_factory::type 2024-11-21 18:36:12 +01:00
Michele Caini
f0aff23469 test: clear context on exit for meta_factory tests 2024-11-21 18:35:19 +01:00
Michele Caini
f1bfbaa981 meta: test (and add a todo note to fix/revert) a corner case of meta_factory 2024-11-21 18:28:10 +01:00
Michele Caini
7052f2ec72 meta: fixed meta_factory default constructor 2024-11-20 18:24:28 +01:00
Michele Caini
6b26c8f37b *: updated TODO 2024-11-20 18:09:52 +01:00
Michele Caini
b337dd616f meta. prepare to test the factory and avoid future regressions 2024-11-20 18:09:34 +01:00
Michele Caini
edee42c11a doc: add a missing piece 2024-11-20 18:09:07 +01:00
Michele Caini
823d194217 any: avoid warnings due to unused attributes 2024-11-20 18:03:26 +01:00
Michele Caini
5cf82d9b3b any: try to trick code coverage tools 2024-11-20 16:48:44 +01:00
Michele Caini
e095c339e1 any: limit ownership construction to non-const pointers 2024-11-20 16:23:33 +01:00
Michele Caini
527ca23d9b any: cleanup/refine 2024-11-19 10:08:32 +01:00
Michele Caini
cad7f11146 doc: any 2024-11-18 14:25:25 +01:00
Michele Caini
5734063de2 any: support taking ownership of passed in pointers 2024-11-18 14:25:15 +01:00
Michele Caini
a7ba2235d6 view: parens as it should be 2024-11-15 18:43:52 +01:00
Michele Caini
0af6c13e40 test: minor changes 2024-11-15 18:34:03 +01:00
Michele Caini
bf66a6cf0e any: destruction support to in_situ-as-dynamic values 2024-11-15 18:32:40 +01:00
Michele Caini
e092ca72f7 *: cleanup TODO 2024-11-15 17:16:52 +01:00
Michele Caini
1997e3d4a1 any: first step to support in_situ-as-dynamic values 2024-11-15 17:16:43 +01:00
Michele Caini
575f06b4f2 reactive mixin: auto disconnection support 2024-11-15 12:40:26 +01:00
Michele Caini
a3d6e708de view: propagate tombstone check request to view iterator 2024-11-13 18:31:52 +01:00
Michele Caini
04e7d104be view: prepare to pass the tombstone check flag to base classes and iterators 2024-11-13 18:13:48 +01:00
Michele Caini
d60801f105 view: cleanup 2024-11-13 17:46:09 +01:00
Michele Caini
9e0ba70f11 *: updated TODO 2024-11-13 17:36:34 +01:00
Michele Caini
d10eea8db8 entity: unspecified deletion policy (for future view optimizations) 2024-11-13 17:36:28 +01:00
Michele Caini
ae582973d0 view: refine ::find and internal asserts 2024-11-12 16:53:38 +01:00
Michele Caini
68c8fc95cf view: slightly improved storage view iterator 2024-11-12 16:39:57 +01:00
Michele Caini
1fd0a6f318 *: coding style 2024-11-12 16:36:33 +01:00
Michele Caini
e64c66ff77 test: internal changes 2024-11-11 08:56:44 +01:00
Michele Caini
ec9153fab6 meta: avoid warnings due to implicit conversions 2024-11-11 08:32:40 +01:00
Michele Caini
0097d7b96a meta: avoid warnings due to unused expressions 2024-11-11 08:31:03 +01:00
Michele Caini
6d3564ce53 iwyu: update to latest 2024-11-11 08:19:33 +01:00
Michele Caini
1c8b9099fd any: perf improvements 2024-11-08 15:35:20 +01:00
Michele Caini
b2110f08e9 meta: deprecate meta_any::data() 2024-11-07 14:27:27 +01:00
Michele Caini
83ef830329 meta: use policy for internal checks on constness 2024-11-07 14:14:32 +01:00
Michele Caini
7c920d64f4 meta: further reduce vtable size 2024-11-06 16:50:57 +01:00
Michele Caini
0177383d5b meta: internal changes 2024-11-06 15:32:57 +01:00
Michele Caini
a217be7d44 meta: meta_any vtable cleanup 2024-11-06 15:31:38 +01:00
Michele Caini
afb6bfc731 meta: minor changes 2024-11-06 14:57:26 +01:00
Michele Caini
50216fb896 meta: deprecate meta_any_policy and meta_any::policy 2024-11-06 13:56:35 +01:00
Michele Caini
044609a536 meta: suppress reorder-ctor warnings 2024-11-05 14:23:32 +01:00
Michele Caini
6b514c3e28 meta_any: internal change to avoid using data() if not needed 2024-11-05 12:17:32 +01:00
Michele Caini
0c5ca2f544 meta_any: replace ::owner (never released yet) with ::base 2024-11-05 12:16:53 +01:00
Michele Caini
0181c2c604 meta_any: rework vtable 2024-11-04 12:06:00 +01:00
Michele Caini
6cceb9082d meta: removed ::rebind function (soft breaking change, sort of internal) 2024-11-04 09:05:02 +01:00
Timothy Langlois
3b78610a38 hashed_string: remove extra spaces (#1190) 2024-11-04 08:14:09 +01:00
Michele Caini
dbee03f462 meta_handle: let meta_any decide on ctx-less constructors 2024-11-02 00:07:29 +01:00
Michele Caini
d3f6b04200 meta_handle: let meta_any decide on its default constructor 2024-11-02 00:05:50 +01:00
Michele Caini
d2f03a3051 meta: avoid init meta ctx for meta iterators 2024-11-02 00:03:07 +01:00
Michele Caini
ed7b1ab8a1 meta: use the vtable to implement operator bool 2024-11-02 00:00:41 +01:00
Michele Caini
331b82ed8e meta: use the context to implement operator bool 2024-11-01 23:58:37 +01:00
Michele Caini
528302641a any (breaking changes):
* added empty/embedded/dynamic policies
* replaced owner policy with owner function
2024-10-31 11:39:03 +01:00
Michele Caini
b900495284 meta: meta_prop_node is no longer needed 2024-10-30 12:00:11 +01:00
Michele Caini
8e8c613616 meta: meta_prop is no longer needed 2024-10-30 11:59:44 +01:00
Michele Caini
a7e294e97f natvis: meta_custom 2024-10-29 11:49:08 +01:00
Michele Caini
faafc30abe *: updated TODO 2024-10-29 11:48:54 +01:00
Michele Caini
7bc9579cf1 meta factory: drop deprecated functions 2024-10-29 00:11:16 +01:00
Michele Caini
0302caa6b3 meta type: drop deprecated functions 2024-10-29 00:10:26 +01:00
Michele Caini
4e9433542b *: updated TODO 2024-10-28 17:14:47 +01:00
Michele Caini
a0f906d6bf test: delete meta_prop tests 2024-10-28 17:14:18 +01:00
Michele Caini
d893af429f test: cleanup/review 2024-10-28 09:09:46 +01:00
Michele Caini
f6159bb8d1 meta data: drop deprecated functions 2024-10-28 09:07:31 +01:00
Michele Caini
42bae0d8b3 doc: do not refer to meta_prop anymore 2024-10-27 22:37:02 +01:00
Michele Caini
84e6a5fa64 meta: do not forward meta_prop anymore 2024-10-27 19:29:44 +01:00
Michele Caini
6240a26685 *: updated TODO 2024-10-27 19:29:15 +01:00
Michele Caini
da1892523e build: bazel test meta 2024-10-27 18:55:17 +01:00
Michele Caini
0f19c8647b natvis: meta 2024-10-27 18:54:48 +01:00
Michele Caini
fd1b66a38f doc: updated links 2024-10-27 18:53:34 +01:00
Michele Caini
eddb81ff9b meta. mark types as deprecated to simplify the cleanup 2024-10-26 19:40:35 +02:00
Michele Caini
1085023c51 test: suppress warning (unused variable) 2024-10-26 19:27:13 +02:00
Michele Caini
12125e6e60 test: remove tests no longer needed 2024-10-26 19:26:54 +02:00
Michele Caini
c76fff8649 test: internal changes 2024-10-26 19:23:33 +02:00
Michele Caini
8507164ca3 test: prepare to cleanup deprecated stuff 2024-10-26 19:23:09 +02:00
Michele Caini
c42a6880e0 meta func: drop deprecated functions 2024-10-26 19:22:43 +02:00
Michele Caini
15b9871205 observer: so long, and thanks for all the fish 2024-10-25 13:01:06 +02:00
Michele Caini
e13c87a447 any: skip vtable on move for reference types 2024-10-25 09:20:30 +02:00
Michele Caini
6405aece3a sparse set: drop deprecated members 2024-10-24 12:51:17 +02:00
Michele Caini
bf0bde8a64 organizer: drop deprecated members 2024-10-24 08:31:41 +02:00
Michele Caini
0a3c2cc400 update single include file 2024-10-23 16:06:27 +02:00
Michele Caini
87283afd80 now working on version 3.15.0 2024-10-23 15:09:34 +02:00
Michele Caini
4a2d1a8541 test: few last changes to please the linter 2024-10-18 09:15:40 +02:00
Michele Caini
59061ec8d3 test: cleanup 2024-10-17 09:35:23 +02:00
Michele Caini
c32b9032fb test: try to please the linter 2024-10-17 09:09:23 +02:00
Michele Caini
48583b6f55 doc: updated links 2024-10-17 09:09:01 +02:00
Michele Caini
68adf8615c view: return the best size for entity storage from size_hint 2024-10-17 09:08:50 +02:00
Michele Caini
8c4a2f79ab doc: updated links 2024-10-16 11:11:56 +02:00
Michele Caini
1098b61060 poly: refined inheritance support - close #1184 2024-10-16 11:05:53 +02:00
Michele Caini
2f8bdb2505 *: updated TODO 2024-10-14 12:32:20 +02:00
Michele Caini
e50f20ebf4 test: minor changes to please the linter 2024-10-14 12:32:08 +02:00
Michele Caini
6b20a3d3ea test: linter directives on deprecated type (no waste of time here) 2024-10-11 19:06:52 +02:00
Michele Caini
aecdb84606 delegate: internal const correctness on return types 2024-10-11 19:06:18 +02:00
Michele Caini
031f340d62 test: make the linter happy 2024-10-11 14:05:56 +02:00
Michele Caini
f9a509a82b test: typo 2024-10-11 13:57:43 +02:00
Michele Caini
3f23bfed61 meta: avoid warnings due to deprecated meta_prop class - close #1182 2024-10-11 13:56:44 +02:00
Michele Caini
771603f8f6 test: minor changes 2024-10-10 09:16:17 +02:00
Michele Caini
331a8257be sigh mixin: const correctness for the return type of ::emplace 2024-10-10 09:14:15 +02:00
Michele Caini
c97e3d4f96 meta: remove const qualifiers from non-reference/non-pointer types 2024-10-10 09:01:19 +02:00
Michele Caini
5388c9b432 any: remove const qualifiers from non-reference/non-pointer types 2024-10-10 09:00:08 +02:00
Michele Caini
95f84d1488 test: let the linter know that things are done on purpose sometimes 2024-10-10 08:48:19 +02:00
Michele Caini
e904300c1f mixin: minor changes 2024-10-10 08:21:06 +02:00
Michele Caini
8df81238e7 test: make the linter happy 2024-10-09 17:10:13 +02:00
Michele Caini
8c6bbda4db test: cleanup 2024-10-09 08:30:13 +02:00
Michele Caini
e1bbbee858 test: avoid using collector directly 2024-10-09 08:29:57 +02:00
Michele Caini
88c77f87fb test: cleanup 2024-10-09 08:29:37 +02:00
Michele Caini
c5e98d3287 test: avoid short names 2024-10-08 17:13:41 +02:00
Michele Caini
b967dbcb90 test: cleanup 2024-10-08 17:11:18 +02:00
Michele Caini
b7cd6fadf4 test: avoid short names 2024-10-08 17:09:33 +02:00
Michele Caini
26000fa019 test: avoid short names 2024-10-08 17:08:54 +02:00
Michele Caini
b8977df86f test: explicit checks 2024-10-08 17:07:35 +02:00
Michele Caini
2cd0ef323d test: avoid short names 2024-10-08 17:06:24 +02:00
Michele Caini
30b685411b test: avoid short names 2024-10-08 09:42:05 +02:00
Michele Caini
a15751a33b test: avoid short names 2024-10-08 09:41:46 +02:00
Michele Caini
8837c7cc1f test: minor changes 2024-10-08 09:41:22 +02:00
Michele Caini
d9ad574a69 storage: internal changes 2024-10-08 09:41:11 +02:00
Michele Caini
d87b7a928d test: const correctness 2024-10-07 15:46:16 +02:00
Michele Caini
a3f449408a test: avoid short names 2024-10-07 15:46:04 +02:00
Michele Caini
53cb4864ab test: make functions static as needed 2024-10-07 15:45:46 +02:00
Michele Caini
aec84b6598 test: avoid short names 2024-10-07 15:45:22 +02:00
Michele Caini
5b7402468f test: avoid short names 2024-10-07 12:56:25 +02:00
Michele Caini
688a1905d8 test: avoid short names 2024-10-07 12:56:09 +02:00
Michele Caini
f4e4dac376 test: use .data() if possible 2024-10-07 12:55:46 +02:00
Michele Caini
628261b5bf test: avoid invoking static members on instance 2024-10-07 10:46:23 +02:00
Michele Caini
2bb7b07191 test: explicit checks + braces 2024-10-07 10:46:00 +02:00
Michele Caini
b331e88b8a test: avoid short names 2024-10-07 10:45:36 +02:00
Michele Caini
9788a3e6c3 test: use data() as it ought to be 2024-10-07 10:45:17 +02:00
Michele Caini
341fd03dc8 test: avoid short names 2024-10-07 10:44:45 +02:00
Michele Caini
c40c2d41de test: const correctness 2024-10-07 10:44:23 +02:00
Michele Caini
8ca5a6c305 meta: explicit checks 2024-10-07 10:43:20 +02:00
Michele Caini
6738e3e77d view: avoid implicit conversions 2024-10-07 10:42:53 +02:00
Michele Caini
36896978b1 view: avoid else after return 2024-10-04 15:55:42 +02:00
Michele Caini
6db5d78e87 meta: typo 2024-10-04 15:09:20 +02:00
Michele Caini
414365c770 meta: avoid implicit conversions 2024-10-04 15:08:13 +02:00
Michele Caini
8cfd635f1d meta: typo 2024-10-04 12:29:36 +02:00
Michele Caini
1de5b066fd meta: avoid implicit conversions 2024-10-04 12:26:29 +02:00
Michele Caini
02f8f00340 test: identifier length 2024-10-04 12:25:07 +02:00
Michele Caini
44ee192b41 meta: explicit check 2024-10-04 12:21:11 +02:00
Michele Caini
00c2ddb300 test: const correctness 2024-10-04 12:18:27 +02:00
Michele Caini
a9e575ee8b any: avoid implicit conversions 2024-10-04 12:16:17 +02:00
Michele Caini
76f988e975 test: identifier length 2024-10-04 12:13:43 +02:00
Michele Caini
a75f864c4f test: isolate declarations 2024-10-04 12:12:58 +02:00
Michele Caini
d8a98fe5f0 linter: check options for readability-magic-numbers 2024-10-04 12:10:16 +02:00
Michele Caini
a348ff7dd1 storage: explicit checks 2024-10-04 11:50:24 +02:00
Michele Caini
1620b63a2f sparse set: avoid implicit conversions 2024-10-04 11:48:36 +02:00
Michele Caini
a70177a725 linter: try to make readability-function-cognitive-complexity ignore gtest 2024-10-04 11:46:27 +02:00
Michele Caini
87ac4c0121 registry: avoid else after return 2024-10-04 11:39:23 +02:00
Michele Caini
14a4788417 registry: avoid implicit bool conversion 2024-10-04 11:39:07 +02:00
Michele Caini
5f18f90571 linter: set MinimumVariableNameLength for readability-identifier-length 2024-10-04 11:35:00 +02:00
Michele Caini
cc79817af3 any: avoid else after return 2024-10-04 11:12:20 +02:00
Michele Caini
f39c68793e sigh: drop redundant initializer 2024-10-04 11:10:52 +02:00
Michele Caini
28adaa5a8b registry: avoid else after return 2024-10-04 11:03:34 +02:00
Michele Caini
cee7c799ce sparse set: make it clear the elem is a pointer 2024-10-04 11:03:23 +02:00
Michele Caini
47ff28d92d sparse set: drop redundant access specifier 2024-10-04 11:02:29 +02:00
Michele Caini
f50750bafd linter: set MinimumVariableNameLength for readability-identifier-length 2024-10-04 10:59:28 +02:00
Michele Caini
fc10b39dc4 linter: disable a couple of checks that I don't like much 2024-10-04 10:48:59 +02:00
Michele Caini
035e17ff1d linter: avoid const params in decls 2024-10-04 10:39:55 +02:00
Michele Caini
41f1b4e97b build: fixed typo in the clang-tidy file - close #1178
Related Work Items: #11, #1178
2024-10-04 10:21:08 +02:00
Michele Caini
1f474e785a test: cleanup 2024-10-03 08:25:26 +02:00
Michele Caini
bf0e78fef9 test: avoid magic numbers 2024-10-03 08:21:24 +02:00
Michele Caini
83ebbea352 mixin: use auto to please the linter 2024-10-03 08:20:27 +02:00
Michele Caini
79e1a9a3d9 organizer: instruct the linter on known uses of C-style arrays 2024-10-03 08:19:34 +02:00
Michele Caini
c9d7feccfa doc: more about the reactive mixin 2024-10-02 10:46:14 +02:00
Michele Caini
4f0b7d31dd mixin: full support for derived registry types 2024-10-02 10:31:07 +02:00
Michele Caini
9eef230340 mixin: internal changes 2024-10-02 10:30:23 +02:00
Michele Caini
69aeeeee30 *: updated TODO 2024-10-01 08:58:09 +02:00
Michele Caini
734dfa2c6f doc: minor changes 2024-10-01 08:57:28 +02:00
Michele Caini
61b5c193e8 sigh mixin: operator bool and registry functions 2024-10-01 08:56:58 +02:00
Michele Caini
84308361ae observer: deprecated, use reactive mixin instead 2024-09-30 10:27:52 +02:00
Michele Caini
db22dd8e69 meta: suppress (wrong) warnings by g++ 2024-09-30 09:32:07 +02:00
Michele Caini
805cbf9c79 reactive mixin: return *this from on_<op> functions 2024-09-30 09:23:28 +02:00
Michele Caini
50e41076c5 doc: minor changes 2024-09-28 19:13:04 +02:00
Michele Caini
1a95c9d03e test: suppress warning due to unused variable 2024-09-28 19:12:36 +02:00
Michele Caini
0b01e14f01 doc. minor changes 2024-09-28 19:10:34 +02:00
Michele Caini
445b11e88c doc: minor changes 2024-09-28 19:10:14 +02:00
Michele Caini
c22224692d doc: reactive mixin/storage 2024-09-28 19:08:45 +02:00
Michele Caini
0207ee7624 *: updated TODO 2024-09-27 15:22:47 +02:00
Michele Caini
84cab81c40 test: reactive mixin view functions 2024-09-27 15:22:28 +02:00
Michele Caini
4859f41c24 test: reactive mixin and entity lifecycle (managed vs standalone) 2024-09-27 13:43:00 +02:00
Michele Caini
1de4b20d1f test: reactive mixin and custom allocators 2024-09-26 15:09:48 +02:00
Michele Caini
2ffd38a355 test: registry and custom registry support for reactive mixin 2024-09-26 10:57:42 +02:00
Michele Caini
ea660aa466 test: reactive mixin on_destroy 2024-09-25 17:20:15 +02:00
Michele Caini
f9fd8432fd test: reactive mixin on_update 2024-09-25 14:59:18 +02:00
Michele Caini
f28917aec7 test: reactive mixin on_construct 2024-09-25 08:37:25 +02:00
Michele Caini
96480cdd54 test: reactive mixin with throwing allocator 2024-09-24 15:08:58 +02:00
Michele Caini
55b91798c3 test: reactive mixin swap 2024-09-24 14:15:24 +02:00
Michele Caini
4b9be6ddb7 test: reactive mixin move 2024-09-24 08:57:28 +02:00
Michele Caini
d6f4bc9909 test: reactive mixin ctors 2024-09-24 08:34:20 +02:00
Michele Caini
76eb4f4ded test: reactive mixin suite 2024-09-24 07:57:32 +02:00
Michele Caini
ff33c2fc02 test: reactive mixin tests setup 2024-09-23 12:16:51 +02:00
Michele Caini
401a8cbb6a *: updated TODO 2024-09-23 12:15:08 +02:00
Michele Caini
a329659e2f test: cleanup 2024-09-23 12:13:06 +02:00
Michele Caini
8efd951561 reactive mixin: precooked storage_type specialization 2024-09-23 10:45:34 +02:00
Michele Caini
911e0d2900 test: shared basic_custom_registry type 2024-09-23 10:13:24 +02:00
Michele Caini
d4f2651e5f reactive mixin: minor changes 2024-09-23 10:12:46 +02:00
Michele Caini
a74a0e4f4e test: refine throwing_allocator impl 2024-09-20 15:16:48 +02:00
Michele Caini
f51352467d reactive mixin: template keyword for g++ although not necessary 2024-09-20 10:16:37 +02:00
Michele Caini
9fafb83a41 reactive mixin: try to make all compilers happy 2024-09-20 09:24:01 +02:00
Michele Caini
e1e1d37506 mixin: avoid shadow warnings/errors 2024-09-20 08:27:37 +02:00
Michele Caini
f8eb94c7ad reactive mixin: const correctness 2024-09-19 14:17:28 +02:00
Michele Caini
4f6246cd11 test: cleanup 2024-09-19 10:49:05 +02:00
Michele Caini
dc6a03e08c reactive mixin: first few fixes 2024-09-19 10:09:50 +02:00
Michele Caini
0bb22f273a entity: fwd declare basic_reactive_mixin and reactive_mixin 2024-09-18 16:51:52 +02:00
Michele Caini
bb6304ecac mixin: basic_reactive_mixin draft 2024-09-18 16:51:00 +02:00
Michele Caini
a46905b2d3 test: minor changes 2024-09-17 17:13:40 +02:00
Michele Caini
a5ce90b268 core: noexcept-ness review 2024-09-17 11:12:10 +02:00
Michele Caini
a92be68bf7 signal: noexcept-ness review 2024-09-17 11:11:53 +02:00
Michele Caini
13223125d7 resource: noexcept-ness review 2024-09-17 11:11:38 +02:00
Michele Caini
9819234238 process: noexcept-ness review 2024-09-17 11:11:24 +02:00
Michele Caini
83641a997d graph: noexcept-ness review 2024-09-17 11:11:15 +02:00
Michele Caini
9907a84b26 entity: noexcept-ness review 2024-09-17 11:10:16 +02:00
Michele Caini
24eed3da7a container: noexcept-ness review 2024-09-17 11:09:44 +02:00
Michele Caini
56f83640a0 storage: let the linter know that I know what I am doing 2024-09-17 09:23:48 +02:00
Michele Caini
e8d007a970 test: make the linter happy 2024-09-17 09:02:02 +02:00
Michele Caini
6e128c47f5 view: minor changes 2024-09-17 08:05:48 +02:00
Michele Caini
9105f742dd view: misplaced assert 2024-09-16 18:18:49 +02:00
Michele Caini
57b46385a9 test: implicit noexcept-ness for dtors 2024-09-16 17:19:05 +02:00
Michele Caini
3f8efac37d signal: implicit noexcept-ness for dtors 2024-09-16 17:18:58 +02:00
Michele Caini
33f00f400b resource: implicit noexcept-ness for dtors 2024-09-16 17:18:52 +02:00
Michele Caini
044bfeefba entity: implicit noexcept-ness for dtors 2024-09-16 17:18:44 +02:00
Michele Caini
21829cc27f process: implicit noexcept-ness for dtors 2024-09-16 17:18:35 +02:00
Michele Caini
c742fcf371 meta: implicit noexcept-ness for dtors 2024-09-16 17:18:29 +02:00
Michele Caini
55f2c0fdc1 locator: implicit noexcept-ness for dtors 2024-09-16 17:18:18 +02:00
Michele Caini
aa0e16fc93 graph: implicit noexcept-ness for dtors 2024-09-16 17:18:09 +02:00
Michele Caini
01fa3a06a7 core: implicit noexcept-ness for dtors 2024-09-16 17:18:01 +02:00
Michele Caini
5bb5f396ed container: implicit noexcept-ness for dtors 2024-09-16 17:17:53 +02:00
Michele Caini
ef552922b3 doc: updated entity.md 2024-09-16 11:13:44 +02:00
Michele Caini
19da9d2358 test: use sparse set generic bind function 2024-09-16 10:56:18 +02:00
Michele Caini
893eb8fabb registry: pass *this directly to sparse set bind function 2024-09-16 10:55:51 +02:00
Michele Caini
9630977c2d sparse set: generic bind function 2024-09-16 10:55:15 +02:00
Michele Caini
dce9abb2f0 doc: more about signals - close #1169 2024-09-13 12:45:18 +02:00
Michele Caini
d62204df4e group: impressed, cannot get all compilers to agree :) 2024-09-12 18:12:45 +02:00
Michele Caini
5f1a14cd43 registry: defaulted trivial destructor with implicit noexcept specification 2024-09-12 18:05:49 +02:00
Michele Caini
80f12a7ac2 group: implicit noexcept specification 2024-09-12 18:05:09 +02:00
Michele Caini
84f8b5d5bb observer: implicit noexcept specification 2024-09-12 18:04:45 +02:00
Michele Caini
24751f6158 observer: defaulted trivial destructor 2024-09-12 17:51:40 +02:00
Michele Caini
fd3e275d86 sigh mixin: avoid nonsense warnings with g++ 2024-09-12 17:50:59 +02:00
Michele Caini
1175bdca62 meta: move node as it ought to be 2024-09-12 17:46:12 +02:00
Michele Caini
8899c9df7e group: defaulted trivial destructor 2024-09-12 17:45:28 +02:00
Michele Caini
3d10b764eb sigh mixin: expect noexcept move ctor/op 2024-09-12 17:38:51 +02:00
Michele Caini
33578dd6da meta: [[nodiscard]] 2024-09-12 13:55:24 +02:00
Michele Caini
2ab5e4deb3 natvis: meta_prop 2024-09-12 11:59:02 +02:00
Michele Caini
a4a87995af meta: reduce indirection for meta_prop 2024-09-12 10:16:58 +02:00
Michele Caini
b5fbc68baa sigh mixin: refine auto signals 2024-09-11 10:46:12 +02:00
Terens
42a1905690 sigh mixin: automatic signal registration for components (#1168) 2024-09-11 08:29:20 +02:00
Michele Caini
6d03f4d6f2 test: avoid unused variables 2024-09-11 08:29:10 +02:00
Michele Caini
e4ba5c31d2 meta: deprecate meta properties in favor of custom data 2024-09-10 16:12:28 +02:00
Michele Caini
355010df3b doc: refine mesta custom doc 2024-09-10 16:04:59 +02:00
Michele Caini
dc7ab63b6f view: drop pointless check 2024-09-10 15:46:08 +02:00
Michele Caini
5ecd5739d0 registry: cleanup/improvement 2024-09-10 15:45:47 +02:00
Michele Caini
8fd4cf7c45 registry: storage reset function - close #1173 2024-09-09 12:42:52 +02:00
Michele Caini
6f7dc0558b *: TODO 2024-09-09 12:11:42 +02:00
Michele Caini
eabe7be7da resource: swap 2024-09-09 12:02:33 +02:00
Michele Caini
7ad5eb8c17 *: updated TODO 2024-09-06 15:48:07 +02:00
Michele Caini
b485d72ec7 signal: swap based move assignment operators 2024-09-06 15:48:07 +02:00
Michele Caini
63d6db37d8 scheduler: swap based move assignment operator 2024-09-06 15:48:07 +02:00
Michele Caini
3b4864aeae registry: swap based move assignment operator 2024-09-06 15:48:07 +02:00
Michele Caini
d40e5f3dd5 table: swap based move assignment operator 2024-09-06 15:48:07 +02:00
Michele Caini
5bb706ceea observer: propagate noexcept-ness 2024-09-06 15:47:26 +02:00
Michele Caini
0966cc9063 entity: try to get around an issue with g++9 2024-09-06 15:47:14 +02:00
Michele Caini
5e1c44195a registry: propagate noexcept(false) to registry destructor 2024-09-06 15:40:37 +02:00
Michele Caini
2147e4bbb2 storage: refine noexcept-ness 2024-09-06 15:30:03 +02:00
Michele Caini
9f36bec353 sparse_set/storage: swap based move assignment operator 2024-09-06 15:24:21 +02:00
Michele Caini
c3826c78aa mixin: refine noexcept-ness 2024-09-06 14:59:53 +02:00
Michele Caini
d0a23fdf49 sparse_set/storage: make it clear that dtor and move op are not noexcept 2024-09-06 14:43:38 +02:00
Michele Caini
a39fd8548d config: wrap macro arguments to avoid warnings 2024-09-06 11:11:55 +02:00
Michele Caini
9c9816110e view: clear dead path 2024-09-05 16:04:30 +02:00
Michele Caini
156bc470ce natvis: update views for meta containers 2024-09-05 12:31:09 +02:00
Michele Caini
50db6f2193 meta: cleanup/reuse internal code 2024-09-04 14:29:33 +02:00
Michele Caini
628bfbd4ca natvis: meta 2024-09-03 15:11:41 +02:00
Michele Caini
ea4470b4a7 meta: minor changes 2024-09-02 15:49:16 +02:00
Michele Caini
90c975904f meta: refine and share find_member/find_overload 2024-09-02 15:05:11 +02:00
Michele Caini
27f7e43b05 meta: internal changes 2024-08-27 16:27:57 +02:00
Michele Caini
c909b40b5a *: updated TODO 2024-08-27 16:27:50 +02:00
Michele Caini
07e1ac5350 meta: asserts to prevent future errors 2024-08-27 16:27:01 +02:00
Michele Caini
c3758edb83 meta: cleanup meta_range impl 2024-08-27 16:26:25 +02:00
Michele Caini
328286d201 meta: minor changes 2024-08-27 16:16:39 +02:00
Michele Caini
8d149ecfc3 meta: minor changes 2024-08-27 15:25:02 +02:00
Michele Caini
c6479e06ae build: clang-format 2024-08-27 15:24:47 +02:00
Michele Caini
eee348f6b1 meta: reduce cost of meta base 2024-08-27 12:28:58 +02:00
Michele Caini
c7e8a998ff meta: reduce cost of meta func 2024-08-27 12:23:10 +02:00
Michele Caini
a4ad1ac3aa *: updated TODO 2024-08-27 12:22:57 +02:00
Michele Caini
942df26402 meta: reduce cost of meta data 2024-08-27 11:58:13 +02:00
Michele Caini
0fc5551998 *: updated TODO 2024-08-27 11:57:56 +02:00
Michele Caini
da615cc8e0 meta: reduce cost of meta prop 2024-08-27 11:19:15 +02:00
Michele Caini
9c6780c0a3 meta: reduce cost of meta conv 2024-08-27 11:05:54 +02:00
Michele Caini
0b5cb5ac84 *: updated TODO 2024-08-27 10:54:13 +02:00
Michele Caini
5da2c3c3c2 meta: reduce cost of meta ctor 2024-08-27 10:18:43 +02:00
Michele Caini
94735d04b3 meta: prepare meta_base/meta_conv/meta_ctor rework 2024-08-26 16:44:00 +02:00
Michele Caini
116bb91123 meta: prepare meta_prop rework 2024-08-26 15:11:20 +02:00
Michele Caini
010e5d4d3a meta: prepare meta_func rework 2024-08-26 14:57:12 +02:00
Michele Caini
0cf60202ff meta: prepare meta_data rework 2024-08-26 14:54:09 +02:00
Michele Caini
becfd01fd4 meta: cleanup 2024-08-26 14:28:22 +02:00
Michele Caini
8163a54b71 meta: prepare for further changes 2024-08-26 12:31:47 +02:00
Michele Caini
07a372e994 build: move googletest to FetchContent_MakeAvailable 2024-08-23 16:31:15 +02:00
Michele Caini
818e8755c2 config: try to make gcc happy 2024-08-20 14:44:22 +02:00
Michele Caini
4a4e2a38d1 delegate: refine function_pointer implementation - see #1167 (same issue) 2024-08-20 14:39:25 +02:00
Michele Caini
236747dd6b view: add missing typename (thanks as usual MSVC) 2024-08-20 14:36:58 +02:00
Michele Caini
1d288e9c42 meta: refine meta_function_helper implementation - close #1167 2024-08-20 14:34:25 +02:00
Michele Caini
9369c94df0 view: just a check to prevent future errors 2024-08-20 08:24:18 +02:00
Michele Caini
e33e1b5e2b view: avoid checking for filter validity 2024-08-19 18:41:18 +02:00
Chris Thrasher
80ee5e43c2 debug: print assertion message upon failure (#1164) 2024-08-19 16:02:54 +02:00
Michele Caini
3d89f04e95 doc: coding style 2024-08-03 11:33:33 +02:00
Chris Thrasher
1923028add doc: fix typos in README (#1162) 2024-08-03 11:31:52 +02:00
Michele Caini
97befad59a storage: suppress wrong use-after-move warning 2024-08-02 17:09:24 +02:00
Michele Caini
cdb1f23cbb *: linter directives 2024-08-02 17:06:50 +02:00
Michele Caini
c97a5f1b50 test: missing include 2024-08-02 17:03:23 +02:00
Michele Caini
a040353632 cmake: cleanup 2024-08-02 10:02:24 +02:00
Michele Caini
55615c118f test: cleanup 2024-08-02 09:24:02 +02:00
Michele Caini
93f7d516f2 view: drop useless [[nodiscard]] 2024-08-01 09:47:02 +02:00
Michele Caini
2c9aa2f544 meta: minor changes for coding style 2024-08-01 09:25:49 +02:00
Michele Caini
89767dc398 meta: update overloaded meta functions and user data (with non-regression tests) 2024-08-01 09:23:47 +02:00
Michele Caini
2310e5143a doc: hazel engine 2024-08-01 08:37:37 +02:00
Michele Caini
587a012ae1 view: further reduce instantiations 2024-07-31 10:50:23 +02:00
Michele Caini
bbd5676bad *: updated TODO 2024-07-31 10:08:25 +02:00
Michele Caini
eb6e29167d meta: avoid multiple lookups 2024-07-31 08:32:28 +02:00
Michele Caini
ebd077f71c natvis: meta custom views 2024-07-30 09:04:06 +02:00
Michele Caini
11e6b65108 meta: non-const access to meta custom 2024-07-30 08:49:30 +02:00
Michele Caini
1beac44dda meta: avoid overwriting traits/custom data on rebind 2024-07-29 15:48:45 +02:00
Michele Caini
7acd6e7f53 meta: check traits before saving 2024-07-29 12:56:49 +02:00
skypjack
8b0ef2b942 meta: support creating multiple types with different factories 2024-07-26 17:48:26 +02:00
skypjack
13284b7013 meta: store parent in the base factory 2024-07-26 17:23:00 +02:00
Michele Caini
689005d452 meta: guaranteed order on overloaded meta functions 2024-07-26 16:00:45 +02:00
Michele Caini
a8118c9389 meta: drop unnecessary attribute 2024-07-26 16:00:24 +02:00
Michele Caini
f4c6aa3f16 meta: avoid using meta type nodes in the base factory 2024-07-26 16:00:23 +02:00
Michele Caini
aa52c301fe meta: maybe unused context 2024-07-26 16:00:23 +02:00
Michele Caini
197ba29276 doc: updated meta.md 2024-07-26 16:00:23 +02:00
Michele Caini
af6dd28c66 meta: drop seek functions 2024-07-26 16:00:23 +02:00
Michele Caini
c91fa066af meta: drop unused data 2024-07-26 16:00:23 +02:00
Michele Caini
872f1c8dd9 meta: persistent traits in the factory class 2024-07-26 16:00:23 +02:00
Michele Caini
bb16607a7d meta: cleanup 2024-07-26 16:00:23 +02:00
Michele Caini
c94014d057 meta: persistent custom data in the factory class 2024-07-26 16:00:23 +02:00
Michele Caini
ca0202934c meta: persistent bucket in the factory class 2024-07-26 16:00:23 +02:00
Michele Caini
9824b44d72 meta: refine debug asserts 2024-07-26 16:00:23 +02:00
Michele Caini
5744119e6b meta: store owner in base factory directly 2024-07-26 16:00:23 +02:00
Michele Caini
4ed58d2230 core: instruct the linter 2024-07-26 09:17:24 +02:00
Michele Caini
4a681e4d00 test: suppress warnings 2024-07-26 09:14:38 +02:00
Michele Caini
85488bf757 *: updated TODO 2024-07-25 16:54:55 +02:00
Michele Caini
42a3d21638 meta: internal changes 2024-07-25 15:38:04 +02:00
Michele Caini
7b0b5ad779 meta: internal changes 2024-07-25 15:26:43 +02:00
Michele Caini
4e10e60c18 meta: rename things to avoid confusion 2024-07-25 12:32:11 +02:00
Michele Caini
8b5fd1ff2e meta: def initialize nodes 2024-07-25 12:30:14 +02:00
Michele Caini
f4ad2bd11b doc: meta custom data 2024-07-24 09:22:19 +02:00
Michele Caini
a5a03de483 test: code coverage 2024-07-23 11:58:14 +02:00
Michele Caini
ab56b89982 *: updated TODO 2024-07-23 11:47:03 +02:00
Michele Caini
ba63284d7a test: meta_custom 2024-07-23 11:40:14 +02:00
Michele Caini
a8f988893e meta: non-const meta_custom_node in meta_custom 2024-07-23 11:39:41 +02:00
Michele Caini
6695bdcb52 meta: fwd declare meta_custom 2024-07-23 11:39:05 +02:00
Michele Caini
baf582fce5 meta: checked meta_custom object 2024-07-23 10:30:46 +02:00
Michele Caini
e76d58c76c meta: prepare to a safer approach to custom data 2024-07-23 09:35:01 +02:00
Michele Caini
24808b11bb meta: support to user defined arbitrary data 2024-07-22 17:51:15 +02:00
Michele Caini
86226ed886 doc: cleanup 2024-07-22 17:32:31 +02:00
Michele Caini
04827bd8dc snapshot: use sparse set iterators 2024-07-22 17:23:12 +02:00
skypjack
8e6c72fdc1 group: [[nodiscard]] as needed 2024-07-19 17:56:34 +02:00
skypjack
b5e7e8c6f6 group: use override rather than virtual 2024-07-19 17:55:59 +02:00
skypjack
566e38b1d1 registry: cleanup to make all compilers happy again 2024-07-19 11:11:44 +02:00
skypjack
fb94471b8d adjacency_matrix: instruct the linter to trust me 2024-07-19 10:45:48 +02:00
skypjack
852aa2969d algorithm: instruct the linter to trust me 2024-07-19 10:44:24 +02:00
skypjack
0f64c2341e group: avoid pointer arithmetic 2024-07-19 10:44:08 +02:00
skypjack
a33212438a runtime_view: instruct the linter to trust me 2024-07-19 10:37:34 +02:00
skypjack
7431d93691 group: simplify non-critical virtual function 2024-07-19 10:31:30 +02:00
skypjack
d16fa5c605 meta: refine user defined traits support 2024-07-18 10:40:45 +02:00
skypjack
aee98b3c0a *: updated TODO 2024-07-17 17:01:26 +02:00
skypjack
2de5b6dd87 meta: self assignment for meta any 2024-07-17 16:11:13 +02:00
skypjack
7e9b27f9c1 any: self assignment 2024-07-17 16:10:58 +02:00
skypjack
f104c5de99 *: updated TODO 2024-07-17 16:10:44 +02:00
skypjack
0475a2404b view: [[nodiscard]] as needed 2024-07-17 14:56:44 +02:00
skypjack
94250af280 meta: internal changes 2024-07-17 14:56:27 +02:00
skypjack
0a00584cc0 doc: typo 2024-07-17 12:42:18 +02:00
skypjack
ff09c4bcbb delegate: suppress warnings due to unused variables 2024-07-17 09:14:56 +02:00
skypjack
dfc2c85be0 *: updated TODO 2024-07-17 09:14:37 +02:00
skypjack
5080289a73 meta: try to get around an issue with g++9 2024-07-16 16:13:56 +02:00
skypjack
34d4b50d0e doc: meta traits and meta properties 2024-07-16 15:38:57 +02:00
skypjack
5a2c092cde test: user defined meta traits 2024-07-16 15:38:43 +02:00
skypjack
56ef214bf3 meta: user defined traits for meta objects 2024-07-16 15:38:28 +02:00
skypjack
d331f750f0 meta: default constructors and member initializers 2024-07-16 15:37:05 +02:00
skypjack
784235c923 meta: factory class review 2024-07-15 17:46:53 +02:00
skypjack
dc070b04e2 meta: explicit return types on public members 2024-07-15 16:53:33 +02:00
skypjack
1ddc887ac4 meta: factory seek functions for meta data and meta functions 2024-07-15 12:43:11 +02:00
skypjack
fea24b1a22 meta: drop unnecessary enum class 2024-07-15 12:04:51 +02:00
skypjack
c4501d09b2 meta: reintroduce fast access to details 2024-07-15 11:44:21 +02:00
skypjack
7f4f779732 meta: seek on request 2024-07-15 11:07:05 +02:00
skypjack
88db7a8f09 meta: prepare to seeking functions 2024-07-15 11:02:04 +02:00
skypjack
36df10fd2c meta: cleanup 2024-07-14 15:35:49 +02:00
skypjack
e5ef550bcf meta: improved meta factory perf (quick access to details) 2024-07-14 15:25:20 +02:00
skypjack
d67f25c23a meta: drop unused function 2024-07-14 15:00:08 +02:00
skypjack
cf099b4511 meta: further reduce cost of creating meta objects 2024-07-14 14:55:51 +02:00
skypjack
8b7f5e5668 meta: reduce cost of constructing meta factories 2024-07-14 14:55:29 +02:00
skypjack
c2f87a70c5 meta: further reduce cost of creating meta func objects 2024-07-14 14:25:04 +02:00
skypjack
1bfe022a57 meta: further reduce cost of creating meta data objects 2024-07-14 12:43:24 +02:00
skypjack
0a996f5682 meta: move context to basic meta factory 2024-07-14 12:32:27 +02:00
Michele Caini
9e76dbce1d *: updated TODO 2024-07-11 16:39:20 +02:00
Michele Caini
86850ca65b meta: move info to basic_meta_factory 2024-07-11 16:32:57 +02:00
Michele Caini
ad320cfa2e meta: common basic_meta_factory class 2024-07-11 09:46:47 +02:00
Michele Caini
873617fbdf view: internal changes 2024-07-10 16:48:25 +02:00
Michele Caini
312c2b1cdf view: drop empty func each support for non-empty types in single views 2024-07-10 16:48:25 +02:00
Michele Caini
32a83ecf50 view: that's why the if was there :) right 2024-07-10 16:48:25 +02:00
Michele Caini
181f33ae7c view: race against the compiler 2024-07-10 16:48:25 +02:00
Michele Caini
b275af5848 view: drop pointless if 2024-07-10 16:48:25 +02:00
Michele Caini
023adf0b38 *: updated TODO 2024-07-10 16:48:25 +02:00
Michele Caini
b4d3ad7279 view: avoid protected data members 2024-07-10 16:48:25 +02:00
Michele Caini
2c7cc005cd *: updated TODO 2024-07-10 16:48:25 +02:00
Michele Caini
7cfbe89ecf test: minor changes 2024-07-10 16:48:25 +02:00
Michele Caini
f433b7fb41 *: updated TODO 2024-07-10 16:48:25 +02:00
Michele Caini
cad70bcd60 test: more tests for in-place single type views 2024-07-10 16:48:25 +02:00
Michele Caini
0753dd1aaa view: enable in-place single type view specialization 2024-07-10 16:48:25 +02:00
Michele Caini
d56a0a5fbd test: more tests for swap-only single type views 2024-07-10 16:48:25 +02:00
Michele Caini
4a5f021496 view: ::each in-place support for single type views 2024-07-10 16:48:25 +02:00
Michele Caini
60019b45c2 view: find/back/front in-place support for single type views 2024-07-10 16:48:25 +02:00
Michele Caini
ce543cf3fb view: ::empty support for in-place 2024-07-10 16:48:25 +02:00
Michele Caini
81b30c41a6 view: begin/end support for in-place 2024-07-10 16:48:25 +02:00
Michele Caini
185dfaa08a view: conditional in-place iterator for single type views 2024-07-10 16:48:25 +02:00
Michele Caini
b2cdc6aa57 view: ::contains in_place support for single type views 2024-07-10 16:48:25 +02:00
Michele Caini
d233a40c3d view: disable rbegin/rend when in_place for single type views 2024-07-10 16:48:24 +02:00
Michele Caini
d1073bb181 view: ::size/::size_hint in_place support for single type views 2024-07-10 16:48:24 +02:00
Michele Caini
fce847820e view: improved entity view (with tests) 2024-07-10 16:48:24 +02:00
Michele Caini
a53b944afc meta: linter directives (waiting for C++20 and std::span) 2024-07-10 16:43:01 +02:00
Michele Caini
98a35f0f0a *: updated TODO 2024-07-10 11:39:24 +02:00
Michele Caini
367f80dca4 test: storage based tests for runtime views 2024-07-10 11:30:19 +02:00
Michele Caini
65f7944b49 doc: add Donner to links.md 2024-07-10 09:58:29 +02:00
Michele Caini
473a53d796 test: cleanup 2024-07-10 09:54:31 +02:00
Michele Caini
9e7985363e entity: update guard for ranges 2024-07-09 10:29:01 +02:00
Michele Caini
fe76478dbc core: update guard for ranges 2024-07-09 10:28:56 +02:00
Michele Caini
59d5e59aa4 core: fix include guard 2024-07-09 10:28:54 +02:00
Michele Caini
6f967b3b72 test: drop ENTT_HAS_HEADER_VERSION 2024-07-09 10:28:50 +02:00
Michele Caini
44b1ae2c3a meta: linter directives (waiting for C++20 and std::span) 2024-07-09 10:28:30 +02:00
Michele Caini
836847b2a7 algorithm: linter directive 2024-07-09 10:28:25 +02:00
Michele Caini
5f7edc4447 sparse_set: cleanup 2024-07-09 10:28:22 +02:00
Michele Caini
e98ccf71b4 view: missed [[nodiscard]] 2024-07-09 10:28:18 +02:00
Michele Caini
b48666d3d1 test: cleanup 2024-07-09 10:28:16 +02:00
Michele Caini
54881e58b5 organizer: cleanup 2024-07-09 10:28:12 +02:00
Michele Caini
013d70a572 any: linter directive 2024-07-09 10:28:11 +02:00
Michele Caini
636e023f5f test: cleanup 2024-07-09 10:28:08 +02:00
Michele Caini
7d8461bae4 test: non-regression test for storage iterator page size deduction 2024-07-03 19:15:22 +02:00
Michele Caini
060590b14f storage: fix component_traits usage in storage_iterator 2024-07-03 18:52:20 +02:00
Michele Caini
b7090b14b7 view: get around an issue of msvc toolset v142 2024-07-01 12:50:28 +02:00
Michele Caini
a220803b09 test: suppress warnings 2024-06-30 16:39:27 +02:00
Michele Caini
8bb83ae64d view: get rid of a nasty trick on views 2024-06-30 16:10:35 +02:00
Michele Caini
bf6c2aa1ee *: updated TODO 2024-06-30 16:09:01 +02:00
Michele Caini
ecb5c9df59 test: standalone view tests 2024-06-30 00:27:39 +02:00
Michele Caini
cd9b564ce9 test: avoid using registry for view tests (wip) 2024-06-28 11:17:53 +02:00
Michele Caini
f420308ff0 test: typo 2024-06-27 16:58:55 +02:00
Michele Caini
b5d850c741 test: internal changes 2024-06-27 16:57:29 +02:00
Michele Caini
c78c0c2bf1 view: make a check that is going to disappear private 2024-06-27 16:50:26 +02:00
Michele Caini
1bf23c5989 test: internal changes 2024-06-27 16:43:07 +02:00
Michele Caini
24256188d0 doc: minor changes 2024-06-27 12:29:10 +02:00
Michele Caini
cb2bf04522 test: minor changes 2024-06-27 09:58:42 +02:00
Michele Caini
b3967dcd39 test: cleanup 2024-06-27 09:58:40 +02:00
Michele Caini
6949f9b945 view: internal changes 2024-06-26 19:42:45 +02:00
Michele Caini
b1b014ebdd *: updated TODO 2024-06-26 16:12:46 +02:00
Michele Caini
758c116c58 observer: decouple things internally 2024-06-26 12:19:55 +02:00
Michele Caini
53b85ecb39 observer: drop mask parameter 2024-06-26 08:38:58 +02:00
Michele Caini
c15b1f778a observer: oops, fixed typo 2024-06-25 11:58:13 +02:00
Michele Caini
d361e73aed doc: update to_entity doc - close #1151 2024-06-25 11:21:46 +02:00
Michele Caini
5ab4c1c5d9 observer: minor changes 2024-06-25 10:32:29 +02:00
Michele Caini
b4e437f1b1 hyelper: avoid using delegate if not needed 2024-06-25 09:49:20 +02:00
Michele Caini
96250f0abb *: use c-style arrays where it makes sense 2024-06-25 09:48:43 +02:00
Michele Caini
3340cf1b3e observer: drop unnecessary delegate 2024-06-23 00:38:11 +02:00
Michele Caini
6bf7e2596a observer: minor changes 2024-06-22 23:37:46 +02:00
Michele Caini
4d3efa7fb9 any: use plain C-style arrays when it makes sense 2024-06-21 18:15:22 +02:00
Michele Caini
834cefc35f *: updated TODO 2024-06-21 18:14:52 +02:00
Michele Caini
ed62adb744 observer: avoid unnecessary local lambda 2024-06-21 15:28:08 +02:00
Michele Caini
cc6e578f63 observer: share discard_if 2024-06-21 15:21:48 +02:00
Michele Caini
f4ff91143f observer: no need to inherit from storage here 2024-06-21 15:15:22 +02:00
Michele Caini
d9e5dbb587 observer: use base_type as argument to listeners 2024-06-21 15:08:38 +02:00
Michele Caini
3927ddeb58 sigh: internal changes 2024-06-21 11:37:04 +02:00
Michele Caini
b7c3cb267c delegate: cleanup 2024-06-21 10:02:01 +02:00
Michele Caini
9ec6f050c4 any: error message 2024-06-20 15:25:05 +02:00
Michele Caini
6c3202e429 any: no args to new[] (fair enough) 2024-06-20 15:24:14 +02:00
Michele Caini
133f6a0a28 any: get around an issue with latest MSVC and C++20 2024-06-20 09:26:39 +02:00
Michele Caini
c2c5342ea4 snapshot: thanks msvc for accepting invalid code 2024-06-19 09:26:42 +02:00
Michele Caini
97ed546e7b snapshot: decouple from component_traits 2024-06-19 09:23:18 +02:00
Michele Caini
5130420dd2 group: decouple from component_traits 2024-06-19 09:23:07 +02:00
Michele Caini
442821b061 storage: use void value_type as needed 2024-06-19 09:11:52 +02:00
Michele Caini
88889c5a8a storage: entity storage value_type 2024-06-19 09:04:13 +02:00
Michele Caini
3acf37100e view: decouple from compopnent_traits 2024-06-19 08:41:58 +02:00
Michele Caini
4fed6228a3 *: updated TODO 2024-06-19 08:41:47 +02:00
Michele Caini
9e2bc6c856 mixin: avoid using component_traits 2024-06-18 15:39:51 +02:00
Michele Caini
f49a405dce test: cleanup 2024-06-18 14:39:30 +02:00
Michele Caini
57a567629f view: rollback of some changes, they didn't lead where expected 2024-06-18 14:31:35 +02:00
Michele Caini
342db5c24f mixin: typo 2024-06-17 23:57:36 +02:00
Michele Caini
41fe71ebf4 doc: typo 2024-06-17 10:22:36 +02:00
Michele Caini
9e6e09fe2b view: internal changes to avoid errors 2024-06-17 10:11:19 +02:00
Michele Caini
8b3a2f7ce9 *: updated TODO 2024-06-14 16:31:19 +02:00
Michele Caini
5f5907c228 view: minor changes 2024-06-14 16:22:39 +02:00
Michele Caini
d67fd1e72c view: drop unnecessary member 2024-06-14 15:16:50 +02:00
Michele Caini
12ae4cd4c1 entity: minor changes 2024-06-13 18:39:57 +02:00
Michele Caini
04460d336e container: minor changes 2024-06-13 18:39:48 +02:00
Michele Caini
e262b60225 test: cleanup 2024-06-13 18:39:35 +02:00
Michele Caini
aca7872ba7 view: internal changes 2024-06-13 15:43:50 +02:00
Michele Caini
c9e48c0a14 view: path towards further reducing istantiations 2024-06-13 15:43:17 +02:00
Michele Caini
f931687ff0 view: avoid pointer arithmetic if possible 2024-06-12 14:31:20 +02:00
Michele Caini
f7cdf202e9 view: drop redundant check 2024-06-12 11:36:03 +02:00
Ezekiel Warren
154b91e835 chore: cleanup bazel files + sync bzlmod version (#1147) 2024-06-12 09:12:19 +02:00
Michele Caini
109434235f doc: core 2024-06-11 09:41:02 +02:00
Michele Caini
081c8f799e core: bit.hpp (waiting for C++20) 2024-06-11 09:34:19 +02:00
Michele Caini
786329c1d0 core: is_power_of_two -> has_single_bit 2024-06-11 09:01:25 +02:00
Michele Caini
fb47314d6f core: public popcount (waiting for C++20) 2024-06-10 18:32:28 +02:00
Michele Caini
3cec7c1065 *: updated TODO 2024-06-10 18:31:12 +02:00
Michele Caini
514e6e710c any: use std::array internally 2024-06-10 12:46:29 +02:00
Michele Caini
e197a04412 linter: exclude cppcoreguidelines-pro-bounds-constant-array-index (too annoying) 2024-06-10 12:46:17 +02:00
Michele Caini
87d69c4e77 meta: ctor review 2024-06-10 12:45:58 +02:00
Michele Caini
75307337ea meta: drop redundant argument 2024-06-07 16:47:25 +02:00
Michele Caini
7ce821f92e linter: I prefer my previous approach :) to coding style 2024-06-07 14:41:09 +02:00
Michele Caini
da8a8650cb core/entity: enable enable_borrowed_range/enable_view for a few types - close #1119 2024-06-07 08:37:52 +02:00
Michele Caini
29bc422a6a iwyu: update entt.imp 2024-06-07 08:33:35 +02:00
Michele Caini
3299fa1e40 sparse_set: review/cleanup constructors 2024-06-06 11:00:10 +02:00
Michele Caini
f8336ba5a1 any: review/cleanup constructors 2024-06-06 10:59:28 +02:00
Michele Caini
c28981e30f meta: minor changes (linter) 2024-06-06 10:58:55 +02:00
Michele Caini
339b78f47c test: minor changes (linter) 2024-06-06 10:58:31 +02:00
Michele Caini
8062bcfb3c registry: review remove/erase to use iterators 2024-06-06 10:58:17 +02:00
Michele Caini
47d71fa6cf group: minor changes (linter) 2024-06-06 10:57:52 +02:00
Michele Caini
43e9083456 dense_set: avoid pointer arithmetic 2024-06-05 13:56:12 +02:00
Michele Caini
059fa5a7a5 test: minor changes (coding style) 2024-06-05 12:52:56 +02:00
Michele Caini
d20a5607c0 test: thanks msvc for accepting invalid code 2024-06-05 12:51:57 +02:00
Michele Caini
58acc7edd1 registry: more stable assure function (with tests) - close #1148 2024-06-05 12:49:55 +02:00
Michele Caini
898dc2a122 registry: minor changes 2024-06-05 09:03:51 +02:00
Michele Caini
bf7fe3e42d test: try to make all compilers happy again 2024-06-04 16:14:51 +02:00
Michele Caini
89e3698599 hashed_string: internal review 2024-06-04 14:42:26 +02:00
Michele Caini
cc1a71ec9d test: minor changes 2024-06-04 14:41:59 +02:00
Michele Caini
b3e07785fc meta: cleanup 2024-06-04 14:41:48 +02:00
Michele Caini
f068b1f005 poly: linter directive on basic_poly 2024-06-04 09:43:25 +02:00
Michele Caini
f5790697ff meta: fool the linter 2024-06-04 09:42:27 +02:00
Michele Caini
00b32cdfe5 meta: coding style and nothing more 2024-06-04 09:41:55 +02:00
Michele Caini
ac8369d393 adjacency_matrix: correctly initialize members 2024-06-04 09:41:38 +02:00
Michele Caini
cb8f695584 any: coding style and nothing more 2024-06-04 09:41:07 +02:00
Michele Caini
2ab27a9b59 hashed_string: cleanup 2024-06-03 23:07:45 +02:00
Michele Caini
eae659301b observer: minor changes 2024-06-03 23:05:35 +02:00
Michele Caini
86cdb6f55d monostate: make it clear to the linter that we want value and monostate_v to be global 2024-06-03 23:05:25 +02:00
Michele Caini
627219f8a2 family: make it clear to the linter that we want identifier to be global 2024-06-03 23:04:59 +02:00
Michele Caini
e56d0a6fc9 test: try to make all compilers happy again 2024-06-03 14:44:46 +02:00
Michele Caini
126f3caba0 hashed_string: internal changes 2024-06-03 14:44:31 +02:00
Michele Caini
69864955af type_traits: try to make clang-cl happy again 2024-06-03 12:54:56 +02:00
Michele Caini
216bd0a629 type_traits: drop repeated branches in conditional chains 2024-06-03 10:20:12 +02:00
Michele Caini
05f758f9ba test: suppress annoying warnings by gcc 2024-06-03 09:35:51 +02:00
Michele Caini
6b84227626 hashed_string: suppress annoying warnings by gcc 2024-05-31 12:03:00 +02:00
Michele Caini
404b6bd99c *: updated TODO 2024-05-31 12:02:43 +02:00
Michele Caini
c51c6da61b locator: make it clear to the linter that we want services to be global 2024-05-31 11:32:23 +02:00
Michele Caini
0405126c25 process: rule of five 2024-05-31 11:31:56 +02:00
Michele Caini
274d9ea89d hashed_string: linter directives for accepted C-like arrays 2024-05-31 08:20:55 +02:00
Michele Caini
659bcbc98c core: linter directive on basic_any 2024-05-31 08:20:23 +02:00
Michele Caini
3291862371 meta: trick the linter about adjacent parameters 2024-05-31 08:19:46 +02:00
Michele Caini
22683f843a sparse_set: trick the linter about adjacent parameters 2024-05-31 08:19:25 +02:00
Michele Caini
9d1b9933c8 adjacency_matrix: minor changes 2024-05-31 08:18:01 +02:00
Michele Caini
e786a3b93d process: avoid C varargs functions 2024-05-31 08:17:42 +02:00
Michele Caini
f2e825b848 meta: linter directive on is_meta_pointer_like 2024-05-31 08:17:13 +02:00
Michele Caini
13a51c84a6 adjacency_matrix: cleanup 2024-05-30 08:39:19 +02:00
Michele Caini
e35f4e751f meta: avoid unnecessary copies 2024-05-30 08:30:17 +02:00
Michele Caini
2b07fae839 meta: linter directive on is_meta_pointer_like 2024-05-30 08:25:51 +02:00
Michele Caini
c4552892aa linter: update config 2024-05-30 08:25:33 +02:00
Michele Caini
51391a9381 sparse_set: linter directive on bind 2024-05-30 08:22:21 +02:00
Michele Caini
0349f4f615 type_info: explicit cast to avoid warnings 2024-05-30 08:22:02 +02:00
Michele Caini
d951b9e817 storage: cleanup 2024-05-30 08:18:27 +02:00
Michele Caini
2dd38bbff2 build: updated workflow 2024-05-30 08:18:14 +02:00
Michele Caini
06929572be compressed_pair: try to make g++ happy again 2024-05-29 09:11:26 +02:00
Michele Caini
c7f412bd7e view: make leading pool private in single type views 2024-05-29 08:26:40 +02:00
Michele Caini
483cc2b913 group: avoid unnecessary pointer arithmetic 2024-05-29 08:25:52 +02:00
Michele Caini
8138094f0a compressed_pair: cleanup 2024-05-29 08:23:23 +02:00
Michele Caini
06a8b05cf8 core: suppress linter warnings on type_info 2024-05-29 08:23:04 +02:00
Michele Caini
d3328accae dense_map: avoid unnecessary arithmetic 2024-05-28 18:39:41 +02:00
Michele Caini
a5e5fded0b view: make unchecked_refresh private as it should be 2024-05-28 18:38:45 +02:00
Michele Caini
a16b9cc092 meta: avoid C-like arrays 2024-05-28 18:37:59 +02:00
Michele Caini
539cf5f011 delegate: cleanup 2024-05-28 18:37:37 +02:00
Michele Caini
abdf06625b clang-tidy: refine config (sometimes I know what I'm doing) 2024-05-28 09:33:01 +02:00
Michele Caini
3c99189ff4 doc: Exhibition of Speed :) 2024-05-28 09:32:13 +02:00
Michele Caini
cd1fd890ea storage: avoid duplicate branches 2024-05-28 09:31:53 +02:00
Michele Caini
d03c0d0b1a sparse_set: minor changes 2024-05-28 09:31:00 +02:00
Michele Caini
7db890ba15 sparse set: avoid C-arrays 2024-05-27 16:59:03 +02:00
Michele Caini
8807499548 storage: rule of five 2024-05-27 16:58:51 +02:00
Michele Caini
bea578ac87 view: avoid C-arrays 2024-05-27 16:58:15 +02:00
Michele Caini
c474fe5b51 spars set: rule of five 2024-05-27 16:57:52 +02:00
Michele Caini
864ae25a2b *: updated TODO 2024-05-27 11:45:43 +02:00
Michele Caini
e864037968 meta: instruct the linter to accept some of my choices 2024-05-27 11:45:29 +02:00
Michele Caini
76d4f00e9f storage: avoid move/swap after move/swap 2024-05-27 11:45:01 +02:00
Michele Caini
4cb70d3c2d mixin: avoid move/swap after move/swap 2024-05-27 11:44:27 +02:00
Michele Caini
4c3fca4673 handle: instruct the linter to accept some of my choices 2024-05-27 11:43:44 +02:00
Michele Caini
7718b1bb9a any: make return type explicit for operator= 2024-05-24 09:25:44 +02:00
Michele Caini
32ffd625ff meta: make return type explicit for operator= 2024-05-24 09:25:25 +02:00
Michele Caini
176b9dafba mixin: cleanup 2024-05-24 09:24:20 +02:00
Michele Caini
82cd118adf storage: cleanup 2024-05-24 09:24:05 +02:00
Michele Caini
88828d2eb5 sparse_set: cleanup 2024-05-24 09:23:44 +02:00
Michele Caini
d9a3990f03 registry: [[nodiscard]] storage<T>(...) const 2024-05-24 09:23:27 +02:00
Michele Caini
699dff81b7 test: minor changes 2024-05-24 09:22:45 +02:00
Michele Caini
7bfc92d20e emitter: cleanup 2024-05-24 09:22:23 +02:00
Michele Caini
c5f6691c72 dispatcher: cleanup 2024-05-24 09:22:13 +02:00
Michele Caini
8433060092 resource: make return type explicit for operator= 2024-05-24 09:22:02 +02:00
Michele Caini
09da09500a scheduler: cleanup 2024-05-24 09:21:25 +02:00
Michele Caini
6ca5b9c1af scheduler: rule of five 2024-05-24 09:21:17 +02:00
Michele Caini
5f6f9b7b23 table: cleanup 2024-05-24 09:20:53 +02:00
Michele Caini
f0191b9afe doc: typo 2024-05-24 08:01:06 +02:00
Michele Caini
c885044ce4 registry: rule of five 2024-05-24 08:00:55 +02:00
Michele Caini
ee64ca4afb registry: avoid c-arrays 2024-05-24 08:00:45 +02:00
Michele Caini
9788610124 organizer: avoid c-arrays 2024-05-24 08:00:10 +02:00
Michele Caini
01f851199b table: rule of five 2024-05-24 07:59:54 +02:00
Michele Caini
d278a0f864 dispatcher: rule of five 2024-05-23 08:40:08 +02:00
Michele Caini
f9bf4c341d locator: rule of three 2024-05-23 08:40:00 +02:00
Michele Caini
2777bade73 snapshot: rule of five 2024-05-23 08:34:25 +02:00
Michele Caini
55637a7284 basic_sigh_mixin: rule of five 2024-05-23 08:24:58 +02:00
Michele Caini
0bd37df6f3 doc: minor changes 2024-05-23 08:23:06 +02:00
Michele Caini
aad65bf436 version: suppress macro usage linter messages 2024-05-23 08:22:25 +02:00
Michele Caini
9db1d085f9 macro: suppress macro usage linter messages 2024-05-23 08:22:18 +02:00
Michele Caini
dccc621f07 emitter: rule of five 2024-05-22 16:08:47 +02:00
Michele Caini
a014656f04 poly: cleanup 2024-05-22 16:07:11 +02:00
Michele Caini
1ffbd845dc config: suppress macro usage linter messages 2024-05-22 16:06:58 +02:00
Michele Caini
d0f8974e08 entity: default destructors and noexcept-ness review 2024-05-21 10:32:05 +02:00
Michele Caini
505bb4c8cb any: minor changes 2024-05-21 10:31:36 +02:00
Michele Caini
5ecf9eb893 signal: default destructor and noexcept-ness review 2024-05-21 10:22:39 +02:00
Michele Caini
ab532af047 resource: default destructor 2024-05-21 10:21:58 +02:00
Michele Caini
16b05ad8a0 basic_scheduler: default destructor 2024-05-21 10:21:27 +02:00
Michele Caini
54ef3c8850 meta_handle: default destructor 2024-05-21 10:20:50 +02:00
Michele Caini
4516210518 locator: minor changes 2024-05-21 10:20:28 +02:00
Michele Caini
387528bdcc adjacency_matrix: default destructor 2024-05-21 10:20:14 +02:00
Michele Caini
84b48c1a30 snapshot: default destructor(s) 2024-05-21 10:19:51 +02:00
Michele Caini
6615792703 compressed_pair: refine dtor noexcept spec 2024-05-21 10:19:29 +02:00
Michele Caini
991d16084d test: noexcept-ness review 2024-05-21 10:19:03 +02:00
Michele Caini
d7c9609ee6 registry: default destructor 2024-05-21 09:33:28 +02:00
Michele Caini
af27c43436 test: iwyu docet 2024-05-21 09:33:15 +02:00
Michele Caini
89e3fcfda0 flow: default destructor 2024-05-21 09:32:46 +02:00
Michele Caini
52de32291f dense_map/dense_set: cleanup 2024-05-21 09:32:30 +02:00
Michele Caini
a530b078d7 basic_sigh_mixin: default dtor 2024-05-21 08:19:19 +02:00
Michele Caini
a68bb70d43 sigh: default dtor 2024-05-21 08:18:25 +02:00
Michele Caini
f9a243b92a adjacency_matrix: internal changes 2024-05-21 08:13:59 +02:00
Michele Caini
4c4bbed0a2 dense_map/dense_set: default dtor 2024-05-21 08:13:38 +02:00
Michele Caini
9ec4d69b1e table: default dtor 2024-05-21 08:13:21 +02:00
Michele Caini
68f3f650eb registry: [[nodiscard]] as it should be 2024-05-20 13:21:48 +02:00
Michele Caini
2cb44f2590 group: [[nodiscard]] as it should be 2024-05-20 13:21:31 +02:00
Michele Caini
f7fd153ee0 group: cleanup 2024-05-20 13:21:19 +02:00
Michele Caini
e5804ac0a2 *: use transparent comparators 2024-05-20 13:20:57 +02:00
Michele Caini
11ade1cbc0 snapshot: noexcept move ctor/op 2024-05-20 13:19:29 +02:00
Michele Caini
46ed48ec48 adjacency_matrix: cleanup 2024-05-20 13:19:09 +02:00
Michele Caini
b3b4facaed sigh/sink: avoid unnecessary moves 2024-05-20 13:18:33 +02:00
Michele Caini
fbe92fc708 dispatcher: [[nodiscard]] as it should be 2024-05-20 13:18:05 +02:00
Michele Caini
3b6a987814 basic_flow: cleanup 2024-05-20 13:17:21 +02:00
Michele Caini
10749d445a resource_cache: noexcept move ctor/op 2024-05-20 13:16:48 +02:00
Michele Caini
83495c967e test. avoid warnings due to unused variables 2024-05-20 13:16:25 +02:00
Michele Caini
32ae3eef08 monstate: return *this from operator= 2024-05-20 13:16:00 +02:00
Michele Caini
efc68bdf39 compressed_pari: cleanup 2024-05-20 13:15:39 +02:00
Michele Caini
1e90bee7ad algorithm: avoid C-arrays 2024-05-20 13:15:26 +02:00
Michele Caini
77606c2d20 test: use transparent comparators 2024-05-19 00:14:58 +02:00
Michele Caini
ca303e24ff organizer: minor changes 2024-05-19 00:12:16 +02:00
Michele Caini
288fcdaf53 hashed_string: avoid magic numbers in return statements 2024-05-19 00:12:01 +02:00
Michele Caini
8f7480ebd1 compressed_pair: default dtor 2024-05-19 00:11:08 +02:00
Michele Caini
a15511d5ff entity: use =default as needed 2024-05-17 12:52:17 +02:00
Michele Caini
16ed46967f compressed-pair: add initializer to suppress warnings 2024-05-17 12:52:00 +02:00
Michele Caini
4d460ce763 test: minor changes 2024-05-17 12:51:39 +02:00
Michele Caini
4dd427aa4d group: mark functions [[nodiscard]] as needed 2024-05-17 12:51:29 +02:00
Michele Caini
4302842b79 poly: mark functions [[nodiscard]] as needed 2024-05-17 12:27:11 +02:00
Michele Caini
5724644653 organizer: mark functions [[nodiscard]] as needed 2024-05-17 12:27:06 +02:00
Michele Caini
a82a5958b3 workflow: increase tools timeout 2024-05-17 12:19:08 +02:00
Michele Caini
59935230dc entity: mark functions [[nodiscard]] as needed 2024-05-17 12:16:23 +02:00
Michele Caini
e9d30cb10e compressed_pair: mark functions [[nodiscard]] as needed 2024-05-17 12:16:07 +02:00
Michele Caini
db35537966 sparse_set/storage: mark functions [[nodiscard]] as needed 2024-05-17 12:08:04 +02:00
Michele Caini
62e990ed8a registry: mark functions [[nodiscard]] as needed 2024-05-17 12:05:55 +02:00
Michele Caini
1f0efb14a0 *: updated TODO 2024-05-17 12:02:32 +02:00
Michele Caini
5ac56d6fec workflow: refine tools 2024-05-17 11:58:02 +02:00
Michele Caini
68a6aef155 workflow:
* add linter job
* bind both linter and iwyu to a specific branch
2024-05-17 11:52:07 +02:00
Michele Caini
b8af4c6d71 workflow: minor changes 2024-05-17 11:51:29 +02:00
Michele Caini
c9ecfa049f doc: updated links.md 2024-05-17 08:28:43 +02:00
Michele Caini
0c0ce5dfc5 compressed_pair: stop supporting ancient compilers 2024-05-16 14:47:10 +02:00
Michele Caini
138347320e build: cleanup workflow 2024-05-16 14:33:06 +02:00
Michele Caini
4f58be5762 runtime_view: internal changes 2024-05-16 14:16:13 +02:00
Michele Caini
ffd3f950f3 build: maybe GH fixed it, let's see 2024-05-16 14:10:14 +02:00
Michele Caini
9ac19020d5 natvis: minimal view for basic tables 2024-05-15 17:33:17 +02:00
Michele Caini
feeb10a5f8 doc: table 2024-05-14 14:39:44 +02:00
Michele Caini
950509a950 *: updated TODO 2024-05-14 14:39:38 +02:00
Michele Caini
0b23b8f342 table: move it to the container module 2024-05-14 11:48:44 +02:00
Michele Caini
1d1761a13b doc: removed dead link 2024-05-14 11:47:12 +02:00
Michele Caini
d42d324adb *: updated TODO 2024-05-13 11:55:57 +02:00
Michele Caini
42cef49a7f doc: general update 2024-05-13 11:55:30 +02:00
Michele Caini
b57758c61e doc: update minimum doxygen version 2024-05-13 11:54:03 +02:00
Michele Caini
e4ef61ca93 build: stop supporting msvc toolset v141 2024-05-13 11:43:35 +02:00
Michele Caini
640f02bd16 test: drop another NOLINT 2024-05-11 12:28:59 +02:00
Michele Caini
2a86b8fe08 test: cleanup 2024-05-11 12:22:35 +02:00
Michele Caini
9edeb731f5 test: ENTT_NO_MIXIN 2024-05-10 15:25:20 +02:00
Michele Caini
8efe02f28f entity: use ENTT_STORAGE to filter mixin as needed 2024-05-10 15:24:36 +02:00
Michele Caini
581f2b0a0c config: ENTT_NO_MIXIN/ENTT_STORAGE 2024-05-10 15:23:52 +02:00
Michele Caini
00e34a2dc7 view: make toolset v141 happy again (for the last time hopefully) 2024-05-10 11:22:58 +02:00
Michele Caini
2590b6c3ba test: avoid using user entity storage 2024-05-10 10:50:52 +02:00
Michele Caini
fb6403f801 doc: cleanup 2024-05-10 10:26:48 +02:00
Michele Caini
e2da374e24 view: refine tombstone check requirements 2024-05-10 10:16:39 +02:00
Michele Caini
ceb4259ae3 storage: make deletion policy constexpr available as data member 2024-05-10 10:16:05 +02:00
Michele Caini
d6d72e6b03 registry: assert on user entity storage - close #1143 2024-05-10 10:14:04 +02:00
Michele Caini
35ebf5c413 hashed_string: avoid unnecessary class specializations 2024-05-09 09:44:00 +02:00
Michele Caini
39e7595b3b hashed_string: drop unnecessary internal type member 2024-05-09 09:29:06 +02:00
Michele Caini
7de152428e type_info: make comparison operators inline when non constexpr 2024-05-09 09:13:39 +02:00
Michele Caini
e2648e418f test: minor changes 2024-05-09 09:12:29 +02:00
Michele Caini
f7e533d061 test: code coverage 2024-05-08 12:02:02 +02:00
Michele Caini
2f8b4835ca table: redesign it as an adaptor rather than a container 2024-05-08 11:56:19 +02:00
Michele Caini
2a0eb2bcd4 test: NOLINT cleanup 2024-05-08 11:54:49 +02:00
Michele Caini
4692e72671 test: minor changes 2024-05-07 10:47:09 +02:00
Michele Caini
30d16b850c view: operator-> for single type views 2024-05-07 10:44:04 +02:00
Michele Caini
81b878d038 platform: drop support to android ndk r17 2024-05-07 10:09:49 +02:00
Michele Caini
cf3d751097 test: cleanup 2024-05-06 16:21:23 +02:00
Michele Caini
1e9ad0fb9c organizer: in/out-edges support - close #1069 2024-05-06 13:38:58 +02:00
Michele Caini
d0786338a9 doc: coding style 2024-05-05 18:19:14 +02:00
Michele Caini
7a605742ee table: refine tests with exceptions enabled 2024-05-03 14:31:24 +02:00
Michele Caini
739bc802b5 table: test with custom/throwing allocators 2024-05-03 14:07:10 +02:00
Michele Caini
97a50c23a9 table: test erase functions 2024-05-03 11:09:32 +02:00
Michele Caini
b6a71dbdbe table: test emplace function 2024-05-03 09:47:23 +02:00
Michele Caini
3a97180b63 table: non-defaulted table iterator's default ctor 2024-05-03 09:46:25 +02:00
Michele Caini
d92ae29132 test: suppress linter warnings 2024-05-03 09:44:46 +02:00
Michele Caini
1e26f3fd81 table: refined implementation/cleanup 2024-05-02 16:31:24 +02:00
Michele Caini
640a06bc68 table: test iterators 2024-05-02 09:40:28 +02:00
Michele Caini
41aab920b0 test: minor changes 2024-04-30 11:03:15 +02:00
Michele Caini
a3cea32f93 table: check pos when invoking operator[] 2024-04-30 11:02:56 +02:00
Michele Caini
aadbe3501e table: test ::clear 2024-04-30 10:12:21 +02:00
Michele Caini
3ba8268bfd table: test indexing 2024-04-30 09:17:31 +02:00
Michele Caini
45b4241f62 table: test ::capacity and related functions 2024-04-30 09:00:26 +02:00
Michele Caini
0a48f25beb table: test ::swap 2024-04-30 08:47:58 +02:00
Michele Caini
7ca3f84891 table: more tests 2024-04-29 10:38:34 +02:00
Michele Caini
3729eab7d5 *: updated TODO 2024-04-29 10:38:09 +02:00
Michele Caini
09ef06db71 test: oops :) 2024-04-29 10:34:50 +02:00
Michele Caini
9f211bacad test: minor changes 2024-04-29 09:02:10 +02:00
Michele Caini
bf854b0b5f table: let's write some tests 2024-04-29 09:01:54 +02:00
Michele Caini
3823d60932 table: clean up 2024-04-29 09:00:58 +02:00
Michele Caini
48353cd4dc table: fixed operator< for table iterators 2024-04-29 09:00:44 +02:00
Michele Caini
caaf332af3 table: fixed operator- for table iterators 2024-04-29 09:00:20 +02:00
Michele Caini
89c87c6d10 table: fixed operator-> for table iterators 2024-04-29 08:59:57 +02:00
Michele Caini
80c05856c2 sparse_set: minor changes 2024-04-26 16:45:49 +02:00
Michele Caini
d990d4e928 table: ::erase functions 2024-04-26 16:45:21 +02:00
Michele Caini
965a7ba4f0 table: make table iterators friends to allow non-const to const conversions 2024-04-26 16:45:04 +02:00
Michele Caini
17c9da4329 table: use compressed local void allocator 2024-04-26 10:37:30 +02:00
Michele Caini
7abe76e4f7 dispatcher: minor changes 2024-04-24 18:33:20 +02:00
Michele Caini
6e9be219ba scheduler: minor changes 2024-04-24 18:33:04 +02:00
Michele Caini
a360d14dad table: expand parameter pack in return type 2024-04-24 18:23:29 +02:00
Michele Caini
43d6b0865f doc: minor changes 2024-04-24 18:22:57 +02:00
Michele Caini
e44b1e9024 table: fixed table_iterator operator[](value) 2024-04-24 18:22:48 +02:00
Michele Caini
c0e68b96f7 table: emplace (back) function 2024-04-24 18:10:43 +02:00
Michele Caini
12e886340c table: operator[] overloads 2024-04-23 13:22:22 +02:00
Michele Caini
9f7668071d table: (c)rbegin/(c)rend functions 2024-04-23 13:21:51 +02:00
Michele Caini
736b0fe92b table: a bunch of fixes (thanks msvc for accepting invalid code) 2024-04-22 14:19:47 +02:00
Michele Caini
a89d1a853a table: (c)begin/(c)end functions 2024-04-22 13:54:11 +02:00
Michele Caini
452e9f8b51 table: iterator types 2024-04-22 13:52:33 +02:00
Michele Caini
c6ec062a4d table: table iterator 2024-04-19 09:13:03 +02:00
Michele Caini
7e413590c6 doc: minor changes 2024-04-19 09:10:20 +02:00
Michele Caini
4a3f83a7b8 table: ::clear function 2024-04-18 16:44:54 +02:00
Michele Caini
dd4a291272 table: ::empty function 2024-04-18 16:44:30 +02:00
Michele Caini
32bd18bf08 table: ::size function 2024-04-18 16:44:13 +02:00
Michele Caini
7d92b42d59 cache: minor changes 2024-04-18 16:41:57 +02:00
Michele Caini
4a542c3ad0 meta: minor changes 2024-04-18 16:40:05 +02:00
Michele Caini
950a48b5aa storage: minor changes 2024-04-18 16:39:52 +02:00
Michele Caini
bcdc6f9ad8 sparse_set: minor changes 2024-04-18 16:39:39 +02:00
Michele Caini
6b06fa9990 registry: minor changes 2024-04-18 16:39:27 +02:00
Michele Caini
8dbfa40c76 container: minor changes 2024-04-18 16:39:15 +02:00
Michele Caini
bb423bb503 *: updated TODO 2024-04-17 15:39:41 +02:00
Michele Caini
3d07cf8781 *: coding style 2024-04-17 15:38:40 +02:00
Michele Caini
f180927380 table: reserve/capacity/shrink_to_fit 2024-04-17 10:28:18 +02:00
Michele Caini
a1a1e9fd28 table: container type 2024-04-17 09:48:13 +02:00
Michele Caini
0d6056fee4 doc: minor changes 2024-04-17 09:45:41 +02:00
Michele Caini
c7a9bbf853 table: basic iface (placeholder) 2024-04-17 09:45:09 +02:00
Michele Caini
206110caaf test: suppress warning due to unused variable 2024-04-16 09:09:00 +02:00
Michele Caini
f223638350 table: constructors and operators 2024-04-16 09:08:40 +02:00
Michele Caini
a863536982 storage: better log message to avoid ambiguity 2024-04-15 17:28:31 +02:00
Michele Caini
430822aaa2 sparse_set/storage: minor changes 2024-04-15 17:26:07 +02:00
Michele Caini
b69390f283 table: refine prototype 2024-04-15 09:42:11 +02:00
Michele Caini
57d3c53014 *: coding style 2024-04-15 09:04:38 +02:00
Michele Caini
d6588f9b59 entity: table class placeholder 2024-04-15 08:45:29 +02:00
Michele Caini
19f510743e test: internal changes 2024-04-12 11:43:51 +02:00
Michele Caini
02a8803f98 test: use shared types as much as possible 2024-04-12 11:42:26 +02:00
Michele Caini
a78c2d0592 test: common bitmask types 2024-04-12 11:41:52 +02:00
Michele Caini
60e5ae22d5 test: use shared types as much as possible 2024-04-12 11:18:23 +02:00
Michele Caini
aa178dd2cf resource: add ::reset functions (with tests) 2024-04-12 11:17:57 +02:00
Michele Caini
0b6e325109 doc: fixed links 2024-04-12 11:17:03 +02:00
Michele Caini
453d32d3fd test: use common types as much as possible 2024-04-10 09:30:32 +02:00
Michele Caini
9891e70a20 test: more common empty types 2024-04-10 09:29:25 +02:00
Michele Caini
6ca01d2faf group: make older compilers happy too 2024-04-10 09:01:06 +02:00
Michele Caini
4c98e3aa1e group: share common setup 2024-04-10 09:01:06 +02:00
Michele Caini
2c700bb812 group: finally decouple handlers and types to further reduce instantiations 2024-04-10 09:01:06 +02:00
Michele Caini
03f94a8834 registry: oops :) forgot to pack storage when constructing groups 2024-04-10 09:01:06 +02:00
Michele Caini
fb87ae392f group: use group_id rather than handler type to mark groups 2024-04-10 09:01:06 +02:00
Michele Caini
48c122fb28 meta: minor changes 2024-04-10 09:01:06 +02:00
Michele Caini
77d646f05d group: add missing include 2024-04-10 09:01:06 +02:00
Michele Caini
35d048304f group: decouple ::owned and owned types 2024-04-10 09:01:06 +02:00
Michele Caini
0d0880d81f group: drop unnecessary index sequence 2024-04-10 09:01:06 +02:00
Michele Caini
08e9e059b5 group: prepare to decouple handlers from types 2024-04-10 09:01:06 +02:00
Michele Caini
4602de245c group: pass common type as template parameter, prepare to get rid of storage types 2024-04-10 09:01:05 +02:00
Michele Caini
0b9d7b3538 group: required template keywords (thanks as usual msvc) 2024-04-10 09:01:05 +02:00
Michele Caini
38c7cddac0 group: move a static assert on storage types out from handlers 2024-04-10 09:01:05 +02:00
Michele Caini
d4588208e3 group: prepare to decouple group handlers from types 2024-04-10 09:01:05 +02:00
Michele Caini
666e988995 group: prepare to further reduce instantiations for groups too 2024-04-10 09:01:05 +02:00
Michele Caini
f8e452c7c8 group: drop unnecessary lambdas/std::apply calls 2024-04-10 09:01:05 +02:00
Michele Caini
f9a9034001 test: minor changes 2024-04-10 09:01:05 +02:00
Michele Caini
d2b6eed671 doc: minor changes 2024-04-10 09:01:05 +02:00
Michele Caini
d734c92ee4 memory: class level new/delete support - close #1131 2024-04-10 09:01:05 +02:00
Michele Caini
69f12a1110 any: class level new/delete support 2024-04-10 09:01:05 +02:00
Michele Caini
717696e7a3 test: common class level new/delete type - see #1131 2024-04-10 09:01:05 +02:00
Michele Caini
affbc668df test: storage type checks 2024-04-10 09:01:05 +02:00
Michele Caini
6ede10fa47 test: suppress a warning by clang 2024-04-10 09:01:05 +02:00
Michele Caini
1ba01ca3e2 test: drop unused alias 2024-04-10 09:01:05 +02:00
Michele Caini
8739e9185a snapshot: suppress a warning by clang 2024-04-10 09:01:05 +02:00
Michele Caini
40208e6b37 build: try to add more compilers on the CI 2024-04-10 09:01:05 +02:00
Michele Caini
32078f033f natvis: basic handle view - see #1052 2024-04-10 09:01:05 +02:00
Michele Caini
54f27382ca handle: a few changes to make natvis views easier 2024-04-10 09:01:05 +02:00
Michele Caini
5908d01017 view. branch-less centralized swap-only mode check 2024-04-10 09:01:05 +02:00
Michele Caini
2f8998bd92 sparse_set: branch-less paths 2024-04-10 09:01:05 +02:00
Michele Caini
48c953d23e update single include file 2024-04-10 09:00:16 +02:00
Michele Caini
df87206c94 snapshot: full support to stable types - close #1118 2024-03-28 10:49:18 +01:00
Michele Caini
abaa5c468e *: updated TODO 2024-03-28 10:48:31 +01:00
Michele Caini
f4431368e3 handle: drop a [[nodiscard]] that breaks toolset v141 2024-03-27 15:32:20 +01:00
Michele Caini
6bc8f4cfa4 handle: full review with test coverage - close #1129 2024-03-27 14:41:19 +01:00
Michele Caini
70c4b1f4c4 clang-tidy: suppress a good amount of wrong warnings 2024-03-27 14:37:29 +01:00
Michele Caini
1be0175a18 sparse_set: drop scoped iterators 2024-03-26 11:40:30 +01:00
Michele Caini
3796f055d6 sigh_mixin: avoid using scoped iterators 2024-03-25 11:19:45 +01:00
Michele Caini
0128620803 sparse_set: review ::push 2024-03-25 11:19:07 +01:00
Michele Caini
a8a86ba090 sparse_set: avoid using scoped iterators internally when sorting 2024-03-25 11:18:35 +01:00
Michele Caini
430d1dda86 test: avoid using scoped iterators if not needed 2024-03-25 10:50:37 +01:00
Michele Caini
f4e7e9738f sparse_set: avoid annoying warnings 2024-03-25 10:25:12 +01:00
Michele Caini
3dfc65a357 view: stop using scoped iterators 2024-03-25 10:24:40 +01:00
Michele Caini
fe83215f52 doc: links 2024-03-25 10:23:53 +01:00
Michele Caini
4df0f7ebf1 doc: updated similar projects 2024-03-25 10:20:55 +01:00
Michele Caini
3d40651112 test: suppress a clang-tidy warning 2024-03-25 08:10:39 +01:00
Michele Caini
b96c8e2eff test: stop using scoped iterators 2024-03-25 08:10:10 +01:00
Michele Caini
ad081f41db sigh_mixin: minor changes 2024-03-22 23:25:34 +01:00
Michele Caini
38b6fb2133 test: drop pointless checks 2024-03-22 14:38:32 +01:00
Michele Caini
b373bb13d1 sparse_set: avoid using scoped iterators (simple cases) 2024-03-22 12:30:54 +01:00
Michele Caini
53895c1b9e view: avoid using scoped iterators (simple cases) 2024-03-22 12:30:29 +01:00
Michele Caini
29af24dac1 test: avoid using scoped iterators (simple cases) 2024-03-22 12:29:57 +01:00
Michele Caini
bb57d94185 registry: avoid using scoped iterators when destroying 2024-03-22 08:24:45 +01:00
Michele Caini
c8a5812cea entity: minor changes around iterable objects 2024-03-22 08:22:15 +01:00
Michele Caini
02836f1f43 handle: explicit iterable type 2024-03-21 11:24:44 +01:00
Michele Caini
0cf7262d2d handle: struct -> class 2024-03-21 10:58:38 +01:00
Michele Caini
ec92b7672e handle: minor changes 2024-03-21 10:56:05 +01:00
Michele Caini
294792060c handle: support comparison with null_t - close #1130 2024-03-21 10:27:14 +01:00
Michele Caini
4bedef459e doc: minor changes 2024-03-21 10:25:49 +01:00
Michele Caini
65da029ff9 view: use iterator traits as needed 2024-03-20 16:49:59 +01:00
Michele Caini
ef563b05e1 runtime_view: use iterator traits as needed 2024-03-20 16:49:46 +01:00
Michele Caini
7952266492 doc: minor change 2024-03-20 16:49:20 +01:00
Michele Caini
9b81532ced storage: branch-less each/reach, no scoped iterators 2024-03-20 12:39:24 +01:00
Michele Caini
4a17395b6a storage: reduce uses of scoped iterators 2024-03-20 09:47:15 +01:00
Michele Caini
56edea562a type_traits: lambda support for nth_argument - close #1127 2024-03-20 09:45:31 +01:00
Michele Caini
bc5dfe3489 test: fix typo from last PR and make test local paths local 2024-03-19 10:19:57 +01:00
Michele Caini
e0e3786089 clang-format: suppress include main regex 2024-03-19 10:19:01 +01:00
Ezekiel Warren
1c30113338 build: update bazel common test target (#1124) 2024-03-18 16:21:20 +01:00
Michele Caini
4a8efa2703 test: try to make toolset v141 happy again 2024-03-18 11:54:13 +01:00
Michele Caini
4fa9767edd iwyu: export a few symbols 2024-03-18 09:30:13 +01:00
Michele Caini
a3b305b708 iwyu: minor changes 2024-03-18 09:29:55 +01:00
Michele Caini
809fdad185 test: cleanup 2024-03-17 09:55:19 +01:00
Michele Caini
bf7db4c585 doc: minor changes 2024-03-17 09:55:09 +01:00
Erik Scholz
17d06d96ad doc: cleanup grammar nitpicks in the context section (#1125) 2024-03-17 09:53:18 +01:00
Michele Caini
b8a0031d9c doc: component <-> element 2024-03-15 11:37:00 +01:00
Michele Caini
283753a3ff *: updated TODO 2024-03-15 09:55:45 +01:00
Michele Caini
2c753ff085 storage: make traits_type private 2024-03-15 09:55:39 +01:00
Michele Caini
12a1e7fffc sigh_mixin: avoid using storage traits_type 2024-03-15 09:54:47 +01:00
Michele Caini
098343d275 view: avoid using storage traits_type 2024-03-15 09:54:18 +01:00
Michele Caini
fc8beac28f group: avoid using storage traits_type 2024-03-15 09:53:37 +01:00
Michele Caini
a4c37a4a93 helper: avoid using storage traits_type 2024-03-15 09:53:12 +01:00
Michele Caini
4e66f6bf68 storage: element_type vs value_type for finer control 2024-03-15 09:52:49 +01:00
Michele Caini
f9d509251e meta: add iwyu pragmas to spec-only files 2024-03-14 09:36:47 +01:00
Michele Caini
0f2ee043ae test: iwyu docet 2024-03-14 09:36:29 +01:00
Michele Caini
af56c554af iwyu: it's a wild beast apparently, remove internal includes for now 2024-03-14 09:36:13 +01:00
Michele Caini
fb31c923db iwyu: add flow mapping end tokens 2024-03-14 08:35:25 +01:00
Michele Caini
f4a4ab339a iwyu: oops, forgot a comma :) 2024-03-14 08:30:45 +01:00
Michele Caini
02ec48ddc5 sigh_mixin: avoid using weak ranges twice - close #1123 2024-03-14 08:25:01 +01:00
Michele Caini
369bebd38b *: updated TODO 2024-03-14 08:24:20 +01:00
Michele Caini
9f29ac8f65 iwyu: add a few dependencies to reduce white noise 2024-03-14 08:24:08 +01:00
Michele Caini
4e89acb2ef test: suppress warnings due to unused aliases 2024-03-13 16:33:39 +01:00
Michele Caini
231ff77283 test: iwyu docet 2024-03-13 12:10:15 +01:00
Michele Caini
a26e35248b test: more on sigh mixin 2024-03-13 10:43:56 +01:00
Michele Caini
bcac7a6ea2 test: further review sigh mixin 2024-03-13 10:39:17 +01:00
Michele Caini
0e567f8f3b test: minor changes 2024-03-13 10:31:03 +01:00
Michele Caini
f03a6c6a92 test: review sigh mixin 2024-03-13 10:27:10 +01:00
Michele Caini
5f9e4344ba test: minor changes 2024-03-13 09:49:04 +01:00
Michele Caini
41a3c190ec *: updated TODO 2024-03-12 10:53:26 +01:00
Michele Caini
5c53209e5d sparse_set: make traits_type private 2024-03-12 10:53:18 +01:00
Michele Caini
d70664c0ad storage: avoid using traits_type from base 2024-03-12 10:52:54 +01:00
Michele Caini
55c257c905 test: avoid using sparse set traits_type 2024-03-12 08:41:11 +01:00
Michele Caini
f0ef2e7270 test: avoid using storage traits_type 2024-03-12 08:40:20 +01:00
Michele Caini
1064857764 test: cleanup 2024-03-12 08:39:51 +01:00
Michele Caini
dc9f0781fc registry: avoid using sparse set traits_type 2024-03-11 11:35:37 +01:00
Michele Caini
5adf7172d1 test: avoid using sparse set traits_type 2024-03-11 11:34:51 +01:00
Michele Caini
1f78a8e768 registry: make traits_type private as it ought to be 2024-03-11 11:15:43 +01:00
Michele Caini
7870cbfa56 snapshot: avoid using registry traits_type 2024-03-11 11:14:23 +01:00
Michele Caini
69d15470b4 test: avoid using registry traits_type 2024-03-11 11:13:30 +01:00
Michele Caini
fefcbb214a sparse_set: drop an extra typename that msvc happily accepted 2024-03-08 12:43:38 +01:00
Michele Caini
94f53958a0 *: updated TODO 2024-03-08 12:14:51 +01:00
Michele Caini
d7ed4dcedb natvis: updated entity views 2024-03-08 12:14:45 +01:00
Michele Caini
1178b8e0cf natvis: updated meta views 2024-03-08 12:14:37 +01:00
Michele Caini
f18234abbd doc: minor changes 2024-03-07 11:44:59 +01:00
Michele Caini
cc0bd48233 sparse_set: turn head into a size (position/length) 2024-03-07 10:25:26 +01:00
Michele Caini
921160a483 sparse_set: use aliases as much as possible 2024-03-07 10:24:40 +01:00
Michele Caini
95d8370da4 sparse_set: shared policy_to_head function 2024-03-06 14:25:05 +01:00
Michele Caini
2fba813e23 sparse_set: const correctness 2024-03-06 10:40:31 +01:00
Michele Caini
6b707d6af1 sparse_set: minor changes 2024-03-06 10:39:07 +01:00
Michele Caini
f481bc307f sparse_set: avoid using free_list internally, prepare to drop scoped iterators 2024-03-06 09:05:16 +01:00
Michele Caini
ebc6f64af0 sparse_set: better return value for range ::push 2024-03-06 08:49:26 +01:00
Michele Caini
ef61d4c3d0 natvis: update dispatcher/emitter views 2024-03-04 10:01:47 +01:00
Michele Caini
cb669d43a2 *: I love when git goes crazy :) 2024-03-04 10:01:21 +01:00
Michele Caini
7dbd00e7e7 natvis: update resource cache view 2024-03-04 10:00:31 +01:00
Michele Caini
698866df76 natvis: meta context 2024-03-04 09:59:31 +01:00
Michele Caini
6922f15095 meta: refine meta_func/data prop views 2024-03-04 09:59:21 +01:00
Michele Caini
cd82501ed2 natvis: internal changes 2024-03-04 09:58:10 +01:00
Michele Caini
84fb9af076 natvis: reintroduce simple views for containers 2024-03-04 09:49:59 +01:00
Michele Caini
062837dd31 test: try to make all compilers happy again 2024-03-01 19:05:21 +01:00
Michele Caini
97d46ce64d nativs: minor changes 2024-03-01 18:22:43 +01:00
Michele Caini
29ec97f464 *: updated TODO 2024-03-01 15:26:30 +01:00
Michele Caini
dc424f7d4c entity: try to make all compilers happy again 2024-03-01 15:22:42 +01:00
Michele Caini
1893a8c78d entity: zero sized version support 2024-03-01 14:46:56 +01:00
Michele Caini
56c881e087 test: linter feedback 2024-03-01 14:42:20 +01:00
Michele Caini
0dcf6c5690 nativs: support to zero-sized versions 2024-03-01 14:33:56 +01:00
Michele Caini
e7d29479e4 natvis: drop simple views for ecs module 2024-02-29 15:11:56 +01:00
Michele Caini
ee55085f3a view: try to make msvc happy again 2024-02-29 14:41:38 +01:00
Michele Caini
c802b2d687 natvis: drop simple views for containers 2024-02-29 11:45:12 +01:00
Michele Caini
dca2fefaba view: avoid duplicating data on iterators 2024-02-29 09:36:54 +01:00
Michele Caini
d8ef21b08b view: internal changes 2024-02-29 09:35:54 +01:00
Michele Caini
2f852806aa workflow: updated iwyu version 2024-02-28 09:47:16 +01:00
Michele Caini
945b8881b6 view: drop unnecessary argument 2024-02-28 09:29:13 +01:00
Michele Caini
0d71afacd8 view: drop redundant member 2024-02-28 09:26:43 +01:00
Michele Caini
0f9ee0ac03 natvis: refine view definition, prepare to drop stuff on the C++ side 2024-02-28 08:46:32 +01:00
Michele Caini
c3fedcc151 storage: drop unnecessary template parameter 2024-02-27 15:04:33 +01:00
Michele Caini
2f1c99ab81 entity: refine checks to fully support zero-sized versions/fully defined entity masks 2024-02-27 13:52:59 +01:00
Michele Caini
83576bf94e view: check the index directly before invoking ::each 2024-02-27 10:36:00 +01:00
Michele Caini
42c8df773c sparse_set: drop an useless check of the mode 2024-02-27 10:23:16 +01:00
Michele Caini
536c536563 test: minor changes 2024-02-27 10:22:29 +01:00
Michele Caini
2212f91c97 test: suppress warnings due to unused types 2024-02-26 15:13:30 +01:00
Michele Caini
f18c8f14bf storage: prevent ::next from spinning forever 2024-02-26 09:20:39 +01:00
Michele Caini
4e5b6de873 doc: minor changes 2024-02-26 09:18:27 +01:00
Michele Caini
84e4b8c3a0 test: use common types 2024-02-26 09:18:01 +01:00
Michele Caini
e33d8b28ca test: more entity types 2024-02-26 09:16:44 +01:00
Michele Caini
72f718458b test: use shared listener type, drop local definitions 2024-02-23 14:03:44 +01:00
Michele Caini
84ce6163a7 test: shared listener type 2024-02-23 14:02:40 +01:00
Michele Caini
a28ff99b08 test: boxed_type<T>::operator T() 2024-02-23 14:02:13 +01:00
Michele Caini
44b3cb1294 test: boxed_ char type 2024-02-23 14:01:50 +01:00
Michele Caini
79dfb454e2 doc: typo 2024-02-23 14:01:10 +01:00
Michele Caini
7b24bc0005 test: minor changes 2024-02-23 14:00:16 +01:00
Michele Caini
463e8e0098 tombstone: zero-sized version support 2024-02-23 13:59:53 +01:00
Michele Caini
a272676738 test: storage entity emplace-with-hint with misplaced placeholder 2024-02-22 09:21:57 +01:00
Michele Caini
3a51f71716 *: updated TODO 2024-02-22 09:20:50 +01:00
Michele Caini
a922216ab4 sparse_set: null/tombstone check when try_emplace-ing 2024-02-22 09:18:28 +01:00
Michele Caini
ba116ba949 sparse_set: drop force_back check for swap_only policy 2024-02-22 09:17:41 +01:00
Michele Caini
8b31b165b5 sparse_set: minor changes 2024-02-22 09:16:29 +01:00
Michele Caini
91a095dba4 storage: avoid emplacing extra entities when using a hint - close #1113 2024-02-21 08:55:25 +01:00
Michele Caini
371f547d59 nativs: storage entity 2024-02-20 09:43:19 +01:00
Michele Caini
bec1f60859 storage: prepare to directly create the single entity upon hint 2024-02-20 09:42:53 +01:00
Michele Caini
9db2daf3a7 test: minor changes 2024-02-19 17:50:39 +01:00
Michele Caini
f2a8c0ffe6 storage: reset next on clear 2024-02-19 17:26:38 +01:00
Michele Caini
70eceea864 *: updated TODO 2024-02-19 17:26:20 +01:00
Michele Caini
15aa6c7727 storage: decouple size and next identifier - see #1113 2024-02-19 14:30:00 +01:00
Michele Caini
948fb8337d test: cleanup 2024-02-19 14:28:31 +01:00
Michele Caini
ac3a3d2fbe test: make things work despite the unofficial name generation function 2024-02-16 14:54:59 +01:00
Michele Caini
20dd52410e test: make all compilers happy again 2024-02-16 14:54:02 +01:00
Michele Caini
48011dcbf8 test: get around an unexpected optimiziation by clang 2024-02-16 14:53:46 +01:00
Michele Caini
f4f7bdbf69 *: updated TODO 2024-02-16 10:36:00 +01:00
Michele Caini
eb531fd7ba test: avoid _t types 2024-02-16 10:35:54 +01:00
Michele Caini
6cab881c51 test: suppress a false warning by clang 2024-02-15 10:36:55 +01:00
Michele Caini
209768cdbe core: make all compilers happy again 2024-02-15 10:31:52 +01:00
Michele Caini
5ecc254a91 core: export more types as fwd decl - close #1114 2024-02-15 10:09:40 +01:00
Michele Caini
d013c69bf1 test: use common types for lib tests (meta) 2024-02-14 09:52:19 +01:00
Michele Caini
1a469a96ce test: use common types for lib tests (locator) 2024-02-14 09:52:07 +01:00
Michele Caini
4f1e0805db test: suppress a wrong warning by clang 2024-02-13 08:52:30 +01:00
Michele Caini
4cc5745346 test: use common types for lib tests (emitter) 2024-02-13 08:29:14 +01:00
Michele Caini
b7622a373b test: use common types for lib tests (dispatcher) 2024-02-13 08:28:41 +01:00
Michele Caini
a0a2ad4404 test: use common types for lib tests (registry) 2024-02-13 08:26:04 +01:00
Michele Caini
1962027c80 test: reuse types if possible 2024-02-12 14:17:17 +01:00
Michele Caini
8c1c899cb8 test: review ident tests 2024-02-09 10:49:08 +01:00
Michele Caini
3b74db258e test: review process tests 2024-02-09 10:48:55 +01:00
Michele Caini
696f78c8a0 test: review emitter tests 2024-02-09 10:48:39 +01:00
Michele Caini
c31e1a939e test: common emitter test class 2024-02-09 10:48:13 +01:00
Michele Caini
dd95c35309 *: updated TODO 2024-02-09 09:32:50 +01:00
Michele Caini
2c9d37efc3 test: make common really common 2024-02-09 09:29:09 +01:00
Michele Caini
1777db0a09 *: updated TODO 2024-02-08 10:12:34 +01:00
Michele Caini
6f7839f8c4 meta: drop a bunch of NOLINT 2024-02-08 10:12:28 +01:00
Michele Caini
ee78d39860 natvis: meta_type_node representation 2024-02-08 10:12:04 +01:00
Michele Caini
8a4a312250 meta: update meta_type::is_pointer to use is_pointer meta traits 2024-02-08 10:11:41 +01:00
Michele Caini
ed21813170 meta: is_pointer meta traits 2024-02-08 10:10:49 +01:00
Michele Caini
9ec8adf5c9 natvis: typo 2024-02-07 13:01:05 +01:00
Michele Caini
48c3f58462 view: use ::index when picking the right ::each 2024-02-07 11:57:09 +01:00
Michele Caini
d0528c7a4d doc: minor changes 2024-02-07 11:50:40 +01:00
Michele Caini
4e9b4371e2 group: try to make vs happy again 2024-02-07 10:16:50 +01:00
Michele Caini
e3fef531e6 test: drop a bunch of NOLINT 2024-02-07 09:27:29 +01:00
Michele Caini
5ca2a95f36 registry: improved ::valid check 2024-02-07 09:25:23 +01:00
Michele Caini
df1968255b group: suppress shadow warning 2024-02-07 09:23:17 +01:00
Michele Caini
6227ff5fcf doc: updated copyright 2024-02-07 09:18:16 +01:00
Michele Caini
3192f20877 test: drop a bunch of NOLINT 2024-02-07 07:59:36 +01:00
Michele Caini
bcdc1ebad9 test: drop a bunch of NOLINT 2024-02-07 07:59:19 +01:00
Michele Caini
a43ffa8f0d test: workaround for a nasty bug of toolset v141 that sees 4 as member pointer (yeah, I'm amazed too) 2024-02-06 10:48:31 +01:00
Michele Caini
3a89dadb30 build: update iwyu version 2024-02-06 08:59:55 +01:00
Michele Caini
4d7b830b20 iwyu: just a few changes 2024-02-06 08:58:26 +01:00
Michele Caini
b92d2c98ae test: minor changes 2024-02-06 08:30:46 +01:00
Michele Caini
5d3f155164 test: final touch for containers 2024-02-06 08:27:47 +01:00
Michele Caini
fa56839bcf doc: typo 2024-02-05 09:00:15 +01:00
Ullrich Praetz
a6a4bbfc8e doc: add friflo-engine-ecs to the list of similar projects (#1108) 2024-02-05 08:59:22 +01:00
Michele Caini
f6aa7dc725 test: registry 2024-02-05 08:54:50 +01:00
Michele Caini
7ea79932ed test: cleanup 2024-02-03 00:31:13 +01:00
Michele Caini
df6da5c208 test: full review of entt::any tests 2024-02-02 11:20:49 +01:00
Michele Caini
a69e0eb661 update single include file 2024-02-02 10:10:49 +01:00
Michele Caini
83aabf7cfc registry: assert when emplacing invalid entities - close #1095 2024-02-02 09:47:06 +01:00
Michele Caini
61f4ad8837 linter: minor changes 2024-02-01 14:31:36 +01:00
Michele Caini
872e0c6dde test: avoid using custom entities when emplacing in a registry - see #1095 2024-02-01 14:29:47 +01:00
Michele Caini
ad743ff327 hashed_string: refine ctos and avoid uninitialized ::length in all cases 2024-02-01 14:28:30 +01:00
Michele Caini
9d07ff18b2 *: updated TODO 2024-01-31 13:50:39 +01:00
Michele Caini
1150b980fa test: drop a bunch of NOLINT 2024-01-31 13:50:06 +01:00
Michele Caini
3ad137f597 test: drop a bunch of NOLINT 2024-01-31 10:27:57 +01:00
Michele Caini
5a4531baba test: drop a bunch of NOLINT 2024-01-31 10:26:41 +01:00
Michele Caini
53f8600ae5 runtime_view: bool operator to test for validity 2024-01-31 10:26:10 +01:00
Michele Caini
11d609314c test: drop a bunch of NOLINT 2024-01-31 10:25:20 +01:00
Michele Caini
97b2b6c5b8 test: drop a bunch of NOLINT 2024-01-30 11:12:47 +01:00
Michele Caini
c1b7229238 adjacency_matrix: ::empty function 2024-01-30 11:12:31 +01:00
Michele Caini
c1a211d422 test: drop a bunch of NOLINT 2024-01-30 11:12:00 +01:00
Michele Caini
00ac590e2f flow: ::empty function 2024-01-30 11:07:00 +01:00
Michele Caini
31a91c764b test: drop a bunch of NOLINT 2024-01-30 11:05:25 +01:00
Michele Caini
553accd5f0 test: drop a bunch of NOLINT 2024-01-29 17:22:43 +01:00
Michele Caini
1ab1676d0b test: drop a bunch of NOLINT 2024-01-29 14:53:38 +01:00
Michele Caini
14ac11e9ca test: drop a bunch of NOLINT 2024-01-29 14:52:19 +01:00
Michele Caini
d45ef425a2 test: drop a bunch of NOLINT 2024-01-27 14:25:07 +01:00
Michele Caini
fdbb6829fa test: suppress a gcc sign-compare warning 2024-01-27 14:24:02 +01:00
Michele Caini
38ede6d0c4 test: drop a bunch of NOLINT 2024-01-26 16:56:19 +01:00
Michele Caini
4b4c038dc7 test: drop a bunch of NOLINT 2024-01-26 16:55:56 +01:00
Michele Caini
7a71084a8e test: drop a bunch of NOLINT 2024-01-26 08:16:40 +01:00
Michele Caini
8a57b94a18 test: drop a bunch of NOLINT 2024-01-26 08:16:17 +01:00
Michele Caini
79d72bd568 test: drop a bunch of NOLINT 2024-01-26 08:15:59 +01:00
Michele Caini
ece46d4aa3 doc: Trollworks engine 2024-01-26 08:14:56 +01:00
Michele Caini
f95b9f0ac9 test: suppress clang missing-braces warnings 2024-01-25 09:38:39 +01:00
Michele Caini
9e9bed06af storage: drop deprecated member pack 2024-01-25 08:30:06 +01:00
Michele Caini
bba8b6bdd8 registry: review destroy to use sort_as 2024-01-25 08:28:58 +01:00
Michele Caini
b96631c486 sparse_set: make sort_as return an iterator past the last shared element 2024-01-25 08:28:28 +01:00
Michele Caini
469a3cd568 meta: drop deprecated function owner 2024-01-24 14:57:45 +01:00
Michele Caini
d0e2edc13f meta: drop deprecated function key_only 2024-01-24 14:57:26 +01:00
Michele Caini
1eb4b36643 any: drop deprecated function owner 2024-01-24 14:56:35 +01:00
Michele Caini
325d3a9552 test: minor changes 2024-01-24 09:37:10 +01:00
Michele Caini
71bd869fc8 test: drop a bunch of NOLINT 2024-01-24 08:21:07 +01:00
Michele Caini
7d0cf207fd helper: drop deprecated function to_entity 2024-01-24 08:18:28 +01:00
Michele Caini
2f625b0a6f storage: drop deprecated member in_use 2024-01-24 08:12:07 +01:00
Michele Caini
01957a73fd meta: const correctness 2024-01-23 13:30:35 +01:00
Michele Caini
93f20984a6 test: drop a bunch of NOLINT 2024-01-23 10:51:04 +01:00
Michele Caini
00a58db0a7 view: drop deprecated member operator[] 2024-01-23 10:27:55 +01:00
Michele Caini
e3cb863f94 sparse_set: drop deprecated member at 2024-01-23 08:06:16 +01:00
Michele Caini
e5381cee86 sparse_set: drop deprecated member sort_as 2024-01-22 17:10:46 +01:00
Michele Caini
406ba3cb7d group: drop deprecated member sort_as 2024-01-22 16:51:44 +01:00
Ezekiel Warren
89622127db build: don't install include/BUILD.bazel (#1107) 2024-01-22 09:52:58 +01:00
Michele Caini
5f73b2e4f2 test: boxed_int -> boxed_type 2024-01-22 09:49:59 +01:00
Michele Caini
baab46e77b *: updated TODO (pending checks verified) 2024-01-21 23:24:28 +01:00
Michele Caini
6889d9d87b test: drop a bunch of NOLINT 2024-01-19 15:25:11 +01:00
Michele Caini
dd3feb87cd test: avoid warnings due to unused variables 2024-01-19 14:35:26 +01:00
Michele Caini
2375c0489b group: avoid swapping observed types - close #1100 2024-01-19 14:22:07 +01:00
Michele Caini
353b3d19ee snapshot: refine the check in the constructor of the loader class - close #1106 2024-01-19 09:21:46 +01:00
Michele Caini
68688b2263 *: updated TODO 2024-01-18 15:22:16 +01:00
Michele Caini
324716f82e test: drop a bunch of NOLINT 2024-01-18 09:20:30 +01:00
Michele Caini
51a063206b test: minor changes 2024-01-17 17:41:43 +01:00
Michele Caini
5e0caa6110 *: updated TODO 2024-01-17 17:41:25 +01:00
Michele Caini
235ed63e3b test: make toolset v141 happy again :) 2024-01-17 10:43:49 +01:00
Michele Caini
1dbeedcf12 test: drop a bunch of NOLINT 2024-01-17 08:47:36 +01:00
Michele Caini
d695bc1b29 test: minor changes 2024-01-17 08:47:08 +01:00
Michele Caini
f08e1ea21c view: avoid unchecked_refresh loops on single get type 2024-01-16 16:11:14 +01:00
Michele Caini
4510ba32b5 now working on version 3.14.0 2024-01-16 16:09:59 +01:00
288 changed files with 44450 additions and 32140 deletions

View File

@@ -1,17 +1,16 @@
common --enable_bzlmod
build --enable_platform_specific_config
build --incompatible_use_platforms_repo_for_constraints
build --incompatible_enable_cc_toolchain_resolution
build --enable_runfiles
build --incompatible_strict_action_env
# required for googletest
# required for googletest
build:linux --cxxopt=-std=c++17
build:macos --cxxopt=-std=c++17
common:ci --announce_rc
common:ci --verbose_failures
common:ci --announce_rc
common:ci --verbose_failures
common:ci --keep_going
test:ci --test_output=errors
try-import %workspace%/user.bazelrc
test:ci --test_output=errors
try-import %workspace%/user.bazelrc

View File

@@ -2,10 +2,11 @@ BasedOnStyle: llvm
---
AccessModifierOffset: -4
AlignEscapedNewlines: DontAlign
AllowShortBlocksOnASingleLine: Empty
AllowShortBlocksOnASingleLine: Always
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: WithoutElse
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: true
AlwaysBreakTemplateDeclarations: Yes
BreakBeforeBinaryOperators: NonAssignment
@@ -23,6 +24,7 @@ IncludeCategories:
Priority: 4
- Regex: '.*'
Priority: 5
IncludeIsMainRegex: "^$"
IndentPPDirectives: AfterHash
IndentWidth: 4
KeepEmptyLinesAtTheStartOfBlocks: false

View File

@@ -1,26 +1,58 @@
Checks: >
bugprone-*,
clang-analyzer-*,
-clang-analyzer-optin.core.EnumCastOutOfRange,
concurrency-*,
cppcoreguidelines-*,
-cppcoreguidelines-owning-memory,
-cppcoreguidelines-pro-bounds-constant-array-index,
-cppcoreguidelines-pro-type-const-cast,
-cppcoreguidelines-pro-type-member-init,
-cppcoreguidelines-pro-type-reinterpret-cast,
-cppcoreguidelines-pro-type-union-access,
misc-*,
-misc-include-cleaner,
-misc-no-recursion,
modernize-*,
-modernize-use-trailing-return-type,
performance-*,
portability-*,
readibility-*
readability-*,
-readability-else-after-return,
-readability-function-cognitive-complexity,
-readability-named-parameter,
-readability-redundant-member-init,
-readability-uppercase-literal-suffix,
CheckOptions:
- key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor
- key: cppcoreguidelines-avoid-magic-numbers.IgnoreAllFloatingPointValues
value: true
- key: cppcoreguidelines-avoid-magic-numbers.IgnorePowersOf2IntegerValues
value: true
- key: cppcoreguidelines-rvalue-reference-param-not-moved.AllowPartialMove
value: true
- key: cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreUnnamedParams
value: true
- key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions
value: true
- key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctionsWhenCopyIsDeleted
value: true
- key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor
value: true
- key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic
value: true
- key: misc-non-private-member-variables-in-classes.IgnorePublicMemberVariables
value: true
- key: cppcoreguidelines-avoid-magic-numbers.IgnorePowersOf2IntegerValues
- key: modernize-avoid-c-arrays.AllowStringArrays
value: true
- key: cppcoreguidelines-avoid-magic-numbers.IgnoreAllFloatingPointValues
- key: performance-enum-size.EnumIgnoreList
value: meta_traits
- key: readability-function-cognitive-complexity.IgnoreMacros
value: true
- key: readability-identifier-length.MinimumParameterNameLength
value: 2
- key: readability-identifier-length.MinimumVariableNameLength
value: 2
- key: readability-magic-numbers.IgnoreAllFloatingPointValues
value: true
- key: readability-magic-numbers.IgnorePowersOf2IntegerValues
value: true

View File

@@ -3,17 +3,16 @@ name: analyzer
on:
push:
branches:
- master
- wip
- analyzer
jobs:
iwyu:
timeout-minutes: 30
timeout-minutes: 60
env:
IWYU: "0.20"
LLVM: "16"
IWYU: "0.24"
LLVM: "20"
runs-on: ubuntu-latest
continue-on-error: true
@@ -24,10 +23,10 @@ jobs:
# 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 add-apt-repository "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-$LLVM main"
sudo apt update
sudo apt remove -y "llvm*"
sudo apt remove -y "libclang-dev*"
sudo apt remove -y "libclang*"
sudo apt remove -y "clang*"
sudo apt install -y llvm-$LLVM-dev
sudo apt install -y libclang-$LLVM-dev
@@ -56,6 +55,30 @@ jobs:
-DENTT_BUILD_EXAMPLE=ON \
-DENTT_BUILD_LIB=ON \
-DENTT_BUILD_SNAPSHOT=ON \
-DENTT_BUILD_TESTBED=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
clang-tidy:
timeout-minutes: 60
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v4
- name: Compile tests
working-directory: build
env:
CXX: clang++
run: |
cmake -DENTT_BUILD_TESTING=ON \
-DENTT_BUILD_BENCHMARK=ON \
-DENTT_BUILD_EXAMPLE=ON \
-DENTT_BUILD_LIB=ON \
-DENTT_BUILD_SNAPSHOT=ON \
-DENTT_BUILD_TESTBED=ON \
-DENTT_USE_CLANG_TIDY=ON \
..
make -j4

View File

@@ -13,7 +13,10 @@ jobs:
permissions:
contents: write
steps:
- uses: actions/setup-go@v5
- run: go install github.com/bazelbuild/buildtools/buildozer@latest
- uses: actions/checkout@v4
- run: ./scripts/sync_bzlmod_version.sh
- run: git archive $GITHUB_REF -o "entt-${GITHUB_REF:10}.tar.gz"
- run: gh release upload ${GITHUB_REF:10} "entt-${GITHUB_REF:10}.tar.gz"
env:

View File

@@ -3,6 +3,7 @@ name: bazel
on: [push, pull_request]
jobs:
test:
strategy:
matrix:
@@ -10,8 +11,10 @@ jobs:
- ubuntu-latest
- windows-latest
- macos-latest
runs-on: ${{ matrix.os }}
continue-on-error: true
steps:
- uses: actions/checkout@v4
- run: bazelisk test --config=ci ...

View File

@@ -5,54 +5,18 @@ on: [push, pull_request]
jobs:
linux:
timeout-minutes: 15
strategy:
matrix:
os: [ubuntu-latest, ubuntu-20.04]
compiler:
- { pkg: g++, exe: 'g++', version: 7 }
- { pkg: g++, exe: 'g++', version: 8 }
- { pkg: g++, exe: 'g++', version: 9 }
- { pkg: g++, exe: 'g++', version: 10 }
- { pkg: g++, exe: 'g++', version: 11 }
- { pkg: g++, exe: 'g++', version: 12 }
- { pkg: clang, exe: 'clang++', version: 8 }
- { pkg: clang, exe: 'clang++', version: 9 }
- { pkg: clang, exe: 'clang++', version: 10 }
- { pkg: clang, exe: 'clang++', version: 11 }
- { pkg: clang, exe: 'clang++', version: 12 }
- { pkg: clang, exe: 'clang++', version: 13 }
- { pkg: clang, exe: 'clang++', version: 14 }
exclude:
- os: ubuntu-latest
compiler: { pkg: g++, exe: 'g++', version: 7 }
- os: ubuntu-latest
compiler: { pkg: g++, exe: 'g++', version: 8 }
- os: ubuntu-latest
compiler: { pkg: g++, exe: 'g++', version: 9 }
- os: ubuntu-latest
compiler: { pkg: clang, exe: 'clang++', version: 8 }
- os: ubuntu-latest
compiler: { pkg: clang, exe: 'clang++', version: 9 }
- os: ubuntu-latest
compiler: { pkg: clang, exe: 'clang++', version: 10 }
- os: ubuntu-latest
compiler: { pkg: clang, exe: 'clang++', version: 11 }
- os: ubuntu-20.04
compiler: { pkg: g++, exe: 'g++', version: 10 }
- os: ubuntu-20.04
compiler: { pkg: g++, exe: 'g++', version: 11 }
- os: ubuntu-20.04
compiler: { pkg: g++, exe: 'g++', version: 12 }
- os: ubuntu-20.04
compiler: { pkg: clang, exe: 'clang++', version: 12 }
- os: ubuntu-20.04
compiler: { pkg: clang, exe: 'clang++', version: 13 }
- os: ubuntu-20.04
compiler: { pkg: clang, exe: 'clang++', version: 14 }
- { pkg: g++, exe: 'g++', version: 13 }
- { pkg: g++, exe: 'g++', version: 14 }
- { pkg: clang, exe: 'clang++', version: 16 }
- { pkg: clang, exe: 'clang++', version: 17 }
- { pkg: clang, exe: 'clang++', version: 18 }
runs-on: ${{ matrix.os }}
timeout-minutes: 15
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -74,19 +38,18 @@ jobs:
run: ctest -C Debug -j4
windows:
timeout-minutes: 15
strategy:
matrix:
toolset: [default, v141, v142, clang-cl]
toolset: [default, v142, v143, clang-cl]
include:
- toolset: v141
toolset_option: -T"v141"
- toolset: v142
toolset_option: -T"v142"
- toolset: v143
toolset_option: -T"v143"
- toolset: clang-cl
toolset_option: -T"ClangCl"
timeout-minutes: 15
runs-on: windows-latest
steps:
@@ -120,14 +83,13 @@ jobs:
run: ctest -C Debug -j4
extra:
timeout-minutes: 15
strategy:
matrix:
os: [windows-latest, macOS-latest, ubuntu-latest]
id_type: ["std::uint32_t", "std::uint64_t"]
cxx_std: [cxx_std_17, cxx_std_20]
timeout-minutes: 15
runs-on: ${{ matrix.os }}
steps:

View File

@@ -27,7 +27,7 @@ jobs:
working-directory: build
run: |
sudo apt install lcov
lcov -c -d . -o coverage.info
lcov -c -d . -o coverage.info --ignore-errors gcov,gcov,mismatch,mismatch
lcov -l coverage.info
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3

View File

@@ -2,7 +2,7 @@ name: deploy
on:
release:
types: published
types: [published]
jobs:

View File

@@ -17,8 +17,6 @@ jobs:
steps:
- uses: actions/checkout@v4
# temporary workaround for https://github.com/actions/runner-images/issues/8659
- uses: mjp41/workaround8649@c8550b715ccdc17f89c8d5c28d7a48eeff9c94a8
- name: Compile tests
working-directory: build
env:

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

@@ -0,0 +1,79 @@
name: testbed
on: [push]
jobs:
linux:
timeout-minutes: 15
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install required packages
run: |
sudo apt update
sudo apt install -y \
build-essential \
git \
make \
pkg-config \
cmake \
ninja-build \
gnome-desktop-testing \
libasound2-dev \
libpulse-dev \
libaudio-dev \
libjack-dev \
libsndio-dev \
libx11-dev \
libxext-dev \
libxrandr-dev \
libxcursor-dev \
libxfixes-dev \
libxi-dev \
libxss-dev \
libxtst-dev \
libxkbcommon-dev \
libdrm-dev \
libgbm-dev \
libgl1-mesa-dev \
libgles2-mesa-dev \
libegl1-mesa-dev \
libdbus-1-dev \
libibus-1.0-dev \
libudev-dev \
libpipewire-0.3-dev \
libwayland-dev \
libdecor-0-dev \
liburing-dev
- name: Compile testbed
working-directory: build
run: |
cmake -DENTT_BUILD_TESTBED=ON ..
make -j4
windows:
timeout-minutes: 15
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: seanmiddleditch/gha-setup-ninja@master
- name: Compile testbed
working-directory: build
run: |
cmake -DENTT_BUILD_TESTBED=ON .. -G Ninja
cmake --build . -j 4
macos:
timeout-minutes: 15
runs-on: macOS-latest
steps:
- uses: actions/checkout@v4
- name: Compile testbed
working-directory: build
run: |
cmake -DENTT_BUILD_TESTBED=ON ..
make -j4

1
.gitignore vendored
View File

@@ -13,3 +13,4 @@ cpp.hint
/bazel-*
/test/bazel-*
/user.bazelrc
*.bazel.lock

View File

@@ -22,10 +22,10 @@ project(
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug)
endif()
message(VERBOSE "*")
message(VERBOSE "* ${PROJECT_NAME} v${PROJECT_VERSION} (${CMAKE_BUILD_TYPE})")
message(VERBOSE "* Copyright (c) 2017-2023 Michele Caini <michele.caini@gmail.com>")
message(VERBOSE "* Copyright (c) 2017-2025 Michele Caini <michele.caini@gmail.com>")
message(VERBOSE "*")
# CMake stuff
@@ -81,20 +81,6 @@ 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)
@@ -109,103 +95,8 @@ target_include_directories(
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/mixin.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/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>
)
if(ENTT_HAS_LIBCPP)
target_compile_options(EnTT BEFORE INTERFACE -stdlib=libc++)
endif()
if(ENTT_HAS_SANITIZER)
@@ -214,103 +105,248 @@ if(ENTT_HAS_SANITIZER)
endif()
if(ENTT_CLANG_TIDY_EXECUTABLE)
set(CMAKE_CXX_CLANG_TIDY "${ENTT_CLANG_TIDY_EXECUTABLE};--config-file=${EnTT_SOURCE_DIR}/.clang-tidy;--header-filter=${EnTT_SOURCE_DIR}/src/entt/.*;--extra-arg=/EHsc")
set(ENTT_CLANG_TIDY_OPTIONS ";--config-file=${EnTT_SOURCE_DIR}/.clang-tidy;--header-filter=${EnTT_SOURCE_DIR}/src/entt/.*")
if(MSVC AND NOT (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang"))
set(ENTT_CLANG_TIDY_OPTIONS "${ENTT_CLANG_TIDY_OPTIONS};--extra-arg=/EHsc;--extra-arg=/wd4996")
endif()
set(CMAKE_CXX_CLANG_TIDY "${ENTT_CLANG_TIDY_EXECUTABLE}${ENTT_CLANG_TIDY_OPTIONS}")
endif()
# Add EnTT goodies
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_HEADERS)
set(
HEADERS_FILES
config/config.h
config/macro.h
config/version.h
container/dense_map.hpp
container/dense_set.hpp
container/table.hpp
container/fwd.hpp
core/algorithm.hpp
core/any.hpp
core/bit.hpp
core/compressed_pair.hpp
core/enum.hpp
core/family.hpp
core/fwd.hpp
core/hashed_string.hpp
core/ident.hpp
core/iterator.hpp
core/memory.hpp
core/monostate.hpp
core/ranges.hpp
core/tuple.hpp
core/type_info.hpp
core/type_traits.hpp
core/utility.hpp
entity/component.hpp
entity/entity.hpp
entity/fwd.hpp
entity/group.hpp
entity/handle.hpp
entity/mixin.hpp
entity/helper.hpp
entity/organizer.hpp
entity/ranges.hpp
entity/registry.hpp
entity/runtime_view.hpp
entity/snapshot.hpp
entity/sparse_set.hpp
entity/storage.hpp
entity/view.hpp
graph/adjacency_matrix.hpp
graph/dot.hpp
graph/flow.hpp
graph/fwd.hpp
locator/locator.hpp
meta/adl_pointer.hpp
meta/container.hpp
meta/context.hpp
meta/factory.hpp
meta/fwd.hpp
meta/meta.hpp
meta/node.hpp
meta/pointer.hpp
meta/policy.hpp
meta/range.hpp
meta/resolve.hpp
meta/template.hpp
meta/type_traits.hpp
meta/utility.hpp
poly/fwd.hpp
poly/poly.hpp
process/fwd.hpp
process/process.hpp
process/scheduler.hpp
resource/cache.hpp
resource/fwd.hpp
resource/loader.hpp
resource/resource.hpp
signal/delegate.hpp
signal/dispatcher.hpp
signal/emitter.hpp
signal/fwd.hpp
signal/sigh.hpp
tools/davey.hpp
entt.hpp
fwd.hpp
tools.hpp
)
list(TRANSFORM HEADERS_FILES APPEND ">" OUTPUT_VARIABLE HEADERS_BUILD_INTERFACE)
list(TRANSFORM HEADERS_BUILD_INTERFACE PREPEND "$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/")
list(TRANSFORM HEADERS_FILES APPEND ">" OUTPUT_VARIABLE HEADERS_INSTALL_INTERFACE)
list(TRANSFORM HEADERS_INSTALL_INTERFACE PREPEND "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/entt/")
target_sources(EnTT INTERFACE ${HEADERS_BUILD_INTERFACE} ${HEADERS_INSTALL_INTERFACE})
endif()
if(ENTT_HAS_LIBCPP)
target_compile_options(EnTT BEFORE INTERFACE -stdlib=libc++)
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()
# Install pkg-config file
if(ENTT_HAS_NATVIS)
set(
NATVIS_FILES
config.natvis
container.natvis
core.natvis
entity.natvis
graph.natvis
locator.natvis
meta.natvis
poly.natvis
process.natvis
resource.natvis
signal.natvis
)
include(JoinPaths)
list(TRANSFORM NATVIS_FILES APPEND ">" OUTPUT_VARIABLE NATVIS_BUILD_INTERFACE)
list(TRANSFORM NATVIS_BUILD_INTERFACE PREPEND "$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/natvis/")
set(EnTT_PKGCONFIG ${CMAKE_CURRENT_BINARY_DIR}/entt.pc)
list(TRANSFORM NATVIS_FILES APPEND ">" OUTPUT_VARIABLE NATVIS_INSTALL_INTERFACE)
list(TRANSFORM NATVIS_INSTALL_INTERFACE PREPEND "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/entt/natvis/")
join_paths(EnTT_PKGCONFIG_INCLUDEDIR "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
target_sources(EnTT INTERFACE ${NATVIS_BUILD_INTERFACE} ${NATVIS_INSTALL_INTERFACE})
endif()
configure_file(
${EnTT_SOURCE_DIR}/cmake/in/entt.pc.in
${EnTT_PKGCONFIG}
@ONLY
)
# Install EnTT and all related files
install(
FILES ${EnTT_PKGCONFIG}
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
)
option(ENTT_INSTALL "Install EnTT and all related files." OFF)
# Install EnTT
if(ENTT_INSTALL)
# Install pkg-config file
include(CMakePackageConfigHelpers)
include(JoinPaths)
install(
TARGETS EnTT
EXPORT EnTTTargets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
set(EnTT_PKGCONFIG ${CMAKE_CURRENT_BINARY_DIR}/entt.pc)
write_basic_package_version_file(
EnTTConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion
)
join_paths(EnTT_PKGCONFIG_INCLUDEDIR "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
configure_package_config_file(
${EnTT_SOURCE_DIR}/cmake/in/EnTTConfig.cmake.in
EnTTConfig.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake
)
configure_file(
${EnTT_SOURCE_DIR}/cmake/in/entt.pc.in
${EnTT_PKGCONFIG}
@ONLY
)
export(
EXPORT EnTTTargets
FILE ${CMAKE_CURRENT_BINARY_DIR}/EnTTTargets.cmake
NAMESPACE EnTT::
)
install(
FILES ${EnTT_PKGCONFIG}
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
)
install(
EXPORT EnTTTargets
FILE EnTTTargets.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake
NAMESPACE EnTT::
)
# Install EnTT
install(
FILES
${PROJECT_BINARY_DIR}/EnTTConfig.cmake
${PROJECT_BINARY_DIR}/EnTTConfigVersion.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake
)
include(CMakePackageConfigHelpers)
install(DIRECTORY src/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(
TARGETS EnTT
EXPORT EnTTTargets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
export(PACKAGE EnTT)
write_basic_package_version_file(
EnTTConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion
)
# Tests
configure_package_config_file(
${EnTT_SOURCE_DIR}/cmake/in/EnTTConfig.cmake.in
EnTTConfig.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake
)
export(
EXPORT EnTTTargets
FILE ${CMAKE_CURRENT_BINARY_DIR}/EnTTTargets.cmake
NAMESPACE EnTT::
)
install(
EXPORT EnTTTargets
FILE EnTTTargets.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake
NAMESPACE EnTT::
)
install(
FILES
${PROJECT_BINARY_DIR}/EnTTConfig.cmake
${PROJECT_BINARY_DIR}/EnTTConfigVersion.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake
)
install(
DIRECTORY src/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
FILES_MATCHING
PATTERN "*.h"
PATTERN "*.hpp"
PATTERN "*.natvis"
)
export(PACKAGE EnTT)
endif()
# Tests and testbed
option(ENTT_BUILD_TESTING "Enable building tests." OFF)
option(ENTT_BUILD_TESTBED "Enable building testbed." OFF)
if(ENTT_BUILD_TESTING)
option(ENTT_FIND_GTEST_PACKAGE "Enable finding gtest package." OFF)
option(ENTT_BUILD_BENCHMARK "Build benchmark." OFF)
option(ENTT_BUILD_EXAMPLE "Build examples." OFF)
option(ENTT_BUILD_LIB "Build lib tests." OFF)
option(ENTT_BUILD_SNAPSHOT "Build snapshot test with Cereal." OFF)
if(ENTT_BUILD_TESTING OR ENTT_BUILD_TESTBED)
set(ENTT_ID_TYPE std::uint32_t CACHE STRING "Type of identifiers to use for tests and testbed")
set(ENTT_CXX_STD cxx_std_17 CACHE STRING "C++ standard revision to use for tests and testbed")
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)
endif()
# Tools
option(ENTT_BUILD_TOOLS "Enable building tools." OFF)
if(ENTT_BUILD_TOOLS)
add_subdirectory(tools)
# Tests and tesetbed do not work together because SDL gets confused with EnTT tests
if(ENTT_BUILD_TESTING)
option(ENTT_FIND_GTEST_PACKAGE "Enable finding gtest package." OFF)
option(ENTT_BUILD_BENCHMARK "Build benchmark." OFF)
option(ENTT_BUILD_EXAMPLE "Build examples." OFF)
option(ENTT_BUILD_LIB "Build lib tests." OFF)
option(ENTT_BUILD_SNAPSHOT "Build snapshot test with Cereal." OFF)
include(CTest)
enable_testing()
add_subdirectory(test)
elseif(ENTT_BUILD_TESTBED)
add_subdirectory(testbed)
endif()
endif()
# Documentation
@@ -318,9 +354,5 @@ endif()
option(ENTT_BUILD_DOCS "Enable building with documentation." OFF)
if(ENTT_BUILD_DOCS)
find_package(Doxygen 1.8)
if(DOXYGEN_FOUND)
add_subdirectory(docs)
endif()
add_subdirectory(docs)
endif()

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2017-2023 Michele Caini, author of EnTT
Copyright (c) 2017-2025 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

@@ -1,8 +1,4 @@
module(
name = "entt",
version = "3.12.2",
compatibility_level = 3012,
)
module(name = "entt")
bazel_dep(name = "rules_cc", version = "0.0.8")
bazel_dep(name = "bazel_skylib", version = "1.4.2")

View File

@@ -1,8 +1,5 @@
![EnTT: Gaming meets modern C++](https://user-images.githubusercontent.com/1812216/103550016-90752280-4ea8-11eb-8667-12ed2219e137.png)
<!--
@cond TURN_OFF_DOXYGEN
-->
[![Build Status](https://github.com/skypjack/entt/workflows/build/badge.svg)](https://github.com/skypjack/entt/actions)
[![Coverage](https://codecov.io/gh/skypjack/entt/branch/master/graph/badge.svg)](https://codecov.io/gh/skypjack/entt)
[![Try online](https://img.shields.io/badge/try-online-brightgreen)](https://godbolt.org/z/zxW73f)
@@ -24,7 +21,7 @@ in [**Minecraft**](https://minecraft.net/en-us/attribution/) by Mojang, the
[**ArcGIS Runtime SDKs**](https://developers.arcgis.com/arcgis-runtime/) by Esri
and the amazing [**Ragdoll**](https://ragdolldynamics.com/).<br/>
If you don't see your project in the list, please open an issue, submit a PR or
add the [#entt](https://github.com/topics/entt) tag to your _topics_! :+1:
add the [\#entt](https://github.com/topics/entt) tag to your _topics_! :+1:
---
@@ -64,9 +61,6 @@ Many thanks to [these people](https://skypjack.github.io/sponsorship/) and
* [EnTT in Action](#entt-in-action)
* [Contributors](#contributors)
* [License](#license)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
@@ -238,20 +232,26 @@ build system of the library.
## CMake
To use `EnTT` from a `CMake` project, just link an existing target to the
`EnTT::EnTT` alias.<br/>
`EnTT::EnTT` alias.
The library offers everything you need for locating (as in `find_package`),
embedding (as in `add_subdirectory`), fetching (as in `FetchContent`) or using
it in many of the ways that you can think of and that involve `CMake`.<br/>
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.
Covering all possible cases would require a treatise 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.
Note that all `install` calls are guarded by the `ENTT_INSTALL` option to allow
using `EnTT` as a submodule without conflicting with user logic.<br/>
It is therefore necessary to set the option to true to take advantage of the
installation logic provided by this library.
## 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/>
in the `natvis` subdirectory, divided by module.<br/>
If you spot errors or have suggestions, any contribution is welcome!
## Packaging Tools
@@ -354,16 +354,10 @@ To navigate it with your favorite browser:
$ cd build
$ your_favorite_browser docs/html/index.html
<!--
@cond TURN_OFF_DOXYGEN
-->
The same version is also available [online](https://skypjack.github.io/entt/)
for the latest release, that is the last stable tag.<br/>
Moreover, there exists a [wiki](https://github.com/skypjack/entt/wiki) dedicated
to the project where users can find all related documentation pages.
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Tests
@@ -380,9 +374,6 @@ To build the most basic set of tests:
Note that benchmarks are not part of this set.
<!--
@cond TURN_OFF_DOXYGEN
-->
# EnTT in Action
`EnTT` is widely used in private and commercial applications. I cannot even
@@ -400,7 +391,7 @@ open an issue or a PR and I'll be glad to add them to the list.
# Contributors
Requests for features, PRs, suggestions ad feedback are highly appreciated.
Requests for features, PRs, suggestions and feedback are highly appreciated.
If you find you can help and want to contribute to the project with your
experience or you do want to get part of the project for some other reason, feel
@@ -410,18 +401,15 @@ I can't promise that each and every contribution will be accepted, but I can
assure that I'll do my best to take them all as soon as possible.
If you decide to participate, please see the guidelines for
[contributing](CONTRIBUTING.md) before to create issues or pull
requests.<br/>
[contributing](https://github.com/skypjack/entt/blob/master/CONTRIBUTING.md)
before to create issues or pull requests.<br/>
Take also a look at the
[contributors list](https://github.com/skypjack/entt/blob/master/AUTHORS) to
know who has participated so far.
<!--
@endcond TURN_OFF_DOXYGEN
-->
# License
Code and documentation Copyright (c) 2017-2023 Michele Caini.<br/>
Code and documentation Copyright (c) 2017-2025 Michele Caini.<br/>
Colorful logo Copyright (c) 2018-2021 Richard Caseres.
Code released under

34
TODO
View File

@@ -10,22 +10,28 @@ DOC:
* bump entities, reserved bits on identifiers
TODO:
* resource cache: avoid using shared ptr with loader and the others
* further optimize exclusion lists in multi type views (no existence check)
* further improve meta resolve function by id (bimap)
* get rid of observers, storage based views made them pointless - document alternatives
* deprecate non-owning groups in favor of owning views and view packs, introduce lazy owning views
* review all NOLINT
* bring nested groups back in place (see bd34e7f)
* work stealing job system (see #100) + mt scheduler based on const awareness for types
* view: reduce inst due to/improve perf with index-based approach in dispatch_get/pick_and_each/each (single type too, define storage ::at and ::at_as_tuple)
* view: update natvis as needed after the last rework, merge pools/filter in the same array, drop check (?) and turn view into a position
* view: type-only view_iterator (dyn get/excl sizes), type-only basic_common_view (dyn get/excl sizes with pointer to array from derived)
* combine version-mask-vs-version-bits tricks with reserved bits to allow things like enabling/disabling
* basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) uses moved-from packed in assert (same for storage)
* maybe drop begin(v)/end(v) from sparse set and only use scoped begin/end at call sites
* view unchecked_refresh loop is never executed if Get is 1u
* review all // NOLINT + linter workflow + review clang-tidy file
* self contained entity traits to avoid explicit specializations (ie enum constants)
* review assure conflicts check, hash doesn't fit the purpose maybe
* allow to zero-sized versions (with non-regression tests)
* auto type info data from types if present
* storage entity: fast range-push from above
* table: pop back to support swap and pop, single column access, empty type optimization
* review cmake warning about FetchContent_Populate (need .28 and EXCLUDE_FROM_ALL for FetchContent)
* suppress -Wself-move on CI with g++13
* runtime types support for meta for types that aren't backed by C++ types
* built-in no-pagination storage - no_pagination page size as limits::max
* any cdynamic to support const ownership construction
* allow passing arguments to meta setter/getter (we can fallback on meta invoke probably)
* FetchContent_Populate -> FetchContent_MakeAvailable warnings
* doc: IMPLICIT_DIR_DOCS for dir docs or \dir
* meta non-const allow_cast overloads: (const int &) to (int &) is not allowed, but (const int &) to (double &) is allowed (support only for convertibles)
* review build process for testbed (i.e. tests first due to SDL)
* use unique_ptr or any for meta_custom_node
* paged vector as a standalone class
* resource: shared_from_this?
* finish the imgui viewer/editor!
* archetype-like a-là EnTT support (see my own notes)
* meta: conversion_helper machinery has lot of room for improvements
* organizer: view/storage only based model, no registry

View File

View File

@@ -15,7 +15,7 @@ void update(entt::registry &registry) {
auto view = registry.view<position, velocity>();
for(auto entity: view) {
// gets only the components that are going to be used ...
// gets only the elements that are going to be used ...
auto &vel = view.get<velocity>(entity);
@@ -28,7 +28,7 @@ void update(entt::registry &registry) {
void update(std::uint64_t dt, entt::registry &registry) {
registry.view<position, velocity>().each([dt](auto &pos, auto &vel) {
// gets all the components of the view at once ...
// gets all the elements of the view at once ...
pos.x += vel.dx * dt;
pos.y += vel.dy * dt;

View File

@@ -1,54 +1,59 @@
# Doxygen configuration (documentation)
include(FetchContent)
find_package(Doxygen 1.13)
FetchContent_Declare(
doxygen-awesome-css
GIT_REPOSITORY https://github.com/jothepro/doxygen-awesome-css
GIT_TAG main
GIT_SHALLOW 1
)
if(DOXYGEN_FOUND)
include(FetchContent)
FetchContent_GetProperties(doxygen-awesome-css)
FetchContent_Declare(
doxygen-awesome-css
GIT_REPOSITORY https://github.com/jothepro/doxygen-awesome-css
GIT_TAG main
GIT_SHALLOW 1
)
if(NOT doxygen-awesome-css_POPULATED)
FetchContent_Populate(doxygen-awesome-css)
set(doxygen-awesome-css_INCLUDE_DIR ${doxygen-awesome-css_SOURCE_DIR})
FetchContent_GetProperties(doxygen-awesome-css)
if(NOT doxygen-awesome-css_POPULATED)
FetchContent_Populate(doxygen-awesome-css)
set(doxygen-awesome-css_INCLUDE_DIR ${doxygen-awesome-css_SOURCE_DIR})
endif()
set(DOXY_SOURCE_DIRECTORY ${EnTT_SOURCE_DIR}/src)
set(DOXY_CSS_DIRECTORY ${doxygen-awesome-css_INCLUDE_DIR})
set(DOXY_DOCS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
set(DOXY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
configure_file(doxy.in doxy.cfg @ONLY)
add_custom_target(
docs ALL
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxy.cfg
WORKING_DIRECTORY ${EnTT_SOURCE_DIR}
VERBATIM
SOURCES
md/config.md
md/container.md
md/core.md
md/entity.md
md/faq.md
md/lib.md
md/links.md
md/locator.md
md/meta.md
md/poly.md
md/process.md
md/reference.md
md/resource.md
md/signal.md
md/unreal.md
doxy.in
)
if(ENTT_INSTALL)
install(
DIRECTORY ${DOXY_OUTPUT_DIRECTORY}/html
DESTINATION share/${PROJECT_NAME}-${PROJECT_VERSION}/
)
endif()
endif()
set(DOXY_SOURCE_DIRECTORY ${EnTT_SOURCE_DIR}/src)
set(DOXY_CSS_DIRECTORY ${doxygen-awesome-css_INCLUDE_DIR})
set(DOXY_DOCS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
set(DOXY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
configure_file(doxy.in doxy.cfg @ONLY)
add_custom_target(
docs ALL
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxy.cfg
WORKING_DIRECTORY ${EnTT_SOURCE_DIR}
VERBATIM
SOURCES
dox/extra.dox
md/config.md
md/container.md
md/core.md
md/entity.md
md/faq.md
md/lib.md
md/links.md
md/locator.md
md/meta.md
md/poly.md
md/process.md
md/reference.md
md/resource.md
md/signal.md
md/unreal.md
doxy.in
)
install(
DIRECTORY ${DOXY_OUTPUT_DIRECTORY}/html
DESTINATION share/${PROJECT_NAME}-${PROJECT_VERSION}/
)

View File

@@ -1,5 +0,0 @@
/**
* @namespace entt
*
* @brief `EnTT` default namespace.
*/

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,5 @@
# Crash Course: configuration
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
@@ -17,9 +14,6 @@
* [ENTT_DISABLE_ASSERT](#entt_disable_assert)
* [ENTT_NO_ETO](#entt_no_eto)
* [ENTT_STANDARD_CPP](#entt_standard_cpp)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
@@ -28,14 +22,14 @@ 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
found below.
with which it is possible to adjust the behavior of the library at runtime can
be found below.
# Definitions
All options are intended as parameters to the compiler (or user-defined macros
within the compilation units, if preferred).<br/>
Each parameter can result in internal library definitions. It's not recommended
Each parameter can result in internal library definitions. It is not recommended
to try to also modify these definitions, since there is no guarantee that they
will remain stable over time unlike the options below.
@@ -48,11 +42,12 @@ also limited to this library only.
## ENTT_USE_ATOMIC
In general, `EnTT` doesn't offer primitives to support multi-threading. Many of
In general, `EnTT` does not 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 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.
However, some internal static data shared between threads should be atomic when
using `EnTT` from multiple threads, even when dealing with local storage. Define
this macro without assigning any value to it to get the job done.
## ENTT_ID_TYPE
@@ -63,24 +58,25 @@ default type if necessary.
## ENTT_SPARSE_PAGE
It's known that the ECS module of `EnTT` is based on _sparse sets_. What is less
known perhaps is that the sparse arrays are paged to reduce memory usage.<br/>
It is known that the ECS module of `EnTT` is based on _sparse sets_. What is
less known perhaps is that the sparse arrays are paged to reduce memory
usage.<br/>
Default size of pages (that is, the number of elements they contain) is 4096 but
users can adjust it if appropriate. In all case, the chosen value **must** be a
users can adjust it if appropriate. In all cases, the chosen value **must** be a
power of 2.
## ENTT_PACKED_PAGE
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
this case the aim is not 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
users can adjust it if appropriate. In all cases, the chosen value **must** be a
power of 2.
## ENTT_ASSERT
For performance reasons, `EnTT` doesn't use exceptions or any other control
For performance reasons, `EnTT` does not 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
@@ -89,7 +85,7 @@ 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
Usually, an assert within a `constexpr` function is not 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
@@ -115,6 +111,6 @@ 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 prevents the library from using non-standard techniques, that
is, functionalities that aren't fully compliant with the standard C++.<br/>
is, functionalities that are not 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.

View File

@@ -1,32 +1,30 @@
# 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
-->
* [Adaptors](#adaptors)
* [Table](#table)
# 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 standard C++ library offers a wide range of containers and adaptors already.
It is really difficult to do better (although it is very easy to do worse, as
many examples available online demonstrate).<br/>
`EnTT` does not 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 functionalities by
making available some containers initially developed for internal use.
making available some containers and adaptors 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.
moment it is quite small and mainly aimed at satisfying some internal
needs.<br/>
For all containers and adaptors made available, full test coverage and stability
over time is guaranteed as usual.
# Containers
@@ -65,3 +63,23 @@ The interface is in all respects similar to its counterpart in the standard
library, that is, the `std::unordered_set` class.<br/>
However, this type of set also supports reverse iteration and therefore offers
all the functions necessary for the purpose (such as `rbegin` and `rend`).
# Adaptors
## Table
The `basic_table` class is a container adaptor which manages multiple sequential
containers together, treating them as different columns of the same table.<br/>
The `table` alias allows users to provide only the types to handle, using
`std::vector` as the default sequential container.
Only a small set of functions is provided, although very close to what the API
of the `std::vector` class offers.<br/>
The internal implementation is purposely supported by a tuple of containers
rather than a container of tuples. The purpose is to allow efficient access to
single columns and not just access to the entire data set of the table.
When a row is accessed, all data is returned in the form of a tuple containing
(possibly const) references to the elements of the row itself.<br/>
Similarly, when a table is iterated, tuples of references to table elements are
returned for each row.

View File

@@ -1,25 +1,22 @@
# Crash Course: core functionalities
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [Any as in any type](#any-as-in-any-type)
* [Small buffer optimization](#small-buffer-optimization)
* [Alignment requirement](#alignment-requirement)
* [Bit](#bit)
* [Compressed pair](#compressed-pair)
* [Enum as bitmask](#enum-as-bitmask)
* [Hashed strings](#hashed-strings)
* [Wide characters](wide-characters)
* [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)
@@ -39,15 +36,12 @@
* [Compile-time generator](#compile-time-generator)
* [Runtime generator](#runtime-generator)
* [Utilities](#utilities)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
`EnTT` comes with a bunch of core functionalities mostly used by the other parts
of the library.<br/>
Many of these tools are also useful in everyday work. Therefore, it's worth
Many of these tools are also useful in everyday work. Therefore, it is worth
describing them so as not to reinvent the wheel in case of need.
# Any as in any type
@@ -55,7 +49,7 @@ describing them so as not to reinvent the wheel in case of need.
`EnTT` offers 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
`std::type_info`, an implementation defined class that is not something everyone
wants to see in a software. Furthermore, there is no way to bind it to the type
system of the library and therefore with its integrated RTTI support.
@@ -74,22 +68,25 @@ 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};
// in place type construction
entt::any in_place_type{std::in_place_type<int>, 42};
// take ownership of already existing, dynamically allocated objects
entt::any in_place{std::in_place, std::make_unique<int>(42).release()};
```
Alternatively, the `make_any` function serves the same purpose but requires to
always be explicit about the type:
Alternatively, the `make_any` function serves the same purpose. It requires to
always be explicit about the type and does not support taking ownership:
```cpp
entt::any any = entt::make_any<int>(42);
```
In both cases, the `any` class takes the burden of destroying the contained
In all 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
Furthermore, an instance of `any` is not tied to an actual type. Therefore, the
wrapper is reconfigured when it is assigned a new object of a type other than
the one it contains.
There is also a way to directly assign a value to the variable contained by an
@@ -121,8 +118,8 @@ The type is also used internally when comparing two `any` objects:
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/>
In this case, before proceeding with a comparison, it is 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 the possible risks of a comparison.
@@ -141,9 +138,9 @@ 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
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
Similarly, it is possible to create non-owning copies of `any` from an existing
object:
```cpp
@@ -151,12 +148,12 @@ object:
entt::any ref = other.as_ref();
```
In this case, it doesn't matter if the original container actually holds an
In this case, it does not matter if the original container actually holds an
object or is as a reference for unmanaged elements already. The new instance
thus created doesn't create copies and only serves as a reference for the
thus created does not create copies and only serves as a reference for the
original item.
It's worth mentioning that, while everything works transparently when it comes
It is 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
@@ -164,9 +161,9 @@ In particular, the `data` member function invoked on a non-const instance of
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`, they 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.
The only difference is that, in the case of `EnTT`, they will not 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
@@ -191,8 +188,8 @@ and always dynamically allocates objects (except for aliasing cases).
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/>
It's provided as an optional second parameter following the desired size for the
internal storage:
It 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])>;
@@ -202,6 +199,22 @@ 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.
# Bit
Finding out the population count of an unsigned integral value (`popcount`),
whether a number is a power of two or not (`has_single_bit`) as well as the next
power of two given a random value (`next_power_of_two`) can be useful.<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.
# Compressed pair
Primarily designed for internal use and far from being feature complete, the
@@ -221,16 +234,16 @@ 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.
There is not much to describe then. It is recommended to rely on documentation
and intuition. At the end of the day, it is 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. Main problem is that they don't convert
implicitly to their underlying type.<br/>
Sometimes it is useful to be able to use enums as bitmasks. However, enum
classes are not really suitable for the purpose. Main problem is that they do
not convert implicitly to their underlying type.<br/>
The choice is then between using old-fashioned enums (with all their problems
that I don't want to discuss here) or writing _ugly_ code.
that I do not 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 bitmasks transparently.<br/>
@@ -263,7 +276,7 @@ struct entt::enum_as_bitmask<my_flag>
```
This is handy when dealing with enum classes defined by third party libraries
and over which the user has no control. However, it's also verbose and can be
and over which the user has no control. However, it is also verbose and can be
avoided by adding a specific value to the enum class itself:
```cpp
@@ -326,7 +339,7 @@ entt::hashed_string str{orig.c_str()};
const auto hash = entt::hashed_string::value(orig.c_str());
```
This possibility shouldn't be exploited in tight loops, since the computation
This possibility should not be exploited in tight loops, since the computation
takes place at runtime and no longer at compile-time. It could therefore affect
performance to some degrees.
@@ -349,16 +362,16 @@ The hash type of `hashed_wstring` is the same as its counterpart.
The hashed string class uses FNV-1a internally to hash strings. Because of the
_pigeonhole principle_, conflicts are possible. This is a fact.<br/>
There is no silver bullet to solve the problem of conflicts when dealing with
hashing functions. In this case, the best solution is likely to give up. That's
hashing functions. In this case, the best solution is likely to give up. That is
all.<br/>
After all, human-readable unique identifiers aren't something strictly defined
After all, human-readable unique identifiers are not something strictly defined
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. More often than not it
Writing and working with iterators is not always easy. More often than not it
also leads to duplicated code.<br/>
`EnTT` tries to overcome this problem by offering some utilities designed to
make this hard work easier.
@@ -366,12 +379,12 @@ 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/>
dereferenced, it is 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
The input iterator pointer is meant for this. It is 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:
@@ -423,7 +436,7 @@ _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
offer a method for 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.
@@ -439,29 +452,13 @@ 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
I will not 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/>
support allocators while unique pointers do not.<br/>
There is a proposal at the moment that also shows (among the other things) how
this can be implemented without any compiler support.
@@ -469,7 +466,7 @@ 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);
std::unique_ptr<my_type, entt::allocation_deleter<allocator_type>> ptr = entt::allocate_unique<my_type>(allocator, arguments);
```
Although the internal implementation is slightly different from what is proposed
@@ -512,9 +509,9 @@ library or that will never be.
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
of the reasons for this, it is 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
The library tries to fill this gap by offering a built-in system that does not
serve as a replacement but comes very close to being one and offers similar
information to that provided by its counterpart.
@@ -526,7 +523,7 @@ Basically, the whole system relies on a handful of classes. In particular:
auto index = entt::type_index<a_type>::value();
```
The returned value isn't guaranteed to be stable across different runs.<br/>
The returned value is not guaranteed to be stable across different runs.<br/>
However, it can be very useful as index in associative and unordered
associative containers or for positional accesses in a vector or an array.
@@ -554,8 +551,8 @@ Basically, the whole system relies on a handful of classes. In particular:
```
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).
but this is not guaranteed for all compilers and platforms (although it is
valid with the most well-known and popular ones).
This function **can** use non-standard features of the language for its own
purposes. This makes it possible to provide compile-time identifiers that
@@ -577,7 +574,7 @@ Basically, the whole system relies on a handful of classes. In particular:
This value is extracted from some information generally made available by the
compiler in use. Therefore, it may differ depending on the compiler and may be
empty in the event that this information isn't available.<br/>
empty in the event that this information is not available.<br/>
For example, given the following class:
```cpp
@@ -589,7 +586,7 @@ Basically, the whole system relies on a handful of classes. In particular:
Most of the time the name is also retrieved at compile-time and is therefore
always returned through an `std::string_view`. Users can easily access it and
modify it as needed, for example by removing the word `struct` to normalize
the result. `EnTT` doesn't do this for obvious reasons, since it would be
the result. `EnTT` does not do this for obvious reasons, since it would be
creating a new string at runtime otherwise.
This function **can** use non-standard features of the language for its own
@@ -605,8 +602,8 @@ similar to that made available by the standard library.
### 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
The `type_info` class is not a drop-in replacement for `std::type_info` but can
provide similar information which are not implementation defined and do not
require to enable RTTI.<br/>
Therefore, they can sometimes be even more reliable than those obtained
otherwise.
@@ -642,7 +639,7 @@ These are the information made available by `type_info`:
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();
auto idx = entt::type_index<std::remove_const_t<std::remove_reference_t<a_type>>>::value();
```
* The hash value associated with a given type:
@@ -654,7 +651,7 @@ These are the information made available by `type_info`:
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();
auto hash = entt::type_hash<std::remove_const_t<std::remove_reference_t<a_type>>>::value();
```
* The name associated with a given type:
@@ -666,7 +663,7 @@ These are the information made available by `type_info`:
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();
auto name = entt::type_name<std::remove_const_t<std::remove_reference_t<a_type>>>::value();
```
Where all accessed features are available at compile-time, the `type_info` class
@@ -679,7 +676,7 @@ described above.
Since the default non-standard, compile-time implementation of `type_hash` makes
use of hashed strings, it may happen that two types are assigned the same hash
value.<br/>
In fact, although this is quite rare, it's not entirely excluded.
In fact, although this is quite rare, it is not entirely excluded.
Another case where two types are assigned the same identifier is when classes
from different contexts (for example two or more libraries loaded at runtime)
@@ -688,8 +685,9 @@ value for the two types.<br/>
Fortunately, there are several easy ways to deal with this:
* The most trivial one is to define the `ENTT_STANDARD_CPP` macro. Runtime
identifiers don't suffer from the same problem in fact. However, this solution
doesn't work well with a plugin system, where the libraries aren't linked.
identifiers do not suffer from the same problem in fact. However, this
solution does not work well with a plugin system, where the libraries are not
linked.
* Another possibility is to specialize the `type_name` class for one of the
conflicting types, in order to assign it a custom identifier. This is probably
@@ -698,8 +696,8 @@ Fortunately, there are several easy ways to deal with this:
* A fully customized identifier generation policy (based for example on enum
classes or preprocessing steps) may represent yet another option.
These are just some examples of possible approaches to the problem but there are
many others. As already mentioned above, since users have full control over
These are just some examples of possible approaches to the problem, but there
are many others. As already mentioned above, since users have full control over
their types, this problem is in any case easy to solve and should not worry too
much.<br/>
In all likelihood, it will never happen to run into a conflict anyway.
@@ -715,10 +713,10 @@ offered by this module.
### Size of
The standard operator `sizeof` complains if users provide it with functions or
incomplete types. On the other hand, it's guaranteed that its result is always
incomplete types. On the other hand, it is guaranteed that its result is always
non-zero, even if applied to an empty class type.<br/>
This small class combines the two and offers an alternative to `sizeof` that
works under all circumstances, returning zero if the type isn't supported:
works under all circumstances, returning zero if the type is not supported:
```cpp
const auto size = entt::size_of_v<void>;
@@ -753,7 +751,7 @@ using type = entt::constness_as_t<dst_type, const src_type>;
```
The trait is subject to the rules of the language. For example, _transferring_
constness between references won't give the desired effect.
constness between references will not give the desired effect.
### Member class type
@@ -811,7 +809,7 @@ where types would be required otherwise. As an example:
registry.emplace<entt::tag<"enemy"_hs>>(entity);
```
However, this isn't the only permitted use. Literally any value convertible to
However, this is not the only permitted use. Literally any value convertible to
`id_type` is a good candidate, such as the named constants of an unscoped enum.
### Type list and value list
@@ -833,7 +831,7 @@ type list:
* `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
I am also pretty sure that more and more utilities will be added over time as
needs become apparent.<br/>
Many of these functionalities also exist in their version dedicated to value
lists. We therefore have `value_list_element[_v]` as well as
@@ -841,11 +839,11 @@ lists. We therefore have `value_list_element[_v]` as well as
# Unique sequential identifiers
Sometimes it's useful to be able to give unique, sequential numeric identifiers
Sometimes it is 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.
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 embrace what modern C++ has to offer.
## Compile-time generator
@@ -875,12 +873,13 @@ 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:
stable across different runs. If used in a production environment where a type
needs to be removed, a placeholder can help to leave the other identifiers the
same:
```cpp
template<typename> struct ignore_type {};
template<typename>
struct ignore_type {};
using id = entt::ident<
a_type_still_valid,
@@ -889,7 +888,7 @@ using id = entt::ident<
>;
```
Perhaps a bit ugly to see in a codebase but it gets the job done at least.
Perhaps a bit ugly to see in a codebase, but it gets the job done at least.
## Runtime generator
@@ -911,17 +910,17 @@ numeric identifier for the given type.<br/>
The generator is customizable, so as to get different _sequences_ for different
purposes if needed.
Identifiers aren't guaranteed to be stable across different runs. Indeed it
Identifiers are not 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
It is not possible to escape the temptation to add utilities of some kind to a
library. In fact, `EnTT` also provides a handful of tools to simplify the
life of developers:
* `entt::identity`: the identity function object that will be available with
C++20. It returns its argument unchanged and nothing more. It's useful as a
C++20. It returns its argument unchanged and nothing more. It is useful as a
sort of _do nothing_ function in template programming.
* `entt::overload`: a tool to disambiguate different overloads from their
@@ -967,7 +966,8 @@ life of developers:
callable object that supports multiple types at once.
* `entt::y_combinator`: this is a C++ implementation of **the** _y-combinator_.
If it's not clear what it is, there is probably no need for this utility.<br/>
If it is not clear what it is, there is probably no need for this
utility.<br/>
Below is a small example to show its use:
```cpp
@@ -978,9 +978,9 @@ life of developers:
const auto result = gauss(3u);
```
Maybe convoluted at a first glance but certainly effective. Unfortunately,
the language doesn't make it possible to do much better.
Maybe convoluted at first glance but certainly effective. Unfortunately,
the language does not make it possible to do much better.
This is a rundown of the (actually few) utilities made available by `EnTT`. The
list will probably grow over time but the size of each will remain rather small,
as has been the case so far.
list will probably grow over time, but the size of each will remain rather
small, as has been the case so far.

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,5 @@
# Frequently Asked Questions
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
@@ -10,21 +7,17 @@
* [Why is my debug build on Windows so slow?](#why-is-my-debug-build-on-windows-so-slow)
* [How can I represent hierarchies with my components?](#how-can-i-represent-hierarchies-with-my-components)
* [Custom entity identifiers: yay or nay?](#custom-entity-identifiers-yay-or-nay)
* [Warning C4307: integral constant overflow](#warning-C4307-integral-constant-overflow)
* [Warning C4003: the min, the max and the macro](#warning-C4003-the-min-the-max-and-the-macro)
* [Warning C4003: the min, the max and the macro](#warning-c4003-the-min-the-max-and-the-macro)
* [The standard and the non-copyable types](#the-standard-and-the-non-copyable-types)
* [Which functions trigger which signals](#which-functions-trigger-which-signals)
* [Duplicate storage for the same component](#duplicate-storage-for-the-same-component)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
This is a constantly updated section where I'm trying to put the answers to the
This is a constantly updated section where I am 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, you can
If you do not find your answer here, there are two cases: nobody has done it
yet, 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/>
@@ -36,12 +29,12 @@ part of the documentation.
## Why is my debug build on Windows so slow?
`EnTT` is an experimental project that I also use to keep me up-to-date with the
latest revision of the language and the standard library. For this reason, it's
likely that some classes you're working with are using standard containers under
the hood.<br/>
Unfortunately, it's known that the standard containers aren't particularly
latest revision of the language and the standard library. For this reason, it is
likely that some classes you are working with are using standard containers
under the hood.<br/>
Unfortunately, it is known that the standard containers are not particularly
performing in debugging (the reasons for this go beyond this document) and are
even less so on Windows apparently. Fortunately this can also be mitigated a
even less so on Windows, apparently. Fortunately, this can also be mitigated a
lot, achieving good results in many cases.
First of all, there are two things to do in a Windows project:
@@ -60,7 +53,7 @@ macro to disable internal debug checks in `EnTT`:
#define ENTT_ASSERT(...) ((void)0)
```
These asserts are introduced to help the users but they require to access to the
These asserts are introduced to help the users, but they require access to the
underlying containers and therefore risk ruining the performance in some cases.
With these changes, debug performance should increase enough in most cases. If
@@ -71,15 +64,15 @@ preferably `O1`.
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 the best one depends mainly on
the real problem one is facing. In all cases, how to do it doesn't strictly
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 does not 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 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/>
come in the future.<br/>
In addition, `EnTT` also offers the possibility to create stable storage types
and therefore have pointer stability for one, all or some components. This is by
far the most convenient solution when it comes to creating hierarchies and
@@ -90,7 +83,7 @@ what concerns the `component_traits` class for further details.
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
* If `std::uint32_t` is not large enough for your purposes, since this is the
underlying type of `entt::entity`.
* If you want to avoid conflicts when using multiple registries.
@@ -105,40 +98,14 @@ enum class entity: std::uint32_t {};
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 (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.
To suppress it and if you don't want to suppress all the other warnings as well,
here is a workaround in the form of a macro:
```cpp
#if defined(_MSC_VER)
#define HS(str) __pragma(warning(suppress:4307)) entt::hashed_string{str}
#else
#define HS(str) entt::hashed_string{str}
#endif
```
With an example of use included:
```cpp
constexpr auto identifier = HS("my/resource/identifier");
```
Thanks to [huwpascoe](https://github.com/huwpascoe) for the courtesy.
## Warning C4003: the min, the max and the macro
On Windows, a header file defines two macros `min` and `max` which may result in
conflicts with their counterparts in the standard library and therefore in
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 is a pretty big problem. However, fortunately it is not a problem of `EnTT`
and there is a fairly simple solution to it.<br/>
It consists in defining the `NOMINMAX` macro before including any other header
so as to get rid of the extra definitions:
@@ -152,9 +119,9 @@ 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. 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/>
component is actually copyable. However, this trait does not 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 that is conditionally
@@ -196,7 +163,7 @@ to mitigate the problem makes it manageable.
Storage classes offer three _signals_ that are emitted following specific
operations. Maybe not everyone knows what these operations are, though.<br/>
If this isn't clear, below you can find a _vademecum_ for this purpose:
If this is not 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.
@@ -207,7 +174,7 @@ If this isn't clear, below you can find a _vademecum_ for this purpose:
from an entity.
Among the most controversial functions can be found `emplace_or_replace` and
`destroy`. However, following the above rules, it's quite simple to know what
`destroy`. However, following the above rules, it is quite simple to know what
will happen.<br/>
In the first case, `on_created` is invoked if the entity has not the component,
otherwise the latter is replaced and therefore `on_update` is triggered. As for
@@ -217,9 +184,9 @@ owned by the entity that is destroyed.
## Duplicate storage for the same component
It's rare but you can see double sometimes, especially when it comes to storage.
This can be caused by a conflict in the hash assigned to the various component
types (one of a kind) or by bugs in your compiler
It is rare, but you can see double sometimes, especially when it comes to
storage. This can be caused by a conflict in the hash assigned to the various
component types (one of a kind) or by bugs in your compiler
([more common](https://github.com/skypjack/entt/issues/1063) apparently).<br/>
Regardless of the cause, `EnTT` offers a customization point that also serves as
a solution in this case:

View File

@@ -1,8 +1,5 @@
# Crash Course: graph
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
@@ -14,26 +11,24 @@
* [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/>
`EnTT` does not 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 though. 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
As anticipated in the introduction, the aim is not 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
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.
development and support of some tools that are also part of the same submodule.
## Adjacency matrix
@@ -114,11 +109,11 @@ Both the 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 functionalities one would expect from this type of containers, such as
`clear` or 'get_allocator` and so on.
`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
As it is 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:
@@ -128,7 +123,7 @@ std::ostringstream output{};
entt::dot(output, adjacency_matrix);
```
It's also possible to provide a callback to which the vertices are passed and
It is also possible to provide a callback to which the vertices are passed and
which can be used to add (`dot`) properties to the output as needed:
```cpp
@@ -145,7 +140,7 @@ externally managed data 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
The implementation is as generic as possible and does not bind to any other part
of the library.
This class is designed as a sort of _state machine_ to which a specific task is
@@ -171,7 +166,7 @@ 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 is created (which requires no constructor arguments), the
first thing to do is to bind a task. This tells to the builder _who_ intends to
first thing to do is to bind a task. This tells the builder _who_ intends to
consume the resources that are specified immediately after:
```cpp
@@ -181,15 +176,15 @@ builder.bind("task_1"_hs);
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 isn't by accident. In fact,
it matches well with the internal hashed string class. Moreover, it's also the
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 is 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 necessary.
Once a task is associated with the flow builder, it's also assigned read-only or
read-write resources as appropriate:
Once a task is associated with the flow builder, it has also assigned read-only
or read-write resources as appropriate:
```cpp
builder
@@ -200,7 +195,7 @@ builder
.rw("resource_2"_hs)
```
As mentioned, many functions return the builder itself and it's therefore easy
As mentioned, many functions return the builder itself, and it is therefore easy
to concatenate the different calls.<br/>
Also in the case of resources, they are identified by numeric values of type
`id_type`. As above, the choice is not entirely random. This goes well with the
@@ -214,13 +209,13 @@ pair of iterators, so that one can pass a range of resources in one go.
The `flow` class is resource based rather than task based. This means that graph
generation is driven by resources and not by the order of _appearance_ of tasks
during flow definition.<br/>
Although this concept is particularly important, it's almost irrelevant for the
Although this concept is particularly important, it is almost irrelevant for the
vast majority of cases. However, it becomes relevant when _rebinding_ resources
or tasks.
In fact, nothing prevents rebinding elements to a flow.<br/>
However, the behavior changes slightly from case to case and has some nuances
that it's worth knowing about.
that it is worth knowing about.
Directly rebinding a resource without the task being replaced trivially results
in the task's access mode for that resource being updated:
@@ -231,7 +226,7 @@ builder.bind("task"_hs).rw("resource"_hs).ro("resource"_hs)
In this case, the resource is accessed in read-only mode, regardless of the
first call to `rw`.<br/>
Behind the scenes, the call doesn't actually _replace_ the previous one but is
Behind the scenes, the call does not actually _replace_ the previous one but is
queued after it instead, overwriting it when generating the graph. Thus, a large
number of resource rebindings may even impact processing times (very difficult
to observe but theoretically possible).
@@ -252,12 +247,12 @@ builder
```
What happens here is that the resource first _sees_ a read-only access request
from the first task, then a read-write request from the second task and finally
a new read-only request from the first task.<br/>
from the first task, then a read-only request from the second task and finally
a read-write request from the first task.<br/>
Although this definition would probably be counted as an error, the resulting
graph may be unexpected. This in fact consists of a single edge outgoing from
graph may be unexpected. In fact, this consists of a single edge outgoing from
the second task and directed to the first task.<br/>
To intuitively understand what happens, it's enough to think of the fact that a
To intuitively understand what happens, it is enough to think of the fact that a
task never has an edge pointing to itself.
While not obvious, this approach has its pros and cons like any other solution.
@@ -278,22 +273,22 @@ As expected, this definition leads to the creation of two edges that define a
loop between the two tasks.
As a general rule, rebinding resources and tasks is highly discouraged because
it could lead to subtle bugs if users don't know what they're doing.<br/>
it could lead to subtle bugs if users do not know what they are doing.<br/>
However, once the mechanisms of resource-based graph generation are understood,
it can offer to the expert user a flexibility and a range of possibilities
it can offer to the expert user flexibility and a range of possibilities
otherwise inaccessible.
## Fake resources and order of execution
The flow builder doesn't offer the ability to specify when a task should execute
The flow builder does not offer the ability to specify when a task should run
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 rule on
the execution order:
than parallel scheduling, it is possible to make use of fake resources to rule
on the execution order:
```cpp
builder
@@ -311,7 +306,7 @@ builder
This snippet forces the execution of `task_1` **before** `task_2` and `task_3`.
This is due to the fact that the former 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:
Similarly, it is possible to force a task to run **after** a certain group:
```cpp
builder
@@ -328,22 +323,22 @@ builder
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.
other tasks.
## Sync points
Sometimes it's useful to assign the role of _sync point_ to a node.<br/>
Sometimes it is 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
assigning this role to a vertex is always the same. First it is 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,
The choice to assign an _identity_ to this type of node 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
If this is not the case, it will still be possible to create no-op vertices to
which empty tasks are assigned.
## Execution graph
@@ -367,6 +362,6 @@ for(auto &&vertex: graph) {
}
```
Then it's possible to instantiate an execution graph by means of other functions
such as `out_edges` to retrieve the children of a given task or `edges` to get
the identifiers.
Then it is possible to instantiate an execution graph by means of other
functions such as `out_edges` to retrieve the children of a given task or
`edges` to get the identifiers.

View File

@@ -1,17 +1,11 @@
# Push EnTT across boundaries
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Working across boundaries](#working-across-boundaries)
* [Smooth until proven otherwise](#smooth-until-proven-otherwise)
* [Meta context](#meta-context)
* [Memory management](#memory-management)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Working across boundaries
@@ -26,31 +20,31 @@ Fortunately, nowadays `EnTT` works smoothly across boundaries.
Many classes in `EnTT` make extensive use of type erasure for their purposes.
This raises the need to identify objects whose type has been erased.<br/>
The `type_hash` class template is how identifiers are generated and thus made
available to the rest of the library. In general, this class doesn't arouse much
available to the rest of the library. In general, this class arouses little
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/>
(definitely uncommon though) or when the default solution proposed by `EnTT` is
not 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.
When working with linked libraries, compile definitions `ENTT_API_EXPORT` and
`ENTT_API_IMPORT` are to import or export symbols, so as to make everything work
nicely across boundaries.<br/>
`ENTT_API_IMPORT` are there 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.
shared libraries that do not export any symbols.
For those who need more details, the test suite contains many 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** possible cases.
It goes without saying that it is 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 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.
Since it is linked already to a static context to which the elements are
attached and different contexts do not relate to each other, they must be
_shared_ to allow the use of meta types across boundaries.
Fortunately, sharing a context is also trivial to do. First of all, the local
one is acquired in the main space:
@@ -59,16 +53,16 @@ one is acquired in the main space:
auto handle = entt::locator<entt::meta_ctx>::handle();
```
Then, it's passed to the receiving space that sets it as its default context,
Then, it is passed to the receiving space that sets it as its default context,
thus discarding or storing aside the local one:
```cpp
entt::locator<entt::meta_ctx>::reset(handle);
```
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 _replacing_ the main context doesn't also propagate changes across
From now on, both spaces refer to the same context and to it are all new meta
types attached, no matter where they are created.<br/>
Note that _replacing_ the main context does not also propagate changes across
boundaries. In other words, replacing a context results in the decoupling of the
two sides and therefore a divergence in the contents.
@@ -87,9 +81,10 @@ is unknown to the former, a dedicated pool is created within the registry on
first use.<br/>
As one can guess, this pool is instantiated on a different side of the boundary
from the `registry`. Therefore, the instance is now managing memory from
different spaces and this can quickly lead to crashes if not properly addressed.
different spaces, and this can quickly lead to crashes if not properly
addressed.
To overcome the risk, it's recommended to use well-defined interfaces that make
To overcome the risk, it is recommended to use well-defined interfaces that make
fundamental types pass through the boundaries, isolating the instances of the
`EnTT` classes from time to time and as appropriate.<br/>
Refer to the test suite for some examples, read the documentation available

View File

@@ -1,8 +1,5 @@
# EnTT in Action
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
@@ -11,25 +8,22 @@
* [Engines and the like](#engines-and-the-like)
* [Articles, videos and blog posts](#articles-videos-and-blog-posts)
* [Any Other Business](#any-other-business)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
`EnTT` is widely used in private and commercial applications. I cannot even
mention most of them because of some signatures I put on some documents time
ago. Fortunately, there are also people who took the time to implement open
source projects based on `EnTT` and didn't hold back when it came to documenting
them.
source projects based on `EnTT` and did not hold back when it came to
documenting them.
Below an incomplete list of games, applications and articles that can be used as
a reference.<br/>
Where I put the word _apparently_ means that the use of `EnTT` is documented but
the authors didn't make explicit announcements or contacted me directly.
the authors did not make explicit announcements or contacted me directly.
If you know of other resources out there that are about `EnTT`, feel free to
open an issue or a PR and I'll be glad to add them to this page.<br/>
open an issue or a PR. I will be glad to add them to this page.<br/>
I hope the following lists can grow much more in the future.
# EnTT in Action
@@ -54,11 +48,11 @@ I hope the following lists can grow much more in the future.
* [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.
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
* [TiltedEvolution](https://github.com/tiltedphoques/TiltedEvolution) 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
@@ -103,20 +97,20 @@ I hope the following lists can grow much more in the future.
by Quake.
* [Destroid](https://github.com/tyrannicaltoucan/destroid): _one-bazillionth_
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
* [Wanderer](https://github.com/albin-johansson/wanderer): a 2D
exploration-based indie game.
* [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
spaceship through a large 2D open world.
* [PokeMaster](https://github.com/utilForever/PokeMaster): Pokémon Battle
simulator using C++ with some reinforcement learning.
* [HomeHearth](https://youtu.be/GrEWl8npL9Y): choose your hero, protect the
town, before it's too late.
town, before it is 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
@@ -137,9 +131,26 @@ I hope the following lists can grow much more in the future.
Interstellar Observation Network (a space shooter game).
* [EnTT Boids](https://github.com/DanielEliasib/entt_boids): a simple boids
implementation using `EnTT` and `Raylib`.
* [PalmRide: After Flight](https://store.steampowered.com/app/2812540/PalmRide_After_Flight/):
an on-rails shooter with retro outrun aesthetics.
* [Exhibition of Speed](https://store.steampowered.com/app/2947450/Exhibition_of_Speed/):
build your own car and go racing.
* [Lichgate](https://buas.itch.io/lichgate): top-down action rogue-like where
users unlock abilities to fight horde of enemies in an endless world.
* [Letalka](https://github.com/dviglo2d/dviglo2d/tree/main/games/letalka):
small demo game with ships and bullets flying everywhere on the screen.
* [Lichgate](https://buas.itch.io/lichgate): step into the robes of a powerful
mage determined to halt the relentless hordes of undead.
* [You Are Circle](https://store.steampowered.com/app/3578190/You_Are_Circle/):
a roguelite top-down shooter with a high-contrast vector line aesthetic.
* [EnTT Dino](https://github.com/omgitsaheadcrab/entt_dino): a Dinosaur Game
clone in C++ using only `SDL2` and `EnTT`.
## Engines and the like:
* [Hazel Engine](https://github.com/TheCherno/Hazel): a work in progress
engine created by [The Cherno](https://github.com/TheCherno/Hazel) during
one of his most famous video series.
* [Aether Engine](https://hadean.com/spatial-simulation/)
[v1.1+](https://docs.hadean.com/v1.1/Licenses/) by
[Hadean](https://hadean.com/): a library designed for spatially partitioning
@@ -185,7 +196,7 @@ I hope the following lists 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
* [OverEngine](https://github.com/OverShifted/OverEngine): an overengineered
game engine.
* [Electro](https://github.com/Electro-Technologies/Electro): high performance
3D game engine with a high emphasis on rendering.
@@ -194,13 +205,13 @@ I hope the following lists can grow much more in the future.
* [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.
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
* [Billy Engine](https://github.com/billy4479/BillyEngine): some kind of 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.
@@ -221,6 +232,21 @@ I hope the following lists can grow much more in the future.
libary that combines its built-in reflection system with `ImGui`.
* [Era Game Engine](https://github.com/EldarMuradov/EraGameEngine): a modern
ECS-based game engine.
* [Core SDK of Trollworks engine](https://github.com/trollworks/sdk-core): 2D
game engine based on procrastination.
* [Rocky](https://github.com/pelicanmapping/rocky): 3D geospatial application
engine.
* [Donner](https://github.com/jwmcglynn/donner): a modern C++20 SVG2 rendering
API with CSS3.
* [Coral Engine](https://github.com/GuusKemperman/CoralEngine): open-source
student engine with the tools to make games in C++ and Visual scripting.
* [Star Engine](https://github.com/HODAKdev/StarEngine): an Advanced C++
DirectX 11 game engine.
* [Darmok](https://github.com/miguelibero/darmok): another C++ game engine.
* [Magique](https://github.com/gk646/magique): 2D game engine for programmers
(or those yet to be).
* [Physecs](https://github.com/thfProjects/Physecs): real-time 3D rigid body
physics simulation built on `EnTT`.
## Articles, videos and blog posts:
@@ -262,7 +288,7 @@ I hope the following lists can grow much more in the future.
by [linkdd](https://github.com/linkdd): an interesting walkthrough of
developing a game (also) with EnTT.
* [Use EnTT When You Need An ECS](https://www.codingwiththomas.com/blog/use-entt-when-you-need-an-ecs)
by [Thomas](https://www.codingwiththomas.com/): I couldn't have said it
by [Thomas](https://www.codingwiththomas.com/): I could not have said it
better.
* [Space Battle: Huge edition](http://victor.madtriangles.com/code%20experiment/2018/06/11/post-ecs-battle-huge.html):
huge space battle built entirely from scratch.
@@ -294,13 +320,16 @@ I hope the following lists can grow much more in the future.
* [ArcGIS Runtime SDKs](https://developers.arcgis.com/arcgis-runtime/) by
[Esri](https://www.esri.com/): they use `EnTT` for the internal ECS and the
cross platform C++ rendering engine. The SDKs are utilized by a lot of
cross-platform C++ rendering engine. The SDKs are used by a lot of
enterprise custom apps, as well as by Esri for its own public applications
such as
[Explorer](https://play.google.com/store/apps/details?id=com.esri.explorer),
[Collector](https://play.google.com/store/apps/details?id=com.esri.arcgis.collector)
and
[Navigator](https://play.google.com/store/apps/details?id=com.esri.navigator).
* [OneArc](https://onearc.com/): [licenses](https://onearc.com/third-party-licensors/)
do not lie. Their products use EnTT in some way, but it is not known _what_
way.
* [FASTSUITE Edition 2](https://www.fastsuite.com/en_EN/fastsuite/fastsuite-edition-2.html)
by [Cenit](http://www.cenit.com/en_EN/about-us/overview.html): they use
`EnTT` to drive their simulation, that is, the communication between robot
@@ -310,7 +339,7 @@ I hope the following lists can grow much more in the future.
* [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.
wallet and noncustodial decentralized exchange rolled into one application.
* [Apparently](https://www.linkedin.com/in/skypjack/)
[NIO](https://www.nio.io/): there was a collaboration to make some changes
to `EnTT`, at the time used for internal projects.

View File

@@ -1,20 +1,14 @@
# Crash Course: service locator
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [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
Usually, service locators are tightly bound to the services they expose. It is
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.
@@ -33,7 +27,7 @@ entt::locator<interface>::allocate_emplace<service>(allocator, argument);
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:
Once a service is set up, it is retrieved using the `value` function:
```cpp
interface &service = entt::locator<interface>::value();
@@ -51,17 +45,17 @@ 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
All arguments are used only if necessary, that is, if a service does not 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
Sometimes it is useful to _transfer_ a copy of a service to another locator. For
example, when working across boundaries it is 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_
Options are not 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/>
@@ -75,14 +69,14 @@ 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
It is worth noting that it is 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 a locator. Replacing it will not 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.
application and the original service was shared, this operation will not
propagate to the other locators. Therefore, a module that shares the ownership
of the original audio service is still able to emit sounds.

View File

@@ -1,15 +1,13 @@
# Crash Course: runtime reflection system
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [Names and identifiers](#names-and-identifiers)
* [Identifiers](#identifiers)
* [Reflection in a nutshell](#reflection-in-a-nutshell)
* [Any to the rescue](#any-to-the-rescue)
* [Enjoy the runtime](#enjoy-the-runtime)
* [Tell me your name](#tell-me-your-name)
* [Container support](#container-support)
* [Pointer-like types](#pointer-like-types)
* [Template information](#template-information)
@@ -18,48 +16,47 @@
* [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)
* [User defined data](#user-defined-data)
* [Traits](#traits)
* [Custom data](#custom-data)
* [Unregister types](#unregister-types)
* [Meta context](#meta-context)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
Reflection (or rather, its lack) is a trending topic in the C++ world and a tool
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
came across some details that I did not like: macros, being intrusive, too many
allocations, and so on.<br/>
I finally decided to write a built-in, non-intrusive and macro-free runtime
reflection system for `EnTT`. Maybe I didn't do better than others or maybe yes,
time will tell me, but at least I can model this tool around the library to
reflection system for `EnTT`. Maybe I did not do better than others or maybe
yes, time will tell me, but at least I can model this tool around the library to
which it belongs and not the opposite.
# Names and identifiers
# Identifiers
The meta system doesn't force users to rely on the tools provided by the library
when it comes to working with names and identifiers. It does this by offering an
API that works with opaque identifiers that may or may not be generated by means
of a hashed string.<br/>
The meta system does not force users to rely on the tools provided by the
library when it comes to working with identifiers. It does this by offering an
API that works with integral values 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're numeric. It doesn't matter if they're generated at runtime, at
compile-time or with custom functions.
long as they are numeric. It does not matter if they are 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 a user defined literal is used as
That being said, some of the examples in the following sections are based on the
`hashed_string` class as provided by this library. Therefore, where an integral
identifier is provided, it is likely that a user defined literal is used as
follows:
```cpp
auto factory = entt::meta<my_type>().type("reflected_type"_hs);
entt::meta_factory<my_type>{}.type("reflected_type"_hs);
```
For what it's worth, this is completely equivalent to:
For what it is worth, this is completely equivalent to:
```cpp
auto factory = entt::meta<my_type>().type(42u);
entt::meta_factory<my_type>{}.type(42u);
```
Obviously, human-readable identifiers are more convenient to use and highly
@@ -69,10 +66,10 @@ recommended.
Reflection always starts from actual C++ types. Users cannot reflect _imaginary_
types.<br/>
The `meta` function is where it all starts:
The `meta_factory` class is where it all starts:
```cpp
auto factory = entt::meta<my_type>();
entt::meta_factory<my_type> factory{};
```
The returned value is a _factory object_ to use to continue building the meta
@@ -80,70 +77,53 @@ 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:
However, it is also possible to assign custom identifiers to meta types:
```cpp
auto factory = entt::meta<my_type>().type("reflected_type"_hs);
entt::meta_factory<my_type>{}.type("reflected_type"_hs);
```
Identifiers are used to _retrieve_ meta types at runtime by _name_ other than by
type.<br/>
Identifiers are used instead of the type to _retrieve_ meta types at runtime, if
necessary.<br/>
However, users can be interested in adding features to a reflected type so that
the reflection system can use it correctly under the hood, while they don't want
to also make the type _searchable_. In this case, it's sufficient not to invoke
`type`.
the reflection system can use it correctly under the hood, while they do not
want to also make the type _searchable_. In this case, it is sufficient not to
invoke `type`.
A factory is such that all its member functions return the factory itself. It's
A factory is such that all its member functions return the factory itself. It is
generally used to create the following:
* _Constructors_. A constructors is assigned to a reflected type by specifying
* _Constructors_. A constructor is assigned to a reflected type by specifying
its _list of arguments_. Free functions are also accepted if the return type
is the expected one. From a client perspective, nothing changes between a free
function or an actual constructor:
```cpp
entt::meta<my_type>().ctor<int, char>().ctor<&factory>();
entt::meta_factory<my_type>{}.ctor<int, char>().ctor<&factory>();
```
Meta default constructors are implicitly generated, if possible.
* _Destructors_. Both free functions and member functions are valid destructors:
```cpp
entt::meta<my_type>().dtor<&destroy>();
```
The purpose is to offer the possibility to free up resources that require
_special treatment_ before an object is actually destroyed.<br/>
A function should neither delete nor explicitly invoke the destructor of a
given instance.
* _Data members_. Meta data members are actual data members of the underlying
type but also static and global variables or constants of any kind. From the
point of view of the client, all the variables associated with the reflected
type appear as if they were part of the type itself:
```cpp
entt::meta<my_type>()
entt::meta_factory<my_type>{}
.data<&my_type::static_variable>("static"_hs)
.data<&my_type::data_member>("member"_hs)
.data<&global_variable>("global"_hs);
```
The `data` function requires the identifier to use for the meta data member.
Users can then access it by _name_ at runtime.<br/>
This is then used for runtime access.<br/>
Data members are also defined by means of a setter and getter pair. These are
either free functions, class members or a mix of them. This approach is also
convenient to create read-only properties from a non-const data member:
```cpp
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);
entt::meta_factory<my_type>{}.data<nullptr, &my_type::data_member>("member"_hs);
```
* _Member functions_. Meta member functions are actual member functions of the
@@ -152,14 +132,14 @@ generally used to create the following:
were part of the type itself:
```cpp
entt::meta<my_type>()
entt::meta_factory<my_type>{}
.func<&my_type::static_function>("static"_hs)
.func<&my_type::member_function>("member"_hs)
.func<&free_function>("free"_hs);
```
The `func` function requires the identifier to use for the meta data function.
Users can then access it by _name_ at runtime.<br/>
This is then used for runtime access.<br/>
Overloading of meta functions is supported. Overloaded functions are resolved
at runtime by the reflection system according to the types of the arguments.
@@ -167,7 +147,7 @@ generally used to create the following:
derived from it:
```cpp
entt::meta<derived_type>().base<base_type>();
entt::meta_factory<derived_type>{}.base<base_type>();
```
The reflection system tracks the relationship and allows for implicit casts at
@@ -178,7 +158,7 @@ generally used to create the following:
that are implicitly performed by the reflection system when required:
```cpp
entt::meta<double>().conv<int>();
entt::meta_factory<double>{}.conv<int>();
```
This is everything users need to create meta types. Refer to the inline
@@ -195,11 +175,12 @@ The API is very similar to that of the `any` type. The class `meta_any` _wraps_
many of the feature to infer a meta node, before forwarding some or all of the
arguments to the underlying storage.<br/>
Among the few relevant differences, `meta_any` adds support for containers and
pointer-like types, while `any` doesn't.<br/>
pointer-like types, while `any` does not.<br/>
Similar to `any`, this class is also used to create _aliases_ for unmanaged
objects either with `forward_as_meta` or using the `std::in_place_type<T &>`
disambiguation tag, as well as from an existing object by means of the `as_ref`
member function.<br/>
member function. Additionally, it can take ownership of pointers passed as
arguments along with `std::in_place`.<br/>
Unlike `any` instead, `meta_any` treats an empty instance and one initialized
with `void` differently:
@@ -209,7 +190,7 @@ entt::meta_any other{std::in_place_type<void>};
```
While `any` considers both as empty, `meta_any` treats objects initialized with
`void` as if they were _valid_ ones. This allows to differentiate between failed
`void` as if they were _valid_ ones. This allows differentiating between failed
function calls and function calls that are successful but return nothing.
Finally, the member functions `try_cast`, `cast` and `allow_cast` are used to
@@ -220,7 +201,7 @@ There is in fact no `any_cast` equivalent for `meta_any`.
## Enjoy the runtime
Once the web of reflected types is constructed, it's a matter of using it at
Once the web of reflected types is constructed, it is a matter of using it at
runtime where required.<br/>
There are a few options to search for a reflected type:
@@ -246,10 +227,10 @@ for(auto &&[id, type]: entt::resolve()) {
```
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/>
Meta data members and functions are accessed by name:
its id). These 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/>
Meta data members and functions are accessed by means of their identifiers:
* Meta data members:
@@ -260,7 +241,7 @@ Meta data members and functions are accessed by name:
The returned type is `meta_data` and may be invalid if there is no meta data
object associated with the given identifier.<br/>
A meta data object offers an API to query the underlying type (for example, to
know if it's a const or a static one), to get the meta type of the variable
know if it is a const or a static one), to get the meta type of the variable
and to set or get the contained value.
* Meta function members:
@@ -272,11 +253,14 @@ Meta data members and functions are accessed by name:
The returned type is `meta_func` and may be invalid if there is no meta
function object associated with the given identifier.<br/>
A meta function object offers an API to query the underlying type (for
example, to know if it's a const or a static function), to know the number of
example, to know if it is a const or a static function), to know the number of
arguments, the meta return type and the meta types of the parameters. In
addition, a meta function object is used to invoke the underlying function and
then get the return value in the form of a `meta_any` object.
Both functions search for the elements throughout the meta type hierarchy.
However, they offer the option of passing a second boolean argument to stop the
search at the top-level meta type.<br/>
All the meta objects thus obtained as well as the meta types explicitly convert
to a boolean value to check for validity:
@@ -286,9 +270,9 @@ if(auto func = entt::resolve<my_type>().func("member"_hs); func) {
}
```
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:
Furthermore, all of 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 &&[id, type]: entt::resolve<my_type>().base()) {
@@ -296,28 +280,58 @@ for(auto &&[id, type]: entt::resolve<my_type>().base()) {
}
```
Meta type are also used to `construct` actual instances of the underlying
Meta types are also 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
or may not be initialized, depending on whether a suitable constructor was found
or not.
There is no object that wraps the destructor of a meta type nor a `destroy`
member function in its API. Destructors are invoked implicitly by `meta_any`
behind the scenes and users have not to deal with them explicitly. Furthermore,
they've no name, cannot be searched and wouldn't have member functions to expose
anyway.<br/>
Similarly, conversion functions aren't directly accessible. They're used
internally by `meta_any` and the meta objects when needed.
or not.<br/>
Conversion functions are not accessible instead. They are used internally by
`meta_any` and the meta objects when needed.
Meta types and meta objects in general contain much more than what was said.
Refer to the inline documentation for further details.
### Tell me your name
For meta types, data and functions, users can also provide custom _names_:
```cpp
entt::meta_factory<my_type>{}
.type("type"_hs, "my_type")
.data<&variable>("data"_hs, "variable")
.func<&function>("func"_hs, "function");
```
The _label_ provided **should** be a string literal. The library does not make
copies. It is up to the user to guarantee the lifetime of the name itself.<br/>
String identifiers are returned from the meta objects via the `name` function:
```cpp
const char *name = entt::resolve<my_type>().name();
```
Since most of the time there is no need to differentiate the _name_ from the
numeric identifier associated with a meta object, `EnTT` also offers a more
compact version of these functions:
```cpp
entt::meta_factory<my_type>{}
.type("my_type")
.data<&variable>("variable")
.func<&function>("function");
```
Again, the name provided **should** be a string literal. The string is then used
to generate a numeric identifier with the `hashed_string` class.<br/>
Despite support for names, there are no string-based lookup functions available.
That is, types (`resolve`) as well as data members (`data`) and function members
(`func`) are only _searchable_ by numeric identifiers.
## Container support
The runtime reflection system also supports containers of all types.<br/>
Moreover, _containers_ doesn't necessarily mean those offered by the C++
Moreover, _containers_ does not necessarily mean those offered by the C++
standard library. In fact, user defined data structures can also work with the
meta system in many cases.
@@ -334,7 +348,7 @@ particular:
* `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
It is important to include the header file `container.hpp` to make these
specializations available to the compiler when needed.<br/>
The same file also contains many examples for the users that are interested in
making their own containers available to the meta system.
@@ -360,12 +374,12 @@ if(any.type().is_sequence_container()) {
The method to use to get a proxy object for associative containers is
`as_associative_container` instead.<br/>
It's not necessary to perform a double check actually. Instead, it's enough to
It is not necessary to perform a double check actually. Instead, it is enough to
query the meta type or verify that the proxy object is valid. In fact, proxies
are contextually convertible to bool to check for validity. For example, invalid
proxies are returned when the wrapped object isn't a container.<br/>
In all cases, users aren't expected to _reflect_ containers explicitly. It's
sufficient to assign a container for which a specialization of the traits
proxies are returned when the wrapped object is not a container.<br/>
In all cases, users are not expected to _reflect_ containers explicitly. It is
sufficient to assign a container for which a specialization of the _traits_
classes exists to a `meta_any` object to be able to get its proxy object.
The interface of the `meta_sequence_container` proxy object is the same for all
@@ -377,17 +391,17 @@ to case. In particular:
* The `size` member function returns the number of elements in the container as
an unsigned integer value.
* The `resize` member function allows to resize the wrapped container and
returns true in case of success.<br/>
For example, it's not possible to resize fixed size containers.
* The `clear` member function allows to clear the wrapped container and returns
* The `resize` member function allows resizing the wrapped container and returns
true in case of success.<br/>
For example, it's not possible to clear fixed size containers.
For example, it is not possible to resize fixed size containers.
* The `reserve` member function allows to increase the capacity of the wrapped
* The `clear` member function allows clearing the wrapped container and returns
true in case of success.<br/>
For example, it is not possible to clear fixed size containers.
* The `reserve` member function allows increasing the capacity of the wrapped
container and returns true in case of success.<br/>
For example, it's not possible to increase capacity of fixed size containers.
For example, it is not possible to increase capacity of fixed size containers.
* The `begin` and `end` member functions return opaque iterators that is used to
iterate the container directly:
@@ -401,8 +415,8 @@ to case. In particular:
In all cases, given an underlying container of type `C`, the returned element
contains an object of type `C::value_type` which therefore depends on the
actual container.<br/>
All meta iterators are input iterators and don't offer an indirection operator
on purpose.
All meta iterators are input iterators and do not offer an indirection
operator on purpose.
* The `insert` member function is used to add elements to the container. It
accepts a meta iterator and the element to insert:
@@ -416,7 +430,7 @@ to case. In particular:
This function returns a meta iterator pointing to the inserted element and a
boolean value to indicate whether the operation was successful or not. A call
to `insert` may silently fail in case of fixed size containers or whether the
arguments aren't at least convertible to the required types.<br/>
arguments are not at least convertible to the required types.<br/>
Since meta iterators are contextually convertible to bool, users can rely on
them to know if the operation failed on the actual container or upstream, for
example due to an argument conversion problem.
@@ -473,12 +487,12 @@ differences in behavior in the case of key-only containers. In particular:
* The `size` member function returns the number of elements in the container as
an unsigned integer value.
* The `clear` member function allows to clear the wrapped container and returns
* The `clear` member function allows clearing the wrapped container and returns
true in case of success.
* The `reserve` member function allows to increase the capacity of the wrapped
* The `reserve` member function allows increasing the capacity of the wrapped
container and returns true in case of success.<br/>
For example, it's not possible to increase capacity of standard maps.
For example, it is not possible to increase capacity of standard maps.
* The `begin` and `end` member functions return opaque iterators that are used
to iterate the container directly:
@@ -491,10 +505,10 @@ differences in behavior in the case of key-only containers. In particular:
In all cases, given an underlying container of type `C`, the returned element
is a key-value pair where the key has type `C::key_type` and the value has
type `C::mapped_type`. Since key-only containers don't have a mapped type,
type `C::mapped_type`. Since key-only containers do not have a mapped type,
their _value_ is nothing more than an invalid `meta_any` object.<br/>
All meta iterators are input iterators and don't offer an indirection operator
on purpose.
All meta iterators are input iterators and do not offer an indirection
operator on purpose.
While the accessed key is usually constant in the associative containers and
is therefore returned by copy, the value (if any) is wrapped by an instance of
@@ -502,7 +516,7 @@ differences in behavior in the case of key-only containers. In particular:
modifies the element inside the container.
* The `insert` member function is used to add elements to a container. It gets
two arguments, respectively the key and the value to insert:
two arguments, the key and the value to insert:
```cpp
auto last = view.end();
@@ -511,7 +525,7 @@ differences in behavior in the case of key-only containers. In particular:
```
This function returns a boolean value to indicate whether the operation was
successful or not. A call to `insert` may fail when the arguments aren't at
successful or not. A call to `insert` may fail when the arguments are not at
least convertible to the required types.
* The `erase` member function is used to remove elements from a container. It
@@ -522,8 +536,8 @@ differences in behavior in the case of key-only containers. In particular:
```
This function returns a boolean value to indicate whether the operation was
successful or not. A call to `erase` may fail when the argument isn't at least
convertible to the required type.
successful or not. A call to `erase` may fail when the argument is not at
least convertible to the required type.
* The `operator[]` is used to access elements in a container. It gets a single
argument, the key of the element to return:
@@ -540,7 +554,7 @@ Container support is minimal but likely sufficient to satisfy all needs.
## Pointer-like types
As with containers, it's also possible to _tell_ to the meta system which types
As with containers, it is also possible to _tell_ to the meta system which types
are _pointers_. This makes it possible to dereference instances of `meta_any`,
thus obtaining light _references_ to pointed objects that are also correctly
associated with their meta types.<br/>
@@ -550,13 +564,21 @@ some common classes. In particular:
* All types of raw pointers.
* `std::unique_ptr` and `std::shared_ptr`.
* All classes that _export_ a type member called `is_meta_pointer_like`:
```cpp
struct smart_pointer {
using is_meta_pointer_like = void;
// ...
};
```
The actual type is irrelevant and will not be used in any way.
It's important to include the header file `pointer.hpp` to make these
It is important to include the header file `pointer.hpp` to make these
specializations available to the compiler when needed.<br/>
The same file also contains many examples for the users that are interested in
making their own pointer-like types available to the meta system.
When a type is recognized as a pointer-like one by the meta system, it's
When a type is recognized as a pointer-like one by the meta system, it is
possible to dereference the instances of `meta_any` that contain these objects.
The following is a deliberately verbose example to show how to use this feature:
@@ -573,18 +595,18 @@ if(any.type().is_pointer_like()) {
}
```
It's not necessary to perform a double check. Instead, it's enough to query the
meta type or verify that the returned object is valid. For example, invalid
instances are returned when the wrapped object isn't a pointer-like type.<br/>
It is not necessary to perform a double check. Instead, it is enough to query
the meta type or verify that the returned object is valid. For example, invalid
instances are returned when the wrapped object is not a pointer-like type.<br/>
Dereferencing a pointer-like object returns an instance of `meta_any` which
_refers_ to the pointed object. Modifying it means modifying the pointed object
directly (unless the returned element is const).
In general, _dereferencing_ a pointer-like type boils down to a `*ptr`. However,
`EnTT` also supports classes that don't offer an `operator*`. In particular:
`EnTT` also supports classes that do not offer an `operator*`. In particular:
* It's possible to exploit a solution based on ADL lookup by offering a function
(also a template one) named `dereference_meta_pointer_like`:
* It is possible to exploit a solution based on ADL lookup by implementing a
function (also a template one) named `dereference_meta_pointer_like`:
```cpp
template<typename Type>
@@ -593,7 +615,7 @@ In general, _dereferencing_ a pointer-like type boils down to a `*ptr`. However,
}
```
* When not in control of the type's namespace, it's possible to inject into the
* When not in control of the type's namespace, it is possible to inject into the
`entt` namespace a specialization of the `adl_meta_pointer_like` class
template to bypass the adl lookup as a whole:
@@ -612,12 +634,12 @@ of the pointed type, no user intervention is required.
## Template information
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 this information
original type in case it is a class template.<br/>
By default, this works out of the box and requires no user action. However, it
is important to include the header file `template.hpp` to make this information
available to the compiler when needed.
Meta template information are easily found:
Meta template information is easily found:
```cpp
// this method returns true if the type is recognized as a class template specialization
@@ -633,7 +655,7 @@ if(auto type = entt::resolve<std::shared_ptr<my_type>>(); type.is_template_speci
}
```
Typically, when template information for a type are required, what the library
Typically, when template information for a type is required, what the library
provides is sufficient. However, there are some cases where a user may want more
details or a different set of information.<br/>
Consider the case of a class template that is meant to wrap function types:
@@ -660,8 +682,8 @@ struct entt::meta_template_traits<function_type<Ret(Args...)>> {
};
```
The reflection system doesn't verify the accuracy of the information nor infer a
correspondence between real types and meta types.<br/>
The reflection system does not verify the accuracy of the information nor infer
a correspondence between real types and meta types.<br/>
Therefore, the specialization is used as is and the information it contains is
associated with the appropriate type when required.
@@ -673,7 +695,7 @@ 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>()
entt::meta_factory<int>{}
.conv<bool>()
.conv<char>()
// ...
@@ -687,7 +709,7 @@ 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>()
entt::meta_factory<my_enum>{}
.conv<std::underlying_type_t<my_enum>>();
```
@@ -712,8 +734,8 @@ int value = any.cast<int>();
This makes working with arithmetic types and scoped or unscoped enums as easy as
it is in C++.<br/>
It's still possible to set up conversion functions manually and these are always
preferred over the automatic ones.
It is still possible to set up conversion functions manually, and these are
always preferred over the automatic ones.
## Implicitly generated default constructor
@@ -726,13 +748,13 @@ them.
For default constructible types only, default constructors are automatically
defined and associated with their meta types, whether they are explicitly or
implicitly generated.<br/>
Therefore, this is all is needed to construct an integer from its meta type:
Therefore, this is all needed to construct an integer from its meta type:
```cpp
entt::resolve<int>().construct();
```
Where the meta type is for example the one returned from a meta container,
Where the meta type is, 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 default constructors, they are preferred both
@@ -750,7 +772,7 @@ designed to convert an opaque pointer into a `meta_any`:
entt::meta_any any = entt::resolve(id).from_void(element);
```
Unfortunately, it's not possible to do a check on the actual type. Therefore,
Unfortunately, it is not possible to do a check on the actual type. Therefore,
this call can be considered as a _static cast_ with all its _problems_.<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.
@@ -763,14 +785,14 @@ Their purpose is to require slightly different behavior than the default in some
specific cases. For example, when reading a given data member, its value is
returned wrapped in a `meta_any` object which, by default, makes a copy of it.
For large objects or if the caller wants to access the original instance, this
behavior isn't desirable. Policies are there to offer a solution to this and
behavior is not desirable. Policies are there to offer a solution to this and
other problems.
There are a few alternatives available at the moment:
* The _as-is_ policy, associated with the type `entt::as_is_t`.<br/>
* The _as-value_ policy, associated with the type `entt::as_value_t`.<br/>
This is the default policy. In general, it should never be used explicitly,
since it's implicitly selected if no other policy is specified.<br/>
since it is implicitly selected if no other policy is specified.<br/>
In this case, the return values of the functions as well as the properties
exposed as data members are always returned by copy in a dedicated wrapper and
therefore associated with their original meta types.
@@ -780,13 +802,13 @@ There are a few alternatives available at the moment:
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);
entt::meta_factory<my_type>{}.func<&my_type::member_function, entt::as_void_t>("member"_hs);
```
If the use with functions is obvious, perhaps less so is use with constructors
and data members. In the first case, the returned wrapper is always empty even
though the constructor is still invoked. In the second case, the property
isn't accessible for reading instead.
is not accessible for reading instead.
* The _as-ref_ and _as-cref_ policies, associated with the types
`entt::as_ref_t` and `entt::as_cref_t`.<br/>
@@ -796,7 +818,7 @@ There are a few alternatives available at the moment:
the wrapper itself:
```cpp
entt::meta<my_type>().data<&my_type::data_member, entt::as_ref_t>("member"_hs);
entt::meta_factory<my_type>{}.data<&my_type::data_member, entt::as_ref_t>("member"_hs);
```
These policies work with constructors (for example, when objects are taken
@@ -806,7 +828,19 @@ There are a few alternatives available at the moment:
`as_ref_t` _adapts_ to the constness of the passed object and to that of the
return type if any.
Some uses are rather trivial, but it's useful to note that there are some less
* The _as-is_ policy, associated with the type `entt::as_is_t`.<br/>
Useful for decoupling meta type creation code from calling code while still
preserving the behavior of data members and member functions as defined:
```cpp
entt::meta_factory<my_type>{}.func<&my_type::any_member, entt::as_is_t>("member"_hs);
```
For data members or member functions that return a reference type, the value
is returned by reference with the same constness. In all other cases, the
value is returned by copy.
Some uses are rather trivial, but it is useful to note that there are some less
obvious corner cases that can in turn be solved with the use of policies.
## Named constants and enums
@@ -823,15 +857,15 @@ between enums and classes in C++ directly in the space of the reflected types.
Exposing constant values or elements from an enum is quite simple:
```cpp
entt::meta<my_enum>()
entt::meta_factory<my_enum>{}
.data<my_enum::a_value>("a_value"_hs)
.data<my_enum::another_value>("another_value"_hs);
entt::meta<int>().data<2048>("max_int"_hs);
entt::meta_factory<int>{}.data<2048>("max_int"_hs);
```
Accessing them is trivial as well. It's a matter of doing the following, as with
any other data member of a meta type:
Accessing them is trivial as well. It is a matter of doing the following, as
with any other data member of a meta type:
```cpp
auto value = entt::resolve<my_enum>().data("a_value"_hs).get({}).cast<my_enum>();
@@ -841,54 +875,94 @@ auto max = entt::resolve<int>().data("max_int"_hs).get({}).cast<int>();
All this happens behind the scenes without any allocation because of the small
object optimization performed by the `meta_any` class.
## Properties and meta objects
## User defined data
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:
to attach _traits_ or arbitrary _custom data_ to the meta objects created.
The main difference between them is that:
* Traits are simple user-defined flags with much higher access performance. The
library reserves up to 16 bits for traits, that is 16 flags for a bitmask or
2^16 values otherwise.
* Custom data are stored in a generic quick access area reserved for the user
and which the library will never use under any circumstances.
In all cases, this support is currently available only for meta types, meta data
and meta functions.
### Traits
User-defined traits are set via a meta factory:
```cpp
entt::meta<my_type>().type("reflected_type"_hs).prop("tooltip"_hs, "message");
entt::meta_factory<my_type>{}.traits(my_traits::required | my_traits::hidden);
```
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:
In the example above, `EnTT` bitmask enum support is used, but any integral
value is fine, as long as it does not exceed 16 bits.<br/>
Traits can be assigned at different times. Subsequent calls to the `traits`
function do not reset previously set values. However, users must reset the
factory to the meta object of interest:
```cpp
entt::meta<my_type>().type("reflected_type"_hs).prop(my_enum::key_only);
entt::meta_factory<my_type>{}
.data<&my_type::data_member, entt::as_ref_t>("member"_hs)
.traits(my_traits::internal);
```
To attach multiple properties to a meta object, just invoke `prop` more than
once.<br/>
It's also possible to call `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 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:
Once created, all meta objects offer a member function named `traits` to get the
currently set value:
```cpp
// iterate all properties of a meta type
for(auto &&[id, prop]: entt::resolve<my_type>().prop()) {
// ...
}
// search for a given property by name
auto prop = entt::resolve<my_type>().prop("tooltip"_hs);
auto value = entt::resolve<my_type>().traits<my_traits>();
```
Meta properties are objects having a fairly poor interface, all in all. They
only provide the `value` member function to retrieve the contained value in the
form of a `meta_any` object.
Note that the type is erased upon registration and must therefore be repeated
when traits are _extracted_, so as to allow the library to _reconstruct_ them
correctly.
### Custom data
Custom arbitrary data are set via a meta factory:
```cpp
entt::meta_factory<my_type>{}.custom<type_data>("name");
```
The way to do this is by specifying the data type to the `custom` function and
passing the necessary arguments to construct it correctly.<br/>
It is not possible to assign custom data at different times. Therefore, multiple
calls to the `custom` function overwrite previous values. However, this value
can be read from meta objects and used to update existing data with a factory,
effectively updating them as needed.<br/>
Likewise, users can also set custom data on meta objects later if needed, as
long as the factory is reset to the meta object of interest:
```cpp
entt::meta_factory<my_type>{}
.func<&my_type::member_function>("member"_hs)
.custom<function_data>("tooltip");
```
Once created, all meta objects offer a member function named `custom` to get the
currently set value as a reference or as a pointer to an element:
```cpp
const type_data &value = entt::resolve<my_type>().custom();
```
Note that the returned object performs an extra check in debug before converting
to the requested type, so as to avoid subtle bugs.<br/>
Only in the case of conversion to a pointer is this check safe and such that a
null pointer is returned to inform the user of the failed attempt.
## 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
and so on. However, base classes are not unregistered as well, since they do not
necessarily depend on it.<br/>
Roughly speaking, unregistering a type means disconnecting all associated meta
objects from it and making its identifier no longer available:
@@ -897,14 +971,14 @@ objects from it and making its identifier no longer available:
entt::meta_reset<my_type>();
```
It's also possible to reset types by their unique identifiers:
It is 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:
does not accept arguments and resets all meta types at once:
```cpp
entt::meta_reset();
@@ -921,7 +995,7 @@ _context_. This is obtained via a service locator as:
auto &&context = entt::locator<entt::meta_context>::value_or();
```
By itself, a context is an opaque object that the user cannot do much with.
By itself, a context is an opaque object that the user can do little with.
However, users can replace an existing context with another at any time:
```cpp
@@ -931,20 +1005,20 @@ std::swap(context, other);
```
This is useful for testing purposes or to define multiple context objects with
different meta type to use as appropriate.
different meta types to use as appropriate.
If _replacing_ the default context isn't enough, `EnTT` also offers the ability
If _replacing_ the default context is not 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:
one, simply pass it as an argument to the `meta_factory` constructor:
```cpp
entt::meta_ctx context{};
auto factory = entt::meta<my_type>(context).type("reflected_type"_hs);
entt::meta_factory<my_type>{context}.type("reflected_type"_hs);
```
By doing so, the new meta type isn't available in the default context but is
By doing so, the new meta type is not available in the default context but is
usable by passing around the new context when needed, such as when creating a
new `meta_any` object:
@@ -953,17 +1027,17 @@ 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's necessary to pass it to the `resolve` function:
it is 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
More generally, when using externally managed contexts, it is 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
For example, once the `meta_type` instant is obtained, it is 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
On the other hand, it is 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

@@ -1,8 +1,5 @@
# Crash Course: poly
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
@@ -14,9 +11,6 @@
* [Inheritance](#inheritance)
* [Static polymorphism in the wild](#static-polymorphism-in-the-wild)
* [Storage size and alignment requirement](#storage-size-and-alignment-requirement)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
@@ -51,12 +45,12 @@ The ones that I like more are:
object wrapper.
The former is admittedly an experimental library, with many interesting ideas.
I've some doubts about the usefulness of some feature in real world projects,
I have some doubts about the usefulness of some features in real world projects,
but perhaps my lack of experience comes into play here. In my opinion, its only
flaw is the API which I find slightly more cumbersome than other solutions.<br/>
The latter was undoubtedly a source of inspiration for this module, although I
flaw is the API that I find slightly more cumbersome than other solutions.<br/>
The latter was undoubtedly a source of inspiration for this module. Although I
opted for different choices in the implementation of both the final API and some
feature.
features.
Either way, the authors are gurus of the C++ community, people I only have to
learn from.
@@ -69,7 +63,7 @@ types will have to adhere to.<br/>
For this purpose, the library offers a single class that supports both deduced
and fully defined interfaces. Although having interfaces deduced automatically
is convenient and allows users to write less code in most cases, it has some
limitations and it's therefore useful to be able to get around the deduction by
limitations. It is 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, a generic implementation is needed to fulfill the
@@ -92,7 +86,7 @@ struct Drawable: entt::type_list<> {
};
```
It's recognizable by the fact that it inherits from an empty type list.<br/>
It is recognizable by the fact that it inherits from an empty type list.<br/>
Functions can also be const, accept any number of parameters and return a type
other than `void`:
@@ -110,7 +104,7 @@ struct Drawable: entt::type_list<> {
In this case, all parameters are passed to `invoke` after the reference to
`this` and the return value is whatever the internal call returns.<br/>
As for `invoke`, this is a name that is injected into the _concept_ through
`Base`, from which one must necessarily inherit. Since it's also a dependent
`Base`, from which one must necessarily inherit. Since it is also a dependent
name, the `this-> template` form is unfortunately necessary due to the rules of
the language. However, there also exists an alternative that goes through an
external call:
@@ -177,9 +171,9 @@ If the concept exposes a member function called `draw` with function type
* Or through a lambda that makes use of existing member functions from the
interface itself.
In other words, it's not possible to make use of functions not belonging to the
interface, even if they're part of the types that fulfill the concept.<br/>
Similarly, it's not possible to deduce a function in the static virtual table
In other words, it is not possible to make use of functions not belonging to the
interface, even if they are part of the types that fulfill the concept.<br/>
Similarly, it is not possible to deduce a function in the static virtual table
with a function type different from that of the associated member function in
the interface itself.
@@ -188,7 +182,7 @@ allows maximum flexibility when providing the implementation for a concept.
## Fulfill a concept
The `impl` alias template of a concept is used to define how it's fulfilled:
The `impl` alias template of a concept is used to define how it is fulfilled:
```cpp
struct Drawable: entt::type_list<> {
@@ -199,7 +193,7 @@ struct Drawable: entt::type_list<> {
};
```
In this case, it's stated that the `draw` method of a generic type is enough to
In this case, it is stated that the `draw` method of a generic type is enough to
satisfy the requirements of the `Drawable` concept.<br/>
Both member functions and free functions are supported to fulfill concepts:
@@ -217,9 +211,9 @@ struct Drawable: entt::type_list<void()> {
Likewise, as long as the parameter types and return type support conversions to
and from those of the function type referenced in the static virtual table, the
actual implementation may differ in its function type since it's erased
actual implementation may differ in its function type since it is erased
internally.<br/>
Moreover, the `self` parameter isn't strictly required by the system and can be
Moreover, the `self` parameter is not strictly required by the system and can be
left out for free functions if not required.
Refer to the inline documentation for more details.
@@ -227,7 +221,7 @@ Refer to the inline documentation for more details.
# Inheritance
_Concept inheritance_ is straightforward due to how poly looks like in `EnTT`.
Therefore, it's quite easy to build hierarchies of concepts if necessary.<br/>
Therefore, it is quite easy to build hierarchies of concepts if necessary.<br/>
The only constraint is that all concepts in a hierarchy must belong to the same
_family_, that is, they must be either all deduced or all defined.
@@ -236,8 +230,8 @@ For a deduced concept, inheritance is achieved in a few steps:
```cpp
struct DrawableAndErasable: entt::type_list<> {
template<typename Base>
struct type: typename Drawable::template type<Base> {
static constexpr auto base = std::tuple_size_v<typename entt::poly_vtable<Drawable>::type>;
struct type: typename Drawable::type<Base> {
static constexpr auto base = Drawable::impl<Drawable::type<entt::poly_inspector>>::size;
void erase() { entt::poly_call<base + 0>(*this); }
};
@@ -259,22 +253,21 @@ in appending the new functions to the previous list.
As for a defined concept instead, the list of types is _extended_ in a similar
way to what is shown for the implementation of the above concept.<br/>
To do this, it's useful to declare a function that allows to convert a _concept_
into its underlying `type_list` object:
To do this, it is useful to declare a function that allows to convert a
_concept_ into its underlying `type_list` object:
```cpp
template<typename... Type>
entt::type_list<Type...> as_type_list(const entt::type_list<Type...> &);
```
The definition isn't strictly required, since the function is only used through
The definition is not strictly required, since the function is only used through
a `decltype` as it follows:
```cpp
struct DrawableAndErasable: entt::type_list_cat_t<
decltype(as_type_list(std::declval<Drawable>())),
entt::type_list<void()>
> {
entt::type_list<void()>> {
// ...
};
```
@@ -285,7 +278,7 @@ Everything else is the same as already shown instead.
# Static polymorphism in the wild
Once the _concept_ and implementation are defined, it's possible to use the
Once the _concept_ and implementation are defined, it is possible to use the
`poly` class template to _wrap_ instances that meet the requirements:
```cpp
@@ -319,15 +312,16 @@ circle shape;
drawable instance{std::in_place_type<circle &>, shape};
```
Similarly, it's possible to create non-owning copies of `poly` from an existing
Similarly, it is possible to create non-owning copies of `poly` from an existing
object:
```cpp
drawable other = instance.as_ref();
```
In both cases, although the interface of the `poly` object doesn't change, it
doesn't construct any element or take care of destroying the referenced objects.
In both cases, although the interface of the `poly` object does not change, it
does not construct any element or take care of destroying the referenced
objects.
Note also how the underlying concept is accessed via a call to `operator->` and
not directly as `instance.draw()`.<br/>
@@ -349,9 +343,9 @@ entt::basic_poly<Drawable, sizeof(double[4]), alignof(double[4])>
The default size is `sizeof(double[2])`, which seems like a good compromise
between a buffer that is too large and one unable to hold anything larger than
an integer. The alignment requirement is optional and by default such that it's
the most stringent (the largest) for any object whose size is at most equal to
the one provided.<br/>
It's worth noting that providing a size of 0 (which is an accepted value in all
an integer. The alignment requirement is optional, and by default such that it
is the most stringent (the largest) for any object whose size is at most equal
to the one provided.<br/>
It is worth noting that providing a size of 0 (which is an accepted value in all
respects) will force the system to dynamically allocate the contained objects in
all cases.

View File

@@ -1,17 +1,12 @@
# Crash Course: cooperative scheduler
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [The process](#the-process)
* [Adaptor](#adaptor)
* [Continuation](#continuation)
* [Shared process](#shared-process)
* [The scheduler](#the-scheduler)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
@@ -23,27 +18,19 @@ to define and execute cooperative processes.
# The process
A typical task inherits from the `process` class template that stays true to the
CRTP idiom. Moreover, derived classes specify what the intended type for elapsed
times is.
A typical task inherits from the `process` class template. Derived classes also
specify what the intended type for elapsed times is.
A process should expose publicly the following member functions whether needed
(note that it isn't required to define a function unless the derived class wants
to _override_ the default behavior):
A process should implement the following member functions whether needed (note
that it is not required to define a function unless the derived class wants to
_override_ the default behavior):
* `void update(Delta, void *);`
This is invoked once per tick until a process is explicitly aborted or ends
either with or without errors. Even though it's not mandatory to declare this
member function, as a rule of thumb each process should at least define it to
work _properly_. The `void *` parameter is an opaque pointer to user data (if
any) forwarded directly to the process during an update.
* `void init();`
This is invoked when the process joins the running queue of a scheduler. It
happens usually as soon as the process is attached to the scheduler if it's a
top level one, otherwise when it replaces its parent if it's a _continuation_.
either with or without errors. Each process should at least define it to work
_properly_. The `void *` parameter is an opaque pointer to user data (if any)
forwarded directly to the process during an update.
* `void succeeded();`
@@ -58,22 +45,24 @@ to _override_ the default behavior):
* `void aborted();`
This is invoked only if a process is explicitly aborted. There is no guarantee
that it executes in the same tick, it depends solely on whether the process is
that it executes in the same tick. It depends solely on whether the process is
aborted immediately or not.
Derived classes can also change the internal state of a process by invoking
`succeed` and `fail`, as well as `pause` and `unpause` the process itself.<br/>
All these are protected member functions made available to manage the life cycle
of a process from a derived class.
A class can also change the state of a process by invoking `succeed` and `fail`,
as well as `pause` and `unpause` the process itself.<br/>
All these are public member functions made available to manage the life cycle of
a process easily.
Here is a minimal example for the sake of curiosity:
```cpp
struct my_process: entt::process<my_process, std::uint32_t> {
using delta_type = std::uint32_t;
struct my_process: entt::process {
using allocator_type = typename entt::process::allocator_type;
using delta_type = typename entt::process::delta_type;
my_process(delta_type delay)
: remaining{delay}
my_process(const allocator_type &allocator, delta_type delay)
: entt::process{allocator},
remaining{delay}
{}
void update(delta_type delta, void *) {
@@ -91,46 +80,62 @@ private:
};
```
## Adaptor
## Continuation
Lambdas and functors can't be used directly with a scheduler because they aren't
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.
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 callbacks to
invoke whenever a process terminates with success or with an error:
A process may be followed by other processes upon successful termination.<br/>
This pairing can be set up at creation time, keeping the processes conceptually
separate from each other while still combining them at runtime:
```cpp
void(Delta delta, void *data, auto succeed, auto fail);
my_process process{};
process.then<my_other_process>();
```
Parameters have the following meaning:
This approach allows processes to be developed in isolation and combined to
define complex actions.<br/>
For example, a delayed operation where a parent process (such as a timer)
_schedules_ a child process (the deferred task) once the time is over.
* `delta` is the elapsed time.
* `data` is an opaque pointer to user data if any, `nullptr` otherwise.
* `succeed` is a function to call when a process terminates with success.
* `fail` is a function to call when a process terminates with errors.
The `then` function also accepts lambdas, which are associated with a dedicated
process internally:
Both `succeed` and `fail` accept no parameters at all.
```cpp
process.then([](entt::process &proc, std::uint32_t delta, void *data) {
// ...
})
```
Note that usually users shouldn't worry about creating adaptors at all. A
scheduler creates them internally each and every time a lambda or a functor is
used as a process.
The lambda function is such that it accepts a reference to the process that
manages it (to be able to terminate it, pause it and so on), plus the usual
values also passed to the `update` function.
## Shared process
All processes inherit from `std::enable_shared_from_this` to allow sharing with
the caller.<br/>
The returned smart pointer was created using the allocator associated with the
scheduler and therefore all its processes. This same allocator is available by
invoking `get_allocator` on the process itself.
As far as possible, sharing a process is not intended to allow the caller to
manage it. This could actually compromise the proper functioning of the
scheduler and the process itself.<br/>
Rather, the purpose is to allow the callers to save a valid reference to the
process, allowing them to intervene in its lifecycle through calls like `pause`
and the like.
# The scheduler
A cooperative scheduler runs different processes and helps managing their life
A cooperative scheduler runs different processes and helps manage their life
cycles.
Each process is invoked once per tick. If it terminates, it's removed
automatically from the scheduler and it's never invoked again. Otherwise, it's
a good candidate to run one more time the next tick.<br/>
Each process is invoked once per tick. If it terminates, it is removed
automatically from the scheduler, and it is never invoked again. Otherwise,
it is a good candidate to run one more time the next tick.<br/>
A process can also have a _child_. In this case, the parent process is replaced
with its child when it terminates and only if it returns with success. In case
of errors, both the parent process and its child are discarded. This way, it's
easy to create chain of processes to run sequentially.
of errors, both the parent process and its child are discarded. This way, it is
easy to create a _chain of processes_ to run sequentially.
Using a scheduler is straightforward. To create it, users must provide only the
type for the elapsed times and no arguments at all:
@@ -143,7 +148,7 @@ Otherwise, the `scheduler` alias is also available for the most common cases. It
uses `std::uint32_t` as a default type:
```cpp
entt::scheduler scheduler;
entt::scheduler scheduler{};
```
The class has member functions to query its internal data structures, like
@@ -160,34 +165,33 @@ entt::scheduler::size_type size = scheduler.size();
scheduler.clear();
```
To attach a process to a scheduler there are mainly two ways:
To attach a process to a scheduler, invoke the `attach` function with a process
type and the arguments to use to construct it:
* If the process inherits from the `process` class template, it's enough to
indicate its type and submit all the parameters required to construct it to
the `attach` member function:
```cpp
scheduler.attach<my_process>(_1000u);
```
```cpp
scheduler.attach<my_process>(1000u);
```
The scheduler will also provide the process with its allocator as the first
argument.<br>
In case of lambdas or functors, the required signature is the one already seen
for the `then` function of a process:
* Otherwise, in case of a lambda or a functor, it's enough to provide an
instance of the class to the `attach` member function:
```cpp
scheduler.attach([](entt::process &, std::uint32_t, void *){ /* ... */ });
```
```cpp
scheduler.attach([](auto...){ /* ... */ });
```
In both cases, the scheduler is returned and its `then` member function can be
used to create chains of processes to run sequentially.<br/>
In both cases, the newly created process is returned by reference and its `then`
member function is used to create chains of processes to run sequentially.<br/>
As a minimal example of use:
```cpp
// schedules a task in the form of a lambda function
scheduler.attach([](auto delta, void *, auto succeed, auto fail) {
scheduler.attach([](entt::process &, std::uint32_t, void *) {
// ...
})
// appends a child in the form of another lambda function
.then([](auto delta, void *, auto succeed, auto fail) {
.then([](entt::process &, std::uint32_t, void *) {
// ...
})
// appends a child in the form of a process class
@@ -215,3 +219,6 @@ scheduler.abort(true);
// ... or gracefully during the next tick
scheduler.abort();
```
The argument passed to the `abort` function indicates whether execution should
be stopped immediately or processes should be notified on the next tick.

View File

@@ -1,15 +1,9 @@
# Similar projects
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [Similar projects](#similar-projects)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
@@ -20,13 +14,14 @@ Others developed different architectures from scratch and therefore offer
alternative solutions with their pros and cons.
If you know of other similar projects out there, feel free to open an issue or a
PR and I'll be glad to add them to this page.<br/>
PR, and I will be glad to add them to this page.<br/>
I hope the following lists can grow much more in the future.
# Similar projects
Below an incomplete list of similar projects that I've come across so far.<br/>
If some terms or designs aren't clear, I recommend referring to the
Below an incomplete list of similar projects that I have come across so
far.<br/>
If some terms or designs are not clear, I recommend referring to the
[_ECS Back and Forth_](https://skypjack.github.io/tags/#ecs) series for all the
details.
@@ -40,12 +35,12 @@ details.
* [lent](https://github.com/nem0/lent): the Donald Trump of the ECS libraries.
* C++:
* [decs](https://github.com/vblanco20-1/decs): a chunk based archetype ECS.
* [decs](https://github.com/vblanco20-1/decs): a chunk-based archetype ECS.
* [ecst](https://github.com/SuperV1234/ecst): a multithreaded compile-time
ECS that uses sparse sets to keep track of entities in systems.
* [EntityX](https://github.com/alecthomas/entityx): a bitset based ECS that
uses a single large matrix of components indexed with entities.
* [Gaia-ECS](https://github.com/richardbiely/gaia-ecs): a chunk based
* [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.
@@ -55,10 +50,16 @@ details.
inspired archetype ECS with optional multithreading.
* [Entitas](https://github.com/sschmid/Entitas-CSharp): the ECS framework for
C# and Unity, where _reactive systems_ were invented.
* [Fennecs](https://github.com/outfox/fennecs): the little archetype ECS that
loves you back.
* [Friflo ECS](https://github.com/friflo/Friflo.Engine.ECS): an archetype ECS
with focus on performance and minimal GC allocations.
* [LeoECS](https://github.com/Leopotam/ecs): simple lightweight C# Entity
Component System framework.
* [Massive ECS](https://github.com/nilpunch/massive): sparse set based ECS
featuring rollbacks.
* [Svelto.ECS](https://github.com/sebas77/Svelto.ECS): a very interesting
platform agnostic and table based ECS framework.
platform-agnostic and table-based ECS framework.
* Go:
* [gecs](https://github.com/tutumagi/gecs): a sparse sets based ECS inspired
@@ -67,7 +68,7 @@ details.
* Javascript:
* [\@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
* [ecsy](https://github.com/MozillaReality/ecsy): I have not had the time to
investigate the underlying design of `ecsy` but it looks cool anyway.
* Perl:
@@ -79,7 +80,6 @@ details.
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

View File

@@ -1,18 +1,12 @@
# Crash Course: resource management
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [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
-->
* [Loaders](#loaders)
* [The cache class](#the-cache-class)
# Introduction
@@ -23,7 +17,7 @@ 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
`EnTT` does not pretend to offer a _one-fits-all_ solution for the different
cases.<br/>
Instead, the library comes with a minimal, general purpose resource cache that
might be useful in many cases.
@@ -37,7 +31,8 @@ The _resource_ is an image, an audio, a video or any other type:
struct my_resource { const int value; };
```
The _loader_ is a callable type the aim of which is to load a specific resource:
The _loader_ is a callable type, the aim of which is to load a specific
resource:
```cpp
struct my_loader final {
@@ -74,9 +69,9 @@ discarded when a player leaves 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
Resources are not 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 is 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
@@ -90,22 +85,22 @@ 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
By default, it is 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.
The loader is not required to return a resource handle. As long as `return_type`
is suitable for constructing a handle, that is 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.
type. However, this is not 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:
@@ -117,13 +112,13 @@ struct my_loader {
struct from_disk_tag{};
struct from_network_tag{};
template<typename Args>
template<typename... Args>
result_type operator()(from_disk_tag, Args&&... args) {
// ...
return std::make_shared<my_resource>(std::forward<Args>(args)...);
}
template<typename Args>
template<typename... Args>
result_type operator()(from_network_tag, Args&&... args) {
// ...
return std::make_shared<my_resource>(std::forward<Args>(args)...);
@@ -145,7 +140,7 @@ entt::resource_cache<my_resource, my_loader> cache{};
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
a map, such as `empty` or `size` and so on. Similarly, it is an iterable type
that also supports indexing by resource id:
```cpp
@@ -164,9 +159,9 @@ 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):
In particular, it does not 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 ret = cache.load("resource/id"_hs);
@@ -182,10 +177,10 @@ 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.
It's worth mentioning that the iterators of a cache as well as its indexing
It is 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
Since the cache has no control over the loader and a resource is not 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.
error in the user logic, but it may also be an _expected_ event.<br/>
It is therefore recommended to verify handles validity with a check in debug
(for example, when loading) or an appropriate logic in retail.

View File

@@ -1,8 +1,5 @@
# Crash Course: events, signals and everything in between
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
@@ -14,9 +11,6 @@
* [Event dispatcher](#event-dispatcher)
* [Named queues](#named-queues)
* [Event emitter](#event-emitter)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
@@ -25,14 +19,14 @@ 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 this regard,
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` doesn't perform
However, there is no guarantee that an `std::function` does not 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
In case that the flexibility and power of an `std::function` is not required or
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.
@@ -41,8 +35,8 @@ lightweight classes to solve the same and many other problems.
A delegate can be used as a general purpose invoker with no memory overhead for
free functions, lambdas and members 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
It does not claim to be a drop-in replacement for an `std::function`, so do not
expect to use it whenever an `std::function` fits well. That said, it is most
likely even a better fit than an `std::function` in a lot of cases, so expect to
use it quite a lot anyway.
@@ -81,8 +75,8 @@ function type of the delegate is such that the parameter list is empty and the
value of the data member is at least convertible to the return type.
Free functions having type equivalent to `void(T &, args...)` are accepted as
well. The first argument `T &` is considered a payload and the function will
receive it back every time it's invoked. In other terms, this works just fine
well. The first argument `T &` is considered a payload, and the function will
receive it back every time it is invoked. In other terms, this works just fine
with the above definition:
```cpp
@@ -112,8 +106,8 @@ is used as a building block of a signal-slot system.<br/>
In fact, this filtering works both ways. The class tries to pass its first
_count_ arguments **first**, then the last _count_. Watch out for conversion
rules if in doubt when connecting a listener!<br/>
Arbitrary functions that pull random arguments from the delegate list aren't
supported instead. Other feature were preferred, such as support for functions
Arbitrary functions that pull random arguments from the delegate list are not
supported instead. Other features were preferred, such as support for functions
with compatible argument lists although not equal to those of the delegate.
To create and initialize a delegate at once, there are a few specialized
@@ -124,7 +118,7 @@ means of the `entt::connect_arg` variable template:
entt::delegate<int(int)> func{entt::connect_arg<&f>};
```
Aside `connect`, a `disconnect` counterpart isn't provided. Instead, there
Aside `connect`, a `disconnect` counterpart is not provided. Instead, there
exists a `reset` member function to use to clear a delegate.<br/>
To know if a delegate is empty, it can be used explicitly in every conditional
statement:
@@ -142,14 +136,14 @@ already shown in the examples above:
auto ret = delegate(42);
```
In all cases, listeners don't have to strictly follow the signature of the
In all cases, listeners do not 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.
As a side note, members of classes may or may not be associated with instances.
If they are not, the first argument of the function type must be that of the
class on which the members operate and an instance of this class must obviously
class on which the members operate, and an instance of this class must obviously
be passed when invoking the delegate:
```cpp
@@ -160,8 +154,8 @@ my_struct instance;
delegate(instance, 42);
```
In this case, it's not possible to _deduce_ the function type since the first
argument doesn't necessarily have to be a reference (for example, it can be a
In this case, it is not possible to _deduce_ the function type since the first
argument does not 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.
@@ -170,13 +164,13 @@ Therefore, the function type must be declared explicitly for unbound members.
The `delegate` class is meant to be used primarily with template arguments.
However, as a consequence of its design, it also offers minimal support for
runtime arguments.<br/>
When used like this, some features aren't supported though. In particular:
When used like this, some features are not supported though. In particular:
* Curried functions aren't accepted.
* Functions with an argument list that differs from that of the delegate aren't
* Curried functions are not accepted.
* Functions with an argument list that differs from that of the delegate are not
supported.
* Return type and types of arguments **must** coincide with those of the
delegate and _being at least convertible_ isn't enough anymore.
delegate and _being at least convertible_ is not enough anymore.
Moreover, for a given function type `Ret(Args...)`, the signature of the
functions connected at runtime must necessarily be `Ret(const void *, Args...)`.
@@ -206,12 +200,12 @@ explained.
## Lambda support
In general, the `delegate` class doesn't fully support lambda functions in all
their nuances. The reason is pretty simple: a `delegate` isn't a drop-in
In general, the `delegate` class does not fully support lambda functions in all
their nuances. The reason is pretty simple: a `delegate` is not 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
features aren't available in this case.
features are not 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
@@ -234,7 +228,7 @@ delegate.connect([](const void *ptr, int value) {
}, &instance);
```
As above, the first parameter (`const void *`) isn't part of the function type
As above, the first parameter (`const void *`) is not part of the function type
of the delegate and is used to dispatch arbitrary user data back and forth. In
other terms, the function type of the delegate above is `int(int)`.
@@ -258,12 +252,12 @@ particular delegate through its descriptive _traits_ instead.
# Signals
Signal handlers work with references to classes, function pointers and pointers
to members. Listeners can be any kind of objects and users are in charge of
Signal handlers work with references to classes, function pointers, and pointers
to members. Listeners can be any kind of objects, and users are in charge of
connecting and disconnecting them from a signal to avoid crashes due to
different lifetimes. On the other side, performance shouldn't be affected that
different lifetimes. On the other side, performance should not be affected that
much by the presence of such a signal handler.<br/>
Signals make use of delegates internally and therefore they undergo the same
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.
@@ -277,7 +271,7 @@ 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 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's sufficient to provide the type of
To create instances of signal handlers it is sufficient to provide the type of
function to which they refer:
```cpp
@@ -321,7 +315,7 @@ sink.disconnect(&instance);
sink.disconnect();
```
As shown above, listeners don't have to strictly follow the signature of the
As shown above, listeners do not 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/>
@@ -363,7 +357,7 @@ assert(vec[1] == 1);
A collector must expose a function operator that accepts as an argument a type
to which the return type of the listeners can be converted. Moreover, it can
optionally return a boolean value that is true to stop collecting data, false
otherwise. This way one can avoid calling all the listeners in case it isn't
otherwise. This way one can avoid calling all the listeners in case it is not
necessary.<br/>
Functors can also be used in place of a lambda. Since the collector is copied
when invoking the `collect` member function, `std::ref` is the way to go in this
@@ -389,7 +383,7 @@ signal.collect(std::ref(collector));
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
This class lazily instantiates its queues. Therefore, it is not necessary to
_announce_ the event types in advance:
```cpp
@@ -436,7 +430,7 @@ dispatcher.trigger(an_event{42});
dispatcher.trigger<another_event>();
```
Listeners are invoked immediately, order of execution isn't guaranteed. This
Listeners are invoked immediately, order of execution is not guaranteed. This
method can be used to push around urgent messages like an _is terminating_
notification on a mobile app.
@@ -465,7 +459,7 @@ once per tick to their systems.
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
However, it is 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:
@@ -482,8 +476,8 @@ parameter for it but rather a different function:
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.
This is mainly due to the template argument deduction rules, and there is no
real (elegant) way to avoid it.
# Event emitter
@@ -501,7 +495,7 @@ struct my_emitter: emitter<my_emitter> {
}
```
Handlers for the different events are created internally on the fly. It's not
Handlers for the different events are created internally on the fly. It is 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.
@@ -561,5 +555,5 @@ if(emitter.contains<my_event>()) {
```
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.
More in general, it is a handy tool when the derived classes _wrap_ asynchronous
operations, but it is not limited to such uses.

View File

@@ -1,16 +1,10 @@
# EnTT and Unreal Engine
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Enable Cpp17](#enable-cpp17)
* [EnTT as a third party module](#entt-as-a-third-party-module)
* [Include EnTT](#include-entt)
<!--
@endcond TURN_OFF_DOXYGEN
-->
## Enable Cpp17
@@ -30,7 +24,7 @@ CppStandard = CppStandardVersion.Cpp17;
Replace `<PCH filename>.h` with the name of the already existing PCH header
file, if any.<br/>
In case the project doesn't already contain a file of this type, it's possible
In case the project does not already contain a file of this type, it is possible
to create one with the following content:
```cpp
@@ -43,8 +37,8 @@ this point, C++17 support should be in place.<br/>
Try to compile the project to ensure it works as expected before following
further steps.
Note that updating a *project* to C++17 doesn't necessarily mean that the IDE in
use will also start to recognize its syntax.<br/>
Note that updating a *project* to C++17 does not necessarily mean that the IDE
in use will also start to recognize its syntax.<br/>
If the plan is to use C++17 in the project too, check the specific instructions
for the IDE in use.
@@ -68,8 +62,8 @@ Source
\---entt (GitHub repository content inside)
```
To make this happen, create the folder `ThirdParty` under `Source` if it doesn't
exist already. Then, add an `EnTT` folder under `ThirdParty`.<br/>
To make this happen, create the folder `ThirdParty` under `Source` if it does
not exist already. Then, add an `EnTT` folder under `ThirdParty`.<br/>
Within the latter, create a new file `EnTT.Build.cs` with the following content:
```cs

View File

@@ -5,7 +5,9 @@
# forward files
{ "include": [ "@[\"<].*/container/fwd\\.hpp[\">]", "private", "<entt/container/dense_map.hpp>", "public" ] },
{ "include": [ "@[\"<].*/container/fwd\\.hpp[\">]", "private", "<entt/container/dense_set.hpp>", "public" ] },
{ "include": [ "@[\"<].*/container/fwd\\.hpp[\">]", "private", "<entt/container/table.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/any.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/compressed_pair.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" ] },
@@ -17,8 +19,9 @@
{ "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/mixin.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/organizer.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/ranges.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" ] },
@@ -38,5 +41,12 @@
{ "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" ] }
{ "include": [ "@[\"<].*/signal/fwd\\.hpp[\">]", "private", "<entt/signal/sigh.hpp>", "public" ] },
# symbols
{ symbol: [ "std::allocator", private, "<entt/container/fwd.hpp>", public ] },
{ symbol: [ "std::allocator", private, "<entt/entity/fwd.hpp>", public ] },
{ symbol: [ "std::allocator", private, "<entt/graph/fwd.hpp>", public ] },
{ symbol: [ "std::allocator", private, "<entt/process/fwd.hpp>", public ] },
{ symbol: [ "std::allocator", private, "<entt/resource/fwd.hpp>", public ] },
{ symbol: [ "std::allocator", private, "<entt/signal/fwd.hpp>", public ] }
]

View File

@@ -1,124 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="entt::basic_registry&lt;*&gt;">
<DisplayString>{{ pools={ pools.size() } }}</DisplayString>
<Expand>
<Item Name="[entities]">entities</Item>
<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.ctx.size() }</DisplayString>
<Expand>
<IndexListItems>
<Size>vars.ctx.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;">
<Intrinsic Name="cap" Expression="(traits_type::version_mask &lt;&lt; traits_type::length)"/>
<DisplayString>{{ size={ packed.size() }, type={ info->alias,na } }}</DisplayString>
<Expand>
<Item Name="[capacity]" ExcludeView="simple">packed.capacity()</Item>
<Item Name="[policy]">mode,en</Item>
<Item Name="[free_list]">head</Item>
<Synthetic Name="[sparse]">
<DisplayString>{ sparse.size() * traits_type::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() * traits_type::page_size"/>
<Loop>
<Break Condition="pos == last"/>
<Exec>page = pos / traits_type::page_size</Exec>
<Exec>offset = pos &amp; (traits_type::page_size - 1)</Exec>
<If Condition="sparse[page] &amp;&amp; (*((traits_type::entity_type *)&amp;sparse[page][offset]) &lt; cap())">
<Item Name="[{ pos }]">*((traits_type::entity_type *)&amp;sparse[page][offset]) &amp; traits_type::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="*((traits_type::entity_type *)&amp;packed[pos]) &lt; cap()">
<Item Name="[{ pos }]">packed[pos]</Item>
</If>
<Exec>++pos</Exec>
</Loop>
</CustomListItems>
</Expand>
</Synthetic>
</Expand>
</Type>
<Type Name="entt::basic_storage&lt;*&gt;">
<Intrinsic Name="cap" Expression="(base_type::traits_type::version_mask &lt;&lt; base_type::traits_type::length)"/>
<DisplayString>{{ size={ base_type::packed.size() }, type={ base_type::info->alias,na } }}</DisplayString>
<Expand>
<Item Name="[capacity]" Optional="true" ExcludeView="simple">payload.capacity() * traits_type::page_size</Item>
<Item Name="[page size]" Optional="true" ExcludeView="simple">traits_type::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="payload.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::traits_type::entity_type *)&amp;base_type::packed[pos]) &lt; cap()">
<Item Name="[{ pos }:{ base_type::packed[pos] }]">payload[pos / traits_type::page_size][pos &amp; (traits_type::page_size - 1)]</Item>
</If>
<Exec>++pos</Exec>
</Loop>
</CustomListItems>
</Expand>
</Type>
<Type Name="entt::basic_view&lt;*&gt;">
<DisplayString Condition="leading != nullptr">{{ size_hint={ leading->packed.size() } }}</DisplayString>
<DisplayString>{{ size_hint=0 }}</DisplayString>
<Expand>
<Item Name="[pools]" Optional="true">pools,na</Item>
<Item Name="[filter]" Optional="true">filter,na</Item>
<Item Name="[handle]" Condition="leading != nullptr">leading,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>

View File

@@ -1,121 +0,0 @@
<?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

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

View File

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

View File

@@ -1,15 +0,0 @@
<?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>

View File

@@ -0,0 +1,28 @@
#!/usr/bin/env bash
set -e
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
VERSION_HEADER=$(realpath "$SCRIPT_DIR/../src/entt/config/version.h" --relative-to=$(pwd))
BAZEL_MODULE=$(realpath "$SCRIPT_DIR/../MODULE.bazel" --relative-to=$(pwd))
if [[ -z "${VERSION_HEADER}" ]]; then
echo "Cannot find version header"
exit 1
fi
echo "Getting version from $VERSION_HEADER ..."
ENTT_MAJOR_VERSION=$(sed -nr 's/#define ENTT_VERSION_MAJOR ([0-9]+)/\1/p' $VERSION_HEADER)
ENTT_MINOR_VERSION=$(sed -nr 's/#define ENTT_VERSION_MINOR ([0-9]+)/\1/p' $VERSION_HEADER)
ENTT_PATCH_VERSION=$(sed -nr 's/#define ENTT_VERSION_PATCH ([0-9]+)/\1/p' $VERSION_HEADER)
VERSION="$ENTT_MAJOR_VERSION.$ENTT_MINOR_VERSION.$ENTT_PATCH_VERSION"
echo "Found $VERSION"
buildozer "set version $VERSION" //MODULE.bazel:%module
# a commit is needed for 'git archive'
git add $BAZEL_MODULE
git commit -m "chore: update MODULE.bazel version to $VERSION"

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,8 @@
#include "version.h"
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
# define ENTT_CONSTEXPR
# define ENTT_THROW throw
@@ -15,6 +17,18 @@
# define ENTT_CATCH if(false)
#endif
#if __has_include(<version>)
# include <version>
#
# if defined(__cpp_consteval)
# define ENTT_CONSTEVAL consteval
# endif
#endif
#ifndef ENTT_CONSTEVAL
# define ENTT_CONSTEVAL constexpr
#endif
#ifdef ENTT_USE_ATOMIC
# include <atomic>
# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
@@ -42,7 +56,7 @@
# define ENTT_ASSERT(condition, msg) (void(0))
#elif !defined ENTT_ASSERT
# include <cassert>
# define ENTT_ASSERT(condition, msg) assert(condition)
# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg)))
#endif
#ifdef ENTT_DISABLE_ASSERT
@@ -60,6 +74,12 @@
# define ENTT_ETO_TYPE(Type) Type
#endif
#ifdef ENTT_NO_MIXIN
# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__
#else
# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__>
#endif
#ifdef ENTT_STANDARD_CPP
# define ENTT_NONSTD false
#else
@@ -75,6 +95,32 @@
# endif
#endif
#ifndef ENTT_EXPORT
# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER
# define ENTT_EXPORT __declspec(dllexport)
# define ENTT_IMPORT __declspec(dllimport)
# define ENTT_HIDDEN
# elif defined __GNUC__ && __GNUC__ >= 4
# define ENTT_EXPORT __attribute__((visibility("default")))
# define ENTT_IMPORT __attribute__((visibility("default")))
# define ENTT_HIDDEN __attribute__((visibility("hidden")))
# else /* Unsupported compiler */
# define ENTT_EXPORT
# define ENTT_IMPORT
# define ENTT_HIDDEN
# endif
#endif
#ifndef ENTT_API
# if defined ENTT_API_EXPORT
# define ENTT_API ENTT_EXPORT
# elif defined ENTT_API_IMPORT
# define ENTT_API ENTT_IMPORT
# else /* No API */
# define ENTT_API
# endif
#endif
#if defined _MSC_VER
# pragma detect_mismatch("entt.version", ENTT_VERSION)
# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
@@ -82,4 +128,6 @@
# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
#endif
// NOLINTEND(cppcoreguidelines-macro-usage)
#endif

View File

@@ -1,7 +1,11 @@
#ifndef ENTT_CONFIG_MACRO_H
#define ENTT_CONFIG_MACRO_H
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
#define ENTT_STR(arg) #arg
#define ENTT_XSTR(arg) ENTT_STR(arg)
// NOLINTEND(cppcoreguidelines-macro-usage)
#endif

View File

@@ -3,12 +3,16 @@
#include "macro.h"
// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*)
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 13
#define ENTT_VERSION_MINOR 16
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
"." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*)
#endif

View File

@@ -12,6 +12,7 @@
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../core/bit.hpp"
#include "../core/compressed_pair.hpp"
#include "../core/iterator.hpp"
#include "../core/memory.hpp"
@@ -23,6 +24,8 @@ namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
static constexpr std::size_t dense_map_placeholder_position = (std::numeric_limits<std::size_t>::max)();
template<typename Key, typename Type>
struct dense_map_node final {
using value_type = std::pair<Key, Type>;
@@ -82,7 +85,7 @@ public:
}
constexpr dense_map_iterator operator++(int) noexcept {
dense_map_iterator orig = *this;
const dense_map_iterator orig = *this;
return ++(*this), orig;
}
@@ -91,7 +94,7 @@ public:
}
constexpr dense_map_iterator operator--(int) noexcept {
dense_map_iterator orig = *this;
const dense_map_iterator orig = *this;
return operator--(), orig;
}
@@ -122,7 +125,7 @@ public:
}
[[nodiscard]] constexpr reference operator*() const noexcept {
return {it->element.first, it->element.second};
return operator[](0);
}
template<typename Lhs, typename Rhs>
@@ -189,9 +192,7 @@ public:
using iterator_category = std::input_iterator_tag;
using iterator_concept = std::forward_iterator_tag;
constexpr dense_map_local_iterator() noexcept
: it{},
offset{} {}
constexpr dense_map_local_iterator() noexcept = default;
constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept
: it{iter},
@@ -203,11 +204,11 @@ public:
offset{other.offset} {}
constexpr dense_map_local_iterator &operator++() noexcept {
return offset = it[offset].next, *this;
return (offset = it[static_cast<typename It::difference_type>(offset)].next), *this;
}
constexpr dense_map_local_iterator operator++(int) noexcept {
dense_map_local_iterator orig = *this;
const dense_map_local_iterator orig = *this;
return ++(*this), orig;
}
@@ -216,7 +217,8 @@ public:
}
[[nodiscard]] constexpr reference operator*() const noexcept {
return {it[offset].element.first, it[offset].element.second};
const auto idx = static_cast<typename It::difference_type>(offset);
return {it[idx].element.first, it[idx].element.second};
}
[[nodiscard]] constexpr std::size_t index() const noexcept {
@@ -224,8 +226,8 @@ public:
}
private:
It it;
std::size_t offset;
It it{};
std::size_t offset{dense_map_placeholder_position};
};
template<typename Lhs, typename Rhs>
@@ -258,6 +260,7 @@ template<typename Key, typename Type, typename Hash, typename KeyEqual, typename
class dense_map {
static constexpr float default_threshold = 0.875f;
static constexpr std::size_t minimum_capacity = 8u;
static constexpr std::size_t placeholder_position = internal::dense_map_placeholder_position;
using node_type = internal::dense_map_node<Key, Type>;
using alloc_traits = std::allocator_traits<Allocator>;
@@ -267,14 +270,15 @@ class dense_map {
template<typename Other>
[[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept {
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
return fast_mod(static_cast<size_type>(sparse.second()(key)), bucket_count());
}
template<typename Other>
[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) {
for(auto it = begin(bucket), last = end(bucket); it != last; ++it) {
if(packed.second()(it->first, key)) {
return begin() + static_cast<typename iterator::difference_type>(it.index());
[[nodiscard]] auto constrained_find(const Other &key, const std::size_t bucket) {
for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].next) {
if(packed.second()(packed.first()[offset].element.first, key)) {
return begin() + static_cast<typename iterator::difference_type>(offset);
}
}
@@ -282,10 +286,10 @@ class dense_map {
}
template<typename Other>
[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const {
for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) {
if(packed.second()(it->first, key)) {
return cbegin() + static_cast<typename iterator::difference_type>(it.index());
[[nodiscard]] auto constrained_find(const Other &key, const std::size_t bucket) const {
for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].next) {
if(packed.second()(packed.first()[offset].element.first, key)) {
return cbegin() + static_cast<typename const_iterator::difference_type>(offset);
}
}
@@ -325,7 +329,7 @@ class dense_map {
void move_and_pop(const std::size_t pos) {
if(const auto last = size() - 1u; pos != last) {
size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first);
size_type *curr = &sparse.first()[key_to_bucket(packed.first().back().element.first)];
packed.first()[pos] = std::move(packed.first().back());
for(; *curr != last; curr = &packed.first()[*curr].next) {}
*curr = pos;
@@ -335,12 +339,14 @@ class dense_map {
}
void rehash_if_required() {
if(size() > (bucket_count() * max_load_factor())) {
rehash(bucket_count() * 2u);
if(const auto bc = bucket_count(); size() > static_cast<size_type>(static_cast<float>(bc) * max_load_factor())) {
rehash(bc * 2u);
}
}
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Key type of the container. */
using key_type = Key;
/*! @brief Mapped type of the container. */
@@ -349,12 +355,12 @@ public:
using value_type = std::pair<const Key, Type>;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Signed integer type. */
using difference_type = std::ptrdiff_t;
/*! @brief Type of function to use to hash the keys. */
using hasher = Hash;
/*! @brief Type of function to use to compare the keys for equality. */
using key_equal = KeyEqual;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Input iterator type. */
using iterator = internal::dense_map_iterator<typename packed_container_type::iterator>;
/*! @brief Constant input iterator type. */
@@ -404,8 +410,7 @@ public:
*/
explicit dense_map(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} {
packed{allocator, equal} {
rehash(cnt);
}
@@ -423,7 +428,7 @@ public:
threshold{other.threshold} {}
/*! @brief Default move constructor. */
dense_map(dense_map &&) 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;
dense_map(dense_map &&) noexcept = default;
/**
* @brief Allocator-extended move constructor.
@@ -435,6 +440,9 @@ public:
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 destructor. */
~dense_map() = default;
/**
* @brief Default copy assignment operator.
* @return This container.
@@ -445,7 +453,18 @@ public:
* @brief Default move assignment operator.
* @return This container.
*/
dense_map &operator=(dense_map &&) 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;
dense_map &operator=(dense_map &&) noexcept = default;
/**
* @brief Exchanges the contents with those of a given container.
* @param other Container to exchange the content with.
*/
void swap(dense_map &other) noexcept {
using std::swap;
swap(sparse, other.sparse);
swap(packed, other.packed);
swap(threshold, other.threshold);
}
/**
* @brief Returns the associated allocator.
@@ -665,7 +684,7 @@ public:
const auto dist = first - cbegin();
for(auto from = last - cbegin(); from != dist; --from) {
erase(packed.first()[from - 1u].element.first);
erase(packed.first()[static_cast<size_type>(from) - 1u].element.first);
}
return (begin() + dist);
@@ -677,7 +696,7 @@ public:
* @return Number of elements removed (either 0 or 1).
*/
size_type erase(const key_type &key) {
for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].next) {
for(size_type *curr = &sparse.first()[key_to_bucket(key)]; *curr != placeholder_position; curr = &packed.first()[*curr].next) {
if(packed.second()(packed.first()[*curr].element.first, key)) {
const auto index = *curr;
*curr = packed.first()[*curr].next;
@@ -689,17 +708,6 @@ public:
return 0u;
}
/**
* @brief Exchanges the contents with those of a given container.
* @param other Container to exchange the content with.
*/
void swap(dense_map &other) {
using std::swap;
swap(sparse, other.sparse);
swap(packed, other.packed);
swap(threshold, other.threshold);
}
/**
* @brief Accesses a given element with bounds checking.
* @param key A key of an element to find.
@@ -718,6 +726,29 @@ public:
return it->second;
}
/**
* @brief Accesses a given element with bounds checking.
* @tparam Other Type of the key of an element to find.
* @param key A key of an element to find.
* @return A reference to the mapped value of the requested element.
*/
template<typename Other>
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, mapped_type const &>>
at(const Other &key) const {
auto it = find(key);
ENTT_ASSERT(it != cend(), "Invalid key");
return it->second;
}
/*! @copydoc at */
template<typename Other>
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, mapped_type &>>
at(const Other &key) {
auto it = find(key);
ENTT_ASSERT(it != end(), "Invalid key");
return it->second;
}
/**
* @brief Accesses or inserts a given element.
* @param key A key of an element to find or insert.
@@ -888,7 +919,7 @@ public:
* @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)()};
return {};
}
/**
@@ -906,7 +937,7 @@ public:
* @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)()};
return {};
}
/**
@@ -948,7 +979,7 @@ public:
* @return The average number of elements per bucket.
*/
[[nodiscard]] float load_factor() const {
return size() / static_cast<float>(bucket_count());
return static_cast<float>(size()) / static_cast<float>(bucket_count());
}
/**
@@ -976,14 +1007,14 @@ public:
*/
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());
const auto cap = static_cast<size_type>(static_cast<float>(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)();
elem = placeholder_position;
}
for(size_type pos{}, last = size(); pos < last; ++pos) {
@@ -1000,7 +1031,7 @@ public:
*/
void reserve(const size_type cnt) {
packed.first().reserve(cnt);
rehash(static_cast<size_type>(std::ceil(cnt / max_load_factor())));
rehash(static_cast<size_type>(std::ceil(static_cast<float>(cnt) / max_load_factor())));
}
/**
@@ -1022,7 +1053,7 @@ public:
private:
compressed_pair<sparse_container_type, hasher> sparse;
compressed_pair<packed_container_type, key_equal> packed;
float threshold;
float threshold{default_threshold};
};
} // namespace entt

View File

@@ -12,8 +12,8 @@
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../core/bit.hpp"
#include "../core/compressed_pair.hpp"
#include "../core/memory.hpp"
#include "../core/type_traits.hpp"
#include "fwd.hpp"
@@ -22,6 +22,8 @@ namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
static constexpr std::size_t dense_set_placeholder_position = (std::numeric_limits<std::size_t>::max)();
template<typename It>
class dense_set_iterator final {
template<typename>
@@ -49,7 +51,7 @@ public:
}
constexpr dense_set_iterator operator++(int) noexcept {
dense_set_iterator orig = *this;
const dense_set_iterator orig = *this;
return ++(*this), orig;
}
@@ -58,7 +60,7 @@ public:
}
constexpr dense_set_iterator operator--(int) noexcept {
dense_set_iterator orig = *this;
const dense_set_iterator orig = *this;
return operator--(), orig;
}
@@ -85,11 +87,11 @@ public:
}
[[nodiscard]] constexpr pointer operator->() const noexcept {
return std::addressof(it->second);
return std::addressof(operator[](0));
}
[[nodiscard]] constexpr reference operator*() const noexcept {
return *operator->();
return operator[](0);
}
template<typename Lhs, typename Rhs>
@@ -152,9 +154,7 @@ public:
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() noexcept = default;
constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept
: it{iter},
@@ -166,16 +166,16 @@ public:
offset{other.offset} {}
constexpr dense_set_local_iterator &operator++() noexcept {
return offset = it[offset].first, *this;
return offset = it[static_cast<typename It::difference_type>(offset)].first, *this;
}
constexpr dense_set_local_iterator operator++(int) noexcept {
dense_set_local_iterator orig = *this;
const dense_set_local_iterator orig = *this;
return ++(*this), orig;
}
[[nodiscard]] constexpr pointer operator->() const noexcept {
return std::addressof(it[offset].second);
return std::addressof(it[static_cast<typename It::difference_type>(offset)].second);
}
[[nodiscard]] constexpr reference operator*() const noexcept {
@@ -187,8 +187,8 @@ public:
}
private:
It it;
std::size_t offset;
It it{};
std::size_t offset{dense_set_placeholder_position};
};
template<typename Lhs, typename Rhs>
@@ -220,6 +220,7 @@ 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;
static constexpr std::size_t placeholder_position = internal::dense_set_placeholder_position;
using node_type = std::pair<std::size_t, Type>;
using alloc_traits = std::allocator_traits<Allocator>;
@@ -233,10 +234,10 @@ class dense_set {
}
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());
[[nodiscard]] auto constrained_find(const Other &value, const std::size_t bucket) {
for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].first) {
if(packed.second()(packed.first()[offset].second, value)) {
return begin() + static_cast<typename iterator::difference_type>(offset);
}
}
@@ -244,10 +245,10 @@ class dense_set {
}
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());
[[nodiscard]] auto constrained_find(const Other &value, const std::size_t bucket) const {
for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].first) {
if(packed.second()(packed.first()[offset].second, value)) {
return cbegin() + static_cast<typename const_iterator::difference_type>(offset);
}
}
@@ -271,7 +272,7 @@ class dense_set {
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);
size_type *curr = &sparse.first()[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;
@@ -281,24 +282,26 @@ class dense_set {
}
void rehash_if_required() {
if(size() > (bucket_count() * max_load_factor())) {
rehash(bucket_count() * 2u);
if(const auto bc = bucket_count(); size() > static_cast<size_type>(static_cast<float>(bc) * max_load_factor())) {
rehash(bc * 2u);
}
}
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @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 Signed integer type. */
using difference_type = std::ptrdiff_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. */
@@ -352,8 +355,7 @@ public:
*/
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} {
packed{allocator, equal} {
rehash(cnt);
}
@@ -371,7 +373,7 @@ public:
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;
dense_set(dense_set &&) noexcept = default;
/**
* @brief Allocator-extended move constructor.
@@ -383,6 +385,9 @@ public:
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 destructor. */
~dense_set() = default;
/**
* @brief Default copy assignment operator.
* @return This container.
@@ -393,7 +398,18 @@ public:
* @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;
dense_set &operator=(dense_set &&) noexcept = default;
/**
* @brief Exchanges the contents with those of a given container.
* @param other Container to exchange the content with.
*/
void swap(dense_set &other) noexcept {
using std::swap;
swap(sparse, other.sparse);
swap(packed, other.packed);
swap(threshold, other.threshold);
}
/**
* @brief Returns the associated allocator.
@@ -597,7 +613,7 @@ public:
const auto dist = first - cbegin();
for(auto from = last - cbegin(); from != dist; --from) {
erase(packed.first()[from - 1u].second);
erase(packed.first()[static_cast<size_type>(from) - 1u].second);
}
return (begin() + dist);
@@ -609,7 +625,7 @@ public:
* @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) {
for(size_type *curr = &sparse.first()[value_to_bucket(value)]; *curr != placeholder_position; curr = &packed.first()[*curr].first) {
if(packed.second()(packed.first()[*curr].second, value)) {
const auto index = *curr;
*curr = packed.first()[*curr].first;
@@ -621,17 +637,6 @@ public:
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.
@@ -783,7 +788,7 @@ public:
* @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)()};
return {};
}
/**
@@ -801,7 +806,7 @@ public:
* @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)()};
return {};
}
/**
@@ -843,7 +848,7 @@ public:
* @return The average number of elements per bucket.
*/
[[nodiscard]] float load_factor() const {
return size() / static_cast<float>(bucket_count());
return static_cast<float>(size()) / static_cast<float>(bucket_count());
}
/**
@@ -871,14 +876,14 @@ public:
*/
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());
const auto cap = static_cast<size_type>(static_cast<float>(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)();
elem = placeholder_position;
}
for(size_type pos{}, last = size(); pos < last; ++pos) {
@@ -895,7 +900,7 @@ public:
*/
void reserve(const size_type cnt) {
packed.first().reserve(cnt);
rehash(static_cast<size_type>(std::ceil(cnt / max_load_factor())));
rehash(static_cast<size_type>(std::ceil(static_cast<float>(cnt) / max_load_factor())));
}
/**
@@ -917,7 +922,7 @@ public:
private:
compressed_pair<sparse_container_type, hasher> sparse;
compressed_pair<packed_container_type, key_equal> packed;
float threshold;
float threshold{default_threshold};
};
} // namespace entt

View File

@@ -4,6 +4,7 @@
#include <functional>
#include <memory>
#include <utility>
#include <vector>
namespace entt {
@@ -11,17 +12,27 @@ template<
typename Key,
typename Type,
typename = std::hash<Key>,
typename = std::equal_to<Key>,
typename = std::equal_to<>,
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::equal_to<>,
typename = std::allocator<Type>>
class dense_set;
template<typename...>
class basic_table;
/**
* @brief Alias declaration for the most common use case.
* @tparam Type Element types.
*/
template<typename... Type>
using table = basic_table<std::vector<Type>...>;
} // namespace entt
#endif

View File

@@ -0,0 +1,462 @@
#ifndef ENTT_CONTAINER_TABLE_HPP
#define ENTT_CONTAINER_TABLE_HPP
#include <cstddef>
#include <iterator>
#include <tuple>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/iterator.hpp"
#include "fwd.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename... It>
class table_iterator {
template<typename...>
friend class table_iterator;
public:
using value_type = decltype(std::forward_as_tuple(*std::declval<It>()...));
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
using iterator_concept = std::random_access_iterator_tag;
constexpr table_iterator() noexcept
: it{} {}
constexpr table_iterator(It... from) noexcept
: it{from...} {}
template<typename... Other, typename = std::enable_if_t<(std::is_constructible_v<It, Other> && ...)>>
constexpr table_iterator(const table_iterator<Other...> &other) noexcept
: table_iterator{std::get<Other>(other.it)...} {}
constexpr table_iterator &operator++() noexcept {
return (++std::get<It>(it), ...), *this;
}
constexpr table_iterator operator++(int) noexcept {
const table_iterator orig = *this;
return ++(*this), orig;
}
constexpr table_iterator &operator--() noexcept {
return (--std::get<It>(it), ...), *this;
}
constexpr table_iterator operator--(int) noexcept {
const table_iterator orig = *this;
return operator--(), orig;
}
constexpr table_iterator &operator+=(const difference_type value) noexcept {
return ((std::get<It>(it) += value), ...), *this;
}
constexpr table_iterator operator+(const difference_type value) const noexcept {
table_iterator copy = *this;
return (copy += value);
}
constexpr table_iterator &operator-=(const difference_type value) noexcept {
return (*this += -value);
}
constexpr table_iterator operator-(const difference_type value) const noexcept {
return (*this + -value);
}
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
return std::forward_as_tuple(std::get<It>(it)[value]...);
}
[[nodiscard]] constexpr pointer operator->() const noexcept {
return {operator[](0)};
}
[[nodiscard]] constexpr reference operator*() const noexcept {
return operator[](0);
}
template<typename... Lhs, typename... Rhs>
friend constexpr std::ptrdiff_t operator-(const table_iterator<Lhs...> &, const table_iterator<Rhs...> &) noexcept;
template<typename... Lhs, typename... Rhs>
friend constexpr bool operator==(const table_iterator<Lhs...> &, const table_iterator<Rhs...> &) noexcept;
template<typename... Lhs, typename... Rhs>
friend constexpr bool operator<(const table_iterator<Lhs...> &, const table_iterator<Rhs...> &) noexcept;
private:
std::tuple<It...> it;
};
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
return std::get<0>(lhs.it) - std::get<0>(rhs.it);
}
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator==(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
return std::get<0>(lhs.it) == std::get<0>(rhs.it);
}
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator!=(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
return !(lhs == rhs);
}
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator<(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
return std::get<0>(lhs.it) < std::get<0>(rhs.it);
}
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator>(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
return rhs < lhs;
}
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator<=(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
return !(lhs > rhs);
}
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator>=(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
return !(lhs < rhs);
}
} // namespace internal
/*! @endcond */
/**
* @brief Basic table implementation.
*
* Internal data structures arrange elements to maximize performance. There are
* no guarantees that objects are returned in the insertion order when iterate
* a table. Do not make assumption on the order in any case.
*
* @tparam Container Sequence container row types.
*/
template<typename... Container>
class basic_table {
using container_type = std::tuple<Container...>;
public:
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Signed integer type. */
using difference_type = std::ptrdiff_t;
/*! @brief Input iterator type. */
using iterator = internal::table_iterator<typename Container::iterator...>;
/*! @brief Constant input iterator type. */
using const_iterator = internal::table_iterator<typename Container::const_iterator...>;
/*! @brief Reverse iterator type. */
using reverse_iterator = internal::table_iterator<typename Container::reverse_iterator...>;
/*! @brief Constant reverse iterator type. */
using const_reverse_iterator = internal::table_iterator<typename Container::const_reverse_iterator...>;
/*! @brief Default constructor. */
basic_table()
: payload{} {
}
/**
* @brief Copy constructs the underlying containers.
* @param container The containers to copy from.
*/
explicit basic_table(const Container &...container) noexcept
: payload{container...} {
ENTT_ASSERT((((std::get<Container>(payload).size() * sizeof...(Container)) == (std::get<Container>(payload).size() + ...)) && ...), "Unexpected container size");
}
/**
* @brief Move constructs the underlying containers.
* @param container The containers to move from.
*/
explicit basic_table(Container &&...container) noexcept
: payload{std::move(container)...} {
ENTT_ASSERT((((std::get<Container>(payload).size() * sizeof...(Container)) == (std::get<Container>(payload).size() + ...)) && ...), "Unexpected container size");
}
/*! @brief Default copy constructor, deleted on purpose. */
basic_table(const basic_table &) = delete;
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
basic_table(basic_table &&other) noexcept
: payload{std::move(other.payload)} {}
/**
* @brief Constructs the underlying containers using a given allocator.
* @tparam Allocator Type of allocator.
* @param allocator A valid allocator.
*/
template<typename Allocator>
explicit basic_table(const Allocator &allocator)
: payload{Container{allocator}...} {}
/**
* @brief Copy constructs the underlying containers using a given allocator.
* @tparam Allocator Type of allocator.
* @param container The containers to copy from.
* @param allocator A valid allocator.
*/
template<class Allocator>
basic_table(const Container &...container, const Allocator &allocator) noexcept
: payload{Container{container, allocator}...} {
ENTT_ASSERT((((std::get<Container>(payload).size() * sizeof...(Container)) == (std::get<Container>(payload).size() + ...)) && ...), "Unexpected container size");
}
/**
* @brief Move constructs the underlying containers using a given allocator.
* @tparam Allocator Type of allocator.
* @param container The containers to move from.
* @param allocator A valid allocator.
*/
template<class Allocator>
basic_table(Container &&...container, const Allocator &allocator) noexcept
: payload{Container{std::move(container), allocator}...} {
ENTT_ASSERT((((std::get<Container>(payload).size() * sizeof...(Container)) == (std::get<Container>(payload).size() + ...)) && ...), "Unexpected container size");
}
/**
* @brief Allocator-extended move constructor.
* @tparam Allocator Type of allocator.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
template<class Allocator>
basic_table(basic_table &&other, const Allocator &allocator)
: payload{Container{std::move(std::get<Container>(other.payload)), allocator}...} {}
/*! @brief Default destructor. */
~basic_table() = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This container.
*/
basic_table &operator=(const basic_table &) = delete;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This container.
*/
basic_table &operator=(basic_table &&other) noexcept {
swap(other);
return *this;
}
/**
* @brief Exchanges the contents with those of a given table.
* @param other Table to exchange the content with.
*/
void swap(basic_table &other) noexcept {
using std::swap;
swap(payload, other.payload);
}
/**
* @brief Increases the capacity of a table.
*
* If the new capacity is greater than the current capacity, new storage is
* allocated, otherwise the method does nothing.
*
* @param cap Desired capacity.
*/
void reserve(const size_type cap) {
(std::get<Container>(payload).reserve(cap), ...);
}
/**
* @brief Returns the number of rows that a table has currently allocated
* space for.
* @return Capacity of the table.
*/
[[nodiscard]] size_type capacity() const noexcept {
return std::get<0>(payload).capacity();
}
/*! @brief Requests the removal of unused capacity. */
void shrink_to_fit() {
(std::get<Container>(payload).shrink_to_fit(), ...);
}
/**
* @brief Returns the number of rows in a table.
* @return Number of rows.
*/
[[nodiscard]] size_type size() const noexcept {
return std::get<0>(payload).size();
}
/**
* @brief Checks whether a table is empty.
* @return True if the table is empty, false otherwise.
*/
[[nodiscard]] bool empty() const noexcept {
return std::get<0>(payload).empty();
}
/**
* @brief Returns an iterator to the beginning.
*
* If the table is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first row of the table.
*/
[[nodiscard]] const_iterator cbegin() const noexcept {
return {std::get<Container>(payload).cbegin()...};
}
/*! @copydoc cbegin */
[[nodiscard]] const_iterator begin() const noexcept {
return cbegin();
}
/*! @copydoc begin */
[[nodiscard]] iterator begin() noexcept {
return {std::get<Container>(payload).begin()...};
}
/**
* @brief Returns an iterator to the end.
* @return An iterator to the element following the last row of the table.
*/
[[nodiscard]] const_iterator cend() const noexcept {
return {std::get<Container>(payload).cend()...};
}
/*! @copydoc cend */
[[nodiscard]] const_iterator end() const noexcept {
return cend();
}
/*! @copydoc end */
[[nodiscard]] iterator end() noexcept {
return {std::get<Container>(payload).end()...};
}
/**
* @brief Returns a reverse iterator to the beginning.
*
* If the table is empty, the returned iterator will be equal to `rend()`.
*
* @return An iterator to the first row of the reversed table.
*/
[[nodiscard]] const_reverse_iterator crbegin() const noexcept {
return {std::get<Container>(payload).crbegin()...};
}
/*! @copydoc crbegin */
[[nodiscard]] const_reverse_iterator rbegin() const noexcept {
return crbegin();
}
/*! @copydoc rbegin */
[[nodiscard]] reverse_iterator rbegin() noexcept {
return {std::get<Container>(payload).rbegin()...};
}
/**
* @brief Returns a reverse iterator to the end.
* @return An iterator to the element following the last row of the reversed
* table.
*/
[[nodiscard]] const_reverse_iterator crend() const noexcept {
return {std::get<Container>(payload).crend()...};
}
/*! @copydoc crend */
[[nodiscard]] const_reverse_iterator rend() const noexcept {
return crend();
}
/*! @copydoc rend */
[[nodiscard]] reverse_iterator rend() noexcept {
return {std::get<Container>(payload).rend()...};
}
/**
* @brief Appends a row to the end of a table.
* @tparam Args Types of arguments to use to construct the row data.
* @param args Parameters to use to construct the row data.
* @return A reference to the newly created row data.
*/
template<typename... Args>
std::tuple<typename Container::value_type &...> emplace(Args &&...args) {
if constexpr(sizeof...(Args) == 0u) {
return std::forward_as_tuple(std::get<Container>(payload).emplace_back()...);
} else {
return std::forward_as_tuple(std::get<Container>(payload).emplace_back(std::forward<Args>(args))...);
}
}
/**
* @brief Removes a row from a table.
* @param pos An iterator to the row to remove.
* @return An iterator following the removed row.
*/
iterator erase(const_iterator pos) {
const auto diff = pos - begin();
return {std::get<Container>(payload).erase(std::get<Container>(payload).begin() + diff)...};
}
/**
* @brief Removes a row from a table.
* @param pos Index of the row to remove.
*/
void erase(const size_type pos) {
ENTT_ASSERT(pos < size(), "Index out of bounds");
erase(begin() + static_cast<difference_type>(pos));
}
/**
* @brief Returns the row data at specified location.
* @param pos The row for which to return the data.
* @return The row data at specified location.
*/
[[nodiscard]] std::tuple<const typename Container::value_type &...> operator[](const size_type pos) const {
ENTT_ASSERT(pos < size(), "Index out of bounds");
return std::forward_as_tuple(std::get<Container>(payload)[pos]...);
}
/*! @copydoc operator[] */
[[nodiscard]] std::tuple<typename Container::value_type &...> operator[](const size_type pos) {
ENTT_ASSERT(pos < size(), "Index out of bounds");
return std::forward_as_tuple(std::get<Container>(payload)[pos]...);
}
/*! @brief Clears a table. */
void clear() {
(std::get<Container>(payload).clear(), ...);
}
private:
container_type payload;
};
} // namespace entt
/*! @cond TURN_OFF_DOXYGEN */
namespace std {
template<typename... Container, typename Allocator>
struct uses_allocator<entt::basic_table<Container...>, Allocator>
: std::bool_constant<(std::uses_allocator_v<Container, Allocator> && ...)> {};
} // namespace std
/*! @endcond */
#endif

View File

@@ -58,9 +58,11 @@ struct insertion_sort {
auto value = std::move(*it);
auto pre = it;
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic)
for(; pre > first && compare(value, *(pre - 1)); --pre) {
*pre = std::move(*(pre - 1));
}
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
*pre = std::move(value);
}
@@ -98,29 +100,34 @@ struct radix_sort {
constexpr auto passes = N / Bit;
using value_type = typename std::iterator_traits<It>::value_type;
std::vector<value_type> aux(std::distance(first, last));
using difference_type = typename std::iterator_traits<It>::difference_type;
std::vector<value_type> aux(static_cast<std::size_t>(std::distance(first, last)));
auto part = [getter = std::move(getter)](auto from, auto to, auto out, auto start) {
constexpr auto mask = (1 << Bit) - 1;
constexpr auto buckets = 1 << Bit;
std::size_t index[buckets]{};
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays, misc-const-correctness)
std::size_t count[buckets]{};
for(auto it = from; it != to; ++it) {
++count[(getter(*it) >> start) & mask];
}
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
std::size_t index[buckets]{};
for(std::size_t pos{}, end = buckets - 1u; pos < end; ++pos) {
index[pos + 1u] = index[pos] + count[pos];
}
for(auto it = from; it != to; ++it) {
out[index[(getter(*it) >> start) & mask]++] = std::move(*it);
const auto pos = index[(getter(*it) >> start) & mask]++;
out[static_cast<difference_type>(pos)] = std::move(*it);
}
};
for(std::size_t pass = 0; pass < (passes & ~1); pass += 2) {
for(std::size_t pass = 0; pass < (passes & ~1u); pass += 2) {
part(first, last, aux.begin(), pass * Bit);
part(aux.begin(), aux.end(), first, (pass + 1) * Bit);
}

View File

@@ -6,150 +6,180 @@
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/utility.hpp"
#include "fwd.hpp"
#include "type_info.hpp"
#include "type_traits.hpp"
#include "utility.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
enum class any_operation : std::uint8_t {
copy,
move,
enum class any_request : std::uint8_t {
info,
transfer,
assign,
destroy,
compare,
get
copy,
move
};
template<std::size_t Len, std::size_t Align>
struct basic_any_storage {
static constexpr bool has_buffer = true;
union {
const void *instance{};
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
alignas(Align) std::byte buffer[Len];
};
};
template<std::size_t Align>
struct basic_any_storage<0u, Align> {
static constexpr bool has_buffer = false;
const void *instance{};
};
template<typename Type, std::size_t Len, std::size_t Align>
// NOLINTNEXTLINE(bugprone-sizeof-expression)
struct in_situ: std::bool_constant<(Len != 0u) && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v<Type>> {};
template<std::size_t Len, std::size_t Align>
struct in_situ<void, Len, Align>: std::false_type {};
} // namespace internal
/*! @endcond */
/*! @brief Possible modes of an any object. */
enum class any_policy : std::uint8_t {
/*! @brief Default mode, the object owns the contained element. */
owner,
/*! @brief Aliasing mode, the object _points_ to a non-const element. */
ref,
/*! @brief Const aliasing mode, the object _points_ to a const element. */
cref
};
/**
* @brief A SBO friendly, type-safe container for single values of any type.
* @tparam Len Size of the storage reserved for the small buffer optimization.
* @tparam Len Size of the buffer reserved for the small buffer optimization.
* @tparam Align Optional alignment requirement.
*/
template<std::size_t Len, std::size_t Align>
class basic_any {
using operation = internal::any_operation;
using vtable_type = const void *(const operation, const basic_any &, const void *);
struct storage_type {
alignas(Align) std::byte data[Len + !Len];
};
class basic_any: private internal::basic_any_storage<Len, Align> {
using request = internal::any_request;
using base_type = internal::basic_any_storage<Len, Align>;
using vtable_type = const void *(const request, const basic_any &, const void *);
using deleter_type = void(const basic_any &);
template<typename Type>
static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v<Type>;
static constexpr bool in_situ_v = internal::in_situ<Type, Len, Align>::value;
template<typename Type>
static const void *basic_vtable(const operation op, const basic_any &value, const void *other) {
static_assert(!std::is_void_v<Type> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
const Type *element = nullptr;
static const void *basic_vtable(const request req, const basic_any &value, const void *other) {
static_assert(std::is_same_v<std::remove_const_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
if constexpr(in_situ<Type>) {
element = (value.mode == any_policy::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.mode == any_policy::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:
switch(const auto *elem = static_cast<const Type *>(value.data()); req) {
case request::info:
return &type_id<Type>();
case request::transfer:
if constexpr(std::is_move_assignable_v<Type>) {
*const_cast<Type *>(element) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
// NOLINTNEXTLINE(bugprone-casting-through-void)
*const_cast<Type *>(elem) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
return other;
}
[[fallthrough]];
case operation::assign:
case request::assign:
if constexpr(std::is_copy_assignable_v<Type>) {
*const_cast<Type *>(element) = *static_cast<const Type *>(other);
*const_cast<Type *>(elem) = *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;
case request::compare:
if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) {
return (*elem == *static_cast<const Type *>(other)) ? other : nullptr;
} else {
delete element;
return (elem == other) ? other : nullptr;
}
case request::copy:
if constexpr(std::is_copy_constructible_v<Type>) {
// NOLINTNEXTLINE(bugprone-casting-through-void)
static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*elem);
}
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 request::move:
ENTT_ASSERT(value.mode == any_policy::embedded, "Unexpected policy");
if constexpr(in_situ_v<Type>) {
// NOLINTNEXTLINE(bugprone-casting-through-void, bugprone-multi-level-implicit-pointer-conversion)
return ::new(&static_cast<basic_any *>(const_cast<void *>(other))->buffer) Type{std::move(*const_cast<Type *>(elem))};
}
case operation::get:
return element;
}
return nullptr;
}
template<typename Type>
static void basic_deleter(const basic_any &value) {
static_assert(std::is_same_v<std::remove_const_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
ENTT_ASSERT((value.mode == any_policy::dynamic) || ((value.mode == any_policy::embedded) && !std::is_trivially_destructible_v<Type>), "Unexpected policy");
const auto *elem = static_cast<const Type *>(value.data());
if constexpr(in_situ_v<Type>) {
(value.mode == any_policy::embedded) ? elem->~Type() : (delete elem);
} else if constexpr(std::is_array_v<Type>) {
delete[] elem;
} else {
delete elem;
}
}
template<typename Type, typename... Args>
void initialize([[maybe_unused]] Args &&...args) {
info = &type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
using plain_type = std::remove_const_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>>>;
vtable = basic_vtable<plain_type>;
underlying_type = type_hash<plain_type>::value();
if constexpr(std::is_lvalue_reference_v<Type>) {
static_assert((std::is_lvalue_reference_v<Args> && ...) && (sizeof...(Args) == 1u), "Invalid arguments");
mode = std::is_const_v<std::remove_reference_t<Type>> ? any_policy::cref : any_policy::ref;
instance = (std::addressof(args), ...);
} else if constexpr(in_situ<std::remove_cv_t<std::remove_reference_t<Type>>>) {
if constexpr(std::is_aggregate_v<std::remove_cv_t<std::remove_reference_t<Type>>> && (sizeof...(Args) != 0u || !std::is_default_constructible_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) std::remove_cv_t<std::remove_reference_t<Type>>(std::forward<Args>(args)...);
}
if constexpr(std::is_void_v<Type>) {
deleter = nullptr;
mode = any_policy::empty;
this->instance = nullptr;
} else if constexpr(std::is_lvalue_reference_v<Type>) {
deleter = nullptr;
mode = std::is_const_v<std::remove_reference_t<Type>> ? any_policy::cref : any_policy::ref;
static_assert((std::is_lvalue_reference_v<Args> && ...) && (sizeof...(Args) == 1u), "Invalid arguments");
// NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion)
this->instance = (std::addressof(args), ...);
} else if constexpr(in_situ_v<plain_type>) {
if constexpr(std::is_trivially_destructible_v<plain_type>) {
deleter = nullptr;
} else {
if constexpr(std::is_aggregate_v<std::remove_cv_t<std::remove_reference_t<Type>>> && (sizeof...(Args) != 0u || !std::is_default_constructible_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 std::remove_cv_t<std::remove_reference_t<Type>>(std::forward<Args>(args)...);
}
deleter = &basic_deleter<plain_type>;
}
mode = any_policy::embedded;
if constexpr(std::is_aggregate_v<plain_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<plain_type>)) {
::new(&this->buffer) plain_type{std::forward<Args>(args)...};
} else {
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
::new(&this->buffer) plain_type(std::forward<Args>(args)...);
}
} else {
deleter = &basic_deleter<plain_type>;
mode = any_policy::dynamic;
if constexpr(std::is_aggregate_v<plain_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<plain_type>)) {
this->instance = new plain_type{std::forward<Args>(args)...};
} else if constexpr(std::is_array_v<plain_type>) {
static_assert(sizeof...(Args) == 0u, "Invalid arguments");
this->instance = new plain_type[std::extent_v<plain_type>]();
} else {
this->instance = new plain_type(std::forward<Args>(args)...);
}
}
}
basic_any(const basic_any &other, const any_policy pol) noexcept
: instance{other.data()},
info{other.info},
vtable{other.vtable},
mode{pol} {}
void invoke_deleter_if_exists() {
if(deleter != nullptr) {
deleter(*this);
}
}
public:
/*! @brief Size of the internal storage. */
/*! @brief Size of the internal buffer. */
static constexpr auto length = Len;
/*! @brief Alignment requirement. */
static constexpr auto alignment = Align;
@@ -166,13 +196,29 @@ public:
*/
template<typename Type, typename... Args>
explicit basic_any(std::in_place_type_t<Type>, Args &&...args)
: instance{},
info{},
vtable{},
mode{any_policy::owner} {
: base_type{} {
initialize<Type>(std::forward<Args>(args)...);
}
/**
* @brief Constructs a wrapper taking ownership of the passed object.
* @tparam Type Type of object to use to initialize the wrapper.
* @param value A pointer to an object to take ownership of.
*/
template<typename Type>
explicit basic_any(std::in_place_t, Type *value)
: base_type{} {
static_assert(!std::is_const_v<Type> && !std::is_void_v<Type>, "Non-const non-void pointer required");
if(value == nullptr) {
initialize<void>();
} else {
initialize<Type &>(*value);
deleter = &basic_deleter<Type>;
mode = any_policy::dynamic;
}
}
/**
* @brief Constructs a wrapper from a given value.
* @tparam Type Type of object to use to initialize the wrapper.
@@ -188,9 +234,7 @@ public:
*/
basic_any(const basic_any &other)
: basic_any{} {
if(other.vtable) {
other.vtable(operation::copy, other, this);
}
other.vtable(request::copy, other, this);
}
/**
@@ -198,20 +242,21 @@ public:
* @param other The instance to move from.
*/
basic_any(basic_any &&other) noexcept
: instance{},
info{other.info},
: base_type{},
vtable{other.vtable},
deleter{other.deleter},
underlying_type{other.underlying_type},
mode{other.mode} {
if(other.vtable) {
other.vtable(operation::move, other, this);
if(other.mode == any_policy::embedded) {
other.vtable(request::move, other, this);
} else if(other.mode != any_policy::empty) {
this->instance = std::exchange(other.instance, nullptr);
}
}
/*! @brief Frees the internal storage, whatever it means. */
/*! @brief Frees the internal buffer, whatever it means. */
~basic_any() {
if(vtable && (mode == any_policy::owner)) {
vtable(operation::destroy, *this, nullptr);
}
invoke_deleter_if_exists();
}
/**
@@ -220,10 +265,14 @@ public:
* @return This any object.
*/
basic_any &operator=(const basic_any &other) {
reset();
if(this != &other) {
invoke_deleter_if_exists();
if(other.vtable) {
other.vtable(operation::copy, other, this);
if(other) {
other.vtable(request::copy, other, this);
} else {
initialize<void>();
}
}
return *this;
@@ -235,12 +284,18 @@ public:
* @return This any object.
*/
basic_any &operator=(basic_any &&other) noexcept {
reset();
if(this != &other) {
invoke_deleter_if_exists();
if(other.mode == any_policy::embedded) {
other.vtable(request::move, other, this);
} else if(other.mode != any_policy::empty) {
this->instance = std::exchange(other.instance, nullptr);
}
if(other.vtable) {
other.vtable(operation::move, other, this);
info = other.info;
vtable = other.vtable;
deleter = other.deleter;
underlying_type = other.underlying_type;
mode = other.mode;
}
@@ -253,19 +308,55 @@ public:
* @param value An instance of an object to use to initialize the wrapper.
* @return This any object.
*/
template<typename Type>
std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>, basic_any &>
operator=(Type &&value) {
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
basic_any &operator=(Type &&value) {
emplace<std::decay_t<Type>>(std::forward<Type>(value));
return *this;
}
/**
* @brief Returns the object type if any, `type_id<void>()` otherwise.
* @return The object type if any, `type_id<void>()` otherwise.
* @brief Returns false if a wrapper is empty, true otherwise.
* @return False if the wrapper is empty, true otherwise.
*/
[[nodiscard]] const type_info &type() const noexcept {
return *info;
[[nodiscard]] bool has_value() const noexcept {
return (mode != any_policy::empty);
}
/**
* @brief Returns false if the wrapper does not contain the expected type,
* true otherwise.
* @param req Expected type.
* @return False if the wrapper does not contain the expected type, true
* otherwise.
*/
[[nodiscard]] bool has_value(const type_info &req) const noexcept {
return (underlying_type == req.hash());
}
/**
* @brief Returns false if the wrapper does not contain the expected type,
* true otherwise.
* @tparam Type Expected type.
* @return False if the wrapper does not contain the expected type, true
* otherwise.
*/
template<typename Type>
[[nodiscard]] bool has_value() const noexcept {
static_assert(std::is_same_v<std::remove_const_t<Type>, Type>, "Invalid type");
return (underlying_type == type_hash<Type>::value());
}
/**
* @brief Returns the object type info if any, `type_id<void>()` otherwise.
* @return The object type info if any, `type_id<void>()` otherwise.
*/
[[nodiscard]] const type_info &info() const noexcept {
return *static_cast<const type_info *>(vtable(request::info, *this, nullptr));
}
/*! @copydoc info */
[[deprecated("use ::info instead")]] [[nodiscard]] const type_info &type() const noexcept {
return info();
}
/**
@@ -273,7 +364,11 @@ public:
* @return An opaque pointer the contained instance, if any.
*/
[[nodiscard]] const void *data() const noexcept {
return vtable ? vtable(operation::get, *this, nullptr) : nullptr;
if constexpr(base_type::has_buffer) {
return (mode == any_policy::embedded) ? &this->buffer : this->instance;
} else {
return this->instance;
}
}
/**
@@ -282,7 +377,17 @@ public:
* @return An opaque pointer the contained instance, if any.
*/
[[nodiscard]] const void *data(const type_info &req) const noexcept {
return *info == req ? data() : nullptr;
return has_value(req) ? data() : nullptr;
}
/**
* @brief Returns an opaque pointer to the contained instance.
* @tparam Type Expected type.
* @return An opaque pointer the contained instance, if any.
*/
template<typename Type>
[[nodiscard]] const Type *data() const noexcept {
return has_value<std::remove_const_t<Type>>() ? static_cast<const Type *>(data()) : nullptr;
}
/**
@@ -290,7 +395,7 @@ public:
* @return An opaque pointer the contained instance, if any.
*/
[[nodiscard]] void *data() noexcept {
return mode == any_policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data());
return (mode == any_policy::cref) ? nullptr : const_cast<void *>(std::as_const(*this).data());
}
/**
@@ -299,7 +404,21 @@ public:
* @return An opaque pointer the contained instance, if any.
*/
[[nodiscard]] void *data(const type_info &req) noexcept {
return mode == any_policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data(req));
return (mode == any_policy::cref) ? nullptr : const_cast<void *>(std::as_const(*this).data(req));
}
/**
* @brief Returns an opaque pointer to the contained instance.
* @tparam Type Expected type.
* @return An opaque pointer the contained instance, if any.
*/
template<typename Type>
[[nodiscard]] Type *data() noexcept {
if constexpr(std::is_const_v<Type>) {
return std::as_const(*this).template data<std::remove_const_t<Type>>();
} else {
return (mode == any_policy::cref) ? nullptr : const_cast<Type *>(std::as_const(*this).template data<std::remove_const_t<Type>>());
}
}
/**
@@ -310,7 +429,7 @@ public:
*/
template<typename Type, typename... Args>
void emplace(Args &&...args) {
reset();
invoke_deleter_if_exists();
initialize<Type>(std::forward<Args>(args)...);
}
@@ -320,21 +439,18 @@ public:
* @return True in case of success, false otherwise.
*/
bool assign(const basic_any &other) {
if(vtable && mode != any_policy::cref && *info == *other.info) {
return (vtable(operation::assign, *this, other.data()) != nullptr);
if(other && (mode != any_policy::cref) && (underlying_type == other.underlying_type)) {
return (vtable(request::assign, *this, other.data()) != nullptr);
}
return false;
}
/*! @copydoc assign */
// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
bool assign(basic_any &&other) {
if(vtable && mode != any_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);
}
if(other && (mode != any_policy::cref) && (underlying_type == other.underlying_type)) {
return (other.mode == any_policy::cref) ? (vtable(request::assign, *this, std::as_const(other).data()) != nullptr) : (vtable(request::transfer, *this, other.data()) != nullptr);
}
return false;
@@ -342,15 +458,8 @@ public:
/*! @brief Destroys contained object */
void reset() {
if(vtable && (mode == any_policy::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 = any_policy::owner;
invoke_deleter_if_exists();
initialize<void>();
}
/**
@@ -358,7 +467,7 @@ public:
* @return False if the wrapper is empty, true otherwise.
*/
[[nodiscard]] explicit operator bool() const noexcept {
return vtable != nullptr;
return has_value();
}
/**
@@ -367,11 +476,11 @@ public:
* @return False if the two objects differ in their content, true otherwise.
*/
[[nodiscard]] bool operator==(const basic_any &other) const noexcept {
if(vtable && *info == *other.info) {
return (vtable(operation::compare, *this, other.data()) != nullptr);
if(other && (underlying_type == other.underlying_type)) {
return (vtable(request::compare, *this, other.data()) != nullptr);
}
return (!vtable && !other.vtable);
return (!*this && !other);
}
/**
@@ -388,20 +497,27 @@ public:
* @return A wrapper that shares a reference to an unmanaged object.
*/
[[nodiscard]] basic_any as_ref() noexcept {
return basic_any{*this, (mode == any_policy::cref ? any_policy::cref : any_policy::ref)};
basic_any other = std::as_const(*this).as_ref();
other.mode = (mode == any_policy::cref ? any_policy::cref : any_policy::ref);
return other;
}
/*! @copydoc as_ref */
[[nodiscard]] basic_any as_ref() const noexcept {
return basic_any{*this, any_policy::cref};
basic_any other{};
other.instance = data();
other.vtable = vtable;
other.underlying_type = underlying_type;
other.mode = any_policy::cref;
return other;
}
/**
* @brief Returns true if a wrapper owns its object, false otherwise.
* @return True if the wrapper owns its object, false otherwise.
*/
[[deprecated("use policy() and any_policy instead")]] [[nodiscard]] bool owner() const noexcept {
return (mode == any_policy::owner);
[[nodiscard]] bool owner() const noexcept {
return (mode == any_policy::dynamic || mode == any_policy::embedded);
}
/**
@@ -413,25 +529,22 @@ public:
}
private:
union {
const void *instance;
storage_type storage;
};
const type_info *info;
vtable_type *vtable;
any_policy mode;
vtable_type *vtable{};
deleter_type *deleter{};
id_type underlying_type{};
any_policy mode{};
};
/**
* @brief Performs type-safe access to the contained object.
* @tparam Type Type to which conversion is required.
* @tparam Len Size of the storage reserved for the small buffer optimization.
* @tparam Len Size of the buffer reserved for the small buffer optimization.
* @tparam Align Alignment requirement.
* @param data Target any object.
* @return The element converted to the requested type.
*/
template<typename Type, std::size_t Len, std::size_t Align>
[[nodiscard]] Type any_cast(const basic_any<Len, Align> &data) noexcept {
[[nodiscard]] std::remove_const_t<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);
@@ -439,7 +552,7 @@ template<typename Type, std::size_t Len, std::size_t Align>
/*! @copydoc any_cast */
template<typename Type, std::size_t Len, std::size_t Align>
[[nodiscard]] Type any_cast(basic_any<Len, Align> &data) noexcept {
[[nodiscard]] std::remove_const_t<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);
ENTT_ASSERT(instance, "Invalid instance");
@@ -448,13 +561,14 @@ template<typename Type, std::size_t Len, std::size_t Align>
/*! @copydoc any_cast */
template<typename Type, std::size_t Len, std::size_t Align>
[[nodiscard]] 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>>>) {
// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
[[nodiscard]] std::remove_const_t<Type> any_cast(basic_any<Len, Align> &&data) noexcept {
if constexpr(std::is_copy_constructible_v<std::remove_const_t<std::remove_reference_t<Type>>>) {
if(auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); instance) {
return static_cast<Type>(std::move(*instance));
} else {
return any_cast<Type>(data);
}
return any_cast<Type>(data);
} else {
auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
ENTT_ASSERT(instance, "Invalid instance");
@@ -465,8 +579,7 @@ template<typename Type, std::size_t Len, std::size_t Align>
/*! @copydoc any_cast */
template<typename Type, std::size_t Len, std::size_t Align>
[[nodiscard]] 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));
return data->template data<std::remove_const_t<Type>>();
}
/*! @copydoc any_cast */
@@ -476,15 +589,14 @@ template<typename Type, std::size_t Len, std::size_t Align>
// 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));
return data->template data<Type>();
}
}
/**
* @brief Constructs a wrapper from a given type, passing it all arguments.
* @tparam Type Type of object to use to initialize the wrapper.
* @tparam Len Size of the storage reserved for the small buffer optimization.
* @tparam Len Size of the buffer reserved for the small buffer optimization.
* @tparam Align Optional alignment requirement.
* @tparam Args Types of arguments to use to construct the new instance.
* @param args Parameters to use to construct the instance.
@@ -497,7 +609,7 @@ template<typename Type, std::size_t Len = basic_any<>::length, std::size_t Align
/**
* @brief Forwards its argument and avoids copies for lvalue references.
* @tparam Len Size of the storage reserved for the small buffer optimization.
* @tparam Len Size of the buffer reserved for the small buffer optimization.
* @tparam Align Optional alignment requirement.
* @tparam Type Type of argument to use to construct the new instance.
* @param value Parameter to use to construct the instance.

View File

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

70
src/entt/core/bit.hpp Normal file
View File

@@ -0,0 +1,70 @@
#ifndef ENTT_CORE_BIT_HPP
#define ENTT_CORE_BIT_HPP
#include <cstddef>
#include <limits>
#include <type_traits>
#include "../config/config.h"
namespace entt {
/**
* @brief Returns the number of set bits in a value (waiting for C++20 and
* `std::popcount`).
* @tparam Type Unsigned integer type.
* @param value A value of unsigned integer type.
* @return The number of set bits in the value.
*/
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, int> popcount(const Type value) noexcept {
return value ? (int(value & 1) + popcount(static_cast<Type>(value >> 1))) : 0;
}
/**
* @brief Checks whether a value is a power of two or not (waiting for C++20 and
* `std::has_single_bit`).
* @tparam Type Unsigned integer type.
* @param value A value of unsigned integer type.
* @return True if the value is a power of two, false otherwise.
*/
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, bool> has_single_bit(const Type value) noexcept {
return value && ((value & (value - 1)) == 0);
}
/**
* @brief Computes the smallest power of two greater than or equal to a value
* (waiting for C++20 and `std::bit_ceil`).
* @tparam Type Unsigned integer type.
* @param value A value of unsigned integer type.
* @return The smallest power of two greater than or equal to the given value.
*/
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, Type> next_power_of_two(const Type value) noexcept {
// NOLINTNEXTLINE(bugprone-assert-side-effect)
ENTT_ASSERT_CONSTEXPR(value < (Type{1u} << (std::numeric_limits<Type>::digits - 1)), "Numeric limits exceeded");
Type curr = value - (value != 0u);
for(int next = 1; next < std::numeric_limits<Type>::digits; next = next * 2) {
curr |= (curr >> next);
}
return ++curr;
}
/**
* @brief Fast module utility function (powers of two only).
* @tparam Type Unsigned integer type.
* @param value A value of unsigned integer type.
* @param mod _Modulus_, it must be a power of two.
* @return The common remainder.
*/
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, Type> fast_mod(const Type value, const std::size_t mod) noexcept {
ENTT_ASSERT_CONSTEXPR(has_single_bit(mod), "Value must be a power of two");
return static_cast<Type>(value & (mod - 1u));
}
} // namespace entt
#endif

View File

@@ -5,6 +5,7 @@
#include <tuple>
#include <type_traits>
#include <utility>
#include "fwd.hpp"
#include "type_traits.hpp"
namespace entt {
@@ -17,11 +18,11 @@ 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 Dummy = Type, typename = std::enable_if_t<std::is_default_constructible_v<Dummy>>>
// NOLINTNEXTLINE(modernize-use-equals-default)
constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<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>>>
template<typename Arg, typename = std::enable_if_t<!std::is_same_v<std::remove_const_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)} {}
@@ -38,7 +39,7 @@ struct compressed_pair_element {
}
private:
Type value;
Type value{};
};
template<typename Type, std::size_t Tag>
@@ -47,11 +48,11 @@ struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Ty
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>>>
template<typename Dummy = Type, typename = std::enable_if_t<std::is_default_constructible_v<Dummy>>>
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>>>
template<typename Arg, typename = std::enable_if_t<!std::is_same_v<std::remove_const_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)} {}
@@ -102,7 +103,7 @@ public:
* @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>)
constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v<first_base> && std::is_nothrow_default_constructible_v<second_base>)
: first_base{},
second_base{} {}
@@ -110,13 +111,13 @@ public:
* @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;
constexpr compressed_pair(const compressed_pair &other) = 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;
constexpr compressed_pair(compressed_pair &&other) noexcept = default;
/**
* @brief Constructs a pair from its values.
@@ -126,7 +127,7 @@ public:
* @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>)
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)} {}
@@ -138,23 +139,26 @@ public:
* @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...>)
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 Default destructor. */
~compressed_pair() = default;
/**
* @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;
constexpr compressed_pair &operator=(const compressed_pair &other) = default;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This compressed pair object.
*/
constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v<first_base> &&std::is_nothrow_move_assignable_v<second_base>) = default;
constexpr compressed_pair &operator=(compressed_pair &&other) noexcept = default;
/**
* @brief Returns the first element that a pair stores.
@@ -186,7 +190,7 @@ public:
* @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>) {
constexpr void swap(compressed_pair &other) noexcept {
using std::swap;
swap(first(), other.first());
swap(second(), other.second());
@@ -199,7 +203,7 @@ public:
* reference to the second element if `Index` is 1.
*/
template<std::size_t Index>
constexpr decltype(auto) get() noexcept {
[[nodiscard]] constexpr decltype(auto) get() noexcept {
if constexpr(Index == 0u) {
return first();
} else {
@@ -210,7 +214,7 @@ public:
/*! @copydoc get */
template<std::size_t Index>
constexpr decltype(auto) get() const noexcept {
[[nodiscard]] constexpr decltype(auto) get() const noexcept {
if constexpr(Index == 0u) {
return first();
} else {
@@ -236,14 +240,12 @@ compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::d
* @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) {
constexpr void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) noexcept {
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 {
/**
@@ -266,6 +268,5 @@ struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<I
};
} // namespace std
#endif
#endif

View File

@@ -15,7 +15,10 @@ namespace entt {
*/
template<typename...>
class family {
inline static ENTT_MAYBE_ATOMIC(id_type) identifier{};
static auto identifier() noexcept {
static ENTT_MAYBE_ATOMIC(id_type) value{};
return value++;
}
public:
/*! @brief Unsigned integer type. */
@@ -24,7 +27,7 @@ public:
/*! @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 value_type value = identifier++;
inline static const value_type value = identifier();
};
} // namespace entt

View File

@@ -2,10 +2,26 @@
#define ENTT_CORE_FWD_HPP
#include <cstddef>
#include <cstdint>
#include "../config/config.h"
namespace entt {
/*! @brief Possible modes of an any object. */
enum class any_policy : std::uint8_t {
/*! @brief Default mode, no element available. */
empty,
/*! @brief Owning mode, dynamically allocated element. */
dynamic,
/*! @brief Owning mode, embedded element. */
embedded,
/*! @brief Aliasing mode, non-const reference. */
ref,
/*! @brief Const aliasing mode, const reference. */
cref
};
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(double[2])>
class basic_any;
@@ -15,6 +31,21 @@ using id_type = ENTT_ID_TYPE;
/*! @brief Alias declaration for the most common use case. */
using any = basic_any<>;
template<typename, typename>
class compressed_pair;
template<typename>
class basic_hashed_string;
/*! @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>;
// NOLINTNEXTLINE(bugprone-forward-declaration-namespace)
struct type_info;
} // namespace entt
#endif

View File

@@ -10,21 +10,19 @@ namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename>
struct fnv1a_traits;
template<typename = id_type>
struct fnv_1a_params;
template<>
struct fnv1a_traits<std::uint32_t> {
using type = std::uint32_t;
static constexpr std::uint32_t offset = 2166136261;
static constexpr std::uint32_t prime = 16777619;
struct fnv_1a_params<std::uint32_t> {
static constexpr auto offset = 2166136261;
static constexpr auto prime = 16777619;
};
template<>
struct fnv1a_traits<std::uint64_t> {
using type = std::uint64_t;
static constexpr std::uint64_t offset = 14695981039346656037ull;
static constexpr std::uint64_t prime = 1099511628211ull;
struct fnv_1a_params<std::uint64_t> {
static constexpr auto offset = 14695981039346656037ull;
static constexpr auto prime = 1099511628211ull;
};
template<typename Char>
@@ -33,9 +31,9 @@ struct basic_hashed_string {
using size_type = std::size_t;
using hash_type = id_type;
const value_type *repr;
size_type length;
hash_type hash;
const value_type *repr{};
hash_type hash{fnv_1a_params<>::offset};
size_type length{};
};
} // namespace internal
@@ -59,38 +57,16 @@ struct basic_hashed_string {
template<typename Char>
class basic_hashed_string: internal::basic_hashed_string<Char> {
using base_type = internal::basic_hashed_string<Char>;
using traits_type = internal::fnv1a_traits<id_type>;
using params = internal::fnv_1a_params<>;
struct const_wrapper {
// non-explicit constructor on purpose
constexpr const_wrapper(const Char *str) noexcept
constexpr const_wrapper(const typename base_type::value_type *str) noexcept
: repr{str} {}
const Char *repr;
const typename base_type::value_type *repr;
};
// FowlerNollVo hash function v. 1a - the good
[[nodiscard]] static constexpr auto helper(const Char *str) noexcept {
base_type base{str, 0u, traits_type::offset};
for(; str[base.length]; ++base.length) {
base.hash = (base.hash ^ static_cast<traits_type::type>(str[base.length])) * traits_type::prime;
}
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, traits_type::offset};
for(size_type pos{}; pos < len; ++pos) {
base.hash = (base.hash ^ static_cast<traits_type::type>(str[pos])) * traits_type::prime;
}
return base;
}
public:
/*! @brief Character type. */
using value_type = typename base_type::value_type;
@@ -116,7 +92,8 @@ public:
* @return The numeric representation of the string.
*/
template<std::size_t N>
[[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept {
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
[[nodiscard]] static ENTT_CONSTEVAL hash_type value(const value_type (&str)[N]) noexcept {
return basic_hashed_string{str};
}
@@ -131,7 +108,7 @@ public:
/*! @brief Constructs an empty hashed string. */
constexpr basic_hashed_string() noexcept
: base_type{} {}
: basic_hashed_string{nullptr, 0u} {}
/**
* @brief Constructs a hashed string from a string view.
@@ -139,7 +116,14 @@ public:
* @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)} {}
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
: base_type{str} {
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic)
for(; base_type::length < len; ++base_type::length) {
base_type::hash = (base_type::hash ^ static_cast<id_type>(str[base_type::length])) * params::prime;
}
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
}
/**
* @brief Constructs a hashed string from an array of const characters.
@@ -147,8 +131,14 @@ public:
* @param str Human-readable identifier.
*/
template<std::size_t N>
constexpr basic_hashed_string(const value_type (&str)[N]) noexcept
: base_type{helper(str)} {}
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
ENTT_CONSTEVAL basic_hashed_string(const value_type (&str)[N]) noexcept
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
: base_type{str} {
for(; str[base_type::length]; ++base_type::length) {
base_type::hash = (base_type::hash ^ static_cast<id_type>(str[base_type::length])) * params::prime;
}
}
/**
* @brief Explicit constructor on purpose to avoid constructing a hashed
@@ -160,14 +150,20 @@ public:
* @param wrapper Helps achieving the purpose by relying on overloading.
*/
explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept
: base_type{helper(wrapper.repr)} {}
: base_type{wrapper.repr} {
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic)
for(; wrapper.repr[base_type::length]; ++base_type::length) {
base_type::hash = (base_type::hash ^ static_cast<id_type>(wrapper.repr[base_type::length])) * params::prime;
}
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
}
/**
* @brief Returns the size a hashed string.
* @brief Returns the size of a hashed string.
* @return The size of the hashed string.
*/
[[nodiscard]] constexpr size_type size() const noexcept {
return base_type::length; // NOLINT
return base_type::length;
}
/**
@@ -187,7 +183,7 @@ public:
}
/*! @copydoc data */
[[nodiscard]] constexpr operator const value_type *() const noexcept {
[[nodiscard]] explicit constexpr operator const value_type *() const noexcept {
return data();
}
@@ -207,7 +203,7 @@ public:
* @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>;
basic_hashed_string(const Char *str, std::size_t len) -> basic_hashed_string<Char>;
/**
* @brief Deduction guide.
@@ -216,6 +212,7 @@ basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_stri
* @param str Human-readable identifier.
*/
template<typename Char, std::size_t N>
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string<Char>;
/**
@@ -293,12 +290,6 @@ template<typename Char>
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 {
/**
@@ -306,7 +297,7 @@ inline namespace literals {
* @param str The literal without its suffix.
* @return A properly initialized hashed string.
*/
[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept {
[[nodiscard]] ENTT_CONSTEVAL hashed_string operator""_hs(const char *str, std::size_t) noexcept {
return hashed_string{str};
}
@@ -315,7 +306,7 @@ inline namespace literals {
* @param str The literal without its suffix.
* @return A properly initialized hashed wstring.
*/
[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept {
[[nodiscard]] ENTT_CONSTEVAL hashed_wstring operator""_hws(const wchar_t *str, std::size_t) noexcept {
return hashed_wstring{str};
}

View File

@@ -92,7 +92,7 @@ public:
* @return This iota iterator.
*/
constexpr iota_iterator operator++(int) noexcept {
iota_iterator orig = *this;
const iota_iterator orig = *this;
return ++(*this), orig;
}
@@ -147,7 +147,7 @@ struct iterable_adaptor final {
using sentinel = Sentinel;
/*! @brief Default constructor. */
constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v<iterator> &&std::is_nothrow_default_constructible_v<sentinel>)
constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v<iterator> && std::is_nothrow_default_constructible_v<sentinel>)
: first{},
last{} {}
@@ -156,7 +156,7 @@ struct iterable_adaptor final {
* @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>)
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)} {}

View File

@@ -2,7 +2,6 @@
#define ENTT_CORE_MEMORY_HPP
#include <cstddef>
#include <limits>
#include <memory>
#include <tuple>
#include <type_traits>
@@ -11,44 +10,6 @@
namespace entt {
/**
* @brief Checks whether a value is a power of two or not (waiting for C++20 and
* `std::has_single_bit`).
* @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
* (waiting for C++20 and `std::bit_ceil`).
* @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.
@@ -275,7 +236,7 @@ constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...ar
*/
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)...));
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

View File

@@ -23,10 +23,12 @@ struct monostate {
* @brief Assigns a value of a specific type to a given key.
* @tparam Type Type of the value to assign.
* @param val User data to assign to the given key.
* @return This monostate object.
*/
template<typename Type>
void operator=(Type val) const noexcept {
monostate &operator=(Type val) noexcept {
value<Type> = val;
return *this;
}
/**
@@ -41,6 +43,7 @@ struct monostate {
private:
template<typename Type>
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
inline static ENTT_MAYBE_ATOMIC(Type) value{};
};
@@ -49,7 +52,8 @@ private:
* @tparam Value Value used to differentiate between different variables.
*/
template<id_type Value>
inline monostate<Value> monostate_v = {};
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
inline monostate<Value> monostate_v{};
} // namespace entt

20
src/entt/core/ranges.hpp Normal file
View File

@@ -0,0 +1,20 @@
#ifndef ENTT_CORE_RANGES_HPP
#define ENTT_CORE_RANGES_HPP
#if __has_include(<version>)
# include <version>
#
# if defined(__cpp_lib_ranges)
# include <ranges>
# include "iterator.hpp"
template<class... Args>
inline constexpr bool std::ranges::enable_borrowed_range<entt::iterable_adaptor<Args...>>{true};
template<class... Args>
inline constexpr bool std::ranges::enable_view<entt::iterable_adaptor<Args...>>{true};
# endif
#endif
#endif

View File

@@ -7,25 +7,20 @@
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
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
/*! @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>> {};
struct is_tuple: std::false_type {};
/**
* @copybrief is_tuple
* @tparam Args Tuple template arguments.
*/
template<typename... Args>
struct is_tuple<std::tuple<Args...>>: std::true_type {};
/**
* @brief Helper variable template.
@@ -88,7 +83,7 @@ struct forward_apply: private Func {
* @tparam Func Type of underlying invocable object.
*/
template<typename Func>
forward_apply(Func) -> forward_apply<std::remove_reference_t<std::remove_cv_t<Func>>>;
forward_apply(Func) -> forward_apply<std::remove_reference_t<std::remove_const_t<Func>>>;
} // namespace entt

View File

@@ -5,7 +5,6 @@
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/attribute.h"
#include "fwd.hpp"
#include "hashed_string.hpp"
@@ -21,15 +20,24 @@ struct ENTT_API type_index final {
}
};
template<typename Type>
[[nodiscard]] constexpr const char *pretty_function() noexcept {
#if defined ENTT_PRETTY_FUNCTION
return static_cast<const char *>(ENTT_PRETTY_FUNCTION);
#else
return "";
#endif
}
template<typename Type>
[[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 value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first);
const std::string_view full_name{pretty_function<Type>()};
auto first = full_name.find_first_not_of(' ', full_name.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1);
auto value = full_name.substr(first, full_name.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first);
return value;
#else
return std::string_view{""};
return std::string_view{};
#endif
}
@@ -136,10 +144,12 @@ struct type_info final {
* @tparam Type Type for which to construct a type info object.
*/
template<typename Type>
// NOLINTBEGIN(modernize-use-transparent-functors)
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()} {}
: seq{type_index<std::remove_const_t<std::remove_reference_t<Type>>>::value()},
identifier{type_hash<std::remove_const_t<std::remove_reference_t<Type>>>::value()},
alias{type_name<std::remove_const_t<std::remove_reference_t<Type>>>::value()} {}
// NOLINTEND(modernize-use-transparent-functors)
/**
* @brief Type index.
@@ -177,7 +187,7 @@ private:
* @param rhs A type info object.
* @return True if the two type info objects are identical, false otherwise.
*/
[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept {
[[nodiscard]] constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept {
return lhs.hash() == rhs.hash();
}
@@ -187,7 +197,7 @@ private:
* @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 {
[[nodiscard]] constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept {
return !(lhs == rhs);
}
@@ -247,18 +257,19 @@ private:
*/
template<typename Type>
[[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>};
if constexpr(std::is_same_v<Type, std::remove_const_t<std::remove_reference_t<Type>>>) {
static const type_info instance{std::in_place_type<Type>};
return instance;
} else {
return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
return type_id<std::remove_const_t<std::remove_reference_t<Type>>>();
}
}
/*! @copydoc type_id */
template<typename Type>
// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
[[nodiscard]] const type_info &type_id(Type &&) noexcept {
return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
return type_id<std::remove_const_t<std::remove_reference_t<Type>>>();
}
} // namespace entt

View File

@@ -63,6 +63,7 @@ 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))>>
// NOLINTNEXTLINE(bugprone-sizeof-expression)
: std::integral_constant<std::size_t, sizeof(Type)> {};
/**
@@ -345,6 +346,7 @@ struct type_list_transform;
template<typename... Type, template<typename...> class Op>
struct type_list_transform<type_list<Type...>, Op> {
/*! @brief Resulting type list after applying the transform function. */
// NOLINTNEXTLINE(modernize-type-traits)
using type = type_list<typename Op<Type>::type...>;
};
@@ -574,7 +576,7 @@ inline constexpr bool value_list_contains_v = value_list_contains<List, Value>::
/*! @brief Primary template isn't defined on purpose. */
template<typename...>
class value_list_diff;
struct value_list_diff;
/**
* @brief Computes the difference between two value lists.
@@ -582,12 +584,9 @@ class value_list_diff;
* @tparam Other Values provided by the second value list.
*/
template<auto... Value, auto... Other>
class value_list_diff<value_list<Value...>, value_list<Other...>> {
using v141_toolset_workaround = value_list<Other...>;
public:
struct value_list_diff<value_list<Value...>, value_list<Other...>> {
/*! @brief A value list that is the difference between the two value lists. */
using type = value_list_cat_t<std::conditional_t<value_list_contains_v<v141_toolset_workaround, Value>, value_list<>, value_list<Value>>...>;
using type = value_list_cat_t<std::conditional_t<value_list_contains_v<value_list<Other...>, Value>, value_list<>, value_list<Value>>...>;
};
/**
@@ -692,7 +691,7 @@ struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Typ
/*! @copydoc is_iterator */
template<typename Type>
struct is_iterator<Type, std::enable_if_t<!std::is_void_v<std::remove_cv_t<std::remove_pointer_t<Type>>>>>
struct is_iterator<Type, std::enable_if_t<!std::is_void_v<std::remove_const_t<std::remove_pointer_t<Type>>>>>
: internal::has_iterator_category<Type> {};
/**
@@ -772,27 +771,25 @@ template<typename Type>
template<typename Type>
[[nodiscard]] constexpr bool dispatch_is_equality_comparable() {
// NOLINTBEGIN(modernize-use-transparent-functors)
if constexpr(std::is_array_v<Type>) {
return false;
} else if constexpr(is_iterator_v<Type>) {
return maybe_equality_comparable<Type>(0);
} else if constexpr(has_value_type<Type>::value) {
if constexpr(std::is_same_v<typename Type::value_type, Type>) {
return maybe_equality_comparable<Type>(0);
} else if constexpr(dispatch_is_equality_comparable<typename Type::value_type>()) {
return maybe_equality_comparable<Type>(0);
} else {
return false;
}
} else if constexpr(is_complete_v<std::tuple_size<std::remove_cv_t<Type>>>) {
} else if constexpr(is_complete_v<std::tuple_size<std::remove_const_t<Type>>>) {
if constexpr(has_tuple_size_value<Type>::value) {
return maybe_equality_comparable<Type>(0) && unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
} else {
return maybe_equality_comparable<Type>(0);
}
} else if constexpr(has_value_type<Type>::value) {
if constexpr(is_iterator_v<Type> || std::is_same_v<typename Type::value_type, Type> || dispatch_is_equality_comparable<typename Type::value_type>()) {
return maybe_equality_comparable<Type>(0);
} else {
return false;
}
} else {
return maybe_equality_comparable<Type>(0);
}
// NOLINTEND(modernize-use-transparent-functors)
}
} // namespace internal
@@ -873,9 +870,9 @@ 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.
* @brief Extracts the n-th argument of a _callable_ type.
* @tparam Index The index of the argument to extract.
* @tparam Candidate A valid function, member function or data member type.
* @tparam Candidate A valid _callable_ type.
*/
template<std::size_t Index, typename Candidate>
class nth_argument {
@@ -891,8 +888,11 @@ class nth_argument {
template<typename Type, typename Class>
static constexpr type_list<Type> pick_up(Type Class ::*);
template<typename Type>
static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&);
public:
/*! @brief N-th argument of the given function or member function. */
/*! @brief N-th argument of the _callable_ type. */
using type = type_list_element_t<Index, decltype(pick_up(std::declval<Candidate>()))>;
};

View File

@@ -36,14 +36,17 @@ struct page_size<Type, std::void_t<decltype(Type::page_size)>>
/**
* @brief Common way to access various properties of components.
* @tparam Type Type of component.
* @tparam Type Element type.
* @tparam Entity A valid entity type.
*/
template<typename Type, typename = void>
template<typename Type, typename Entity, typename>
struct component_traits {
static_assert(std::is_same_v<std::decay_t<Type>, Type>, "Unsupported type");
/*! @brief Component type. */
using type = Type;
/*! @brief Element type. */
using element_type = Type;
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
/*! @brief Pointer stability, default is `false`. */
static constexpr bool in_place_delete = internal::in_place_delete<Type>::value;

View File

@@ -5,6 +5,7 @@
#include <cstdint>
#include <type_traits>
#include "../config/config.h"
#include "../core/bit.hpp"
#include "fwd.hpp"
namespace entt {
@@ -12,12 +13,6 @@ namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
// waiting for C++20 and std::popcount
template<typename Type>
constexpr int popcount(Type value) noexcept {
return value ? (int(value & 1) + popcount(value >> 1)) : 0;
}
template<typename, typename = void>
struct entt_traits;
@@ -64,10 +59,10 @@ struct entt_traits<std::uint64_t> {
*/
template<typename Traits>
class basic_entt_traits {
static constexpr auto length = internal::popcount(Traits::entity_mask);
static constexpr auto length = popcount(Traits::entity_mask);
static_assert(Traits::entity_mask && ((typename Traits::entity_type{1} << length) == (Traits::entity_mask + 1)), "Invalid entity mask");
static_assert((typename Traits::entity_type{1} << internal::popcount(Traits::version_mask)) == (Traits::version_mask + 1), "Invalid version mask");
static_assert(Traits::entity_mask && ((Traits::entity_mask & (Traits::entity_mask + 1)) == 0), "Invalid entity mask");
static_assert((Traits::version_mask & (Traits::version_mask + 1)) == 0, "Invalid version mask");
public:
/*! @brief Value type. */
@@ -106,7 +101,11 @@ public:
* @return The integral representation of the version part.
*/
[[nodiscard]] static constexpr version_type to_version(const value_type value) noexcept {
return (static_cast<version_type>(to_integral(value) >> length) & version_mask);
if constexpr(Traits::version_mask == 0u) {
return version_type{};
} else {
return (static_cast<version_type>(to_integral(value) >> length) & version_mask);
}
}
/**
@@ -130,7 +129,11 @@ public:
* @return A properly constructed identifier.
*/
[[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) noexcept {
return value_type{(entity & entity_mask) | (static_cast<entity_type>(version & version_mask) << length)};
if constexpr(Traits::version_mask == 0u) {
return value_type{entity & entity_mask};
} else {
return value_type{(entity & entity_mask) | (static_cast<entity_type>(version & version_mask) << length)};
}
}
/**
@@ -144,7 +147,11 @@ public:
* @return A properly constructed identifier.
*/
[[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept {
return value_type{(lhs & entity_mask) | (rhs & (version_mask << length))};
if constexpr(Traits::version_mask == 0u) {
return value_type{lhs & entity_mask};
} else {
return value_type{(lhs & entity_mask) | (rhs & (version_mask << length))};
}
}
};
@@ -252,25 +259,25 @@ struct null_t {
/**
* @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.
* @param lhs Identifier with which to compare.
* @param rhs 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) noexcept {
return other.operator==(entity);
[[nodiscard]] constexpr bool operator==(const Entity lhs, const null_t rhs) noexcept {
return rhs.operator==(lhs);
}
/**
* @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.
* @param lhs Identifier with which to compare.
* @param rhs 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) noexcept {
return !(other == entity);
[[nodiscard]] constexpr bool operator!=(const Entity lhs, const null_t rhs) noexcept {
return !(rhs == lhs);
}
/*! @brief Tombstone object for all identifiers. */
@@ -314,7 +321,12 @@ struct tombstone_t {
template<typename Entity>
[[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept {
using traits_type = entt_traits<Entity>;
return traits_type::to_version(entity) == traits_type::to_version(*this);
if constexpr(traits_type::version_mask == 0u) {
return false;
} else {
return (traits_type::to_version(entity) == traits_type::to_version(*this));
}
}
/**
@@ -332,25 +344,25 @@ struct tombstone_t {
/**
* @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.
* @param lhs Identifier with which to compare.
* @param rhs 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) noexcept {
return other.operator==(entity);
[[nodiscard]] constexpr bool operator==(const Entity lhs, const tombstone_t rhs) noexcept {
return rhs.operator==(lhs);
}
/**
* @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.
* @param lhs Identifier with which to compare.
* @param rhs 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) noexcept {
return !(other == entity);
[[nodiscard]] constexpr bool operator!=(const Entity lhs, const tombstone_t rhs) noexcept {
return !(rhs == lhs);
}
/**

View File

@@ -4,6 +4,7 @@
#include <cstdint>
#include <memory>
#include <type_traits>
#include "../config/config.h"
#include "../core/fwd.hpp"
#include "../core/type_traits.hpp"
@@ -19,9 +20,14 @@ enum class deletion_policy : std::uint8_t {
/*! @brief In-place deletion policy. */
in_place = 1u,
/*! @brief Swap-only deletion policy. */
swap_only = 2u
swap_only = 2u,
/*! @brief Unspecified deletion policy. */
unspecified = swap_and_pop
};
template<typename Type, typename Entity = entity, typename = void>
struct component_traits;
template<typename Entity = entity, typename = std::allocator<Entity>>
class basic_sparse_set;
@@ -31,6 +37,9 @@ class basic_storage;
template<typename, typename>
class basic_sigh_mixin;
template<typename, typename>
class basic_reactive_mixin;
template<typename Entity = entity, typename = std::allocator<Entity>>
class basic_registry;
@@ -43,14 +52,11 @@ class basic_runtime_view;
template<typename, typename, typename>
class basic_group;
template<typename, typename Mask = std::uint32_t, typename = std::allocator<Mask>>
class basic_observer;
template<typename>
class basic_organizer;
template<typename, typename...>
struct basic_handle;
class basic_handle;
template<typename>
class basic_snapshot;
@@ -66,7 +72,7 @@ using sparse_set = basic_sparse_set<>;
/**
* @brief Alias declaration for the most common use case.
* @tparam Type Type of objects assigned to the entities.
* @tparam Type Element type.
*/
template<typename Type>
using storage = basic_storage<Type>;
@@ -78,11 +84,15 @@ using storage = basic_storage<Type>;
template<typename Type>
using sigh_mixin = basic_sigh_mixin<Type, basic_registry<typename Type::entity_type, typename Type::base_type::allocator_type>>;
/*! @brief Alias declaration for the most common use case. */
using registry = basic_registry<>;
/**
* @brief Alias declaration for the most common use case.
* @tparam Type Underlying storage type.
*/
template<typename Type>
using reactive_mixin = basic_reactive_mixin<Type, basic_registry<typename Type::entity_type, typename Type::base_type::allocator_type>>;
/*! @brief Alias declaration for the most common use case. */
using observer = basic_observer<registry>;
using registry = basic_registry<>;
/*! @brief Alias declaration for the most common use case. */
using organizer = basic_organizer<registry>;
@@ -129,7 +139,7 @@ using const_runtime_view = basic_runtime_view<const sparse_set>;
template<typename... Type>
struct exclude_t final: type_list<Type...> {
/*! @brief Default constructor. */
explicit constexpr exclude_t() {}
explicit constexpr exclude_t() = default;
};
/**
@@ -140,34 +150,34 @@ template<typename... Type>
inline constexpr exclude_t<Type...> exclude{};
/**
* @brief Alias for lists of observed components.
* @brief Alias for lists of observed elements.
* @tparam Type List of types.
*/
template<typename... Type>
struct get_t final: type_list<Type...> {
/*! @brief Default constructor. */
explicit constexpr get_t() {}
explicit constexpr get_t() = default;
};
/**
* @brief Variable template for lists of observed components.
* @brief Variable template for lists of observed elements.
* @tparam Type List of types.
*/
template<typename... Type>
inline constexpr get_t<Type...> get{};
/**
* @brief Alias for lists of owned components.
* @brief Alias for lists of owned elements.
* @tparam Type List of types.
*/
template<typename... Type>
struct owned_t final: type_list<Type...> {
/*! @brief Default constructor. */
explicit constexpr owned_t() {}
explicit constexpr owned_t() = default;
};
/**
* @brief Variable template for lists of owned components.
* @brief Variable template for lists of owned elements.
* @tparam Type List of types.
*/
template<typename... Type>
@@ -215,7 +225,21 @@ struct type_list_transform<owned_t<Type...>, Op> {
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_mixin<basic_storage<Type, Entity, Allocator>>;
using type = ENTT_STORAGE(sigh_mixin, basic_storage<Type, Entity, Allocator>);
};
/*! @brief Empty value type for reactive storage types. */
struct reactive final {};
/**
* @ brief Partial specialization for reactive storage types.
* @tparam Entity A valid entity type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Entity, typename Allocator>
struct storage_type<reactive, Entity, Allocator> {
/*! @brief Type-to-storage conversion result. */
using type = ENTT_STORAGE(reactive_mixin, basic_storage<reactive, Entity, Allocator>);
};
/**
@@ -258,7 +282,7 @@ using view = basic_view<type_list_transform_t<Get, storage_for>, type_list_trans
* @tparam Get Types of storage _observed_ by the group.
* @tparam Exclude Types of storage used to filter the group.
*/
template<typename Owned, typename Get, typename Exclude>
template<typename Owned, typename Get = get_t<>, typename Exclude = exclude_t<>>
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

View File

@@ -1,18 +1,20 @@
#ifndef ENTT_ENTITY_GROUP_HPP
#define ENTT_ENTITY_GROUP_HPP
#include <array>
#include <cstddef>
#include <iterator>
#include <tuple>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/algorithm.hpp"
#include "../core/fwd.hpp"
#include "../core/iterator.hpp"
#include "../core/type_info.hpp"
#include "../core/type_traits.hpp"
#include "entity.hpp"
#include "fwd.hpp"
#include "sparse_set.hpp"
#include "storage.hpp"
namespace entt {
@@ -25,8 +27,8 @@ class extended_group_iterator;
template<typename It, typename... Owned, typename... Get>
class extended_group_iterator<It, owned_t<Owned...>, get_t<Get...>> {
template<typename Type>
auto index_to_element([[maybe_unused]] Type &cpool) const {
if constexpr(Type::traits_type::page_size == 0u) {
[[nodiscard]] auto index_to_element([[maybe_unused]] Type &cpool) const {
if constexpr(std::is_void_v<typename Type::value_type>) {
return std::make_tuple();
} else {
return std::forward_as_tuple(cpool.rbegin()[it.index()]);
@@ -35,10 +37,10 @@ class extended_group_iterator<It, owned_t<Owned...>, get_t<Get...>> {
public:
using iterator_type = It;
using difference_type = std::ptrdiff_t;
using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::declval<Owned>().get_as_tuple({})..., std::declval<Get>().get_as_tuple({})...));
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
using iterator_concept = std::forward_iterator_tag;
@@ -46,16 +48,16 @@ public:
: it{},
pools{} {}
extended_group_iterator(iterator_type from, const std::tuple<Owned *..., Get *...> &cpools)
extended_group_iterator(iterator_type from, std::tuple<Owned *..., Get *...> cpools)
: it{from},
pools{cpools} {}
pools{std::move(cpools)} {}
extended_group_iterator &operator++() noexcept {
return ++it, *this;
}
extended_group_iterator operator++(int) noexcept {
extended_group_iterator orig = *this;
const extended_group_iterator orig = *this;
return ++(*this), orig;
}
@@ -92,100 +94,93 @@ template<typename... Lhs, typename... Rhs>
struct group_descriptor {
using size_type = std::size_t;
virtual ~group_descriptor() = default;
virtual size_type owned(const id_type *, const size_type) const noexcept {
return 0u;
[[nodiscard]] virtual bool owned(const id_type) const noexcept {
return false;
}
};
template<typename, typename, typename>
class group_handler;
template<typename... Owned, typename... Get, typename... Exclude>
class group_handler<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> final: public group_descriptor {
// nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here
static_assert(!std::disjunction_v<std::bool_constant<Owned::traits_type::in_place_delete>...>, "Groups do not support in-place delete");
static_assert(!std::disjunction_v<std::is_const<Owned>..., std::is_const<Get>..., std::is_const<Exclude>...>, "Const storage type not allowed");
using base_type = std::common_type_t<typename Owned::base_type..., typename Get::base_type..., typename Exclude::base_type...>;
using entity_type = typename base_type::entity_type;
template<typename Type, std::size_t Owned, std::size_t Get, std::size_t Exclude>
class group_handler final: public group_descriptor {
using entity_type = typename Type::entity_type;
void swap_elements(const std::size_t pos, const entity_type entt) {
std::apply([pos, entt](auto *...cpool) { (cpool->swap_elements(cpool->data()[pos], entt), ...); }, pools);
for(size_type next{}; next < Owned; ++next) {
pools[next]->swap_elements((*pools[next])[pos], entt);
}
}
void push_on_construct(const entity_type entt) {
if(std::apply([entt, len = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < len) && (other->contains(entt) && ...); }, pools)
if(std::apply([entt, pos = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < pos) && (other->contains(entt) && ...); }, pools)
&& std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) {
swap_elements(len++, entt);
}
}
void push_on_destroy(const entity_type entt) {
if(std::apply([entt, len = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < len) && (other->contains(entt) && ...); }, pools)
if(std::apply([entt, pos = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < pos) && (other->contains(entt) && ...); }, pools)
&& std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) {
swap_elements(len++, entt);
}
}
void remove_if(const entity_type entt) {
if(std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < len)) {
if(pools[0u]->contains(entt) && (pools[0u]->index(entt) < len)) {
swap_elements(--len, entt);
}
}
public:
using size_type = typename base_type::size_type;
group_handler(Owned &...opool, Get &...gpool, Exclude &...epool)
: pools{&opool..., &gpool...},
filter{&epool...},
len{} {
std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::push_on_construct>(*this), cpool->on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, pools);
std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::remove_if>(*this), cpool->on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, filter);
void common_setup() {
// we cannot iterate backwards because we want to leave behind valid entities in case of owned types
for(auto *first = std::get<0>(pools)->data(), *last = first + std::get<0>(pools)->size(); first != last; ++first) {
for(auto first = pools[0u]->rbegin(), last = first + static_cast<typename decltype(pools)::difference_type>(pools[0u]->size()); first != last; ++first) {
push_on_construct(*first);
}
}
size_type owned(const id_type *elem, const size_type length) const noexcept final {
size_type cnt = 0u;
public:
using common_type = Type;
using size_type = typename Type::size_type;
for(auto pos = 0u; pos < length; ++pos) {
cnt += ((elem[pos] == entt::type_hash<typename Owned::value_type>::value()) || ...);
template<typename... OGType, typename... EType>
group_handler(std::tuple<OGType &...> ogpool, std::tuple<EType &...> epool)
: pools{std::apply([](auto &&...cpool) { return std::array<common_type *, (Owned + Get)>{&cpool...}; }, ogpool)},
filter{std::apply([](auto &&...cpool) { return std::array<common_type *, Exclude>{&cpool...}; }, epool)} {
std::apply([this](auto &...cpool) { ((cpool.on_construct().template connect<&group_handler::push_on_construct>(*this), cpool.on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, ogpool);
std::apply([this](auto &...cpool) { ((cpool.on_construct().template connect<&group_handler::remove_if>(*this), cpool.on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, epool);
common_setup();
}
[[nodiscard]] bool owned(const id_type hash) const noexcept override {
for(size_type pos{}; pos < Owned; ++pos) {
if(pools[pos]->info().hash() == hash) {
return true;
}
}
return cnt;
return false;
}
[[nodiscard]] size_type length() const noexcept {
return len;
}
template<typename Type>
Type pools_as() const noexcept {
return pools;
}
template<typename Type>
Type filter_as() const noexcept {
return filter;
template<std::size_t Index>
[[nodiscard]] common_type *storage() const noexcept {
if constexpr(Index < (Owned + Get)) {
return pools[Index];
} else {
return filter[Index - (Owned + Get)];
}
}
private:
std::tuple<Owned *..., Get *...> pools;
std::tuple<Exclude *...> filter;
std::size_t len;
std::array<common_type *, (Owned + Get)> pools;
std::array<common_type *, Exclude> filter;
std::size_t len{};
};
template<typename... Get, typename... Exclude>
class group_handler<owned_t<>, get_t<Get...>, exclude_t<Exclude...>> final: public group_descriptor {
// nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here
static_assert(!std::disjunction_v<std::is_const<Get>..., std::is_const<Exclude>...>, "Const storage type not allowed");
using base_type = std::common_type_t<typename Get::base_type..., typename Exclude::base_type...>;
using entity_type = typename base_type::entity_type;
template<typename Type, std::size_t Get, std::size_t Exclude>
class group_handler<Type, 0u, Get, Exclude> final: public group_descriptor {
using entity_type = typename Type::entity_type;
void push_on_construct(const entity_type entt) {
if(!elem.contains(entt)
@@ -207,44 +202,46 @@ class group_handler<owned_t<>, get_t<Get...>, exclude_t<Exclude...>> final: publ
elem.remove(entt);
}
public:
using common_type = base_type;
template<typename Alloc>
group_handler(const Alloc &alloc, Get &...gpool, Exclude &...epool)
: pools{&gpool...},
filter{&epool...},
elem{alloc} {
std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::push_on_construct>(*this), cpool->on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, pools);
std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::remove_if>(*this), cpool->on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, filter);
for(const auto entity: static_cast<base_type &>(*std::get<0>(pools))) {
void common_setup() {
for(const auto entity: *pools[0u]) {
push_on_construct(entity);
}
}
common_type &handle() noexcept {
public:
using common_type = Type;
template<typename Allocator, typename... GType, typename... EType>
group_handler(const Allocator &allocator, std::tuple<GType &...> gpool, std::tuple<EType &...> epool)
: pools{std::apply([](auto &&...cpool) { return std::array<common_type *, Get>{&cpool...}; }, gpool)},
filter{std::apply([](auto &&...cpool) { return std::array<common_type *, Exclude>{&cpool...}; }, epool)},
elem{allocator} {
std::apply([this](auto &...cpool) { ((cpool.on_construct().template connect<&group_handler::push_on_construct>(*this), cpool.on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, gpool);
std::apply([this](auto &...cpool) { ((cpool.on_construct().template connect<&group_handler::remove_if>(*this), cpool.on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, epool);
common_setup();
}
[[nodiscard]] common_type &handle() noexcept {
return elem;
}
const common_type &handle() const noexcept {
[[nodiscard]] const common_type &handle() const noexcept {
return elem;
}
template<typename Type>
Type pools_as() const noexcept {
return pools;
}
template<typename Type>
Type filter_as() const noexcept {
return filter;
template<std::size_t Index>
[[nodiscard]] common_type *storage() const noexcept {
if constexpr(Index < Get) {
return pools[Index];
} else {
return filter[Index - Get];
}
}
private:
std::tuple<Get *...> pools;
std::tuple<Exclude *...> filter;
base_type elem;
std::array<common_type *, Get> pools;
std::array<common_type *, Exclude> filter;
common_type elem;
};
} // namespace internal
@@ -271,7 +268,7 @@ class basic_group;
* Iterators aren't invalidated if:
*
* * New elements are added to the storage.
* * The entity currently pointed is modified (for example, components are added
* * The entity currently pointed is modified (for example, elements are added
* or removed from it).
* * The entity currently pointed is destroyed.
*
@@ -287,16 +284,12 @@ class basic_group<owned_t<>, get_t<Get...>, exclude_t<Exclude...>> {
using underlying_type = typename base_type::entity_type;
template<typename Type>
static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Get::value_type..., typename Exclude::value_type...>>;
static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Get::element_type..., typename Exclude::element_type...>>;
auto pools() const noexcept {
template<std::size_t... Index>
[[nodiscard]] auto pools_for(std::index_sequence<Index...>) const noexcept {
using return_type = std::tuple<Get *...>;
return descriptor ? descriptor->template pools_as<return_type>() : return_type{};
}
auto filter() const noexcept {
using return_type = std::tuple<Exclude *...>;
return descriptor ? descriptor->template filter_as<return_type>() : return_type{};
return descriptor ? return_type{static_cast<Get *>(descriptor->template storage<Index>())...} : return_type{};
}
public:
@@ -304,16 +297,26 @@ public:
using entity_type = underlying_type;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Signed integer type. */
using difference_type = std::ptrdiff_t;
/*! @brief Common type among all storage types. */
using common_type = base_type;
/*! @brief Random access iterator type. */
using iterator = typename common_type::iterator;
/*! @brief Reversed iterator type. */
/*! @brief Reverse iterator type. */
using reverse_iterator = typename common_type::reverse_iterator;
/*! @brief Iterable group type. */
using iterable = iterable_adaptor<internal::extended_group_iterator<iterator, owned_t<>, get_t<Get...>>>;
/*! @brief Group handler type. */
using handler = internal::group_handler<owned_t<>, get_t<std::remove_const_t<Get>...>, exclude_t<std::remove_const_t<Exclude>...>>;
using handler = internal::group_handler<common_type, 0u, sizeof...(Get), sizeof...(Exclude)>;
/**
* @brief Group opaque identifier.
* @return Group opaque identifier.
*/
static id_type group_id() noexcept {
return type_hash<basic_group<owned_t<>, get_t<std::remove_const_t<Get>...>, exclude_t<std::remove_const_t<Exclude>...>>>::value();
}
/*! @brief Default constructor to use to create empty, invalid groups. */
basic_group() noexcept
@@ -335,9 +338,9 @@ public:
}
/**
* @brief Returns the storage for a given component type, if any.
* @tparam Type Type of component of which to return the storage.
* @return The storage for the given component type.
* @brief Returns the storage for a given element type, if any.
* @tparam Type Type of element of which to return the storage.
* @return The storage for the given element type.
*/
template<typename Type>
[[nodiscard]] auto *storage() const noexcept {
@@ -351,13 +354,8 @@ public:
*/
template<std::size_t Index>
[[nodiscard]] auto *storage() const noexcept {
constexpr auto offset = sizeof...(Get);
if constexpr(Index < offset) {
return std::get<Index>(pools());
} else {
return std::get<Index - offset>(filter());
}
using type = type_list_element_t<Index, type_list<Get..., Exclude...>>;
return *this ? static_cast<type *>(descriptor->template storage<Index>()) : nullptr;
}
/**
@@ -469,7 +467,7 @@ public:
* @return The identifier that occupies the given position.
*/
[[nodiscard]] entity_type operator[](const size_type pos) const {
return begin()[pos];
return begin()[static_cast<difference_type>(pos)];
}
/**
@@ -490,11 +488,11 @@ public:
}
/**
* @brief Returns the components assigned to the given entity.
* @tparam Type Type of the component to get.
* @tparam Other Other types of components to get.
* @brief Returns the elements assigned to the given entity.
* @tparam Type Type of the element to get.
* @tparam Other Other types of elements to get.
* @param entt A valid identifier.
* @return The components assigned to the entity.
* @return The elements assigned to the entity.
*/
template<typename Type, typename... Other>
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
@@ -502,14 +500,14 @@ public:
}
/**
* @brief Returns the components assigned to the given entity.
* @tparam Index Indexes of the components to get.
* @brief Returns the elements assigned to the given entity.
* @tparam Index Indexes of the elements to get.
* @param entt A valid identifier.
* @return The components assigned to the entity.
* @return The elements assigned to the entity.
*/
template<std::size_t... Index>
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
const auto cpools = pools();
const auto cpools = pools_for(std::index_sequence_for<Get...>{});
if constexpr(sizeof...(Index) == 0) {
return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools);
@@ -521,12 +519,12 @@ public:
}
/**
* @brief Iterates entities and components and applies the given function
* @brief Iterates entities and elements and applies the given function
* object to them.
*
* The function object is invoked for each entity. It is provided with the
* entity itself and a set of references to non-empty components. The
* _constness_ of the components is as requested.<br/>
* entity itself and a set of references to non-empty elements. The
* _constness_ of the elements is as requested.<br/>
* The signature of the function must be equivalent to one of the following
* forms:
*
@@ -557,8 +555,8 @@ public:
* @brief Returns an iterable object to use to _visit_ a group.
*
* The iterable object returns tuples that contain the current entity and a
* set of references to its non-empty components. The _constness_ of the
* components is as requested.
* set of references to its non-empty elements. The _constness_ of the
* elements is as requested.
*
* @note
* Empty types aren't explicitly instantiated and therefore they are never
@@ -567,7 +565,7 @@ public:
* @return An iterable object to use to _visit_ the group.
*/
[[nodiscard]] iterable each() const noexcept {
const auto cpools = pools();
const auto cpools = pools_for(std::index_sequence_for<Get...>{});
return iterable{{begin(), cpools}, {end(), cpools}};
}
@@ -595,8 +593,8 @@ public:
* * An iterator past the last element of the range to sort.
* * A comparison function to use to compare the elements.
*
* @tparam Type Optional type of component to compare.
* @tparam Other Other optional types of components to compare.
* @tparam Type Optional type of element to compare.
* @tparam Other Other optional types of elements to compare.
* @tparam Compare Type of comparison function object.
* @tparam Sort Type of sort function object.
* @tparam Args Types of arguments to forward to the sort function object.
@@ -614,7 +612,7 @@ public:
*
* @sa sort
*
* @tparam Index Optional indexes of components to compare.
* @tparam Index Optional indexes of elements to compare.
* @tparam Compare Type of comparison function object.
* @tparam Sort Type of sort function object.
* @tparam Args Types of arguments to forward to the sort function object.
@@ -629,7 +627,7 @@ public:
static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function");
descriptor->handle().sort(std::move(compare), std::move(algo), std::forward<Args>(args)...);
} else {
auto comp = [&compare, cpools = pools()](const entity_type lhs, const entity_type rhs) {
auto comp = [&compare, cpools = pools_for(std::index_sequence_for<Get...>{})](const entity_type lhs, const entity_type rhs) {
if constexpr(sizeof...(Index) == 1) {
return compare((std::get<Index>(cpools)->get(lhs), ...), (std::get<Index>(cpools)->get(rhs), ...));
} else {
@@ -659,14 +657,6 @@ public:
}
}
/**
* @brief Sort entities according to their order in a range.
* @param other The storage to use to impose the order.
*/
[[deprecated("use iterator based sort_as instead")]] void sort_as(const common_type &other) const {
sort_as(other.begin(), other.end());
}
private:
handler *descriptor;
};
@@ -679,7 +669,7 @@ private:
*
* * It's guaranteed that the entity list is tightly packed in memory for fast
* iterations.
* * It's guaranteed that all components in the owned storage are tightly packed
* * It's guaranteed that all elements in the owned storage are tightly packed
* in memory for even faster iterations and to allow direct access.
* * They stay true to the order of the owned storage and all instances have the
* same order in memory.
@@ -691,7 +681,7 @@ private:
* Iterators aren't invalidated if:
*
* * New elements are added to the storage.
* * The entity currently pointed is modified (for example, components are added
* * The entity currently pointed is modified (for example, elements are added
* or removed from it).
* * The entity currently pointed is destroyed.
*
@@ -704,20 +694,18 @@ private:
*/
template<typename... Owned, typename... Get, typename... Exclude>
class basic_group<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> {
static_assert(((Owned::storage_policy != deletion_policy::in_place) && ...), "Groups do not support in-place delete");
using base_type = std::common_type_t<typename Owned::base_type..., typename Get::base_type..., typename Exclude::base_type...>;
using underlying_type = typename base_type::entity_type;
template<typename Type>
static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Owned::value_type..., typename Get::value_type..., typename Exclude::value_type...>>;
static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Owned::element_type..., typename Get::element_type..., typename Exclude::element_type...>>;
auto pools() const noexcept {
template<std::size_t... Index, std::size_t... Other>
[[nodiscard]] auto pools_for(std::index_sequence<Index...>, std::index_sequence<Other...>) const noexcept {
using return_type = std::tuple<Owned *..., Get *...>;
return descriptor ? descriptor->template pools_as<return_type>() : return_type{};
}
auto filter() const noexcept {
using return_type = std::tuple<Exclude *...>;
return descriptor ? descriptor->template filter_as<return_type>() : return_type{};
return descriptor ? return_type{static_cast<Owned *>(descriptor->template storage<Index>())..., static_cast<Get *>(descriptor->template storage<sizeof...(Owned) + Other>())...} : return_type{};
}
public:
@@ -725,16 +713,26 @@ public:
using entity_type = underlying_type;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Signed integer type. */
using difference_type = std::ptrdiff_t;
/*! @brief Common type among all storage types. */
using common_type = base_type;
/*! @brief Random access iterator type. */
using iterator = typename common_type::iterator;
/*! @brief Reversed iterator type. */
/*! @brief Reverse iterator type. */
using reverse_iterator = typename common_type::reverse_iterator;
/*! @brief Iterable group type. */
using iterable = iterable_adaptor<internal::extended_group_iterator<iterator, owned_t<Owned...>, get_t<Get...>>>;
/*! @brief Group handler type. */
using handler = internal::group_handler<owned_t<std::remove_const_t<Owned>...>, get_t<std::remove_const_t<Get>...>, exclude_t<std::remove_const_t<Exclude>...>>;
using handler = internal::group_handler<common_type, sizeof...(Owned), sizeof...(Get), sizeof...(Exclude)>;
/**
* @brief Group opaque identifier.
* @return Group opaque identifier.
*/
static id_type group_id() noexcept {
return type_hash<basic_group<owned_t<std::remove_const_t<Owned>...>, get_t<std::remove_const_t<Get>...>, exclude_t<std::remove_const_t<Exclude>...>>>::value();
}
/*! @brief Default constructor to use to create empty, invalid groups. */
basic_group() noexcept
@@ -756,9 +754,9 @@ public:
}
/**
* @brief Returns the storage for a given component type, if any.
* @tparam Type Type of component of which to return the storage.
* @return The storage for the given component type.
* @brief Returns the storage for a given element type, if any.
* @tparam Type Type of element of which to return the storage.
* @return The storage for the given element type.
*/
template<typename Type>
[[nodiscard]] auto *storage() const noexcept {
@@ -772,13 +770,8 @@ public:
*/
template<std::size_t Index>
[[nodiscard]] auto *storage() const noexcept {
constexpr auto offset = sizeof...(Owned) + sizeof...(Get);
if constexpr(Index < offset) {
return std::get<Index>(pools());
} else {
return std::get<Index - offset>(filter());
}
using type = type_list_element_t<Index, type_list<Owned..., Get..., Exclude...>>;
return *this ? static_cast<type *>(descriptor->template storage<Index>()) : nullptr;
}
/**
@@ -805,7 +798,7 @@ public:
* @return An iterator to the first entity of the group.
*/
[[nodiscard]] iterator begin() const noexcept {
return *this ? (handle().end() - descriptor->length()) : iterator{};
return *this ? (handle().end() - static_cast<difference_type>(descriptor->length())) : iterator{};
}
/**
@@ -835,7 +828,7 @@ public:
* reversed group.
*/
[[nodiscard]] reverse_iterator rend() const noexcept {
return *this ? (handle().rbegin() + descriptor->length()) : reverse_iterator{};
return *this ? (handle().rbegin() + static_cast<difference_type>(descriptor->length())) : reverse_iterator{};
}
/**
@@ -875,7 +868,7 @@ public:
* @return The identifier that occupies the given position.
*/
[[nodiscard]] entity_type operator[](const size_type pos) const {
return begin()[pos];
return begin()[static_cast<difference_type>(pos)];
}
/**
@@ -896,11 +889,11 @@ public:
}
/**
* @brief Returns the components assigned to the given entity.
* @tparam Type Type of the component to get.
* @tparam Other Other types of components to get.
* @brief Returns the elements assigned to the given entity.
* @tparam Type Type of the element to get.
* @tparam Other Other types of elements to get.
* @param entt A valid identifier.
* @return The components assigned to the entity.
* @return The elements assigned to the entity.
*/
template<typename Type, typename... Other>
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
@@ -908,14 +901,14 @@ public:
}
/**
* @brief Returns the components assigned to the given entity.
* @tparam Index Indexes of the components to get.
* @brief Returns the elements assigned to the given entity.
* @tparam Index Indexes of the elements to get.
* @param entt A valid identifier.
* @return The components assigned to the entity.
* @return The elements assigned to the entity.
*/
template<std::size_t... Index>
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
const auto cpools = pools();
const auto cpools = pools_for(std::index_sequence_for<Owned...>{}, std::index_sequence_for<Get...>{});
if constexpr(sizeof...(Index) == 0) {
return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools);
@@ -927,12 +920,12 @@ public:
}
/**
* @brief Iterates entities and components and applies the given function
* @brief Iterates entities and elements and applies the given function
* object to them.
*
* The function object is invoked for each entity. It is provided with the
* entity itself and a set of references to non-empty components. The
* _constness_ of the components is as requested.<br/>
* entity itself and a set of references to non-empty elements. The
* _constness_ of the elements is as requested.<br/>
* The signature of the function must be equivalent to one of the following
* forms:
*
@@ -963,8 +956,8 @@ public:
* @brief Returns an iterable object to use to _visit_ a group.
*
* The iterable object returns tuples that contain the current entity and a
* set of references to its non-empty components. The _constness_ of the
* components is as requested.
* set of references to its non-empty elements. The _constness_ of the
* elements is as requested.
*
* @note
* Empty types aren't explicitly instantiated and therefore they are never
@@ -973,8 +966,8 @@ public:
* @return An iterable object to use to _visit_ the group.
*/
[[nodiscard]] iterable each() const noexcept {
const auto cpools = pools();
return {{begin(), cpools}, {end(), cpools}};
const auto cpools = pools_for(std::index_sequence_for<Owned...>{}, std::index_sequence_for<Get...>{});
return iterable{{begin(), cpools}, {end(), cpools}};
}
/**
@@ -1002,8 +995,8 @@ public:
* * An iterator past the last element of the range to sort.
* * A comparison function to use to compare the elements.
*
* @tparam Type Optional type of component to compare.
* @tparam Other Other optional types of components to compare.
* @tparam Type Optional type of element to compare.
* @tparam Other Other optional types of elements to compare.
* @tparam Compare Type of comparison function object.
* @tparam Sort Type of sort function object.
* @tparam Args Types of arguments to forward to the sort function object.
@@ -1021,7 +1014,7 @@ public:
*
* @sa sort
*
* @tparam Index Optional indexes of components to compare.
* @tparam Index Optional indexes of elements to compare.
* @tparam Compare Type of comparison function object.
* @tparam Sort Type of sort function object.
* @tparam Args Types of arguments to forward to the sort function object.
@@ -1031,7 +1024,7 @@ public:
*/
template<std::size_t... Index, typename Compare, typename Sort = std_sort, typename... Args>
void sort(Compare compare, Sort algo = Sort{}, Args &&...args) const {
const auto cpools = pools();
const auto cpools = pools_for(std::index_sequence_for<Owned...>{}, std::index_sequence_for<Get...>{});
if constexpr(sizeof...(Index) == 0) {
static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function");

View File

@@ -5,6 +5,7 @@
#include <tuple>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/iterator.hpp"
#include "../core/type_traits.hpp"
#include "entity.hpp"
@@ -46,12 +47,12 @@ public:
}
constexpr handle_storage_iterator &operator++() noexcept {
while(++it != last && !it->second.contains(entt)) {}
for(++it; it != last && !it->second.contains(entt); ++it) {}
return *this;
}
constexpr handle_storage_iterator operator++(int) noexcept {
handle_storage_iterator orig = *this;
const handle_storage_iterator orig = *this;
return ++(*this), orig;
}
@@ -94,19 +95,29 @@ template<typename ILhs, typename IRhs>
* @tparam Scope Types to which to restrict the scope of a handle.
*/
template<typename Registry, typename... Scope>
struct basic_handle {
class basic_handle {
using traits_type = entt_traits<typename Registry::entity_type>;
[[nodiscard]] auto &owner_or_assert() const noexcept {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
return static_cast<Registry &>(*owner);
}
public:
/*! @brief Type of registry accepted by the handle. */
using registry_type = Registry;
/*! @brief Underlying entity identifier. */
using entity_type = typename registry_type::entity_type;
using entity_type = typename traits_type::value_type;
/*! @brief Underlying version type. */
using version_type = typename registry_type::version_type;
using version_type = typename traits_type::version_type;
/*! @brief Unsigned integer type. */
using size_type = typename registry_type::size_type;
using size_type = std::size_t;
/*! @brief Iterable handle type. */
using iterable = iterable_adaptor<internal::handle_storage_iterator<typename decltype(std::declval<registry_type>().storage())::iterator>>;
/*! @brief Constructs an invalid handle. */
basic_handle() noexcept
: reg{},
: owner{},
entt{null} {}
/**
@@ -115,7 +126,7 @@ struct basic_handle {
* @param value A valid identifier.
*/
basic_handle(registry_type &ref, entity_type value) noexcept
: reg{&ref},
: owner{&ref},
entt{value} {}
/**
@@ -128,49 +139,23 @@ struct basic_handle {
*
* @return An iterable object to use to _visit_ the handle.
*/
[[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()}};
[[nodiscard]] iterable storage() const noexcept {
auto underlying = owner_or_assert().storage();
return iterable{{entt, underlying.begin(), underlying.end()}, {entt, underlying.end(), underlying.end()}};
}
/**
* @brief Constructs a const handle from a non-const one.
* @tparam Other A valid entity type.
* @tparam Args Scope of the handle to construct.
* @return A const handle referring to the same registry and the same
* entity.
*/
template<typename Other, typename... Args>
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 The contained identifier.
*/
[[nodiscard]] operator entity_type() const noexcept {
return entity();
}
/**
* @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.
*/
/*! @copydoc valid */
[[nodiscard]] explicit operator bool() const noexcept {
return reg && reg->valid(entt);
return owner && owner->valid(entt);
}
/**
* @brief Checks if a handle refers to a valid entity or not.
* @return True if the handle refers to a valid entity, false otherwise.
* @brief Checks if a handle refers to a valid registry and entity.
* @return True if the handle refers to a valid registry and entity, false
* otherwise.
*/
[[nodiscard]] bool valid() const {
return reg->valid(entt);
return static_cast<bool>(*this);
}
/**
@@ -178,7 +163,7 @@ struct basic_handle {
* @return A pointer to the underlying registry, if any.
*/
[[nodiscard]] registry_type *registry() const noexcept {
return reg;
return owner;
}
/**
@@ -189,9 +174,14 @@ struct basic_handle {
return entt;
}
/*! @copydoc entity */
[[nodiscard]] operator entity_type() const noexcept {
return entity();
}
/*! @brief Destroys the entity associated with a handle. */
void destroy() {
reg->destroy(std::exchange(entt, null));
owner_or_assert().destroy(std::exchange(entt, null));
}
/**
@@ -199,148 +189,164 @@ struct basic_handle {
* @param version A desired version upon destruction.
*/
void destroy(const version_type version) {
reg->destroy(std::exchange(entt, null), version);
owner_or_assert().destroy(std::exchange(entt, null), version);
}
/**
* @brief Assigns the given component to a handle.
* @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.
* @brief Assigns the given element to a handle.
* @tparam Type Type of element to create.
* @tparam Args Types of arguments to use to construct the element.
* @param args Parameters to use to initialize the element.
* @return A reference to the newly created element.
*/
template<typename Component, typename... Args>
template<typename Type, typename... Args>
// NOLINTNEXTLINE(modernize-use-nodiscard)
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)...);
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type");
return owner_or_assert().template emplace<Type>(entt, std::forward<Args>(args)...);
}
/**
* @brief Assigns or replaces the given component for a handle.
* @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.
* @brief Assigns or replaces the given element for a handle.
* @tparam Type Type of element to assign or replace.
* @tparam Args Types of arguments to use to construct the element.
* @param args Parameters to use to initialize the element.
* @return A reference to the newly created element.
*/
template<typename Component, typename... Args>
template<typename Type, typename... Args>
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)...);
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type");
return owner_or_assert().template emplace_or_replace<Type>(entt, std::forward<Args>(args)...);
}
/**
* @brief Patches the given component for a handle.
* @tparam Component Type of component to patch.
* @brief Patches the given element for a handle.
* @tparam Type Type of element to patch.
* @tparam Func Types of the function objects to invoke.
* @param func Valid function objects.
* @return A reference to the patched component.
* @return A reference to the patched element.
*/
template<typename Component, typename... Func>
template<typename Type, typename... Func>
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)...);
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type");
return owner_or_assert().template patch<Type>(entt, std::forward<Func>(func)...);
}
/**
* @brief Replaces the given component for a handle.
* @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.
* @brief Replaces the given element for a handle.
* @tparam Type Type of element to replace.
* @tparam Args Types of arguments to use to construct the element.
* @param args Parameters to use to initialize the element.
* @return A reference to the element being replaced.
*/
template<typename Component, typename... Args>
template<typename Type, typename... Args>
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)...);
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type");
return owner_or_assert().template replace<Type>(entt, std::forward<Args>(args)...);
}
/**
* @brief Removes the given components from a handle.
* @tparam Component Types of components to remove.
* @return The number of components actually removed.
* @brief Removes the given elements from a handle.
* @tparam Type Types of elements to remove.
* @return The number of elements actually removed.
*/
template<typename... Component>
template<typename... Type>
// NOLINTNEXTLINE(modernize-use-nodiscard)
size_type remove() const {
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Component> && ...), "Invalid type");
return reg->template remove<Component...>(entt);
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Type> && ...), "Invalid type");
return owner_or_assert().template remove<Type...>(entt);
}
/**
* @brief Erases the given components from a handle.
* @tparam Component Types of components to erase.
* @brief Erases the given elements from a handle.
* @tparam Type Types of elements to erase.
*/
template<typename... Component>
template<typename... Type>
void erase() const {
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Component> && ...), "Invalid type");
reg->template erase<Component...>(entt);
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Type> && ...), "Invalid type");
owner_or_assert().template erase<Type...>(entt);
}
/**
* @brief Checks if a handle has all the given components.
* @tparam Component Components for which to perform the check.
* @return True if the handle has all the components, false otherwise.
* @brief Checks if a handle has all the given elements.
* @tparam Type Elements for which to perform the check.
* @return True if the handle has all the elements, false otherwise.
*/
template<typename... Component>
template<typename... Type>
[[nodiscard]] decltype(auto) all_of() const {
return reg->template all_of<Component...>(entt);
return owner_or_assert().template all_of<Type...>(entt);
}
/**
* @brief Checks if a handle has at least one of the given components.
* @tparam Component Components for which to perform the check.
* @return True if the handle has at least one of the given components,
* @brief Checks if a handle has at least one of the given elements.
* @tparam Type Elements for which to perform the check.
* @return True if the handle has at least one of the given elements,
* false otherwise.
*/
template<typename... Component>
template<typename... Type>
[[nodiscard]] decltype(auto) any_of() const {
return reg->template any_of<Component...>(entt);
return owner_or_assert().template any_of<Type...>(entt);
}
/**
* @brief Returns references to the given components for a handle.
* @tparam Component Types of components to get.
* @return References to the components owned by the handle.
* @brief Returns references to the given elements for a handle.
* @tparam Type Types of elements to get.
* @return References to the elements owned by the handle.
*/
template<typename... Component>
template<typename... Type>
[[nodiscard]] decltype(auto) get() const {
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Component> && ...), "Invalid type");
return reg->template get<Component...>(entt);
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Type> && ...), "Invalid type");
return owner_or_assert().template get<Type...>(entt);
}
/**
* @brief Returns a reference to the given component for a handle.
* @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.
* @brief Returns a reference to the given element for a handle.
* @tparam Type Type of element to get.
* @tparam Args Types of arguments to use to construct the element.
* @param args Parameters to use to initialize the element.
* @return Reference to the element owned by the handle.
*/
template<typename Component, typename... Args>
template<typename Type, typename... Args>
[[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)...);
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type");
return owner_or_assert().template get_or_emplace<Type>(entt, std::forward<Args>(args)...);
}
/**
* @brief Returns pointers to the given components for a handle.
* @tparam Component Types of components to get.
* @return Pointers to the components owned by the handle.
* @brief Returns pointers to the given elements for a handle.
* @tparam Type Types of elements to get.
* @return Pointers to the elements owned by the handle.
*/
template<typename... Component>
template<typename... Type>
[[nodiscard]] auto try_get() const {
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Component> && ...), "Invalid type");
return reg->template try_get<Component...>(entt);
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Type> && ...), "Invalid type");
return owner_or_assert().template try_get<Type...>(entt);
}
/**
* @brief Checks if a handle has components assigned.
* @return True if the handle has no components assigned, false otherwise.
* @brief Checks if a handle has elements assigned.
* @return True if the handle has no elements assigned, false otherwise.
*/
[[nodiscard]] bool orphan() const {
return reg->orphan(entt);
return owner_or_assert().orphan(entt);
}
/**
* @brief Returns a const handle from a non-const one.
* @tparam Other A valid entity type.
* @tparam Args Scope of the handle to construct.
* @return A const handle referring to the same registry and the same
* entity.
*/
template<typename Other, typename... Args>
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 owner ? basic_handle<Other, Args...>{*owner, entt} : basic_handle<Other, Args...>{};
}
private:
registry_type *reg;
registry_type *owner;
entity_type entt;
};
@@ -372,6 +378,54 @@ template<typename... Args, typename... Other>
return !(lhs == rhs);
}
/**
* @brief Compares a handle with the null object.
* @tparam Args Scope of the handle.
* @param lhs A valid handle.
* @param rhs A null object yet to be converted.
* @return False if the two elements differ, true otherwise.
*/
template<typename... Args>
[[nodiscard]] constexpr bool operator==(const basic_handle<Args...> &lhs, const null_t rhs) noexcept {
return (lhs.entity() == rhs);
}
/**
* @brief Compares a handle with the null object.
* @tparam Args Scope of the handle.
* @param lhs A null object yet to be converted.
* @param rhs A valid handle.
* @return False if the two elements differ, true otherwise.
*/
template<typename... Args>
[[nodiscard]] constexpr bool operator==(const null_t lhs, const basic_handle<Args...> &rhs) noexcept {
return (rhs == lhs);
}
/**
* @brief Compares a handle with the null object.
* @tparam Args Scope of the handle.
* @param lhs A valid handle.
* @param rhs A null object yet to be converted.
* @return True if the two elements differ, false otherwise.
*/
template<typename... Args>
[[nodiscard]] constexpr bool operator!=(const basic_handle<Args...> &lhs, const null_t rhs) noexcept {
return (lhs.entity() != rhs);
}
/**
* @brief Compares a handle with the null object.
* @tparam Args Scope of the handle.
* @param lhs A null object yet to be converted.
* @param rhs A valid handle.
* @return True if the two elements differ, false otherwise.
*/
template<typename... Args>
[[nodiscard]] constexpr bool operator!=(const null_t lhs, const basic_handle<Args...> &rhs) noexcept {
return (rhs != lhs);
}
} // namespace entt
#endif

View File

@@ -6,7 +6,7 @@
#include <utility>
#include "../core/fwd.hpp"
#include "../core/type_traits.hpp"
#include "../signal/delegate.hpp"
#include "component.hpp"
#include "fwd.hpp"
#include "group.hpp"
#include "storage.hpp"
@@ -21,8 +21,8 @@ namespace entt {
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>...>{});
[[nodiscard]] auto dispatch(get_t<Get...>, exclude_t<Exclude...>) const {
return reg->template view<constness_as_t<typename Get::element_type, Get>...>(exclude_t<constness_as_t<typename Exclude::element_type, Exclude>...>{});
}
public:
@@ -36,7 +36,7 @@ public:
* @param source A valid reference to a registry.
*/
as_view(registry_type &source) noexcept
: reg{source} {}
: reg{&source} {}
/**
* @brief Conversion function from a registry to a view.
@@ -50,7 +50,7 @@ public:
}
private:
registry_type &reg;
registry_type *reg;
};
/**
@@ -60,11 +60,11 @@ private:
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 {
[[nodiscard]] 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...>{});
return reg->template group_if_exists<typename Owned::element_type...>(get_t<typename Get::element_type...>{}, exclude_t<typename Exclude::element_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>...>{});
return reg->template group<constness_as_t<typename Owned::element_type, Owned>...>(get_t<constness_as_t<typename Get::element_type, Get>...>{}, exclude_t<constness_as_t<typename Exclude::element_type, Exclude>...>{});
}
}
@@ -79,7 +79,7 @@ public:
* @param source A valid reference to a registry.
*/
as_group(registry_type &source) noexcept
: reg{source} {}
: reg{&source} {}
/**
* @brief Conversion function from a registry to a group.
@@ -94,64 +94,47 @@ public:
}
private:
registry_type &reg;
registry_type *reg;
};
/**
* @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 Member Member function to invoke on an element of the given type.
* @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.
* @param reg A registry that contains the given entity and its elements.
* @param entt Entity from which to get the element.
*/
template<auto Member, typename Registry = std::decay_t<nth_argument_t<0u, decltype(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(Registry &, const typename Registry::entity_type)> func;
func.template connect<Member>(reg.template get<member_class_t<decltype(Member)>>(entt));
func(reg, entt);
(reg.template get<member_class_t<decltype(Member)>>(entt).*Member)(reg, entt);
}
/**
* @brief Returns the entity associated with a given component.
* @brief Returns the entity associated with a given element.
*
* @warning
* Currently, this function only works correctly with the default storage as it
* makes assumptions about how the components are laid out.
* makes assumptions about how the elements are laid out.
*
* @tparam Args Storage type template parameters.
* @param storage A storage that contains the given component.
* @param instance A valid component instance.
* @return The entity associated with the given component.
* @param storage A storage that contains the given element.
* @param instance A valid element instance.
* @return The entity associated with the given element.
*/
template<typename... Args>
auto to_entity(const basic_storage<Args...> &storage, const typename basic_storage<Args...>::value_type &instance) -> typename basic_storage<Args...>::entity_type {
constexpr auto page_size = basic_storage<Args...>::traits_type::page_size;
const typename basic_storage<Args...>::base_type &base = storage;
const auto *addr = std::addressof(instance);
typename basic_storage<Args...>::entity_type to_entity(const basic_storage<Args...> &storage, const typename basic_storage<Args...>::value_type &instance) {
using traits_type = component_traits<typename basic_storage<Args...>::value_type, typename basic_storage<Args...>::entity_type>;
static_assert(traits_type::page_size != 0u, "Unexpected page size");
const auto *page = storage.raw();
for(auto it = base.rbegin(), last = base.rend(); it < last; it += page_size) {
if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < static_cast<decltype(dist)>(page_size)) {
return *(it + dist);
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic)
for(std::size_t pos{}, count = storage.size(); pos < count; pos += traits_type::page_size, ++page) {
if(const auto dist = (std::addressof(instance) - *page); dist >= 0 && dist < static_cast<decltype(dist)>(traits_type::page_size)) {
return *(static_cast<const typename basic_storage<Args...>::base_type &>(storage).rbegin() + static_cast<decltype(dist)>(pos) + dist);
}
}
return null;
}
/**
* @copybrief to_entity
* @tparam Args Registry type template parameters.
* @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... Args, typename Component>
[[deprecated("use storage based to_entity instead")]] typename basic_registry<Args...>::entity_type to_entity(const basic_registry<Args...> &reg, const Component &instance) {
if(const auto *storage = reg.template storage<Component>(); storage) {
return to_entity(*storage, instance);
}
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
return null;
}

View File

@@ -5,12 +5,40 @@
#include <utility>
#include "../config/config.h"
#include "../core/any.hpp"
#include "../core/type_info.hpp"
#include "../signal/sigh.hpp"
#include "entity.hpp"
#include "fwd.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename, typename, typename = void>
struct has_on_construct final: std::false_type {};
template<typename Type, typename Registry>
struct has_on_construct<Type, Registry, std::void_t<decltype(Type::on_construct(std::declval<Registry &>(), std::declval<Registry>().create()))>>
: std::true_type {};
template<typename, typename, typename = void>
struct has_on_update final: std::false_type {};
template<typename Type, typename Registry>
struct has_on_update<Type, Registry, std::void_t<decltype(Type::on_update(std::declval<Registry &>(), std::declval<Registry>().create()))>>
: std::true_type {};
template<typename, typename, typename = void>
struct has_on_destroy final: std::false_type {};
template<typename Type, typename Registry>
struct has_on_destroy<Type, Registry, std::void_t<decltype(Type::on_destroy(std::declval<Registry &>(), std::declval<Registry>().create()))>>
: std::true_type {};
} // namespace internal
/*! @endcond */
/**
* @brief Mixin type used to add signal support to storage types.
*
@@ -30,17 +58,18 @@ class basic_sigh_mixin final: public Type {
using underlying_type = Type;
using owner_type = Registry;
using basic_registry_type = basic_registry<typename underlying_type::entity_type, typename underlying_type::base_type::allocator_type>;
using basic_registry_type = basic_registry<typename owner_type::entity_type, typename owner_type::allocator_type>;
using sigh_type = sigh<void(owner_type &, const typename underlying_type::entity_type), typename underlying_type::allocator_type>;
using underlying_iterator = typename underlying_type::base_type::basic_iterator;
static_assert(std::is_base_of_v<basic_registry_type, owner_type>, "Invalid registry type");
owner_type &owner_or_assert() const noexcept {
[[nodiscard]] auto &owner_or_assert() const noexcept {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
return static_cast<owner_type &>(*owner);
}
private:
void pop(underlying_iterator first, underlying_iterator last) final {
if(auto &reg = owner_or_assert(); destruction.empty()) {
underlying_type::pop(first, last);
@@ -56,16 +85,18 @@ class basic_sigh_mixin final: public Type {
void pop_all() final {
if(auto &reg = owner_or_assert(); !destruction.empty()) {
for(auto it = underlying_type::base_type::begin(0), last = underlying_type::base_type::end(0); it != last; ++it) {
if constexpr(std::is_same_v<typename underlying_type::value_type, typename underlying_type::entity_type>) {
destruction.publish(reg, *it);
} else {
if constexpr(underlying_type::traits_type::in_place_delete) {
if(const auto entt = *it; entt != tombstone) {
if constexpr(std::is_same_v<typename underlying_type::element_type, entity_type>) {
for(typename underlying_type::size_type pos{}, last = underlying_type::free_list(); pos < last; ++pos) {
destruction.publish(reg, underlying_type::base_type::operator[](pos));
}
} else {
for(auto entt: static_cast<typename underlying_type::base_type &>(*this)) {
if constexpr(underlying_type::storage_policy == deletion_policy::in_place) {
if(entt != tombstone) {
destruction.publish(reg, entt);
}
} else {
destruction.publish(reg, *it);
destruction.publish(reg, entt);
}
}
}
@@ -84,6 +115,18 @@ class basic_sigh_mixin final: public Type {
return it;
}
void bind_any(any value) noexcept final {
owner = any_cast<basic_registry_type>(&value);
if constexpr(!std::is_same_v<registry_type, basic_registry_type>) {
if(owner == nullptr) {
owner = any_cast<registry_type>(&value);
}
}
underlying_type::bind_any(std::move(value));
}
public:
/*! @brief Allocator type. */
using allocator_type = typename underlying_type::allocator_type;
@@ -105,14 +148,29 @@ public:
owner{},
construction{allocator},
destruction{allocator},
update{allocator} {}
update{allocator} {
if constexpr(internal::has_on_construct<typename underlying_type::element_type, Registry>::value) {
sink{construction}.template connect<&underlying_type::element_type::on_construct>();
}
if constexpr(internal::has_on_update<typename underlying_type::element_type, Registry>::value) {
sink{update}.template connect<&underlying_type::element_type::on_update>();
}
if constexpr(internal::has_on_destroy<typename underlying_type::element_type, Registry>::value) {
sink{destruction}.template connect<&underlying_type::element_type::on_destroy>();
}
}
/*! @brief Default copy constructor, deleted on purpose. */
basic_sigh_mixin(const basic_sigh_mixin &) = delete;
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
basic_sigh_mixin(basic_sigh_mixin &&other) noexcept
: underlying_type{std::move(other)},
: underlying_type{static_cast<underlying_type &&>(other)},
owner{other.owner},
construction{std::move(other.construction)},
destruction{std::move(other.destruction)},
@@ -123,24 +181,29 @@ public:
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
basic_sigh_mixin(basic_sigh_mixin &&other, const allocator_type &allocator) noexcept
: underlying_type{std::move(other), allocator},
basic_sigh_mixin(basic_sigh_mixin &&other, const allocator_type &allocator)
: underlying_type{static_cast<underlying_type &&>(other), allocator},
owner{other.owner},
construction{std::move(other.construction), allocator},
destruction{std::move(other.destruction), allocator},
update{std::move(other.update), allocator} {}
/*! @brief Default destructor. */
~basic_sigh_mixin() override = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This mixin.
*/
basic_sigh_mixin &operator=(const basic_sigh_mixin &) = delete;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This storage.
* @return This mixin.
*/
basic_sigh_mixin &operator=(basic_sigh_mixin &&other) noexcept {
underlying_type::operator=(std::move(other));
owner = other.owner;
construction = std::move(other.construction);
destruction = std::move(other.destruction);
update = std::move(other.update);
swap(other);
return *this;
}
@@ -148,13 +211,13 @@ public:
* @brief Exchanges the contents with those of a given storage.
* @param other Storage to exchange the content with.
*/
void swap(basic_sigh_mixin &other) {
void swap(basic_sigh_mixin &other) noexcept {
using std::swap;
underlying_type::swap(other);
swap(owner, other.owner);
swap(construction, other.construction);
swap(destruction, other.destruction);
swap(update, other.update);
underlying_type::swap(other);
}
/**
@@ -203,47 +266,80 @@ public:
}
/**
* @brief Emplace elements into a storage.
*
* The behavior of this operation depends on the underlying storage type
* (for example, components vs entities).<br/>
* Refer to the specific documentation for more details.
*
* @return A return value as returned by the underlying storage.
* @brief Checks if a mixin refers to a valid registry.
* @return True if the mixin refers to a valid registry, false otherwise.
*/
auto emplace() {
const auto entt = underlying_type::emplace();
[[nodiscard]] explicit operator bool() const noexcept {
return (owner != nullptr);
}
/**
* @brief Returns a pointer to the underlying registry, if any.
* @return A pointer to the underlying registry, if any.
*/
[[nodiscard]] const registry_type &registry() const noexcept {
return owner_or_assert();
}
/*! @copydoc registry */
[[nodiscard]] registry_type &registry() noexcept {
return owner_or_assert();
}
/**
* @brief Creates a new identifier or recycles a destroyed one.
* @return A valid identifier.
*/
auto generate() {
const auto entt = underlying_type::generate();
construction.publish(owner_or_assert(), entt);
return entt;
}
/**
* @brief Emplace elements into a storage.
*
* The behavior of this operation depends on the underlying storage type
* (for example, components vs entities).<br/>
* Refer to the specific documentation for more details.
*
* @tparam Args Types of arguments to forward to the underlying storage.
* @param hint A valid identifier.
* @param args Parameters to forward to the underlying storage.
* @return A return value as returned by the underlying storage.
* @brief Creates a new identifier or recycles a destroyed one.
* @param hint Required identifier.
* @return A valid identifier.
*/
template<typename... Args>
decltype(auto) emplace(const entity_type hint, Args &&...args) {
if constexpr(std::is_same_v<typename underlying_type::value_type, typename underlying_type::entity_type>) {
const auto entt = underlying_type::emplace(hint, std::forward<Args>(args)...);
construction.publish(owner_or_assert(), entt);
return entt;
} else {
underlying_type::emplace(hint, std::forward<Args>(args)...);
construction.publish(owner_or_assert(), hint);
return this->get(hint);
entity_type generate(const entity_type hint) {
const auto entt = underlying_type::generate(hint);
construction.publish(owner_or_assert(), entt);
return entt;
}
/**
* @brief Assigns each element in a range an identifier.
* @tparam It Type of mutable forward iterator.
* @param first An iterator to the first element of the range to generate.
* @param last An iterator past the last element of the range to generate.
*/
template<typename It>
void generate(It first, It last) {
underlying_type::generate(first, last);
if(auto &reg = owner_or_assert(); !construction.empty()) {
for(; first != last; ++first) {
construction.publish(reg, *first);
}
}
}
/**
* @brief Patches the given instance for an entity.
* @brief Assigns an entity to a storage and constructs its object.
* @tparam Args Types of arguments to forward to the underlying storage.
* @param entt A valid identifier.
* @param args Parameters to forward to the underlying storage.
* @return A reference to the newly created object.
*/
template<typename... Args>
decltype(auto) emplace(const entity_type entt, Args &&...args) {
underlying_type::emplace(entt, std::forward<Args>(args)...);
construction.publish(owner_or_assert(), entt);
return this->get(entt);
}
/**
* @brief Updates the instance assigned to a given entity in-place.
* @tparam Func Types of the function objects to invoke.
* @param entt A valid identifier.
* @param func Valid function objects.
@@ -257,39 +353,27 @@ public:
}
/**
* @brief Emplace elements into a storage.
*
* The behavior of this operation depends on the underlying storage type
* (for example, components vs entities).<br/>
* Refer to the specific documentation for more details.
*
* @tparam It Iterator type (as required by the underlying storage type).
* @brief Assigns one or more entities to a storage and constructs their
* objects from a given instance.
* @tparam It Type of input iterator.
* @tparam Args Types of arguments to forward to the underlying storage.
* @param first An iterator to the first element of the range.
* @param last An iterator past the last element of the range.
* @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 forward to the underlying storage.
*/
template<typename It, typename... Args>
void insert(It first, It last, Args &&...args) {
auto from = underlying_type::size();
underlying_type::insert(first, last, std::forward<Args>(args)...);
if(auto &reg = owner_or_assert(); !construction.empty()) {
for(; first != last; ++first) {
construction.publish(reg, *first);
// fine as long as insert passes force_back true to try_emplace
for(const auto to = underlying_type::size(); from != to; ++from) {
construction.publish(reg, underlying_type::operator[](from));
}
}
}
/**
* @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;
underlying_type::bind(std::move(value));
}
private:
basic_registry_type *owner;
sigh_type construction;
@@ -297,6 +381,211 @@ private:
sigh_type update;
};
/**
* @brief Mixin type used to add _reactive_ support to storage types.
* @tparam Type Underlying storage type.
* @tparam Registry Basic registry type.
*/
template<typename Type, typename Registry>
class basic_reactive_mixin final: public Type {
using underlying_type = Type;
using owner_type = Registry;
using alloc_traits = std::allocator_traits<typename underlying_type::allocator_type>;
using basic_registry_type = basic_registry<typename owner_type::entity_type, typename owner_type::allocator_type>;
using container_type = std::vector<connection, typename alloc_traits::template rebind_alloc<connection>>;
static_assert(std::is_base_of_v<basic_registry_type, owner_type>, "Invalid registry type");
[[nodiscard]] auto &owner_or_assert() const noexcept {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
return static_cast<owner_type &>(*owner);
}
void emplace_element(const Registry &, typename underlying_type::entity_type entity) {
if(!underlying_type::contains(entity)) {
underlying_type::emplace(entity);
}
}
private:
void bind_any(any value) noexcept final {
owner = any_cast<basic_registry_type>(&value);
if constexpr(!std::is_same_v<registry_type, basic_registry_type>) {
if(owner == nullptr) {
owner = any_cast<registry_type>(&value);
}
}
underlying_type::bind_any(std::move(value));
}
public:
/*! @brief Allocator type. */
using allocator_type = typename underlying_type::allocator_type;
/*! @brief Underlying entity identifier. */
using entity_type = typename underlying_type::entity_type;
/*! @brief Expected registry type. */
using registry_type = owner_type;
/*! @brief Default constructor. */
basic_reactive_mixin()
: basic_reactive_mixin{allocator_type{}} {}
/**
* @brief Constructs an empty storage with a given allocator.
* @param allocator The allocator to use.
*/
explicit basic_reactive_mixin(const allocator_type &allocator)
: underlying_type{allocator},
owner{},
conn{allocator} {
}
/*! @brief Default copy constructor, deleted on purpose. */
basic_reactive_mixin(const basic_reactive_mixin &) = delete;
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
basic_reactive_mixin(basic_reactive_mixin &&other) noexcept
: underlying_type{static_cast<underlying_type &&>(other)},
owner{other.owner},
conn{std::move(other.conn)} {
}
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
basic_reactive_mixin(basic_reactive_mixin &&other, const allocator_type &allocator)
: underlying_type{static_cast<underlying_type &&>(other), allocator},
owner{other.owner},
conn{std::move(other.conn), allocator} {
}
/*! @brief Default destructor. */
~basic_reactive_mixin() override = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This mixin.
*/
basic_reactive_mixin &operator=(const basic_reactive_mixin &) = delete;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This mixin.
*/
basic_reactive_mixin &operator=(basic_reactive_mixin &&other) noexcept {
underlying_type::swap(other);
return *this;
}
/**
* @brief Makes storage _react_ to creation of objects of the given type.
* @tparam Clazz Type of element to _react_ to.
* @tparam Candidate Function to use to _react_ to the event.
* @param id Optional name used to map the storage within the registry.
* @return This mixin.
*/
template<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
basic_reactive_mixin &on_construct(const id_type id = type_hash<Clazz>::value()) {
auto curr = owner_or_assert().template storage<Clazz>(id).on_construct().template connect<Candidate>(*this);
conn.push_back(std::move(curr));
return *this;
}
/**
* @brief Makes storage _react_ to update of objects of the given type.
* @tparam Clazz Type of element to _react_ to.
* @tparam Candidate Function to use to _react_ to the event.
* @param id Optional name used to map the storage within the registry.
* @return This mixin.
*/
template<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
basic_reactive_mixin &on_update(const id_type id = type_hash<Clazz>::value()) {
auto curr = owner_or_assert().template storage<Clazz>(id).on_update().template connect<Candidate>(*this);
conn.push_back(std::move(curr));
return *this;
}
/**
* @brief Makes storage _react_ to destruction of objects of the given type.
* @tparam Clazz Type of element to _react_ to.
* @tparam Candidate Function to use to _react_ to the event.
* @param id Optional name used to map the storage within the registry.
* @return This mixin.
*/
template<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
basic_reactive_mixin &on_destroy(const id_type id = type_hash<Clazz>::value()) {
auto curr = owner_or_assert().template storage<Clazz>(id).on_destroy().template connect<Candidate>(*this);
conn.push_back(std::move(curr));
return *this;
}
/**
* @brief Checks if a mixin refers to a valid registry.
* @return True if the mixin refers to a valid registry, false otherwise.
*/
[[nodiscard]] explicit operator bool() const noexcept {
return (owner != nullptr);
}
/**
* @brief Returns a pointer to the underlying registry, if any.
* @return A pointer to the underlying registry, if any.
*/
[[nodiscard]] const registry_type &registry() const noexcept {
return owner_or_assert();
}
/*! @copydoc registry */
[[nodiscard]] registry_type &registry() noexcept {
return owner_or_assert();
}
/**
* @brief Returns a view that is filtered by the underlying storage.
* @tparam Get Types of elements used to construct the view.
* @tparam Exclude Types of elements used to filter the view.
* @return A newly created view.
*/
template<typename... Get, typename... Exclude>
[[nodiscard]] basic_view<get_t<const basic_reactive_mixin, typename basic_registry_type::template storage_for_type<const Get>...>, exclude_t<typename basic_registry_type::template storage_for_type<const Exclude>...>>
view(exclude_t<Exclude...> = exclude_t{}) const {
const owner_type &parent = owner_or_assert();
basic_view<get_t<const basic_reactive_mixin, typename basic_registry_type::template storage_for_type<const Get>...>, exclude_t<typename basic_registry_type::template storage_for_type<const Exclude>...>> elem{};
[&elem](const auto *...curr) { ((curr ? elem.storage(*curr) : void()), ...); }(parent.template storage<std::remove_const_t<Exclude>>()..., parent.template storage<std::remove_const_t<Get>>()..., this);
return elem;
}
/*! @copydoc view */
template<typename... Get, typename... Exclude>
[[nodiscard]] basic_view<get_t<const basic_reactive_mixin, typename basic_registry_type::template storage_for_type<Get>...>, exclude_t<typename basic_registry_type::template storage_for_type<Exclude>...>>
view(exclude_t<Exclude...> = exclude_t{}) {
std::conditional_t<((std::is_const_v<Get> && ...) && (std::is_const_v<Exclude> && ...)), const owner_type, owner_type> &parent = owner_or_assert();
return {*this, parent.template storage<std::remove_const_t<Get>>()..., parent.template storage<std::remove_const_t<Exclude>>()...};
}
/*! @brief Releases all connections to the underlying registry, if any. */
void reset() {
for(auto &&curr: conn) {
curr.release();
}
conn.clear();
}
private:
basic_registry_type *owner;
container_type conn;
};
} // namespace entt
#endif

View File

@@ -1,436 +0,0 @@
#ifndef ENTT_ENTITY_OBSERVER_HPP
#define ENTT_ENTITY_OBSERVER_HPP
#include <cstddef>
#include <cstdint>
#include <limits>
#include <type_traits>
#include <utility>
#include "../core/type_traits.hpp"
#include "../signal/delegate.hpp"
#include "fwd.hpp"
#include "storage.hpp"
namespace entt {
/*! @brief Grouping matcher. */
template<typename...>
struct matcher {};
/**
* @brief Collector.
*
* Primary template isn't defined on purpose. All the specializations give a
* compile-time error, but for a few reasonable cases.
*/
template<typename...>
struct basic_collector;
/**
* @brief Collector.
*
* A collector contains a set of rules (literally, matchers) to use to track
* entities.<br/>
* Its main purpose is to generate a descriptor that allows an observer to know
* how to connect to a registry.
*/
template<>
struct basic_collector<> {
/**
* @brief Adds a grouping matcher to the collector.
* @tparam AllOf Types of components tracked by the matcher.
* @tparam NoneOf Types of components used to filter out entities.
* @return The updated collector.
*/
template<typename... AllOf, typename... NoneOf>
static constexpr auto group(exclude_t<NoneOf...> = exclude_t{}) noexcept {
return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>>{};
}
/**
* @brief Adds an observing matcher to the collector.
* @tparam AnyOf Type of component for which changes should be detected.
* @return The updated collector.
*/
template<typename AnyOf>
static constexpr auto update() noexcept {
return basic_collector<matcher<type_list<>, type_list<>, AnyOf>>{};
}
};
/**
* @brief Collector.
* @copydetails basic_collector<>
* @tparam Reject Untracked types used to filter out entities.
* @tparam Require Untracked types required by the matcher.
* @tparam Rule Specific details of the current matcher.
* @tparam Other Other matchers.
*/
template<typename... Reject, typename... Require, typename... Rule, typename... Other>
struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule...>, Other...> {
/*! @brief Current matcher. */
using current_type = matcher<type_list<Reject...>, type_list<Require...>, Rule...>;
/**
* @brief Adds a grouping matcher to the collector.
* @tparam AllOf Types of components tracked by the matcher.
* @tparam NoneOf Types of components used to filter out entities.
* @return The updated collector.
*/
template<typename... AllOf, typename... NoneOf>
static constexpr auto group(exclude_t<NoneOf...> = exclude_t{}) noexcept {
return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>, current_type, Other...>{};
}
/**
* @brief Adds an observing matcher to the collector.
* @tparam AnyOf Type of component for which changes should be detected.
* @return The updated collector.
*/
template<typename AnyOf>
static constexpr auto update() noexcept {
return basic_collector<matcher<type_list<>, type_list<>, AnyOf>, current_type, Other...>{};
}
/**
* @brief Updates the filter of the last added matcher.
* @tparam AllOf Types of components required by the matcher.
* @tparam NoneOf Types of components used to filter out entities.
* @return The updated collector.
*/
template<typename... AllOf, typename... NoneOf>
static constexpr auto where(exclude_t<NoneOf...> = exclude_t{}) 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.
*
* An observer returns all the entities and only the entities that fit the
* requirements of at least one matcher. Moreover, it's guaranteed that the
* entity list is tightly packed in memory for fast iterations.<br/>
* In general, observers don't stay true to the order of any set of components.
*
* Observers work mainly with two types of matchers, provided through a
* collector:
*
* * Observing matcher: an observer will return at least all the living entities
* for which one or more of the given components have been updated and not yet
* destroyed.
* * Grouping matcher: an observer will return at least all the living entities
* that would have entered the given group if it existed and that would have
* not yet left it.
*
* If an entity respects the requirements of multiple matchers, it will be
* returned once and only once by the observer in any case.
*
* Matchers support also filtering by means of a _where_ clause that accepts
* both a list of types and an exclusion list.<br/>
* Whenever a matcher finds that an entity matches its requirements, the
* condition of the filter is verified before to register the entity itself.
* Moreover, a registered entity isn't returned by the observer if the condition
* set by the filter is broken in the meantime.
*
* @b Important
*
* Iterators aren't invalidated if:
*
* * New instances of the given components are created and assigned to entities.
* * The entity currently pointed is modified (as an example, if one of the
* given components is removed from the entity to which the iterator points).
* * The entity currently pointed is destroyed.
*
* In all the other cases, modifying the pools of the given components in any
* way invalidates all the iterators.
*
* @warning
* Lifetime of an observer doesn't necessarily have to overcome that of the
* registry to which it is connected. However, the observer must be disconnected
* from the registry before being destroyed to avoid crashes due to dangling
* pointers.
*
* @tparam Registry Basic registry type.
* @tparam Mask Mask type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Registry, typename Mask, typename Allocator>
class basic_observer: private basic_storage<Mask, typename Registry::entity_type, Allocator> {
using base_type = basic_storage<Mask, typename Registry::entity_type, Allocator>;
template<typename>
struct matcher_handler;
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, Registry &reg, const typename Registry::entity_type entt) {
if(reg.template all_of<Require...>(entt) && !reg.template any_of<Reject...>(entt)) {
if(!obs.contains(entt)) {
obs.emplace(entt);
}
obs.get(entt) |= (1 << Index);
}
}
template<std::size_t Index>
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, 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, Registry &reg) {
(reg.template on_destroy<Require>().disconnect(&obs), ...);
(reg.template on_construct<Reject>().disconnect(&obs), ...);
reg.template on_update<AnyOf>().disconnect(&obs);
reg.template on_destroy<AnyOf>().disconnect(&obs);
}
};
template<typename... Reject, typename... Require, typename... NoneOf, typename... AllOf>
struct matcher_handler<matcher<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, 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(condition()) {
if(!obs.contains(entt)) {
obs.emplace(entt);
}
obs.get(entt) |= (1 << Index);
}
}
template<std::size_t Index>
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, 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), ...);
(reg.template on_destroy<NoneOf>().template connect<&maybe_valid_if<Index, NoneOf>>(obs), ...);
(reg.template on_destroy<AllOf>().template connect<&discard_if<Index>>(obs), ...);
(reg.template on_construct<NoneOf>().template connect<&discard_if<Index>>(obs), ...);
}
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), ...);
(reg.template on_destroy<NoneOf>().disconnect(&obs), ...);
(reg.template on_destroy<AllOf>().disconnect(&obs), ...);
(reg.template on_construct<NoneOf>().disconnect(&obs), ...);
}
};
template<typename... Matcher>
static void disconnect(Registry &reg, basic_observer &obs) {
(matcher_handler<Matcher>::disconnect(obs, reg), ...);
}
template<typename... Matcher, std::size_t... Index>
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 = typename registry_type::entity_type;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Random access iterator type. */
using iterator = typename registry_type::common_type::iterator;
/*! @brief Default constructor. */
basic_observer()
: basic_observer{allocator_type{}} {}
/**
* @brief Constructs an empty storage with a given allocator.
* @param allocator The allocator to use.
*/
explicit basic_observer(const allocator_type &allocator)
: base_type{allocator},
release{} {}
/*! @brief Default copy constructor, deleted on purpose. */
basic_observer(const basic_observer &) = delete;
/*! @brief Default move constructor, deleted on purpose. */
basic_observer(basic_observer &&) = delete;
/**
* @brief Creates an observer and connects it to a given registry.
* @tparam Matcher Types of matchers to use to initialize the observer.
* @param reg A valid reference to a registry.
* @param allocator The allocator to use.
*/
template<typename... Matcher>
basic_observer(registry_type &reg, basic_collector<Matcher...>, const allocator_type &allocator = allocator_type{})
: basic_observer{allocator} {
connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{});
}
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This observer.
*/
basic_observer &operator=(const basic_observer &) = delete;
/**
* @brief Default move assignment operator, deleted on purpose.
* @return This observer.
*/
basic_observer &operator=(basic_observer &&) = delete;
/**
* @brief Connects an observer to a given registry.
* @tparam Matcher Types of matchers to use to initialize the observer.
* @param reg A valid reference to a registry.
*/
template<typename... Matcher>
void connect(registry_type &reg, basic_collector<Matcher...>) {
disconnect();
connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{});
base_type::clear();
}
/*! @brief Disconnects an observer from the registry it keeps track of. */
void disconnect() {
if(release) {
release(*this);
release.reset();
}
}
/**
* @brief Returns the number of elements in an observer.
* @return Number of elements.
*/
[[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 noexcept {
return base_type::empty();
}
/**
* @brief Direct access to the list of entities of the observer.
*
* The returned pointer is such that range `[data(), data() + size())` is
* always a valid range, even if the container is empty.
*
* @note
* Entities are in the reverse order as returned by the `begin`/`end`
* iterators.
*
* @return A pointer to the array of entities.
*/
[[nodiscard]] const entity_type *data() const noexcept {
return base_type::data();
}
/**
* @brief Returns an iterator to the first entity of the observer.
*
* If the observer is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first entity of the observer.
*/
[[nodiscard]] iterator begin() const noexcept {
return base_type::base_type::begin();
}
/**
* @brief Returns an iterator that is past the last entity of the observer.
* @return An iterator to the entity following the last entity of the
* observer.
*/
[[nodiscard]] iterator end() const noexcept {
return base_type::base_type::end();
}
/*! @brief Clears the underlying container. */
void clear() noexcept {
base_type::clear();
}
/**
* @brief Iterates entities and applies the given function object to them.
*
* The function object is invoked for each entity.<br/>
* The signature of the function must be equivalent to the following form:
*
* @code{.cpp}
* void(const entity_type);
* @endcode
*
* @tparam Func Type of the function object to invoke.
* @param func A valid function object.
*/
template<typename Func>
void each(Func func) const {
for(const auto entity: *this) {
func(entity);
}
}
/**
* @brief Iterates entities and applies the given function object to them,
* then clears the observer.
*
* @sa each
*
* @tparam Func Type of the function object to invoke.
* @param func A valid function object.
*/
template<typename Func>
void each(Func func) {
std::as_const(*this).each(std::move(func));
clear();
}
private:
delegate<void(basic_observer &)> release;
};
} // namespace entt
#endif

View File

@@ -27,6 +27,15 @@ struct is_view<basic_view<Args...>>: std::true_type {};
template<typename Type>
inline constexpr bool is_view_v = is_view<Type>::value;
template<typename>
struct is_group: std::false_type {};
template<typename... Args>
struct is_group<basic_group<Args...>>: std::true_type {};
template<typename Type>
inline constexpr bool is_group_v = is_group<Type>::value;
template<typename Type, typename Override>
struct unpack_type {
using ro = std::conditional_t<
@@ -52,35 +61,46 @@ struct unpack_type<const basic_registry<Args...>, type_list<Override...>>
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...>;
using ro = type_list_cat_t<type_list<typename Exclude::element_type...>, typename unpack_type<constness_as_t<typename Get::element_type, Get>, type_list<Override...>>::ro...>;
using rw = type_list_cat_t<typename unpack_type<constness_as_t<typename Get::element_type, Get>, type_list<Override...>>::rw...>;
};
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>
template<typename... Owned, typename... Get, typename... Exclude, typename... Override>
struct unpack_type<basic_group<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>> {
using ro = type_list_cat_t<type_list<typename Exclude::element_type...>, typename unpack_type<constness_as_t<typename Get::element_type, Get>, type_list<Override...>>::ro..., typename unpack_type<constness_as_t<typename Owned::element_type, Owned>, type_list<Override...>>::ro...>;
using rw = type_list_cat_t<typename unpack_type<constness_as_t<typename Get::element_type, Get>, type_list<Override...>>::rw..., typename unpack_type<constness_as_t<typename Owned::element_type, Owned>, type_list<Override...>>::rw...>;
};
template<typename... Owned, typename... Get, typename... Exclude, typename... Override>
struct unpack_type<const basic_group<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>>
: unpack_type<basic_group<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>> {};
template<typename, typename, typename>
struct resource_traits;
template<typename... Args, typename... Req>
struct resource_traits<type_list<Args...>, type_list<Req...>> {
template<typename Registry, typename... Args, typename... Req>
struct resource_traits<Registry, 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...>;
static constexpr auto sync_point = (std::is_same_v<Args, Registry> || ...);
};
template<typename... Req, typename Ret, typename... Args>
resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> free_function_to_resource_traits(Ret (*)(Args...));
template<typename Registry, typename... Req, typename Ret, typename... Args>
resource_traits<Registry, 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_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (*)(Type &, Args...));
template<typename Registry, typename... Req, typename Ret, typename Type, typename... Args>
resource_traits<Registry, 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_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...));
template<typename Registry, typename... Req, typename Ret, typename Class, typename... Args>
resource_traits<Registry, 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_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...) const);
template<typename Registry, typename... Req, typename Ret, typename Class, typename... Args>
resource_traits<Registry, type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...) const);
} // namespace internal
/*! @endcond */
@@ -108,7 +128,7 @@ class basic_organizer final {
const char *name{};
const void *payload{};
callback_type *callback{};
dependency_type *dependency;
dependency_type *dependency{};
prepare_type *prepare{};
const type_info *info{};
};
@@ -119,6 +139,8 @@ class basic_organizer final {
return reg;
} else if constexpr(internal::is_view_v<Type>) {
return static_cast<Type>(as_view{reg});
} else if constexpr(internal::is_group_v<Type>) {
return static_cast<Type>(as_group{reg});
} else {
return reg.ctx().template emplace<std::remove_reference_t<Type>>();
}
@@ -130,14 +152,16 @@ class basic_organizer final {
}
template<typename... Type>
static std::size_t fill_dependencies(type_list<Type...>, [[maybe_unused]] const type_info **buffer, [[maybe_unused]] const std::size_t count) {
[[nodiscard]] 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 {
const type_info *info[sizeof...(Type)]{&type_id<Type>()...};
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
const type_info *info[]{&type_id<Type>()...};
const auto length = count < sizeof...(Type) ? count : sizeof...(Type);
for(std::size_t pos{}; pos < length; ++pos) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
buffer[pos] = info[pos];
}
@@ -146,9 +170,9 @@ class basic_organizer final {
}
template<typename... RO, typename... RW>
void track_dependencies(std::size_t index, const bool requires_registry, type_list<RO...>, type_list<RW...>) {
void track_dependencies(std::size_t index, const bool sync_point, type_list<RO...>, type_list<RW...>) {
builder.bind(static_cast<id_type>(index));
builder.set(type_hash<Registry>::value(), requires_registry || (sizeof...(RO) + sizeof...(RW) == 0u));
builder.set(type_hash<Registry>::value(), sync_point || (sizeof...(RO) + sizeof...(RW) == 0u));
(builder.ro(type_hash<RO>::value()), ...);
(builder.rw(type_hash<RW>::value()), ...);
}
@@ -167,14 +191,14 @@ public:
struct vertex {
/**
* @brief Constructs a vertex of the task graph.
* @param vtype True if the vertex is a top-level one, false otherwise.
* @param data The data associated with the vertex.
* @param edges The indices of the children in the adjacency list.
* @param from List of in-edges of the vertex.
* @param to List of out-edges of the vertex.
*/
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)} {}
vertex(vertex_data data, std::vector<std::size_t> from, std::vector<std::size_t> to)
: node{std::move(data)},
in{std::move(from)},
out{std::move(to)} {}
/**
* @brief Fills a buffer with the type info objects for the writable
@@ -183,7 +207,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(const type_info **buffer, const std::size_t length) const noexcept {
[[nodiscard]] size_type ro_dependency(const type_info **buffer, const std::size_t length) const noexcept {
return node.dependency(false, buffer, length);
}
@@ -194,7 +218,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(const type_info **buffer, const std::size_t length) const noexcept {
[[nodiscard]] size_type rw_dependency(const type_info **buffer, const std::size_t length) const noexcept {
return node.dependency(true, buffer, length);
}
@@ -202,7 +226,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 noexcept {
[[nodiscard]] size_type ro_count() const noexcept {
return node.ro_count;
}
@@ -210,7 +234,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 noexcept {
[[nodiscard]] size_type rw_count() const noexcept {
return node.rw_count;
}
@@ -218,15 +242,15 @@ 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 noexcept {
return is_top_level;
[[nodiscard]] bool top_level() const noexcept {
return in.empty();
}
/**
* @brief Returns a type info object associated with a vertex.
* @return A properly initialized type info object.
*/
const type_info &info() const noexcept {
[[nodiscard]] const type_info &info() const noexcept {
return *node.info;
}
@@ -234,7 +258,7 @@ public:
* @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 noexcept {
[[nodiscard]] const char *name() const noexcept {
return node.name;
}
@@ -242,7 +266,7 @@ public:
* @brief Returns the function associated with a vertex.
* @return The function associated with the vertex.
*/
function_type *callback() const noexcept {
[[nodiscard]] function_type *callback() const noexcept {
return node.callback;
}
@@ -250,16 +274,24 @@ public:
* @brief Returns the payload associated with a vertex, if any.
* @return The payload associated with the vertex, if any.
*/
const void *data() const noexcept {
[[nodiscard]] const void *data() const noexcept {
return node.payload;
}
/**
* @brief Returns the list of nodes reachable from a given vertex.
* @return The list of nodes reachable from the vertex.
* @brief Returns the list of in-edges of a vertex.
* @return The list of in-edges of a vertex.
*/
const std::vector<std::size_t> &children() const noexcept {
return reachable;
[[nodiscard]] const std::vector<std::size_t> &in_edges() const noexcept {
return in;
}
/**
* @brief Returns the list of out-edges of a vertex.
* @return The list of out-edges of a vertex.
*/
[[nodiscard]] const std::vector<std::size_t> &out_edges() const noexcept {
return out;
}
/**
@@ -272,9 +304,9 @@ public:
}
private:
bool is_top_level;
vertex_data node;
std::vector<std::size_t> reachable;
std::vector<std::size_t> in;
std::vector<std::size_t> out;
};
/**
@@ -285,8 +317,7 @@ public:
*/
template<auto Candidate, typename... Req>
void emplace(const char *name = nullptr) {
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>;
using resource_type = decltype(internal::free_function_to_resource_traits<registry_type, Req...>(Candidate));
callback_type *callback = +[](const void *, registry_type &reg) {
std::apply(Candidate, to_args(reg, typename resource_type::args{}));
@@ -302,7 +333,7 @@ public:
+[](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{});
track_dependencies(vertices.size(), resource_type::sync_point, typename resource_type::ro{}, typename resource_type::rw{});
vertices.push_back(std::move(vdata));
}
@@ -317,8 +348,7 @@ public:
*/
template<auto Candidate, typename... Req, typename Type>
void emplace(Type &value_or_instance, const char *name = nullptr) {
using resource_type = decltype(internal::constrained_function_to_resource_traits<Req...>(Candidate));
constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, registry_type>;
using resource_type = decltype(internal::constrained_function_to_resource_traits<registry_type, Req...>(Candidate));
callback_type *callback = +[](const void *payload, registry_type &reg) {
Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
@@ -335,7 +365,7 @@ public:
+[](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{});
track_dependencies(vertices.size(), resource_type::sync_point, typename resource_type::ro{}, typename resource_type::rw{});
vertices.push_back(std::move(vdata));
}
@@ -349,7 +379,7 @@ public:
*/
template<typename... Req>
void emplace(function_type *func, const void *payload = nullptr, const char *name = nullptr) {
using resource_type = internal::resource_traits<type_list<>, type_list<Req...>>;
using resource_type = internal::resource_traits<registry_type, type_list<>, type_list<Req...>>;
track_dependencies(vertices.size(), true, typename resource_type::ro{}, typename resource_type::rw{});
vertex_data vdata{
@@ -369,20 +399,24 @@ public:
* @brief Generates a task graph for the current content.
* @return The adjacency list of the task graph.
*/
std::vector<vertex> graph() {
[[nodiscard]] std::vector<vertex> graph() const {
std::vector<vertex> adjacency_list{};
adjacency_list.reserve(vertices.size());
auto adjacency_matrix = builder.graph();
for(auto curr: adjacency_matrix.vertices()) {
const auto iterable = adjacency_matrix.in_edges(curr);
std::vector<std::size_t> reachable{};
std::vector<std::size_t> in{};
std::vector<std::size_t> out{};
for(auto &&edge: adjacency_matrix.out_edges(curr)) {
reachable.push_back(edge.second);
for(auto &&edge: adjacency_matrix.in_edges(curr)) {
in.push_back(edge.first);
}
adjacency_list.emplace_back(iterable.cbegin() == iterable.cend(), vertices[curr], std::move(reachable));
for(auto &&edge: adjacency_matrix.out_edges(curr)) {
out.push_back(edge.second);
}
adjacency_list.emplace_back(vertices[curr], std::move(in), std::move(out));
}
return adjacency_list;

View File

@@ -0,0 +1,26 @@
#ifndef ENTT_ENTITY_RANGES_HPP
#define ENTT_ENTITY_RANGES_HPP
#if __has_include(<version>)
# include <version>
#
# if defined(__cpp_lib_ranges)
# include <ranges>
# include "fwd.hpp"
template<class... Args>
inline constexpr bool std::ranges::enable_borrowed_range<entt::basic_view<Args...>>{true};
template<class... Args>
inline constexpr bool std::ranges::enable_borrowed_range<entt::basic_group<Args...>>{true};
template<class... Args>
inline constexpr bool std::ranges::enable_view<entt::basic_view<Args...>>{true};
template<class... Args>
inline constexpr bool std::ranges::enable_view<entt::basic_group<Args...>>{true};
# endif
#endif
#endif

View File

@@ -2,6 +2,7 @@
#define ENTT_ENTITY_REGISTRY_HPP
#include <algorithm>
#include <array>
#include <cstddef>
#include <functional>
#include <iterator>
@@ -63,7 +64,7 @@ public:
}
constexpr registry_storage_iterator operator++(int) noexcept {
registry_storage_iterator orig = *this;
const registry_storage_iterator orig = *this;
return ++(*this), orig;
}
@@ -72,7 +73,7 @@ public:
}
constexpr registry_storage_iterator operator--(int) noexcept {
registry_storage_iterator orig = *this;
const registry_storage_iterator orig = *this;
return operator--(), orig;
}
@@ -99,7 +100,7 @@ public:
}
[[nodiscard]] constexpr reference operator*() const noexcept {
return {it->first, *it->second};
return operator[](0);
}
[[nodiscard]] constexpr pointer operator->() const noexcept {
@@ -163,6 +164,10 @@ public:
explicit registry_context(const allocator_type &allocator)
: ctx{allocator} {}
void clear() noexcept {
ctx.clear();
}
template<typename Type, typename... Args>
Type &emplace_as(const id_type id, Args &&...args) {
return any_cast<Type &>(ctx.try_emplace(id, std::in_place_type<Type>, std::forward<Args>(args)...).first->second);
@@ -175,7 +180,7 @@ public:
template<typename Type>
Type &insert_or_assign(const id_type id, Type &&value) {
return any_cast<std::remove_cv_t<std::remove_reference_t<Type>> &>(ctx.insert_or_assign(id, std::forward<Type>(value)).first->second);
return any_cast<std::remove_const_t<std::remove_reference_t<Type>> &>(ctx.insert_or_assign(id, std::forward<Type>(value)).first->second);
}
template<typename Type>
@@ -186,7 +191,7 @@ public:
template<typename Type>
bool erase(const id_type id = type_id<Type>().hash()) {
const auto it = ctx.find(id);
return it != ctx.end() && it->second.type() == type_id<Type>() ? (ctx.erase(it), true) : false;
return it != ctx.end() && it->second.info() == type_id<Type>() ? (ctx.erase(it), true) : false;
}
template<typename Type>
@@ -214,11 +219,11 @@ public:
template<typename Type>
[[nodiscard]] bool contains(const id_type id = type_id<Type>().hash()) const {
const auto it = ctx.find(id);
return it != ctx.cend() && it->second.type() == type_id<Type>();
return it != ctx.cend() && it->second.info() == type_id<Type>();
}
private:
dense_map<id_type, basic_any<0u>, identity, std::equal_to<id_type>, allocator_type> ctx;
dense_map<id_type, basic_any<0u>, identity, std::equal_to<>, allocator_type> ctx;
};
} // namespace internal
@@ -232,39 +237,42 @@ private:
template<typename Entity, typename Allocator>
class basic_registry {
using base_type = basic_sparse_set<Entity, Allocator>;
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, Entity>, "Invalid value type");
// std::shared_ptr because of its type erased allocator which is useful here
using pool_container_type = dense_map<id_type, std::shared_ptr<base_type>, identity, std::equal_to<id_type>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, std::shared_ptr<base_type>>>>;
using group_container_type = dense_map<id_type, std::shared_ptr<internal::group_descriptor>, identity, std::equal_to<id_type>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, std::shared_ptr<internal::group_descriptor>>>>;
using pool_container_type = dense_map<id_type, std::shared_ptr<base_type>, identity, std::equal_to<>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, std::shared_ptr<base_type>>>>;
using group_container_type = dense_map<id_type, std::shared_ptr<internal::group_descriptor>, identity, std::equal_to<>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, std::shared_ptr<internal::group_descriptor>>>>;
using traits_type = entt_traits<Entity>;
template<typename Type>
[[nodiscard]] auto &assure([[maybe_unused]] const id_type id = type_hash<Type>::value()) {
static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Non-decayed types not allowed");
if constexpr(std::is_same_v<Type, entity_type>) {
ENTT_ASSERT(id == type_hash<Type>::value(), "User entity storage not allowed");
return entities;
} else {
auto &cpool = pools[id];
using storage_type = storage_for_type<Type>;
if(!cpool) {
using storage_type = storage_for_type<Type>;
using alloc_type = typename storage_type::allocator_type;
if constexpr(std::is_void_v<Type> && !std::is_constructible_v<alloc_type, allocator_type>) {
// std::allocator<void> has no cross constructors (waiting for C++20)
cpool = std::allocate_shared<storage_type>(get_allocator(), alloc_type{});
} else {
cpool = std::allocate_shared<storage_type>(get_allocator(), get_allocator());
}
cpool->bind(forward_as_any(*this));
if(auto it = pools.find(id); it != pools.cend()) {
ENTT_ASSERT(it->second->info() == type_id<Type>(), "Unexpected type");
return static_cast<storage_type &>(*it->second);
}
ENTT_ASSERT(cpool->type() == type_id<Type>(), "Unexpected type");
return static_cast<storage_for_type<Type> &>(*cpool);
using alloc_type = typename storage_type::allocator_type;
typename pool_container_type::mapped_type cpool{};
if constexpr(std::is_void_v<Type> && !std::is_constructible_v<alloc_type, allocator_type>) {
// std::allocator<void> has no cross constructors (waiting for C++20)
cpool = std::allocate_shared<storage_type>(get_allocator(), alloc_type{});
} else {
cpool = std::allocate_shared<storage_type>(get_allocator(), get_allocator());
}
pools.emplace(id, cpool);
cpool->bind(*this);
return static_cast<storage_type &>(*cpool);
}
}
@@ -273,10 +281,11 @@ class basic_registry {
static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Non-decayed types not allowed");
if constexpr(std::is_same_v<Type, entity_type>) {
ENTT_ASSERT(id == type_hash<Type>::value(), "User entity storage not allowed");
return &entities;
} else {
if(const auto it = pools.find(id); it != pools.cend()) {
ENTT_ASSERT(it->second->type() == type_id<Type>(), "Unexpected type");
ENTT_ASSERT(it->second->info() == type_id<Type>(), "Unexpected type");
return static_cast<const storage_for_type<Type> *>(it->second.get());
}
@@ -285,16 +294,14 @@ class basic_registry {
}
void rebind() {
entities.bind(forward_as_any(*this));
entities.bind(*this);
for(auto &&curr: pools) {
curr.second->bind(forward_as_any(*this));
curr.second->bind(*this);
}
}
public:
/*! @brief Entity traits. */
using traits_type = typename base_type::traits_type;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Underlying entity identifier. */
@@ -344,6 +351,9 @@ public:
rebind();
}
/*! @brief Default copy constructor, deleted on purpose. */
basic_registry(const basic_registry &) = delete;
/**
* @brief Move constructor.
* @param other The instance to move from.
@@ -356,19 +366,22 @@ public:
rebind();
}
/*! @brief Default destructor. */
~basic_registry() = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This mixin.
*/
basic_registry &operator=(const basic_registry &) = delete;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This registry.
*/
basic_registry &operator=(basic_registry &&other) noexcept {
vars = std::move(other.vars);
pools = std::move(other.pools);
groups = std::move(other.groups);
entities = std::move(other.entities);
rebind();
swap(other);
return *this;
}
@@ -376,7 +389,7 @@ public:
* @brief Exchanges the contents with those of a given registry.
* @param other Registry to exchange the content with.
*/
void swap(basic_registry &other) {
void swap(basic_registry &other) noexcept {
using std::swap;
swap(vars, other.vars);
@@ -405,12 +418,12 @@ public:
* @return An iterable object to use to _visit_ the registry.
*/
[[nodiscard]] iterable storage() noexcept {
return iterable_adaptor{internal::registry_storage_iterator{pools.begin()}, internal::registry_storage_iterator{pools.end()}};
return iterable{pools.begin(), pools.end()};
}
/*! @copydoc storage */
[[nodiscard]] const_iterable storage() const noexcept {
return iterable_adaptor{internal::registry_storage_iterator{pools.cbegin()}, internal::registry_storage_iterator{pools.cend()}};
return const_iterable{pools.cbegin(), pools.cend()};
}
/**
@@ -433,25 +446,35 @@ public:
}
/**
* @brief Returns the storage for a given component type.
* @tparam Type Type of component of which to return the storage.
* @brief Returns the storage for a given element type.
* @tparam Type Type of element of which to return the storage.
* @param id Optional name used to map the storage within the registry.
* @return The storage for the given component type.
* @return The storage for the given element type.
*/
template<typename Type>
storage_for_type<Type> &storage(const id_type id = type_hash<Type>::value()) {
return assure<Type>(id);
return assure<std::remove_const_t<Type>>(id);
}
/**
* @brief Returns the storage for a given component type, if any.
* @tparam Type Type of component of which to return the storage.
* @brief Returns the storage for a given element type, if any.
* @tparam Type Type of element of which to return the storage.
* @param id Optional name used to map the storage within the registry.
* @return The storage for the given component type.
* @return The storage for the given element type.
*/
template<typename Type>
const storage_for_type<Type> *storage(const id_type id = type_hash<Type>::value()) const {
return assure<Type>(id);
[[nodiscard]] const storage_for_type<Type> *storage(const id_type id = type_hash<Type>::value()) const {
return assure<std::remove_const_t<Type>>(id);
}
/**
* @brief Discards the storage associated with a given name, if any.
* @param id Name used to map the storage within the registry.
* @return True in case of success, false otherwise.
*/
bool reset(const id_type id) {
ENTT_ASSERT(id != type_hash<entity_type>::value(), "Cannot reset entity storage");
return !(pools.erase(id) == 0u);
}
/**
@@ -460,7 +483,7 @@ public:
* @return True if the identifier is valid, false otherwise.
*/
[[nodiscard]] bool valid(const entity_type entt) const {
return entities.contains(entt) && (entities.index(entt) < entities.free_list());
return static_cast<size_type>(entities.find(entt).index()) < entities.free_list();
}
/**
@@ -478,7 +501,7 @@ public:
* @return A valid identifier.
*/
[[nodiscard]] entity_type create() {
return entities.emplace();
return entities.generate();
}
/**
@@ -491,7 +514,7 @@ public:
* @return A valid identifier.
*/
[[nodiscard]] entity_type create(const entity_type hint) {
return entities.emplace(hint);
return entities.generate(hint);
}
/**
@@ -505,22 +528,22 @@ public:
*/
template<typename It>
void create(It first, It last) {
entities.insert(std::move(first), std::move(last));
entities.generate(std::move(first), std::move(last));
}
/**
* @brief Destroys an entity and releases its identifier.
*
* @warning
* Adding or removing components to an entity that is being destroyed can
* Adding or removing elements to an entity that is being destroyed can
* result in undefined behavior.
*
* @param entt A valid identifier.
* @return The version of the recycled entity.
*/
version_type destroy(const entity_type entt) {
for(size_type pos = pools.size(); pos; --pos) {
pools.begin()[pos - 1u].second->remove(entt);
for(size_type pos = pools.size(); pos != 0u; --pos) {
pools.begin()[static_cast<typename pool_container_type::difference_type>(pos - 1u)].second->remove(entt);
}
entities.erase(entt);
@@ -556,10 +579,8 @@ public:
*/
template<typename It>
void destroy(It first, It last) {
entities.sort_as(first, last);
const auto from = entities.cbegin(0);
const auto to = from + std::distance(first, last);
const auto to = entities.sort_as(first, last);
const auto from = entities.cend() - static_cast<typename common_type::difference_type>(entities.free_list());
for(auto &&curr: pools) {
curr.second->remove(from, to);
@@ -569,81 +590,98 @@ public:
}
/**
* @brief Assigns the given component to an entity.
* @brief Assigns the given element to an entity.
*
* The component must have a proper constructor or be of aggregate type.
* The element must have a proper constructor or be of aggregate type.
*
* @warning
* Attempting to assign a component to an entity that already owns it
* results in undefined behavior.
* Attempting to assign an element to an entity that already owns it results
* in undefined behavior.
*
* @tparam Type Type of component to create.
* @tparam Args Types of arguments to use to construct the component.
* @tparam Type Type of element to create.
* @tparam Args Types of arguments to use to construct the element.
* @param entt A valid identifier.
* @param args Parameters to use to initialize the component.
* @return A reference to the newly created component.
* @param args Parameters to use to initialize the element.
* @return A reference to the newly created element.
*/
template<typename Type, typename... Args>
decltype(auto) emplace(const entity_type entt, Args &&...args) {
ENTT_ASSERT(valid(entt), "Invalid entity");
return assure<Type>().emplace(entt, std::forward<Args>(args)...);
}
/**
* @brief Assigns each entity in a range the given component.
* @brief Assigns each entity in a range the given element.
*
* @sa emplace
*
* @tparam Type Type of component to create.
* @tparam Type Type of element to create.
* @tparam It Type of input iterator.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @param value An instance of the component to assign.
*/
template<typename Type, typename It>
void insert(It first, It last, const Type &value = {}) {
void insert(It first, It last) {
ENTT_ASSERT(std::all_of(first, last, [this](const auto entt) { return valid(entt); }), "Invalid entity");
assure<Type>().insert(std::move(first), std::move(last));
}
/**
* @brief Assigns each entity in a range the given element.
*
* @sa emplace
*
* @tparam Type Type of element to create.
* @tparam It Type of input iterator.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @param value An instance of the element to assign.
*/
template<typename Type, typename It>
void insert(It first, It last, const Type &value) {
ENTT_ASSERT(std::all_of(first, last, [this](const auto entt) { return valid(entt); }), "Invalid entity");
assure<Type>().insert(std::move(first), std::move(last), value);
}
/**
* @brief Assigns each entity in a range the given components.
* @brief Assigns each entity in a range the given elements.
*
* @sa emplace
*
* @tparam Type Type of component to create.
* @tparam Type Type of element to create.
* @tparam EIt Type of input iterator.
* @tparam CIt Type of input iterator.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @param from An iterator to the first element of the range of components.
* @param from An iterator to the first element of the range of elements.
*/
template<typename Type, typename EIt, typename CIt, typename = std::enable_if_t<std::is_same_v<typename std::iterator_traits<CIt>::value_type, Type>>>
void insert(EIt first, EIt last, CIt from) {
ENTT_ASSERT(std::all_of(first, last, [this](const auto entt) { return valid(entt); }), "Invalid entity");
assure<Type>().insert(first, last, from);
}
/**
* @brief Assigns or replaces the given component for an entity.
* @brief Assigns or replaces the given element for an entity.
*
* @sa emplace
* @sa replace
*
* @tparam Type Type of component to assign or replace.
* @tparam Args Types of arguments to use to construct the component.
* @tparam Type Type of element to assign or replace.
* @tparam Args Types of arguments to use to construct the element.
* @param entt A valid identifier.
* @param args Parameters to use to initialize the component.
* @return A reference to the newly created component.
* @param args Parameters to use to initialize the element.
* @return A reference to the newly created element.
*/
template<typename Type, typename... Args>
decltype(auto) emplace_or_replace(const entity_type entt, Args &&...args) {
if(auto &cpool = assure<Type>(); cpool.contains(entt)) {
return cpool.patch(entt, [&args...](auto &...curr) { ((curr = Type{std::forward<Args>(args)...}), ...); });
} else {
return cpool.emplace(entt, std::forward<Args>(args)...);
}
auto &cpool = assure<Type>();
ENTT_ASSERT(valid(entt), "Invalid entity");
return cpool.contains(entt) ? cpool.patch(entt, [&args...](auto &...curr) { ((curr = Type{std::forward<Args>(args)...}), ...); }) : cpool.emplace(entt, std::forward<Args>(args)...);
}
/**
* @brief Patches the given component for an entity.
* @brief Patches the given element for an entity.
*
* The signature of the function should be equivalent to the following:
*
@@ -652,14 +690,14 @@ public:
* @endcode
*
* @warning
* Attempting to patch a component of an entity that doesn't own it
* results in undefined behavior.
* Attempting to patch an element of an entity that doesn't own it results
* in undefined behavior.
*
* @tparam Type Type of component to patch.
* @tparam Type Type of element to patch.
* @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 component.
* @return A reference to the patched element.
*/
template<typename Type, typename... Func>
decltype(auto) patch(const entity_type entt, Func &&...func) {
@@ -667,19 +705,19 @@ public:
}
/**
* @brief Replaces the given component for an entity.
* @brief Replaces the given element for an entity.
*
* The component must have a proper constructor or be of aggregate type.
* The element must have a proper constructor or be of aggregate type.
*
* @warning
* Attempting to replace a component of an entity that doesn't own it
* results in undefined behavior.
* Attempting to replace an element of an entity that doesn't own it results
* in undefined behavior.
*
* @tparam Type Type of component to replace.
* @tparam Args Types of arguments to use to construct the component.
* @tparam Type Type of element to replace.
* @tparam Args Types of arguments to use to construct the element.
* @param entt A valid identifier.
* @param args Parameters to use to initialize the component.
* @return A reference to the component being replaced.
* @param args Parameters to use to initialize the element.
* @return A reference to the element being replaced.
*/
template<typename Type, typename... Args>
decltype(auto) replace(const entity_type entt, Args &&...args) {
@@ -687,11 +725,11 @@ public:
}
/**
* @brief Removes the given components from an entity.
* @tparam Type Type of component to remove.
* @tparam Other Other types of components to remove.
* @brief Removes the given elements from an entity.
* @tparam Type Type of element to remove.
* @tparam Other Other types of elements to remove.
* @param entt A valid identifier.
* @return The number of components actually removed.
* @return The number of elements actually removed.
*/
template<typename Type, typename... Other>
size_type remove(const entity_type entt) {
@@ -699,33 +737,34 @@ public:
}
/**
* @brief Removes the given components from all the entities in a range.
* @brief Removes the given elements from all the entities in a range.
*
* @sa remove
*
* @tparam Type Type of component to remove.
* @tparam Other Other types of components to remove.
* @tparam Type Type of element to remove.
* @tparam Other Other types of elements to remove.
* @tparam It Type of input iterator.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @return The number of components actually removed.
* @return The number of elements actually removed.
*/
template<typename Type, typename... Other, typename It>
size_type remove(It first, It last) {
size_type count{};
if constexpr(std::is_same_v<It, typename common_type::iterator>) {
common_type *cpools[sizeof...(Other) + 1u]{&assure<Type>(), &assure<Other>()...};
std::array cpools{static_cast<common_type *>(&assure<Type>()), static_cast<common_type *>(&assure<Other>())...};
for(size_type pos{}, len = sizeof...(Other) + 1u; pos < len; ++pos) {
for(auto from = cpools.begin(), to = cpools.end(); from != to; ++from) {
if constexpr(sizeof...(Other) != 0u) {
if(cpools[pos]->data() == first.data()) {
std::swap(cpools[pos], cpools[sizeof...(Other)]);
if((*from)->data() == first.data()) {
std::swap((*from), cpools.back());
}
}
count += cpools[pos]->remove(first, last);
count += (*from)->remove(first, last);
}
} else {
for(auto cpools = std::forward_as_tuple(assure<Type>(), assure<Other>()...); first != last; ++first) {
count += std::apply([entt = *first](auto &...curr) { return (curr.remove(entt) + ... + 0u); }, cpools);
@@ -736,14 +775,14 @@ public:
}
/**
* @brief Erases the given components from an entity.
* @brief Erases the given elements from an entity.
*
* @warning
* Attempting to erase a component from an entity that doesn't own it
* results in undefined behavior.
* Attempting to erase an element from an entity that doesn't own it results
* in undefined behavior.
*
* @tparam Type Types of components to erase.
* @tparam Other Other types of components to erase.
* @tparam Type Types of elements to erase.
* @tparam Other Other types of elements to erase.
* @param entt A valid identifier.
*/
template<typename Type, typename... Other>
@@ -752,12 +791,12 @@ public:
}
/**
* @brief Erases the given components from all the entities in a range.
* @brief Erases the given elements from all the entities in a range.
*
* @sa erase
*
* @tparam Type Types of components to erase.
* @tparam Other Other types of components to erase.
* @tparam Type Types of elements to erase.
* @tparam Other Other types of elements to erase.
* @tparam It Type of input iterator.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
@@ -765,16 +804,16 @@ public:
template<typename Type, typename... Other, typename It>
void erase(It first, It last) {
if constexpr(std::is_same_v<It, typename common_type::iterator>) {
common_type *cpools[sizeof...(Other) + 1u]{&assure<Type>(), &assure<Other>()...};
std::array cpools{static_cast<common_type *>(&assure<Type>()), static_cast<common_type *>(&assure<Other>())...};
for(size_type pos{}, len = sizeof...(Other) + 1u; pos < len; ++pos) {
for(auto from = cpools.begin(), to = cpools.end(); from != to; ++from) {
if constexpr(sizeof...(Other) != 0u) {
if(cpools[pos]->data() == first.data()) {
std::swap(cpools[pos], cpools[sizeof...(Other)]);
if((*from)->data() == first.data()) {
std::swap(*from, cpools.back());
}
}
cpools[pos]->erase(first, last);
(*from)->erase(first, last);
}
} else {
for(auto cpools = std::forward_as_tuple(assure<Type>(), assure<Other>()...); first != last; ++first) {
@@ -784,12 +823,12 @@ public:
}
/**
* @brief Erases components satisfying specific criteria from an entity.
* @brief Erases elements satisfying specific criteria from an entity.
*
* The function type is equivalent to:
*
* @code{.cpp}
* void(const id_type, typename basic_registry<Entity>::base_type &);
* void(const id_type, typename basic_registry<Entity>::common_type &);
* @endcode
*
* Only storage where the entity exists are passed to the function.
@@ -809,8 +848,8 @@ public:
/**
* @brief Removes all tombstones from a registry or only the pools for the
* given components.
* @tparam Type Types of components for which to clear all tombstones.
* given elements.
* @tparam Type Types of elements for which to clear all tombstones.
*/
template<typename... Type>
void compact() {
@@ -852,15 +891,15 @@ public:
}
/**
* @brief Returns references to the given components for an entity.
* @brief Returns references to the given elements for an entity.
*
* @warning
* Attempting to get a component from an entity that doesn't own it results
* Attempting to get an element from an entity that doesn't own it results
* in undefined behavior.
*
* @tparam Type Types of components to get.
* @tparam Type Types of elements to get.
* @param entt A valid identifier.
* @return References to the components owned by the entity.
* @return References to the elements owned by the entity.
*/
template<typename... Type>
[[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entt) const {
@@ -882,38 +921,36 @@ public:
}
/**
* @brief Returns a reference to the given component for an entity.
* @brief Returns a reference to the given element for an entity.
*
* In case the entity doesn't own the component, the parameters provided are
* In case the entity doesn't own the element, the parameters provided are
* used to construct it.
*
* @sa get
* @sa emplace
*
* @tparam Type Type of component to get.
* @tparam Args Types of arguments to use to construct the component.
* @tparam Type Type of element to get.
* @tparam Args Types of arguments to use to construct the element.
* @param entt A valid identifier.
* @param args Parameters to use to initialize the component.
* @return Reference to the component owned by the entity.
* @param args Parameters to use to initialize the element.
* @return Reference to the element owned by the entity.
*/
template<typename Type, typename... Args>
[[nodiscard]] decltype(auto) get_or_emplace(const entity_type entt, Args &&...args) {
if(auto &cpool = assure<Type>(); cpool.contains(entt)) {
return cpool.get(entt);
} else {
return cpool.emplace(entt, std::forward<Args>(args)...);
}
auto &cpool = assure<Type>();
ENTT_ASSERT(valid(entt), "Invalid entity");
return cpool.contains(entt) ? cpool.get(entt) : cpool.emplace(entt, std::forward<Args>(args)...);
}
/**
* @brief Returns pointers to the given components for an entity.
* @brief Returns pointers to the given elements for an entity.
*
* @note
* The registry retains ownership of the pointed-to components.
* The registry retains ownership of the pointed-to elements.
*
* @tparam Type Types of components to get.
* @tparam Type Types of elements to get.
* @param entt A valid identifier.
* @return Pointers to the components owned by the entity.
* @return Pointers to the elements owned by the entity.
*/
template<typename... Type>
[[nodiscard]] auto try_get([[maybe_unused]] const entity_type entt) const {
@@ -936,14 +973,14 @@ public:
}
/**
* @brief Clears a whole registry or the pools for the given components.
* @tparam Type Types of components to remove from their entities.
* @brief Clears a whole registry or the pools for the given elements.
* @tparam Type Types of elements to remove from their entities.
*/
template<typename... Type>
void clear() {
if constexpr(sizeof...(Type) == 0u) {
for(size_type pos = pools.size(); pos; --pos) {
pools.begin()[pos - 1u].second->clear();
pools.begin()[static_cast<typename pool_container_type::difference_type>(pos - 1u)].second->clear();
}
const auto elem = entities.each();
@@ -954,30 +991,30 @@ public:
}
/**
* @brief Checks if an entity has components assigned.
* @brief Checks if an entity has elements assigned.
* @param entt A valid identifier.
* @return True if the entity has no components assigned, false otherwise.
* @return True if the entity has no elements assigned, false otherwise.
*/
[[nodiscard]] bool orphan(const entity_type entt) const {
return std::none_of(pools.cbegin(), pools.cend(), [entt](auto &&curr) { return curr.second->contains(entt); });
}
/**
* @brief Returns a sink object for the given component.
* @brief Returns a sink object for the given element.
*
* Use this function to receive notifications whenever a new instance of the
* given component is created and assigned to an entity.<br/>
* given element is created and assigned to an entity.<br/>
* The function type for a listener is equivalent to:
*
* @code{.cpp}
* void(basic_registry<Entity> &, Entity);
* @endcode
*
* Listeners are invoked **after** assigning the component to the entity.
* Listeners are invoked **after** assigning the element to the entity.
*
* @sa sink
*
* @tparam Type Type of component of which to get the sink.
* @tparam Type Type of element of which to get the sink.
* @param id Optional name used to map the storage within the registry.
* @return A temporary sink object.
*/
@@ -987,21 +1024,21 @@ public:
}
/**
* @brief Returns a sink object for the given component.
* @brief Returns a sink object for the given element.
*
* Use this function to receive notifications whenever an instance of the
* given component is explicitly updated.<br/>
* given element is explicitly updated.<br/>
* The function type for a listener is equivalent to:
*
* @code{.cpp}
* void(basic_registry<Entity> &, Entity);
* @endcode
*
* Listeners are invoked **after** updating the component.
* Listeners are invoked **after** updating the element.
*
* @sa sink
*
* @tparam Type Type of component of which to get the sink.
* @tparam Type Type of element of which to get the sink.
* @param id Optional name used to map the storage within the registry.
* @return A temporary sink object.
*/
@@ -1011,21 +1048,21 @@ public:
}
/**
* @brief Returns a sink object for the given component.
* @brief Returns a sink object for the given element.
*
* Use this function to receive notifications whenever an instance of the
* given component is removed from an entity and thus destroyed.<br/>
* given element is removed from an entity and thus destroyed.<br/>
* The function type for a listener is equivalent to:
*
* @code{.cpp}
* void(basic_registry<Entity> &, Entity);
* @endcode
*
* Listeners are invoked **before** removing the component from the entity.
* Listeners are invoked **before** removing the element from the entity.
*
* @sa sink
*
* @tparam Type Type of component of which to get the sink.
* @tparam Type Type of element of which to get the sink.
* @param id Optional name used to map the storage within the registry.
* @return A temporary sink object.
*/
@@ -1035,18 +1072,17 @@ public:
}
/**
* @brief Returns a view for the given components.
* @tparam Type Type of component used to construct the view.
* @tparam Other Other types of components used to construct the view.
* @tparam Exclude Types of components used to filter the view.
* @brief Returns a view for the given elements.
* @tparam Type Type of element used to construct the view.
* @tparam Other Other types of elements used to construct the view.
* @tparam Exclude Types of elements used to filter the view.
* @return A newly created view.
*/
template<typename Type, typename... Other, typename... Exclude>
[[nodiscard]] basic_view<get_t<storage_for_type<const Type>, storage_for_type<const Other>...>, exclude_t<storage_for_type<const Exclude>...>>
view(exclude_t<Exclude...> = exclude_t{}) const {
const auto cpools = std::make_tuple(assure<std::remove_const_t<Type>>(), assure<std::remove_const_t<Other>>()..., assure<std::remove_const_t<Exclude>>()...);
basic_view<get_t<storage_for_type<const Type>, storage_for_type<const Other>...>, exclude_t<storage_for_type<const Exclude>...>> elem{};
std::apply([&elem](const auto *...curr) { ((curr ? elem.storage(*curr) : void()), ...); }, cpools);
[&elem](const auto *...curr) { ((curr ? elem.storage(*curr) : void()), ...); }(assure<std::remove_const_t<Exclude>>()..., assure<std::remove_const_t<Other>>()..., assure<std::remove_const_t<Type>>());
return elem;
}
@@ -1058,7 +1094,7 @@ public:
}
/**
* @brief Returns a group for the given components.
* @brief Returns a group for the given elements.
* @tparam Owned Types of storage _owned_ by the group.
* @tparam Get Types of storage _observed_ by the group, if any.
* @tparam Exclude Types of storage used to filter the group, if any.
@@ -1067,33 +1103,34 @@ public:
template<typename... Owned, typename... Get, typename... Exclude>
basic_group<owned_t<storage_for_type<Owned>...>, get_t<storage_for_type<Get>...>, exclude_t<storage_for_type<Exclude>...>>
group(get_t<Get...> = get_t{}, exclude_t<Exclude...> = exclude_t{}) {
using handler_type = typename basic_group<owned_t<storage_for_type<Owned>...>, get_t<storage_for_type<Get>...>, exclude_t<storage_for_type<Exclude>...>>::handler;
using group_type = basic_group<owned_t<storage_for_type<Owned>...>, get_t<storage_for_type<Get>...>, exclude_t<storage_for_type<Exclude>...>>;
using handler_type = typename group_type::handler;
if(auto it = groups.find(type_hash<handler_type>::value()); it != groups.cend()) {
if(auto it = groups.find(group_type::group_id()); it != groups.cend()) {
return {*std::static_pointer_cast<handler_type>(it->second)};
}
std::shared_ptr<handler_type> handler{};
if constexpr(sizeof...(Owned) == 0u) {
handler = std::allocate_shared<handler_type>(get_allocator(), get_allocator(), assure<std::remove_const_t<Get>>()..., assure<std::remove_const_t<Exclude>>()...);
handler = std::allocate_shared<handler_type>(get_allocator(), get_allocator(), std::forward_as_tuple(assure<std::remove_const_t<Get>>()...), std::forward_as_tuple(assure<std::remove_const_t<Exclude>>()...));
} else {
handler = std::allocate_shared<handler_type>(get_allocator(), assure<std::remove_const_t<Owned>>()..., assure<std::remove_const_t<Get>>()..., assure<std::remove_const_t<Exclude>>()...);
[[maybe_unused]] const id_type elem[]{type_hash<std::remove_const_t<Owned>>::value()..., type_hash<std::remove_const_t<Get>>::value()..., type_hash<std::remove_const_t<Exclude>>::value()...};
ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), [&elem](const auto &data) { return data.second->owned(elem, sizeof...(Owned)) == 0u; }), "Conflicting groups");
handler = std::allocate_shared<handler_type>(get_allocator(), std::forward_as_tuple(assure<std::remove_const_t<Owned>>()..., assure<std::remove_const_t<Get>>()...), std::forward_as_tuple(assure<std::remove_const_t<Exclude>>()...));
ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), [](const auto &data) { return !(data.second->owned(type_id<Owned>().hash()) || ...); }), "Conflicting groups");
}
groups.emplace(type_hash<handler_type>::value(), handler);
groups.emplace(group_type::group_id(), handler);
return {*handler};
}
/*! @copydoc group */
template<typename... Owned, typename... Get, typename... Exclude>
basic_group<owned_t<storage_for_type<const Owned>...>, get_t<storage_for_type<const Get>...>, exclude_t<storage_for_type<const Exclude>...>>
[[nodiscard]] basic_group<owned_t<storage_for_type<const Owned>...>, get_t<storage_for_type<const Get>...>, exclude_t<storage_for_type<const Exclude>...>>
group_if_exists(get_t<Get...> = get_t{}, exclude_t<Exclude...> = exclude_t{}) const {
using handler_type = typename basic_group<owned_t<storage_for_type<const Owned>...>, get_t<storage_for_type<const Get>...>, exclude_t<storage_for_type<const Exclude>...>>::handler;
using group_type = basic_group<owned_t<storage_for_type<const Owned>...>, get_t<storage_for_type<const Get>...>, exclude_t<storage_for_type<const Exclude>...>>;
using handler_type = typename group_type::handler;
if(auto it = groups.find(type_hash<handler_type>::value()); it != groups.cend()) {
if(auto it = groups.find(group_type::group_id()); it != groups.cend()) {
return {*std::static_pointer_cast<handler_type>(it->second)};
}
@@ -1101,20 +1138,18 @@ public:
}
/**
* @brief Checks whether the given components belong to any group.
* @tparam Type Type of component in which one is interested.
* @tparam Other Other types of components in which one is interested.
* @return True if the pools of the given components are _free_, false
* @brief Checks whether the given elements belong to any group.
* @tparam Type Types of elements in which one is interested.
* @return True if the pools of the given elements are _free_, false
* otherwise.
*/
template<typename Type, typename... Other>
template<typename... Type>
[[nodiscard]] bool owned() const {
const id_type elem[]{type_hash<std::remove_const_t<Type>>::value(), type_hash<std::remove_const_t<Other>>::value()...};
return std::any_of(groups.cbegin(), groups.cend(), [&elem](auto &&data) { return data.second->owned(elem, 1u + sizeof...(Other)); });
return std::any_of(groups.cbegin(), groups.cend(), [](auto &&data) { return (data.second->owned(type_id<Type>().hash()) || ...); });
}
/**
* @brief Sorts the elements of a given component.
* @brief Sorts the elements of a given element.
*
* The comparison function object returns `true` if the first element is
* _less_ than the second one, `false` otherwise. Its signature is also
@@ -1136,9 +1171,9 @@ public:
* passed along with the other parameters to this member function.
*
* @warning
* Pools of components owned by a group cannot be sorted.
* Pools of elements owned by a group cannot be sorted.
*
* @tparam Type Type of components to sort.
* @tparam Type Type of elements to sort.
* @tparam Compare Type of comparison function object.
* @tparam Sort Type of sort function object.
* @tparam Args Types of arguments to forward to the sort function object.
@@ -1160,17 +1195,17 @@ public:
}
/**
* @brief Sorts two pools of components in the same way.
* @brief Sorts two pools of elements in the same way.
*
* Entities and components in `To` which are part of both storage are sorted
* Entities and elements in `To` which are part of both storage are sorted
* internally with the order they have in `From`. The others follow in no
* particular order.
*
* @warning
* Pools of components owned by a group cannot be sorted.
* Pools of elements owned by a group cannot be sorted.
*
* @tparam To Type of components to sort.
* @tparam From Type of components to use to sort.
* @tparam To Type of elements to sort.
* @tparam From Type of elements to use to sort.
*/
template<typename To, typename From>
void sort() {
@@ -1183,12 +1218,12 @@ public:
* @brief Returns the context object, that is, a general purpose container.
* @return The context object, that is, a general purpose container.
*/
context &ctx() noexcept {
[[nodiscard]] context &ctx() noexcept {
return vars;
}
/*! @copydoc ctx */
const context &ctx() const noexcept {
[[nodiscard]] const context &ctx() const noexcept {
return vars;
}

View File

@@ -17,6 +17,7 @@ namespace internal {
template<typename Set>
class runtime_view_iterator final {
using iterator_type = typename Set::iterator;
using iterator_traits = std::iterator_traits<iterator_type>;
[[nodiscard]] bool valid() const {
return (!tombstone_check || *it != tombstone)
@@ -25,10 +26,10 @@ class runtime_view_iterator final {
}
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 value_type = typename iterator_traits::value_type;
using pointer = typename iterator_traits::pointer;
using reference = typename iterator_traits::reference;
using difference_type = typename iterator_traits::difference_type;
using iterator_category = std::bidirectional_iterator_tag;
constexpr runtime_view_iterator() noexcept
@@ -37,7 +38,7 @@ public:
it{},
tombstone_check{} {}
runtime_view_iterator(const std::vector<Set *> &cpools, const std::vector<Set *> &ignore, iterator_type curr) noexcept
runtime_view_iterator(const std::vector<Set *> &cpools, iterator_type curr, const std::vector<Set *> &ignore) noexcept
: pools{&cpools},
filter{&ignore},
it{curr},
@@ -48,22 +49,24 @@ public:
}
runtime_view_iterator &operator++() {
while(++it != (*pools)[0]->end() && !valid()) {}
++it;
for(const auto last = (*pools)[0]->end(); it != last && !valid(); ++it) {}
return *this;
}
runtime_view_iterator operator++(int) {
runtime_view_iterator orig = *this;
const runtime_view_iterator orig = *this;
return ++(*this), orig;
}
runtime_view_iterator &operator--() {
while(--it != (*pools)[0]->begin() && !valid()) {}
--it;
for(const auto first = (*pools)[0]->begin(); it != first && !valid(); --it) {}
return *this;
}
runtime_view_iterator operator--(int) {
runtime_view_iterator orig = *this;
const runtime_view_iterator orig = *this;
return operator--(), orig;
}
@@ -98,15 +101,15 @@ private:
*
* Runtime views iterate over those entities that are at least in the given
* storage. During initialization, a runtime view looks at the number of
* entities available for each component and uses the smallest set in order to
* get a performance boost when iterating.
* entities available for each element and uses the smallest set in order to get
* a performance boost when iterating.
*
* @b Important
*
* Iterators aren't invalidated if:
*
* * New elements are added to the storage.
* * The entity currently pointed is modified (for example, components are added
* * The entity currently pointed is modified (for example, elements are added
* or removed from it).
* * The entity currently pointed is destroyed.
*
@@ -122,6 +125,12 @@ class basic_runtime_view {
static_assert(std::is_same_v<typename alloc_traits::value_type, Type *>, "Invalid value type");
using container_type = std::vector<Type *, Allocator>;
[[nodiscard]] auto offset() const noexcept {
ENTT_ASSERT(!pools.empty(), "Invalid view");
const auto &leading = *pools.front();
return (leading.policy() == deletion_policy::swap_only) ? leading.free_list() : leading.size();
}
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
@@ -129,6 +138,8 @@ public:
using entity_type = typename Type::entity_type;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Signed integer type. */
using difference_type = std::ptrdiff_t;
/*! @brief Common type among all storage types. */
using common_type = Type;
/*! @brief Bidirectional iterator type. */
@@ -159,7 +170,7 @@ public:
filter{other.filter, allocator} {}
/*! @brief Default move constructor. */
basic_runtime_view(basic_runtime_view &&) noexcept(std::is_nothrow_move_constructible_v<container_type>) = default;
basic_runtime_view(basic_runtime_view &&) noexcept = default;
/**
* @brief Allocator-extended move constructor.
@@ -170,23 +181,26 @@ public:
: pools{std::move(other.pools), allocator},
filter{std::move(other.filter), allocator} {}
/*! @brief Default destructor. */
~basic_runtime_view() = default;
/**
* @brief Default copy assignment operator.
* @return This container.
* @return This runtime view.
*/
basic_runtime_view &operator=(const basic_runtime_view &) = default;
/**
* @brief Default move assignment operator.
* @return This container.
* @return This runtime view.
*/
basic_runtime_view &operator=(basic_runtime_view &&) noexcept(std::is_nothrow_move_assignable_v<container_type>) = default;
basic_runtime_view &operator=(basic_runtime_view &&) noexcept = 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) {
void swap(basic_runtime_view &other) noexcept {
using std::swap;
swap(pools, other.pools);
swap(filter, other.filter);
@@ -212,10 +226,10 @@ public:
* @return This runtime view.
*/
basic_runtime_view &iterate(common_type &base) {
if(pools.empty() || !(base.size() < pools[0u]->size())) {
if(pools.empty() || !(base.size() < pools.front()->size())) {
pools.push_back(&base);
} else {
pools.push_back(std::exchange(pools[0u], &base));
pools.push_back(std::exchange(pools.front(), &base));
}
return *this;
@@ -236,29 +250,37 @@ public:
* @return Estimated number of entities iterated by the view.
*/
[[nodiscard]] size_type size_hint() const {
return pools.empty() ? size_type{} : pools.front()->size();
return pools.empty() ? size_type{} : offset();
}
/**
* @brief Returns an iterator to the first entity that has the given
* components.
* elements.
*
* If the view is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first entity that has the given components.
* @return An iterator to the first entity that has the given elements.
*/
[[nodiscard]] iterator begin() const {
return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->begin()};
return pools.empty() ? iterator{} : iterator{pools, pools.front()->end() - static_cast<difference_type>(offset()), filter};
}
/**
* @brief Returns an iterator that is past the last entity that has the
* given components.
* given elements.
* @return An iterator to the entity following the last entity that has the
* given components.
* given elements.
*/
[[nodiscard]] iterator end() const {
return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->end()};
return pools.empty() ? iterator{} : iterator{pools, pools.front()->end(), filter};
}
/**
* @brief Checks whether a view is initialized or not.
* @return True if the view is initialized, false otherwise.
*/
[[nodiscard]] explicit operator bool() const noexcept {
return !(pools.empty() && filter.empty());
}
/**
@@ -269,7 +291,8 @@ public:
[[nodiscard]] bool contains(const entity_type entt) const {
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); });
&& std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); })
&& pools.front()->index(entt) < offset();
}
/**

View File

@@ -37,7 +37,7 @@ void orphans(Registry &registry) {
* @brief Utility class to create snapshots from a registry.
*
* A _snapshot_ can be either a dump of the entire registry or a narrower
* selection of components of interest.<br/>
* selection of elements of interest.<br/>
* This type can be used in both cases if provided with a correctly configured
* output archive.
*
@@ -46,7 +46,7 @@ void orphans(Registry &registry) {
template<typename Registry>
class basic_snapshot {
static_assert(!std::is_const_v<Registry>, "Non-const registry type required");
using traits_type = typename Registry::traits_type;
using traits_type = entt_traits<typename Registry::entity_type>;
public:
/*! Basic registry type. */
@@ -61,10 +61,25 @@ public:
basic_snapshot(const registry_type &source) noexcept
: reg{&source} {}
/*! @brief Default copy constructor, deleted on purpose. */
basic_snapshot(const basic_snapshot &) = delete;
/*! @brief Default move constructor. */
basic_snapshot(basic_snapshot &&) noexcept = default;
/*! @brief Default move assignment operator. @return This snapshot. */
/*! @brief Default destructor. */
~basic_snapshot() = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This snapshot.
*/
basic_snapshot &operator=(const basic_snapshot &) = delete;
/**
* @brief Default move assignment operator.
* @return This snapshot.
*/
basic_snapshot &operator=(basic_snapshot &&) noexcept = default;
/**
@@ -78,14 +93,25 @@ public:
template<typename Type, typename Archive>
const basic_snapshot &get(Archive &archive, const id_type id = type_hash<Type>::value()) const {
if(const auto *storage = reg->template storage<Type>(id); storage) {
const typename registry_type::common_type &base = *storage;
archive(static_cast<typename traits_type::entity_type>(storage->size()));
if constexpr(std::is_same_v<Type, entity_type>) {
archive(static_cast<typename traits_type::entity_type>(storage->free_list()));
for(auto first = storage->data(), last = first + storage->size(); first != last; ++first) {
for(auto first = base.rbegin(), last = base.rend(); first != last; ++first) {
archive(*first);
}
} else if constexpr(registry_type::template storage_for_type<Type>::storage_policy == deletion_policy::in_place) {
for(auto it = base.rbegin(), last = base.rend(); it != last; ++it) {
if(const auto entt = *it; entt == tombstone) {
archive(static_cast<entity_type>(null));
} else {
archive(entt);
std::apply([&archive](auto &&...args) { (archive(std::forward<decltype(args)>(args)), ...); }, storage->get_as_tuple(entt));
}
}
} else {
for(auto elem: storage->reach()) {
std::apply([&archive](auto &&...args) { (archive(std::forward<decltype(args)>(args)), ...); }, elem);
@@ -149,7 +175,7 @@ private:
template<typename Registry>
class basic_snapshot_loader {
static_assert(!std::is_const_v<Registry>, "Non-const registry type required");
using traits_type = typename Registry::traits_type;
using traits_type = entt_traits<typename Registry::entity_type>;
public:
/*! Basic registry type. */
@@ -164,13 +190,28 @@ public:
basic_snapshot_loader(registry_type &source) noexcept
: reg{&source} {
// restoring a snapshot as a whole requires a clean registry
ENTT_ASSERT(reg->template storage<entity_type>().empty() && (reg->storage().begin() == reg->storage().end()), "Registry must be empty");
ENTT_ASSERT(reg->template storage<entity_type>().free_list() == 0u, "Registry must be empty");
}
/*! @brief Default copy constructor, deleted on purpose. */
basic_snapshot_loader(const basic_snapshot_loader &) = delete;
/*! @brief Default move constructor. */
basic_snapshot_loader(basic_snapshot_loader &&) noexcept = default;
/*! @brief Default move assignment operator. @return This loader. */
/*! @brief Default destructor. */
~basic_snapshot_loader() = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This loader.
*/
basic_snapshot_loader &operator=(const basic_snapshot_loader &) = delete;
/**
* @brief Default move assignment operator.
* @return This loader.
*/
basic_snapshot_loader &operator=(basic_snapshot_loader &&) noexcept = default;
/**
@@ -190,15 +231,18 @@ public:
if constexpr(std::is_same_v<Type, entity_type>) {
typename traits_type::entity_type count{};
entity_type placeholder{};
storage.reserve(length);
archive(count);
for(entity_type entity = null; length; --length) {
archive(entity);
storage.emplace(entity);
storage.generate(entity);
placeholder = (entity > placeholder) ? entity : placeholder;
}
storage.start_from(traits_type::next(placeholder));
storage.free_list(count);
} else {
auto &other = reg->template storage<entity_type>();
@@ -206,7 +250,7 @@ public:
while(length--) {
if(archive(entt); entt != null) {
const auto entity = other.contains(entt) ? entt : other.emplace(entt);
const auto entity = other.contains(entt) ? entt : other.generate(entt);
ENTT_ASSERT(entity == entt, "Entity not available for use");
if constexpr(std::tuple_size_v<decltype(storage.get_as_tuple({}))> == 0u) {
@@ -224,10 +268,10 @@ public:
}
/**
* @brief Destroys those entities that have no components.
* @brief Destroys those entities that have no elements.
*
* In case all the entities were serialized but only part of the components
* was saved, it could happen that some of the entities have no components
* In case all the entities were serialized but only part of the elements
* was saved, it could happen that some of the entities have no elements
* once restored.<br/>
* This function helps to identify and destroy those entities.
*
@@ -261,7 +305,7 @@ private:
template<typename Registry>
class basic_continuous_loader {
static_assert(!std::is_const_v<Registry>, "Non-const registry type required");
using traits_type = typename Registry::traits_type;
using traits_type = entt_traits<typename Registry::entity_type>;
void restore(typename Registry::entity_type entt) {
if(const auto entity = to_entity(entt); remloc.contains(entity) && remloc[entity].first == entt) {
@@ -332,11 +376,26 @@ public:
: remloc{source.get_allocator()},
reg{&source} {}
/*! @brief Default move constructor. */
basic_continuous_loader(basic_continuous_loader &&) = default;
/*! @brief Default copy constructor, deleted on purpose. */
basic_continuous_loader(const basic_continuous_loader &) = delete;
/*! @brief Default move assignment operator. @return This loader. */
basic_continuous_loader &operator=(basic_continuous_loader &&) = default;
/*! @brief Default move constructor. */
basic_continuous_loader(basic_continuous_loader &&) noexcept = default;
/*! @brief Default destructor. */
~basic_continuous_loader() = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This loader.
*/
basic_continuous_loader &operator=(const basic_continuous_loader &) = delete;
/**
* @brief Default move assignment operator.
* @return This loader.
*/
basic_continuous_loader &operator=(basic_continuous_loader &&) noexcept = default;
/**
* @brief Restores all elements of a type with associated identifiers.
@@ -406,10 +465,10 @@ public:
}
/**
* @brief Destroys those entities that have no components.
* @brief Destroys those entities that have no elements.
*
* In case all the entities were serialized but only part of the components
* was saved, it could happen that some of the entities have no components
* In case all the entities were serialized but only part of the elements
* was saved, it could happen that some of the entities have no elements
* once restored.<br/>
* This function helps to identify and destroy those entities.
*

View File

@@ -10,7 +10,7 @@
#include "../config/config.h"
#include "../core/algorithm.hpp"
#include "../core/any.hpp"
#include "../core/memory.hpp"
#include "../core/bit.hpp"
#include "../core/type_info.hpp"
#include "entity.hpp"
#include "fwd.hpp"
@@ -33,7 +33,7 @@ struct sparse_set_iterator final {
offset{} {}
constexpr sparse_set_iterator(const Container &ref, const difference_type idx) noexcept
: packed{std::addressof(ref)},
: packed{&ref},
offset{idx} {}
constexpr sparse_set_iterator &operator++() noexcept {
@@ -41,7 +41,7 @@ struct sparse_set_iterator final {
}
constexpr sparse_set_iterator operator++(int) noexcept {
sparse_set_iterator orig = *this;
const sparse_set_iterator orig = *this;
return ++(*this), orig;
}
@@ -50,7 +50,7 @@ struct sparse_set_iterator final {
}
constexpr sparse_set_iterator operator--(int) noexcept {
sparse_set_iterator orig = *this;
const sparse_set_iterator orig = *this;
return operator--(), orig;
}
@@ -73,15 +73,15 @@ struct sparse_set_iterator final {
}
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
return packed->data()[index() - value];
return (*packed)[static_cast<typename Container::size_type>(index() - value)];
}
[[nodiscard]] constexpr pointer operator->() const noexcept {
return packed->data() + index();
return std::addressof(operator[](0));
}
[[nodiscard]] constexpr reference operator*() const noexcept {
return *operator->();
return operator[](0);
}
[[nodiscard]] constexpr pointer data() const noexcept {
@@ -136,7 +136,7 @@ template<typename Container>
/*! @endcond */
/**
* @brief Basic sparse set implementation.
* @brief Sparse set implementation.
*
* Sparse set or packed array or whatever is the name users give it.<br/>
* Two arrays: an _external_ one and an _internal_ one; a _sparse_ one and a
@@ -160,27 +160,42 @@ class basic_sparse_set {
static_assert(std::is_same_v<typename alloc_traits::value_type, Entity>, "Invalid value type");
using sparse_container_type = std::vector<typename alloc_traits::pointer, typename alloc_traits::template rebind_alloc<typename alloc_traits::pointer>>;
using packed_container_type = std::vector<Entity, Allocator>;
using underlying_type = typename entt_traits<Entity>::entity_type;
using traits_type = entt_traits<Entity>;
static constexpr auto max_size = static_cast<std::size_t>(traits_type::to_entity(null));
// it could be auto but gcc complains and emits a warning due to a false positive
[[nodiscard]] std::size_t policy_to_head() const noexcept {
return static_cast<size_type>(max_size * static_cast<std::remove_const_t<decltype(max_size)>>(mode != deletion_policy::swap_only));
}
[[nodiscard]] auto entity_to_pos(const Entity entt) const noexcept {
return static_cast<size_type>(traits_type::to_entity(entt));
}
[[nodiscard]] auto pos_to_page(const std::size_t pos) const noexcept {
return static_cast<size_type>(pos / traits_type::page_size);
}
[[nodiscard]] auto sparse_ptr(const Entity entt) const {
const auto pos = static_cast<size_type>(traits_type::to_entity(entt));
const auto page = pos / traits_type::page_size;
const auto pos = entity_to_pos(entt);
const auto page = pos_to_page(pos);
return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, traits_type::page_size)) : nullptr;
}
[[nodiscard]] auto &sparse_ref(const Entity entt) const {
ENTT_ASSERT(sparse_ptr(entt), "Invalid element");
const auto pos = static_cast<size_type>(traits_type::to_entity(entt));
return sparse[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)];
const auto pos = entity_to_pos(entt);
return sparse[pos_to_page(pos)][fast_mod(pos, traits_type::page_size)];
}
[[nodiscard]] auto to_iterator(const Entity entt) const {
return --(end() - index(entt));
return --(end() - static_cast<difference_type>(index(entt)));
}
[[nodiscard]] auto &assure_at_least(const Entity entt) {
const auto pos = static_cast<size_type>(traits_type::to_entity(entt));
const auto page = pos / traits_type::page_size;
const auto pos = entity_to_pos(entt);
const auto page = pos_to_page(pos);
if(!(page < sparse.size())) {
sparse.resize(page + 1u, nullptr);
@@ -208,27 +223,23 @@ class basic_sparse_set {
}
}
void swap_at(const std::size_t from, const std::size_t to) {
auto &lhs = packed[from];
auto &rhs = packed[to];
void swap_at(const std::size_t lhs, const std::size_t rhs) {
auto &from = packed[lhs];
auto &to = packed[rhs];
sparse_ref(lhs) = traits_type::combine(static_cast<typename traits_type::entity_type>(to), traits_type::to_integral(lhs));
sparse_ref(rhs) = traits_type::combine(static_cast<typename traits_type::entity_type>(from), traits_type::to_integral(rhs));
sparse_ref(from) = traits_type::combine(static_cast<typename traits_type::entity_type>(rhs), traits_type::to_integral(from));
sparse_ref(to) = traits_type::combine(static_cast<typename traits_type::entity_type>(lhs), traits_type::to_integral(to));
std::swap(lhs, rhs);
}
underlying_type policy_to_head() {
return traits_type::entity_mask * (mode != deletion_policy::swap_only);
std::swap(from, to);
}
private:
virtual const void *get_at(const std::size_t) const {
[[nodiscard]] virtual const void *get_at(const std::size_t) const {
return nullptr;
}
virtual void swap_or_move([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) {
ENTT_ASSERT((mode != deletion_policy::swap_only) || (((lhs < free_list()) + (rhs < free_list())) != 1u), "Cross swapping is not supported");
ENTT_ASSERT((mode != deletion_policy::swap_only) || ((lhs < head) == (rhs < head)), "Cross swapping is not supported");
}
protected:
@@ -241,9 +252,9 @@ protected:
*/
void swap_only(const basic_iterator it) {
ENTT_ASSERT(mode == deletion_policy::swap_only, "Deletion policy mismatch");
const auto pos = static_cast<underlying_type>(index(*it));
const auto pos = index(*it);
bump(traits_type::next(*it));
swap_at(pos, static_cast<size_type>(head -= (pos < head)));
swap_at(pos, head -= (pos < head));
}
/**
@@ -257,6 +268,7 @@ protected:
sparse_ref(packed.back()) = traits_type::combine(entt, traits_type::to_integral(packed.back()));
packed[static_cast<size_type>(entt)] = packed.back();
// unnecessary but it helps to detect nasty bugs
// NOLINTNEXTLINE(bugprone-assert-side-effect)
ENTT_ASSERT((packed.back() = null, true), "");
// lazy self-assignment guard
self = null;
@@ -269,11 +281,10 @@ protected:
*/
void in_place_pop(const basic_iterator it) {
ENTT_ASSERT(mode == deletion_policy::in_place, "Deletion policy mismatch");
const auto entt = traits_type::to_entity(std::exchange(sparse_ref(*it), null));
packed[static_cast<size_type>(entt)] = traits_type::combine(std::exchange(head, entt), tombstone);
const auto pos = entity_to_pos(std::exchange(sparse_ref(*it), null));
packed[pos] = traits_type::combine(static_cast<typename traits_type::entity_type>(std::exchange(head, pos)), tombstone);
}
protected:
/**
* @brief Erases entities from a sparse set.
* @param first An iterator to the first element of the range of entities.
@@ -303,10 +314,10 @@ protected:
virtual void pop_all() {
switch(mode) {
case deletion_policy::in_place:
if(head != traits_type::to_entity(null)) {
for(auto first = begin(); !(first.index() < 0); ++first) {
if(*first != tombstone) {
sparse_ref(*first) = null;
if(head != max_size) {
for(auto &&elem: packed) {
if(elem != tombstone) {
sparse_ref(elem) = null;
}
}
break;
@@ -314,8 +325,8 @@ protected:
[[fallthrough]];
case deletion_policy::swap_only:
case deletion_policy::swap_and_pop:
for(auto first = begin(); !(first.index() < 0); ++first) {
sparse_ref(*first) = null;
for(auto &&elem: packed) {
sparse_ref(elem) = null;
}
break;
}
@@ -331,16 +342,17 @@ protected:
* @return Iterator pointing to the emplaced element.
*/
virtual basic_iterator try_emplace(const Entity entt, const bool force_back, const void * = nullptr) {
ENTT_ASSERT(entt != null && entt != tombstone, "Invalid element");
auto &elem = assure_at_least(entt);
auto pos = size();
switch(mode) {
case deletion_policy::in_place:
if(head != traits_type::to_entity(null) && !force_back) {
pos = static_cast<size_type>(head);
if(head != max_size && !force_back) {
pos = head;
ENTT_ASSERT(elem == null, "Slot not available");
elem = traits_type::combine(head, traits_type::to_integral(entt));
head = traits_type::to_entity(std::exchange(packed[pos], entt));
elem = traits_type::combine(static_cast<typename traits_type::entity_type>(head), traits_type::to_integral(entt));
head = entity_to_pos(std::exchange(packed[pos], entt));
break;
}
[[fallthrough]];
@@ -354,32 +366,33 @@ protected:
packed.push_back(entt);
elem = traits_type::combine(static_cast<typename traits_type::entity_type>(packed.size() - 1u), traits_type::to_integral(entt));
} else {
ENTT_ASSERT(!(traits_type::to_entity(elem) < head), "Slot not available");
ENTT_ASSERT(!(entity_to_pos(elem) < head), "Slot not available");
bump(entt);
}
if(force_back) {
pos = static_cast<size_type>(head++);
swap_at(static_cast<size_type>(traits_type::to_entity(elem)), pos);
}
pos = head++;
swap_at(entity_to_pos(elem), pos);
break;
}
return --(end() - pos);
return iterator{packed, static_cast<difference_type>(++pos)};
}
/*! @brief Forwards variables to derived classes, if any. */
// NOLINTNEXTLINE(performance-unnecessary-value-param)
virtual void bind_any(any) noexcept {}
public:
/*! @brief Entity traits. */
using traits_type = entt_traits<Entity>;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Underlying entity identifier. */
using entity_type = typename traits_type::value_type;
/*! @brief Underlying version type. */
using version_type = typename traits_type::version_type;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Signed integer type. */
using difference_type = std::ptrdiff_t;
/*! @brief Pointer type to contained entities. */
using pointer = typename packed_container_type::const_pointer;
/*! @brief Random access iterator type. */
@@ -400,7 +413,7 @@ public:
* @param allocator The allocator to use.
*/
explicit basic_sparse_set(const allocator_type &allocator)
: basic_sparse_set{type_id<void>(), deletion_policy::swap_and_pop, allocator} {}
: basic_sparse_set{deletion_policy::swap_and_pop, allocator} {}
/**
* @brief Constructs an empty container with the given policy and allocator.
@@ -420,9 +433,14 @@ public:
explicit basic_sparse_set(const type_info &elem, deletion_policy pol = deletion_policy::swap_and_pop, const allocator_type &allocator = {})
: sparse{allocator},
packed{allocator},
info{&elem},
descriptor{&elem},
mode{pol},
head{policy_to_head()} {}
head{policy_to_head()} {
ENTT_ASSERT(traits_type::version_mask || mode != deletion_policy::in_place, "Policy does not support zero-sized versions");
}
/*! @brief Default copy constructor, deleted on purpose. */
basic_sparse_set(const basic_sparse_set &) = delete;
/**
* @brief Move constructor.
@@ -431,7 +449,7 @@ public:
basic_sparse_set(basic_sparse_set &&other) noexcept
: sparse{std::move(other.sparse)},
packed{std::move(other.packed)},
info{other.info},
descriptor{other.descriptor},
mode{other.mode},
head{std::exchange(other.head, policy_to_head())} {}
@@ -440,13 +458,13 @@ public:
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) noexcept
basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator)
: sparse{std::move(other.sparse), allocator},
packed{std::move(other.packed), allocator},
info{other.info},
descriptor{other.descriptor},
mode{other.mode},
head{std::exchange(other.head, policy_to_head())} {
ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed");
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a sparse set is not allowed");
}
/*! @brief Default destructor. */
@@ -454,20 +472,20 @@ public:
release_sparse_pages();
}
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This sparse set.
*/
basic_sparse_set &operator=(const basic_sparse_set &) = delete;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This sparse set.
*/
basic_sparse_set &operator=(basic_sparse_set &&other) noexcept {
ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed");
release_sparse_pages();
sparse = std::move(other.sparse);
packed = std::move(other.packed);
info = other.info;
mode = other.mode;
head = std::exchange(other.head, policy_to_head());
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a sparse set is not allowed");
swap(other);
return *this;
}
@@ -475,11 +493,11 @@ public:
* @brief Exchanges the contents with those of a given sparse set.
* @param other Sparse set to exchange the content with.
*/
void swap(basic_sparse_set &other) {
void swap(basic_sparse_set &other) noexcept {
using std::swap;
swap(sparse, other.sparse);
swap(packed, other.packed);
swap(info, other.info);
swap(descriptor, other.descriptor);
swap(mode, other.mode);
swap(head, other.head);
}
@@ -501,20 +519,20 @@ public:
}
/**
* @brief Returns the head of the free list, if any.
* @return The head of the free list.
* @brief Returns data on the free list whose meaning depends on the mode.
* @return Free list information that is mode dependent.
*/
[[nodiscard]] size_type free_list() const noexcept {
return static_cast<size_type>(head);
return head;
}
/**
* @brief Sets the head of the free list, if possible.
* @param len The value to use as the new head of the free list.
* @brief Sets data on the free list whose meaning depends on the mode.
* @param value Free list information that is mode dependent.
*/
void free_list(const size_type len) noexcept {
ENTT_ASSERT((mode == deletion_policy::swap_only) && !(len > packed.size()), "Invalid value");
head = static_cast<underlying_type>(len);
void free_list(const size_type value) noexcept {
ENTT_ASSERT((mode == deletion_policy::swap_only) && !(value > packed.size()), "Invalid value");
head = value;
}
/**
@@ -540,6 +558,33 @@ public:
/*! @brief Requests the removal of unused capacity. */
virtual void shrink_to_fit() {
sparse_container_type other{sparse.get_allocator()};
const auto len = sparse.size();
size_type cnt{};
other.reserve(len);
for(auto &&elem: std::as_const(packed)) {
if(elem != tombstone) {
if(const auto page = pos_to_page(entity_to_pos(elem)); sparse[page] != nullptr) {
if(const auto sz = page + 1u; sz > other.size()) {
other.resize(sz, nullptr);
}
other[page] = std::exchange(sparse[page], nullptr);
if(++cnt == len) {
// early exit due to lack of pages
break;
}
}
}
}
release_sparse_pages();
sparse.swap(other);
sparse.shrink_to_fit();
packed.shrink_to_fit();
}
@@ -547,9 +592,8 @@ public:
* @brief Returns the extent of a sparse set.
*
* The extent of a sparse set is also the size of the internal sparse array.
* There is no guarantee that the internal packed array has the same size.
* Usually the size of the internal sparse array is equal or greater than
* the one of the internal packed array.
* There is no guarantee that all pages have been allocated, nor that the
* internal packed array is be the same size.
*
* @return Extent of the sparse set.
*/
@@ -584,7 +628,7 @@ public:
* @return True if the sparse set is fully packed, false otherwise.
*/
[[nodiscard]] bool contiguous() const noexcept {
return (mode != deletion_policy::in_place) || (head == traits_type::to_entity(null));
return (mode != deletion_policy::in_place) || (head == max_size);
}
/**
@@ -604,7 +648,7 @@ public:
* @return An iterator to the first entity of the sparse set.
*/
[[nodiscard]] iterator begin() const noexcept {
const auto pos = static_cast<typename iterator::difference_type>(packed.size());
const auto pos = static_cast<difference_type>(packed.size());
return iterator{packed, pos};
}
@@ -659,46 +703,6 @@ public:
return rend();
}
/*! @copydoc begin Useful only in case of swap-only policy. */
[[nodiscard]] iterator begin(int) const noexcept {
return (mode == deletion_policy::swap_only) ? (end() - static_cast<typename iterator::difference_type>(head)) : begin();
}
/*! @copydoc cbegin Useful only in case of swap-only policy. */
[[nodiscard]] const_iterator cbegin(int) const noexcept {
return begin(0);
}
/*! @copydoc end Useful only in case of swap-only policy. */
[[nodiscard]] iterator end(int) const noexcept {
return end();
}
/*! @copydoc cend Useful only in case of swap-only policy. */
[[nodiscard]] const_iterator cend(int) const noexcept {
return end(0);
}
/*! @copydoc rbegin Useful only in case of swap-only policy. */
[[nodiscard]] reverse_iterator rbegin(int) const noexcept {
return std::make_reverse_iterator(end(0));
}
/*! @copydoc rbegin Useful only in case of swap-only policy. */
[[nodiscard]] const_reverse_iterator crbegin(int) const noexcept {
return rbegin(0);
}
/*! @copydoc rbegin Useful only in case of swap-only policy. */
[[nodiscard]] reverse_iterator rend(int) const noexcept {
return std::make_reverse_iterator(begin(0));
}
/*! @copydoc rbegin Useful only in case of swap-only policy. */
[[nodiscard]] const_reverse_iterator crend(int) const noexcept {
return rend(0);
}
/**
* @brief Finds an entity.
* @param entt A valid identifier.
@@ -715,7 +719,7 @@ public:
* @return True if the sparse set contains the entity, false otherwise.
*/
[[nodiscard]] bool contains(const entity_type entt) const noexcept {
const auto elem = sparse_ptr(entt);
const auto *elem = sparse_ptr(entt);
constexpr auto cap = traits_type::entity_mask;
constexpr auto mask = traits_type::to_integral(null) & ~cap;
// testing versions permits to avoid accessing the packed array
@@ -729,7 +733,7 @@ public:
* version otherwise.
*/
[[nodiscard]] version_type current(const entity_type entt) const noexcept {
const auto elem = sparse_ptr(entt);
const auto *elem = sparse_ptr(entt);
constexpr auto fallback = traits_type::to_version(tombstone);
return elem ? traits_type::to_version(*elem) : fallback;
}
@@ -746,25 +750,16 @@ public:
*/
[[nodiscard]] size_type index(const entity_type entt) const noexcept {
ENTT_ASSERT(contains(entt), "Set does not contain entity");
return static_cast<size_type>(traits_type::to_entity(sparse_ref(entt)));
return entity_to_pos(sparse_ref(entt));
}
/**
* @brief Returns the entity at specified location, with bounds checking.
* @param pos The position for which to return the entity.
* @return The entity at specified location if any, a null entity otherwise.
*/
[[deprecated("use .begin()[pos] instead")]] [[nodiscard]] entity_type at(const size_type pos) const noexcept {
return pos < packed.size() ? packed[pos] : null;
}
/**
* @brief Returns the entity at specified location, without bounds checking.
* @brief Returns the entity at specified location.
* @param pos The position for which to return the entity.
* @return The entity at specified location.
*/
[[nodiscard]] entity_type operator[](const size_type pos) const noexcept {
ENTT_ASSERT(pos < packed.size(), "Position is out of bounds");
ENTT_ASSERT(pos < packed.size(), "Index out of bounds");
return packed[pos];
}
@@ -800,7 +795,7 @@ public:
* `end()` iterator otherwise.
*/
iterator push(const entity_type entt, const void *elem = nullptr) {
return try_emplace(entt, (mode == deletion_policy::swap_only), elem);
return try_emplace(entt, false, elem);
}
/**
@@ -818,11 +813,13 @@ public:
*/
template<typename It>
iterator push(It first, It last) {
for(auto it = first; it != last; ++it) {
try_emplace(*it, true);
auto curr = end();
for(; first != last; ++first) {
curr = try_emplace(*first, true);
}
return first == last ? end() : find(*first);
return curr;
}
/**
@@ -836,10 +833,10 @@ public:
* @return The version of the given identifier.
*/
version_type bump(const entity_type entt) {
auto &entity = sparse_ref(entt);
ENTT_ASSERT(entt != tombstone && entity != null, "Cannot set the required version");
entity = traits_type::combine(traits_type::to_integral(entity), traits_type::to_integral(entt));
packed[static_cast<size_type>(traits_type::to_entity(entity))] = entt;
auto &elem = sparse_ref(entt);
ENTT_ASSERT(entt != null && elem != tombstone, "Cannot set the required version");
elem = traits_type::combine(traits_type::to_integral(elem), traits_type::to_integral(entt));
packed[entity_to_pos(elem)] = entt;
return traits_type::to_version(entt);
}
@@ -909,7 +906,7 @@ public:
++first;
}
count += std::distance(it, first);
count += static_cast<size_type>(std::distance(it, first));
erase(it, first);
}
} else {
@@ -925,23 +922,24 @@ public:
void compact() {
if(mode == deletion_policy::in_place) {
size_type from = packed.size();
for(; from && packed[from - 1u] == tombstone; --from) {}
underlying_type pos = std::exchange(head, traits_type::entity_mask);
size_type pos = std::exchange(head, max_size);
while(pos != traits_type::to_entity(null)) {
if(const auto to = static_cast<size_type>(std::exchange(pos, traits_type::to_entity(packed[pos]))); to < from) {
for(; from && packed[from - 1u] == tombstone; --from) {}
while(pos != max_size) {
if(const auto to = std::exchange(pos, entity_to_pos(packed[pos])); to < from) {
--from;
swap_or_move(from, to);
packed[to] = packed[from];
const auto entity = static_cast<typename traits_type::entity_type>(to);
sparse_ref(packed[to]) = traits_type::combine(entity, traits_type::to_integral(packed[to]));
const auto elem = static_cast<typename traits_type::entity_type>(to);
sparse_ref(packed[to]) = traits_type::combine(elem, traits_type::to_integral(packed[to]));
for(; from && packed[from - 1u] == tombstone; --from) {}
}
}
packed.erase(packed.begin() + from, packed.end());
packed.erase(packed.begin() + static_cast<difference_type>(from), packed.end());
}
}
@@ -999,10 +997,10 @@ public:
*/
template<typename Compare, typename Sort = std_sort, typename... Args>
void sort_n(const size_type length, Compare compare, Sort algo = Sort{}, Args &&...args) {
ENTT_ASSERT((mode != deletion_policy::in_place) || (head == traits_type::to_entity(null)), "Sorting with tombstones not allowed");
ENTT_ASSERT((mode != deletion_policy::in_place) || (head == max_size), "Sorting with tombstones not allowed");
ENTT_ASSERT(!(length > packed.size()), "Length exceeds the number of elements");
algo(packed.rend() - length, packed.rend(), std::move(compare), std::forward<Args>(args)...);
algo(packed.rend() - static_cast<difference_type>(length), packed.rend(), std::move(compare), std::forward<Args>(args)...);
for(size_type pos{}; pos < length; ++pos) {
auto curr = pos;
@@ -1013,8 +1011,8 @@ public:
const auto entt = packed[curr];
swap_or_move(next, idx);
const auto entity = static_cast<typename traits_type::entity_type>(curr);
sparse_ref(entt) = traits_type::combine(entity, traits_type::to_integral(packed[curr]));
const auto elem = static_cast<typename traits_type::entity_type>(curr);
sparse_ref(entt) = traits_type::combine(elem, traits_type::to_integral(packed[curr]));
curr = std::exchange(next, idx);
}
}
@@ -1034,7 +1032,8 @@ public:
*/
template<typename Compare, typename Sort = std_sort, typename... Args>
void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
sort_n(static_cast<size_type>(end(0) - begin(0)), std::move(compare), std::move(algo), std::forward<Args>(args)...);
const size_type len = (mode == deletion_policy::swap_only) ? head : packed.size();
sort_n(len, std::move(compare), std::move(algo), std::forward<Args>(args)...);
}
/**
@@ -1048,12 +1047,15 @@ public:
* @tparam It Type of input iterator.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @return An iterator past the last of the elements actually shared.
*/
template<typename It>
void sort_as(It first, It last) {
ENTT_ASSERT((mode != deletion_policy::in_place) || (head == traits_type::to_entity(null)), "Sorting with tombstones not allowed");
iterator sort_as(It first, It last) {
ENTT_ASSERT((mode != deletion_policy::in_place) || (head == max_size), "Sorting with tombstones not allowed");
const size_type len = (mode == deletion_policy::swap_only) ? head : packed.size();
auto it = end() - static_cast<difference_type>(len);
for(auto it = begin(0); it.index() && first != last; ++first) {
for(const auto other = end(); (it != other) && (first != last); ++first) {
if(const auto curr = *first; contains(curr)) {
if(const auto entt = *it; entt != curr) {
// basic no-leak guarantee (with invalid state) if swapping throws
@@ -1063,14 +1065,8 @@ public:
++it;
}
}
}
/**
* @copybrief sort_as
* @param other The sparse sets that imposes the order of the entities.
*/
[[deprecated("use iterator based sort_as instead")]] void sort_as(const basic_sparse_set &other) {
sort_as(other.begin(), other.end());
return it;
}
/*! @brief Clears a sparse set. */
@@ -1083,22 +1079,34 @@ public:
}
/**
* @brief Returned value type, if any.
* @return Returned value type, if any.
* @brief Returns a type info object for the value type, if any.
* @return A type info object for the value type, if any.
*/
const type_info &type() const noexcept {
return *info;
[[nodiscard]] const type_info &info() const noexcept {
return *descriptor;
}
/*! @brief Forwards variables to derived classes, if any. */
virtual void bind(any) noexcept {}
/*! @copydoc info */
[[deprecated("use ::info instead")]] [[nodiscard]] const type_info &type() const noexcept {
return info();
}
/**
* @brief Forwards variables to derived classes, if any.
* @tparam Type Type of the element to forward.
* @param value The element to forward.
*/
template<typename Type>
void bind(Type &&value) noexcept {
bind_any(forward_as_any(std::forward<Type>(value)));
}
private:
sparse_container_type sparse;
packed_container_type packed;
const type_info *info;
const type_info *descriptor;
deletion_policy mode;
underlying_type head;
size_type head;
};
} // namespace entt

View File

@@ -9,6 +9,7 @@
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../core/bit.hpp"
#include "../core/iterator.hpp"
#include "../core/memory.hpp"
#include "../core/type_info.hpp"
@@ -22,9 +23,9 @@ namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename Container, std::size_t Size>
template<typename Container, auto Page>
class storage_iterator final {
friend storage_iterator<const Container, Size>;
friend storage_iterator<const Container, Page>;
using container_type = std::remove_const_t<Container>;
using alloc_traits = std::allocator_traits<typename container_type::allocator_type>;
@@ -48,7 +49,7 @@ public:
offset{idx} {}
template<bool Const = std::is_const_v<Container>, typename = std::enable_if_t<Const>>
constexpr storage_iterator(const storage_iterator<std::remove_const_t<Container>, Size> &other) noexcept
constexpr storage_iterator(const storage_iterator<std::remove_const_t<Container>, Page> &other) noexcept
: storage_iterator{other.payload, other.offset} {}
constexpr storage_iterator &operator++() noexcept {
@@ -56,7 +57,7 @@ public:
}
constexpr storage_iterator operator++(int) noexcept {
storage_iterator orig = *this;
const storage_iterator orig = *this;
return ++(*this), orig;
}
@@ -65,7 +66,7 @@ public:
}
constexpr storage_iterator operator--(int) noexcept {
storage_iterator orig = *this;
const storage_iterator orig = *this;
return operator--(), orig;
}
@@ -88,17 +89,16 @@ public:
}
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
const auto pos = index() - value;
return (*payload)[pos / Size][fast_mod(pos, Size)];
const auto pos = static_cast<typename Container::size_type>(index() - value);
return (*payload)[pos / Page][fast_mod(static_cast<std::size_t>(pos), Page)];
}
[[nodiscard]] constexpr pointer operator->() const noexcept {
const auto pos = index();
return (*payload)[pos / Size] + fast_mod(pos, Size);
return std::addressof(operator[](0));
}
[[nodiscard]] constexpr reference operator*() const noexcept {
return *operator->();
return operator[](0);
}
[[nodiscard]] constexpr difference_type index() const noexcept {
@@ -110,38 +110,38 @@ private:
difference_type offset;
};
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
template<typename Lhs, typename Rhs, auto Page>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator<Lhs, Page> &lhs, const storage_iterator<Rhs, Page> &rhs) noexcept {
return rhs.index() - lhs.index();
}
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator==(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
template<typename Lhs, typename Rhs, auto Page>
[[nodiscard]] constexpr bool operator==(const storage_iterator<Lhs, Page> &lhs, const storage_iterator<Rhs, Page> &rhs) noexcept {
return lhs.index() == rhs.index();
}
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator!=(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
template<typename Lhs, typename Rhs, auto Page>
[[nodiscard]] constexpr bool operator!=(const storage_iterator<Lhs, Page> &lhs, const storage_iterator<Rhs, Page> &rhs) noexcept {
return !(lhs == rhs);
}
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator<(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
template<typename Lhs, typename Rhs, auto Page>
[[nodiscard]] constexpr bool operator<(const storage_iterator<Lhs, Page> &lhs, const storage_iterator<Rhs, Page> &rhs) noexcept {
return lhs.index() > rhs.index();
}
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator>(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
template<typename Lhs, typename Rhs, auto Page>
[[nodiscard]] constexpr bool operator>(const storage_iterator<Lhs, Page> &lhs, const storage_iterator<Rhs, Page> &rhs) noexcept {
return rhs < lhs;
}
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator<=(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
template<typename Lhs, typename Rhs, auto Page>
[[nodiscard]] constexpr bool operator<=(const storage_iterator<Lhs, Page> &lhs, const storage_iterator<Rhs, Page> &rhs) noexcept {
return !(lhs > rhs);
}
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator>=(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
template<typename Lhs, typename Rhs, auto Page>
[[nodiscard]] constexpr bool operator>=(const storage_iterator<Lhs, Page> &lhs, const storage_iterator<Rhs, Page> &rhs) noexcept {
return !(lhs < rhs);
}
@@ -152,10 +152,10 @@ class extended_storage_iterator final {
public:
using iterator_type = It;
using difference_type = std::ptrdiff_t;
using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::forward_as_tuple(*std::declval<Other>()...)));
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
using iterator_concept = std::forward_iterator_tag;
@@ -174,7 +174,7 @@ public:
}
constexpr extended_storage_iterator operator++(int) noexcept {
extended_storage_iterator orig = *this;
const extended_storage_iterator orig = *this;
return ++(*this), orig;
}
@@ -211,7 +211,7 @@ template<typename... Lhs, typename... Rhs>
/*! @endcond */
/**
* @brief Basic storage implementation.
* @brief Storage implementation.
*
* Internal data structures arrange elements to maximize performance. There are
* no guarantees that objects are returned in the insertion order when iterate
@@ -221,7 +221,7 @@ template<typename... Lhs, typename... Rhs>
* Empty types aren't explicitly instantiated. Therefore, many of the functions
* normally available for non-empty types will not be available for empty ones.
*
* @tparam Type Type of objects assigned to the entities.
* @tparam Type Element type.
* @tparam Entity A valid entity type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
@@ -232,6 +232,7 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
using container_type = std::vector<typename alloc_traits::pointer, typename alloc_traits::template rebind_alloc<typename alloc_traits::pointer>>;
using underlying_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
using underlying_iterator = typename underlying_type::basic_iterator;
using traits_type = component_traits<Type, Entity>;
[[nodiscard]] auto &element_at(const std::size_t pos) const {
return payload[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)];
@@ -264,8 +265,8 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
const auto it = base_type::try_emplace(entt, force_back);
ENTT_TRY {
auto elem = assure_at_least(static_cast<size_type>(it.index()));
entt::uninitialized_construct_using_allocator(to_address(elem), get_allocator(), std::forward<Args>(args)...);
auto *elem = to_address(assure_at_least(static_cast<size_type>(it.index())));
entt::uninitialized_construct_using_allocator(elem, get_allocator(), std::forward<Args>(args)...);
}
ENTT_CATCH {
base_type::pop(it, it + 1u);
@@ -294,32 +295,37 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
}
payload.resize(from);
payload.shrink_to_fit();
}
void swap_at(const std::size_t lhs, const std::size_t rhs) {
using std::swap;
swap(element_at(lhs), element_at(rhs));
}
void move_to(const std::size_t lhs, const std::size_t rhs) {
auto &elem = element_at(lhs);
allocator_type allocator{get_allocator()};
entt::uninitialized_construct_using_allocator(to_address(assure_at_least(rhs)), allocator, std::move(elem));
alloc_traits::destroy(allocator, std::addressof(elem));
}
private:
const void *get_at(const std::size_t pos) const final {
[[nodiscard]] const void *get_at(const std::size_t pos) const final {
return std::addressof(element_at(pos));
}
void swap_or_move([[maybe_unused]] const std::size_t from, [[maybe_unused]] const std::size_t to) override {
static constexpr bool is_pinned_type_v = !(std::is_move_constructible_v<Type> && std::is_move_assignable_v<Type>);
static constexpr bool is_pinned_type = !(std::is_move_constructible_v<Type> && std::is_move_assignable_v<Type>);
// use a runtime value to avoid compile-time suppression that drives the code coverage tool crazy
ENTT_ASSERT((from + 1u) && !is_pinned_type_v, "Pinned type");
if constexpr(!is_pinned_type_v) {
auto &elem = element_at(from);
ENTT_ASSERT((from + 1u) && !is_pinned_type, "Pinned type");
if constexpr(!is_pinned_type) {
if constexpr(traits_type::in_place_delete) {
if(base_type::operator[](to) == tombstone) {
allocator_type allocator{get_allocator()};
entt::uninitialized_construct_using_allocator(to_address(assure_at_least(to)), allocator, std::move(elem));
alloc_traits::destroy(allocator, std::addressof(elem));
return;
}
(base_type::operator[](to) == tombstone) ? move_to(from, to) : swap_at(from, to);
} else {
swap_at(from, to);
}
using std::swap;
swap(elem, element_at(to));
}
}
@@ -372,14 +378,14 @@ protected:
* @return Iterator pointing to the emplaced element.
*/
underlying_iterator try_emplace([[maybe_unused]] const Entity entt, [[maybe_unused]] const bool force_back, const void *value) override {
if(value) {
if constexpr(std::is_copy_constructible_v<value_type>) {
return emplace_element(entt, force_back, *static_cast<const value_type *>(value));
if(value != nullptr) {
if constexpr(std::is_copy_constructible_v<element_type>) {
return emplace_element(entt, force_back, *static_cast<const element_type *>(value));
} else {
return base_type::end();
}
} else {
if constexpr(std::is_default_constructible_v<value_type>) {
if constexpr(std::is_default_constructible_v<element_type>) {
return emplace_element(entt, force_back);
} else {
return base_type::end();
@@ -388,18 +394,20 @@ protected:
}
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Base type. */
using base_type = underlying_type;
/*! @brief Element type. */
using element_type = Type;
/*! @brief Type of the objects assigned to entities. */
using value_type = Type;
/*! @brief Component traits. */
using traits_type = component_traits<value_type>;
using value_type = element_type;
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Signed integer type. */
using difference_type = std::ptrdiff_t;
/*! @brief Pointer type to contained elements. */
using pointer = typename container_type::pointer;
/*! @brief Constant pointer type to contained elements. */
@@ -420,6 +428,8 @@ public:
using reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::reverse_iterator, reverse_iterator>>;
/*! @brief Constant extended reverse iterable storage proxy. */
using const_reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_reverse_iterator, const_reverse_iterator>>;
/*! @brief Storage deletion policy. */
static constexpr deletion_policy storage_policy{traits_type::in_place_delete};
/*! @brief Default constructor. */
basic_storage()
@@ -430,15 +440,18 @@ public:
* @param allocator The allocator to use.
*/
explicit basic_storage(const allocator_type &allocator)
: base_type{type_id<value_type>(), deletion_policy{traits_type::in_place_delete}, allocator},
: base_type{type_id<element_type>(), storage_policy, allocator},
payload{allocator} {}
/*! @brief Default copy constructor, deleted on purpose. */
basic_storage(const basic_storage &) = delete;
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
basic_storage(basic_storage &&other) noexcept
: base_type{std::move(other)},
: base_type{static_cast<base_type &&>(other)},
payload{std::move(other.payload)} {}
/**
@@ -446,28 +459,32 @@ public:
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept
: base_type{std::move(other), allocator},
basic_storage(basic_storage &&other, const allocator_type &allocator)
: base_type{static_cast<base_type &&>(other), allocator},
payload{std::move(other.payload), allocator} {
ENTT_ASSERT(alloc_traits::is_always_equal::value || payload.get_allocator() == other.payload.get_allocator(), "Copying a storage is not allowed");
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a storage is not allowed");
}
/*! @brief Default destructor. */
// NOLINTNEXTLINE(bugprone-exception-escape)
~basic_storage() override {
shrink_to_size(0u);
}
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This storage.
*/
basic_storage &operator=(const basic_storage &) = delete;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This storage.
*/
basic_storage &operator=(basic_storage &&other) noexcept {
ENTT_ASSERT(alloc_traits::is_always_equal::value || payload.get_allocator() == other.payload.get_allocator(), "Copying a storage is not allowed");
shrink_to_size(0u);
base_type::operator=(std::move(other));
payload = std::move(other.payload);
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a storage is not allowed");
swap(other);
return *this;
}
@@ -475,10 +492,10 @@ public:
* @brief Exchanges the contents with those of a given storage.
* @param other Storage to exchange the content with.
*/
void swap(basic_storage &other) {
void swap(basic_storage &other) noexcept {
using std::swap;
base_type::swap(other);
swap(payload, other.payload);
base_type::swap(other);
}
/**
@@ -540,7 +557,7 @@ public:
* @return An iterator to the first instance of the internal array.
*/
[[nodiscard]] const_iterator cbegin() const noexcept {
const auto pos = static_cast<typename iterator::difference_type>(base_type::size());
const auto pos = static_cast<difference_type>(base_type::size());
return const_iterator{&payload, pos};
}
@@ -551,7 +568,7 @@ public:
/*! @copydoc begin */
[[nodiscard]] iterator begin() noexcept {
const auto pos = static_cast<typename iterator::difference_type>(base_type::size());
const auto pos = static_cast<difference_type>(base_type::size());
return iterator{&payload, pos};
}
@@ -697,7 +714,7 @@ public:
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @param value An instance of the object to construct.
* @return Iterator pointing to the last element inserted, if any.
* @return Iterator pointing to the first element inserted, if any.
*/
template<typename It>
iterator insert(It first, It last, const value_type &value = {}) {
@@ -734,17 +751,17 @@ public:
* @brief Returns an iterable object to use to _visit_ a storage.
*
* The iterable object returns a tuple that contains the current entity and
* a reference to its component.
* a reference to its element.
*
* @return An iterable object to use to _visit_ the storage.
*/
[[nodiscard]] iterable each() noexcept {
return {internal::extended_storage_iterator{base_type::begin(), begin()}, internal::extended_storage_iterator{base_type::end(), end()}};
return iterable{{base_type::begin(), begin()}, {base_type::end(), end()}};
}
/*! @copydoc each */
[[nodiscard]] const_iterable each() const noexcept {
return {internal::extended_storage_iterator{base_type::cbegin(), cbegin()}, internal::extended_storage_iterator{base_type::cend(), cend()}};
return const_iterable{{base_type::cbegin(), cbegin()}, {base_type::cend(), cend()}};
}
/**
@@ -755,12 +772,12 @@ public:
* @return A reverse iterable object to use to _visit_ the storage.
*/
[[nodiscard]] reverse_iterable reach() noexcept {
return {internal::extended_storage_iterator{base_type::rbegin(), rbegin()}, internal::extended_storage_iterator{base_type::rend(), rend()}};
return reverse_iterable{{base_type::rbegin(), rbegin()}, {base_type::rend(), rend()}};
}
/*! @copydoc reach */
[[nodiscard]] const_reverse_iterable reach() const noexcept {
return {internal::extended_storage_iterator{base_type::crbegin(), crbegin()}, internal::extended_storage_iterator{base_type::crend(), crend()}};
return const_reverse_iterable{{base_type::crbegin(), crbegin()}, {base_type::crend(), crend()}};
}
private:
@@ -769,24 +786,27 @@ private:
/*! @copydoc basic_storage */
template<typename Type, typename Entity, typename Allocator>
class basic_storage<Type, Entity, Allocator, std::enable_if_t<component_traits<Type>::page_size == 0u>>
class basic_storage<Type, Entity, Allocator, std::enable_if_t<component_traits<Type, Entity>::page_size == 0u>>
: public basic_sparse_set<Entity, typename std::allocator_traits<Allocator>::template rebind_alloc<Entity>> {
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
using traits_type = component_traits<Type, Entity>;
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Base type. */
using base_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
/*! @brief Element type. */
using element_type = Type;
/*! @brief Type of the objects assigned to entities. */
using value_type = Type;
/*! @brief Component traits. */
using traits_type = component_traits<value_type>;
using value_type = void;
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Signed integer type. */
using difference_type = std::ptrdiff_t;
/*! @brief Extended iterable storage proxy. */
using iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::iterator>>;
/*! @brief Constant extended iterable storage proxy. */
@@ -795,6 +815,8 @@ public:
using reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::reverse_iterator>>;
/*! @brief Constant extended reverse iterable storage proxy. */
using const_reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_reverse_iterator>>;
/*! @brief Storage deletion policy. */
static constexpr deletion_policy storage_policy{traits_type::in_place_delete};
/*! @brief Default constructor. */
basic_storage()
@@ -805,7 +827,10 @@ public:
* @param allocator The allocator to use.
*/
explicit basic_storage(const allocator_type &allocator)
: base_type{type_id<value_type>(), deletion_policy{traits_type::in_place_delete}, allocator} {}
: base_type{type_id<element_type>(), storage_policy, allocator} {}
/*! @brief Default copy constructor, deleted on purpose. */
basic_storage(const basic_storage &) = delete;
/**
* @brief Move constructor.
@@ -818,9 +843,18 @@ public:
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept
basic_storage(basic_storage &&other, const allocator_type &allocator)
: base_type{std::move(other), allocator} {}
/*! @brief Default destructor. */
~basic_storage() override = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This storage.
*/
basic_storage &operator=(const basic_storage &) = delete;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
@@ -834,7 +868,7 @@ public:
*/
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
// std::allocator<void> has no cross constructors (waiting for C++20)
if constexpr(std::is_void_v<value_type> && !std::is_constructible_v<allocator_type, typename base_type::allocator_type>) {
if constexpr(std::is_void_v<element_type> && !std::is_constructible_v<allocator_type, typename base_type::allocator_type>) {
return allocator_type{};
} else {
return allocator_type{base_type::get_allocator()};
@@ -851,21 +885,16 @@ public:
* @param entt A valid identifier.
*/
void get([[maybe_unused]] const entity_type entt) const noexcept {
ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity");
ENTT_ASSERT(base_type::contains(entt), "Invalid entity");
}
/**
* @brief Returns an empty tuple.
*
* @warning
* Attempting to use an entity that doesn't belong to the storage results in
* undefined behavior.
*
* @param entt A valid identifier.
* @return Returns an empty tuple.
*/
[[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const noexcept {
ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity");
ENTT_ASSERT(base_type::contains(entt), "Invalid entity");
return std::tuple{};
}
@@ -876,11 +905,9 @@ public:
* Attempting to use an entity that already belongs to the storage results
* in undefined behavior.
*
* @tparam Args Types of arguments to use to construct the object.
* @param entt A valid identifier.
*/
template<typename... Args>
void emplace(const entity_type entt, Args &&...) {
void emplace(const entity_type entt) {
base_type::try_emplace(entt, false);
}
@@ -892,19 +919,18 @@ public:
*/
template<typename... Func>
void patch([[maybe_unused]] const entity_type entt, Func &&...func) {
ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity");
ENTT_ASSERT(base_type::contains(entt), "Invalid entity");
(std::forward<Func>(func)(), ...);
}
/**
* @brief Assigns entities to a storage.
* @tparam It Type of input iterator.
* @tparam Args Types of optional arguments.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
*/
template<typename It, typename... Args>
void insert(It first, It last, Args &&...) {
template<typename It>
void insert(It first, It last) {
for(; first != last; ++first) {
base_type::try_emplace(*first, true);
}
@@ -918,12 +944,12 @@ public:
* @return An iterable object to use to _visit_ the storage.
*/
[[nodiscard]] iterable each() noexcept {
return {internal::extended_storage_iterator{base_type::begin()}, internal::extended_storage_iterator{base_type::end()}};
return iterable{base_type::begin(), base_type::end()};
}
/*! @copydoc each */
[[nodiscard]] const_iterable each() const noexcept {
return {internal::extended_storage_iterator{base_type::cbegin()}, internal::extended_storage_iterator{base_type::cend()}};
return const_iterable{base_type::cbegin(), base_type::cend()};
}
/**
@@ -934,12 +960,12 @@ public:
* @return A reverse iterable object to use to _visit_ the storage.
*/
[[nodiscard]] reverse_iterable reach() noexcept {
return {internal::extended_storage_iterator{base_type::rbegin()}, internal::extended_storage_iterator{base_type::rend()}};
return reverse_iterable{{base_type::rbegin()}, {base_type::rend()}};
}
/*! @copydoc reach */
[[nodiscard]] const_reverse_iterable reach() const noexcept {
return {internal::extended_storage_iterator{base_type::crbegin()}, internal::extended_storage_iterator{base_type::crend()}};
return const_reverse_iterable{{base_type::crbegin()}, {base_type::crend()}};
}
};
@@ -953,35 +979,57 @@ class basic_storage<Entity, Entity, Allocator>
: public basic_sparse_set<Entity, Allocator> {
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, Entity>, "Invalid value type");
using underlying_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
using underlying_iterator = typename underlying_type::basic_iterator;
using underlying_iterator = typename basic_sparse_set<Entity, Allocator>::basic_iterator;
using traits_type = entt_traits<Entity>;
auto entity_at(const std::size_t pos) const noexcept {
ENTT_ASSERT(pos < underlying_type::traits_type::to_entity(null), "Invalid element");
return underlying_type::traits_type::combine(static_cast<typename underlying_type::traits_type::entity_type>(pos), {});
auto from_placeholder() noexcept {
const auto entt = traits_type::combine(static_cast<typename traits_type::entity_type>(placeholder), {});
ENTT_ASSERT(entt != null, "No more entities available");
placeholder += static_cast<size_type>(entt != null);
return entt;
}
auto next() noexcept {
entity_type entt = from_placeholder();
while(base_type::current(entt) != traits_type::to_version(tombstone) && entt != null) {
entt = from_placeholder();
}
return entt;
}
protected:
/*! @brief Erases all entities of a storage. */
void pop_all() override {
base_type::pop_all();
placeholder = {};
}
/**
* @brief Assigns an entity to a storage.
* @param hint A valid identifier.
* @return Iterator pointing to the emplaced element.
*/
underlying_iterator try_emplace(const Entity hint, const bool, const void *) override {
return base_type::find(emplace(hint));
return base_type::find(generate(hint));
}
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Base type. */
using base_type = basic_sparse_set<Entity, Allocator>;
/*! @brief Element type. */
using element_type = Entity;
/*! @brief Type of the objects assigned to entities. */
using value_type = Entity;
using value_type = void;
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Signed integer type. */
using difference_type = std::ptrdiff_t;
/*! @brief Extended iterable storage proxy. */
using iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::iterator>>;
/*! @brief Constant extended iterable storage proxy. */
@@ -990,6 +1038,8 @@ public:
using reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::reverse_iterator>>;
/*! @brief Constant extended reverse iterable storage proxy. */
using const_reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_reverse_iterator>>;
/*! @brief Storage deletion policy. */
static constexpr deletion_policy storage_policy = deletion_policy::swap_only;
/*! @brief Default constructor. */
basic_storage()
@@ -1001,22 +1051,38 @@ public:
* @param allocator The allocator to use.
*/
explicit basic_storage(const allocator_type &allocator)
: base_type{type_id<void>(), deletion_policy::swap_only, allocator} {}
: base_type{type_id<void>(), storage_policy, allocator} {}
/*! @brief Default copy constructor, deleted on purpose. */
basic_storage(const basic_storage &) = delete;
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
basic_storage(basic_storage &&other) noexcept
: base_type{std::move(other)} {}
: base_type{static_cast<base_type &&>(other)},
placeholder{other.placeholder} {}
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept
: base_type{std::move(other), allocator} {}
// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
basic_storage(basic_storage &&other, const allocator_type &allocator)
: base_type{static_cast<base_type &&>(other), allocator},
placeholder{other.placeholder} {}
/*! @brief Default destructor. */
~basic_storage() override = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This storage.
*/
basic_storage &operator=(const basic_storage &) = delete;
/**
* @brief Move assignment operator.
@@ -1024,10 +1090,21 @@ public:
* @return This storage.
*/
basic_storage &operator=(basic_storage &&other) noexcept {
placeholder = other.placeholder;
base_type::operator=(std::move(other));
return *this;
}
/**
* @brief Exchanges the contents with those of a given storage.
* @param other Storage to exchange the content with.
*/
void swap(basic_storage &other) noexcept {
using std::swap;
swap(placeholder, other.placeholder);
base_type::swap(other);
}
/**
* @brief Returns the object assigned to an entity, that is `void`.
*
@@ -1043,11 +1120,6 @@ public:
/**
* @brief Returns an empty tuple.
*
* @warning
* Attempting to use an entity that doesn't belong to the storage results in
* undefined behavior.
*
* @param entt A valid identifier.
* @return Returns an empty tuple.
*/
@@ -1060,9 +1132,9 @@ public:
* @brief Creates a new identifier or recycles a destroyed one.
* @return A valid identifier.
*/
entity_type emplace() {
entity_type generate() {
const auto len = base_type::free_list();
const auto entt = (len == base_type::size()) ? entity_at(len) : base_type::data()[len];
const auto entt = (len == base_type::size()) ? next() : base_type::data()[len];
return *base_type::try_emplace(entt, true);
}
@@ -1075,22 +1147,30 @@ public:
* @param hint Required identifier.
* @return A valid identifier.
*/
entity_type emplace(const entity_type hint) {
if(hint == null || hint == tombstone) {
return emplace();
} else if(const auto curr = underlying_type::traits_type::construct(underlying_type::traits_type::to_entity(hint), base_type::current(hint)); curr == tombstone) {
const auto pos = static_cast<size_type>(underlying_type::traits_type::to_entity(hint));
const auto entt = *base_type::try_emplace(hint, true);
while(!(pos < base_type::size())) {
base_type::try_emplace(entity_at(base_type::size() - 1u), false);
entity_type generate(const entity_type hint) {
if(hint != null && hint != tombstone) {
if(const auto curr = traits_type::construct(traits_type::to_entity(hint), base_type::current(hint)); curr == tombstone || !(base_type::index(curr) < base_type::free_list())) {
return *base_type::try_emplace(hint, true);
}
}
return entt;
} else if(const auto idx = base_type::index(curr); idx < base_type::free_list()) {
return emplace();
} else {
return *base_type::try_emplace(hint, true);
return generate();
}
/**
* @brief Assigns each element in a range an identifier.
* @tparam It Type of mutable forward iterator.
* @param first An iterator to the first element of the range to generate.
* @param last An iterator past the last element of the range to generate.
*/
template<typename It>
void generate(It first, It last) {
for(const auto sz = base_type::size(); first != last && base_type::free_list() != sz; ++first) {
*first = *base_type::try_emplace(base_type::data()[base_type::free_list()], true);
}
for(; first != last; ++first) {
*first = *base_type::try_emplace(next(), true);
}
}
@@ -1106,52 +1186,6 @@ public:
(std::forward<Func>(func)(), ...);
}
/**
* @brief Assigns each element in a range an identifier.
* @tparam It Type of mutable forward iterator.
* @param first An iterator to the first element of the range to generate.
* @param last An iterator past the last element of the range to generate.
*/
template<typename It>
void insert(It first, It last) {
for(const auto sz = base_type::size(); first != last && base_type::free_list() != sz; ++first) {
*first = *base_type::try_emplace(base_type::data()[base_type::free_list()], true);
}
for(; first != last; ++first) {
*first = *base_type::try_emplace(entity_at(base_type::free_list()), true);
}
}
/**
* @brief Makes all elements in a range contiguous.
* @tparam It Type of forward iterator.
* @param first An iterator to the first element of the range to pack.
* @param last An iterator past the last element of the range to pack.
* @return The number of elements within the newly created range.
*/
template<typename It>
[[deprecated("use sort_as instead")]] size_type pack(It first, It last) {
base_type::sort_as(first, last);
return static_cast<size_type>(std::distance(first, last));
}
/**
* @brief Returns the number of elements considered still in use.
* @return The number of elements considered still in use.
*/
[[deprecated("use free_list() instead")]] [[nodiscard]] size_type in_use() const noexcept {
return base_type::free_list();
}
/**
* @brief Sets the number of elements considered still in use.
* @param len The number of elements considered still in use.
*/
[[deprecated("use free_list(len) instead")]] void in_use(const size_type len) noexcept {
base_type::free_list(len);
}
/**
* @brief Returns an iterable object to use to _visit_ a storage.
*
@@ -1160,12 +1194,14 @@ public:
* @return An iterable object to use to _visit_ the storage.
*/
[[nodiscard]] iterable each() noexcept {
return {internal::extended_storage_iterator{base_type::begin(0)}, internal::extended_storage_iterator{base_type::end(0)}};
return std::as_const(*this).each();
}
/*! @copydoc each */
[[nodiscard]] const_iterable each() const noexcept {
return {internal::extended_storage_iterator{base_type::cbegin(0)}, internal::extended_storage_iterator{base_type::cend(0)}};
const auto it = base_type::cend();
const auto offset = static_cast<difference_type>(base_type::free_list());
return const_iterable{it - offset, it};
}
/**
@@ -1176,13 +1212,29 @@ public:
* @return A reverse iterable object to use to _visit_ the storage.
*/
[[nodiscard]] reverse_iterable reach() noexcept {
return {internal::extended_storage_iterator{base_type::rbegin()}, internal::extended_storage_iterator{base_type::rend(0)}};
return std::as_const(*this).reach();
}
/*! @copydoc reach */
[[nodiscard]] const_reverse_iterable reach() const noexcept {
return {internal::extended_storage_iterator{base_type::crbegin()}, internal::extended_storage_iterator{base_type::crend(0)}};
const auto it = base_type::crbegin();
const auto offset = static_cast<difference_type>(base_type::free_list());
return const_reverse_iterable{it, it + offset};
}
/**
* @brief Sets the starting identifier for generation.
*
* The version is ignored, regardless of the value.
*
* @param hint A valid identifier.
*/
void start_from(const entity_type hint) {
placeholder = static_cast<size_type>(traits_type::to_entity(hint));
}
private:
size_type placeholder{};
};
} // namespace entt

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,16 @@
/*! @brief `EnTT` default namespace. */
namespace entt {}
// 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 "container/table.hpp"
#include "core/algorithm.hpp"
#include "core/any.hpp"
#include "core/attribute.h"
#include "core/bit.hpp"
#include "core/compressed_pair.hpp"
#include "core/enum.hpp"
#include "core/family.hpp"
@@ -15,6 +19,7 @@
#include "core/iterator.hpp"
#include "core/memory.hpp"
#include "core/monostate.hpp"
#include "core/ranges.hpp"
#include "core/tuple.hpp"
#include "core/type_info.hpp"
#include "core/type_traits.hpp"
@@ -25,8 +30,8 @@
#include "entity/handle.hpp"
#include "entity/helper.hpp"
#include "entity/mixin.hpp"
#include "entity/observer.hpp"
#include "entity/organizer.hpp"
#include "entity/ranges.hpp"
#include "entity/registry.hpp"
#include "entity/runtime_view.hpp"
#include "entity/snapshot.hpp"
@@ -50,7 +55,6 @@
#include "meta/template.hpp"
#include "meta/type_traits.hpp"
#include "meta/utility.hpp"
#include "platform/android-ndk-r17.hpp"
#include "poly/poly.hpp"
#include "process/process.hpp"
#include "process/scheduler.hpp"

View File

@@ -20,6 +20,10 @@ template<typename It>
class edge_iterator {
using size_type = std::size_t;
void find_next() noexcept {
for(; pos != last && !it[static_cast<typename It::difference_type>(pos)]; pos += offset) {}
}
public:
using value_type = std::pair<size_type, size_type>;
using pointer = input_iterator_pointer<value_type>;
@@ -28,29 +32,26 @@ public:
using iterator_category = std::input_iterator_tag;
using iterator_concept = std::forward_iterator_tag;
constexpr edge_iterator() noexcept
: it{},
vert{},
pos{},
last{},
offset{} {}
constexpr edge_iterator() noexcept = default;
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
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) {}
find_next();
}
constexpr edge_iterator &operator++() noexcept {
for(pos += offset; pos != last && !it[pos]; pos += offset) {}
pos += offset;
find_next();
return *this;
}
constexpr edge_iterator operator++(int) noexcept {
edge_iterator orig = *this;
const edge_iterator orig = *this;
return ++(*this), orig;
}
@@ -66,20 +67,20 @@ public:
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;
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 {
[[nodiscard]] 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 {
[[nodiscard]] constexpr bool operator!=(const edge_iterator<Container> &lhs, const edge_iterator<Container> &rhs) noexcept {
return !(lhs == rhs);
}
@@ -111,16 +112,17 @@ public:
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. */
/*! @brief Out-edge iterator type. */
using out_edge_iterator = edge_iterator;
/*! @brief In edge iterator type. */
/*! @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} {}
: adjacency_matrix{0u} {
}
/**
* @brief Constructs an empty container with a given allocator.
@@ -139,12 +141,8 @@ public:
: 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 Default copy constructor. */
adjacency_matrix(const adjacency_matrix &) = default;
/**
* @brief Allocator-extended copy constructor.
@@ -155,12 +153,8 @@ public:
: 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 Default move constructor. */
adjacency_matrix(adjacency_matrix &&) noexcept = default;
/**
* @brief Allocator-extended move constructor.
@@ -169,28 +163,31 @@ public:
*/
adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator)
: matrix{std::move(other.matrix), allocator},
vert{std::exchange(other.vert, 0u)} {}
vert{other.vert} {}
/*! @brief Default destructor. */
~adjacency_matrix() = default;
/**
* @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;
}
adjacency_matrix &operator=(const adjacency_matrix &) = default;
/**
* @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;
adjacency_matrix &operator=(adjacency_matrix &&) noexcept = default;
/**
* @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) noexcept {
using std::swap;
swap(matrix, other.matrix);
swap(vert, other.vert);
}
/**
@@ -208,13 +205,16 @@ public:
}
/**
* @brief Exchanges the contents with those of a given adjacency matrix.
* @param other Adjacency matrix to exchange the content with.
* @brief Returns true if an adjacency matrix is empty, false otherwise.
*
* @warning
* Potentially expensive, try to avoid it on hot paths.
*
* @return True if the adjacency matrix is empty, false otherwise.
*/
void swap(adjacency_matrix &other) {
using std::swap;
swap(matrix, other.matrix);
swap(vert, other.vert);
[[nodiscard]] bool empty() const noexcept {
const auto iterable = edges();
return (iterable.begin() == iterable.end());
}
/**
@@ -244,9 +244,9 @@ public:
}
/**
* @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.
* @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();
@@ -256,9 +256,9 @@ public:
}
/**
* @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.
* @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();

View File

@@ -29,9 +29,9 @@ 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 task_container_type = dense_set<id_type, identity, std::equal_to<>, 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>>>;
using deps_container_type = dense_map<id_type, ro_rw_container_type, identity, std::equal_to<>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, ro_rw_container_type>>>;
using adjacency_matrix_type = adjacency_matrix<directed_tag, typename alloc_traits::template rebind_alloc<std::size_t>>;
void emplace(const id_type res, const bool is_rw) {
@@ -135,8 +135,7 @@ public:
explicit basic_flow(const allocator_type &allocator)
: index{0u, allocator},
vertices{allocator},
deps{allocator},
sync_on{} {}
deps{allocator} {}
/*! @brief Default copy constructor. */
basic_flow(const basic_flow &) = default;
@@ -166,6 +165,9 @@ public:
deps{std::move(other.deps), allocator},
sync_on{other.sync_on} {}
/*! @brief Default destructor. */
~basic_flow() = default;
/**
* @brief Default copy assignment operator.
* @return This flow builder.
@@ -178,6 +180,18 @@ public:
*/
basic_flow &operator=(basic_flow &&) noexcept = default;
/**
* @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) noexcept {
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 associated allocator.
* @return The associated allocator.
@@ -192,7 +206,7 @@ public:
* @return The requested identifier.
*/
[[nodiscard]] id_type operator[](const size_type pos) const {
return vertices.cbegin()[pos];
return vertices.cbegin()[static_cast<typename task_container_type::difference_type>(pos)];
}
/*! @brief Clears the flow builder. */
@@ -204,15 +218,11 @@ public:
}
/**
* @brief Exchanges the contents with those of a given flow builder.
* @param other Flow builder to exchange the content with.
* @brief Returns true if a flow builder contains no tasks, false otherwise.
* @return True if the flow builder contains no tasks, false otherwise.
*/
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);
[[nodiscard]] bool empty() const noexcept {
return vertices.empty();
}
/**
@@ -333,7 +343,7 @@ private:
compressed_pair<size_type, allocator_type> index;
task_container_type vertices;
deps_container_type deps;
size_type sync_on;
size_type sync_on{};
};
} // namespace entt

View File

@@ -38,9 +38,19 @@ public:
/*! @brief Default constructor, deleted on purpose. */
locator() = delete;
/*! @brief Default copy constructor, deleted on purpose. */
locator(const locator &) = delete;
/*! @brief Default destructor, deleted on purpose. */
~locator() = delete;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This locator.
*/
locator &operator=(const locator &) = delete;
/**
* @brief Checks whether a service locator contains a value.
* @return True if the service locator contains a value, false otherwise.
@@ -139,6 +149,7 @@ public:
private:
// std::shared_ptr because of its type erased allocator which is useful here
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
inline static std::shared_ptr<Service> service{};
};

View File

@@ -1,7 +1,10 @@
// IWYU pragma: always_keep
#ifndef ENTT_META_CONTAINER_HPP
#define ENTT_META_CONTAINER_HPP
#include <array>
#include <cstddef>
#include <deque>
#include <iterator>
#include <list>
@@ -13,7 +16,9 @@
#include <vector>
#include "../container/dense_map.hpp"
#include "../container/dense_set.hpp"
#include "../core/type_traits.hpp"
#include "context.hpp"
#include "fwd.hpp"
#include "meta.hpp"
#include "type_traits.hpp"
@@ -22,14 +27,14 @@ namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename, typename = void>
struct fixed_size_sequence_container: std::true_type {};
template<typename Type, typename = void>
struct sequence_container_extent: integral_constant<meta_dynamic_extent> {};
template<typename Type>
struct fixed_size_sequence_container<Type, std::void_t<decltype(&Type::clear)>>: std::false_type {};
struct sequence_container_extent<Type, std::enable_if_t<is_complete_v<std::tuple_size<Type>>>>: integral_constant<std::tuple_size_v<Type>> {};
template<typename Type>
inline constexpr bool fixed_size_sequence_container_v = fixed_size_sequence_container<Type>::value;
inline constexpr std::size_t sequence_container_extent_v = sequence_container_extent<Type>::value;
template<typename, typename = void>
struct key_only_associative_container: std::true_type {};
@@ -58,16 +63,18 @@ inline constexpr bool reserve_aware_container_v = reserve_aware_container<Type>:
*/
template<typename Type>
struct basic_meta_sequence_container_traits {
static_assert(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>, "Unexpected type");
/*! @brief True in case of key-only containers, false otherwise. */
static constexpr bool fixed_size = internal::fixed_size_sequence_container_v<Type>;
static_assert(std::is_same_v<Type, std::remove_const_t<std::remove_reference_t<Type>>>, "Unexpected type");
/*! @brief Unsigned integer type. */
using size_type = typename meta_sequence_container::size_type;
/*! @brief Meta iterator type. */
using iterator = typename meta_sequence_container::iterator;
/*! @brief Number of elements, or `meta_dynamic_extent` if dynamic. */
static constexpr std::size_t extent = internal::sequence_container_extent_v<Type>;
/*! @brief True in case of fixed size containers, false otherwise. */
[[deprecated("use ::extent instead")]] static constexpr bool fixed_size = (extent != meta_dynamic_extent);
/**
* @brief Returns the number of elements in a container.
* @param container Opaque pointer to a container of the given type.
@@ -83,11 +90,11 @@ struct basic_meta_sequence_container_traits {
* @return True in case of success, false otherwise.
*/
[[nodiscard]] static bool clear([[maybe_unused]] void *container) {
if constexpr(fixed_size) {
return false;
} else {
if constexpr(extent == meta_dynamic_extent) {
static_cast<Type *>(container)->clear();
return true;
} else {
return false;
}
}
@@ -113,36 +120,27 @@ struct basic_meta_sequence_container_traits {
* @return True in case of success, false otherwise.
*/
[[nodiscard]] static bool resize([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) {
if constexpr(fixed_size || !std::is_default_constructible_v<typename Type::value_type>) {
return false;
} else {
if constexpr((extent == meta_dynamic_extent) && std::is_default_constructible_v<typename Type::value_type>) {
static_cast<Type *>(container)->resize(sz);
return true;
} else {
return false;
}
}
/**
* @brief Returns a possibly const iterator to the beginning.
* @brief Returns a possibly const iterator to the beginning or the end.
* @param area The context to pass to the newly created iterator.
* @param container Opaque pointer to a container of the given type.
* @param as_const Const opaque pointer fallback.
* @return An iterator to the first element of the container.
* @param end False to get a pointer that is past the last element.
* @return An iterator to the first or past the last element of the
* container.
*/
static iterator begin(const meta_ctx &area, void *container, const void *as_const) {
return container ? iterator{area, static_cast<Type *>(container)->begin()}
: iterator{area, static_cast<const Type *>(as_const)->begin()};
}
/**
* @brief Returns a possibly const iterator to the end.
* @param area The context to pass to the newly created iterator.
* @param container Opaque pointer to a container of the given type.
* @param as_const Const opaque pointer fallback.
* @return An iterator that is past the last element of the container.
*/
static iterator end(const meta_ctx &area, void *container, const void *as_const) {
return container ? iterator{area, static_cast<Type *>(container)->end()}
: iterator{area, static_cast<const Type *>(as_const)->end()};
static iterator iter(const meta_ctx &area, void *container, const void *as_const, const bool end) {
return (container == nullptr)
? iterator{area, end ? static_cast<const Type *>(as_const)->cend() : static_cast<const Type *>(as_const)->cbegin()}
: iterator{area, end ? static_cast<Type *>(container)->end() : static_cast<Type *>(container)->begin()};
}
/**
@@ -157,14 +155,14 @@ struct basic_meta_sequence_container_traits {
* @param it Iterator before which the element will be inserted.
* @return A possibly invalid iterator to the inserted element.
*/
[[nodiscard]] static iterator insert(const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const void *value, [[maybe_unused]] const void *cref, [[maybe_unused]] const iterator &it) {
if constexpr(fixed_size) {
return iterator{area};
} else {
[[nodiscard]] static iterator insert([[maybe_unused]] const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const void *value, [[maybe_unused]] const void *cref, [[maybe_unused]] const iterator &it) {
if constexpr(extent == meta_dynamic_extent) {
auto *const non_const = any_cast<typename Type::iterator>(&it.base());
return {area, static_cast<Type *>(container)->insert(
non_const ? *non_const : any_cast<const typename Type::const_iterator &>(it.base()),
value ? *static_cast<const typename Type::value_type *>(value) : *static_cast<const std::remove_reference_t<typename Type::const_reference> *>(cref))};
(value != nullptr) ? *static_cast<const typename Type::value_type *>(value) : *static_cast<const std::remove_reference_t<typename Type::const_reference> *>(cref))};
} else {
return iterator{};
}
}
@@ -175,12 +173,12 @@ struct basic_meta_sequence_container_traits {
* @param it An opaque iterator to the element to erase.
* @return A possibly invalid iterator following the last removed element.
*/
[[nodiscard]] static iterator erase(const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const iterator &it) {
if constexpr(fixed_size) {
return iterator{area};
} else {
[[nodiscard]] static iterator erase([[maybe_unused]] const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const iterator &it) {
if constexpr(extent == meta_dynamic_extent) {
auto *const non_const = any_cast<typename Type::iterator>(&it.base());
return {area, static_cast<Type *>(container)->erase(non_const ? *non_const : any_cast<const typename Type::const_iterator &>(it.base()))};
} else {
return iterator{};
}
}
};
@@ -191,16 +189,16 @@ struct basic_meta_sequence_container_traits {
*/
template<typename Type>
struct basic_meta_associative_container_traits {
static_assert(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>, "Unexpected type");
/*! @brief True in case of key-only containers, false otherwise. */
static constexpr bool key_only = internal::key_only_associative_container_v<Type>;
static_assert(std::is_same_v<Type, std::remove_const_t<std::remove_reference_t<Type>>>, "Unexpected type");
/*! @brief Unsigned integer type. */
using size_type = typename meta_associative_container::size_type;
/*! @brief Meta iterator type. */
using iterator = typename meta_associative_container::iterator;
/*! @brief True in case of key-only containers, false otherwise. */
static constexpr bool key_only = internal::key_only_associative_container_v<Type>;
/**
* @brief Returns the number of elements in a container.
* @param container Opaque pointer to a container of the given type.
@@ -236,27 +234,18 @@ struct basic_meta_associative_container_traits {
}
/**
* @brief Returns a possibly const iterator to the beginning.
* @brief Returns a possibly const iterator to the beginning or the end.
* @param area The context to pass to the newly created iterator.
* @param container Opaque pointer to a container of the given type.
* @param as_const Const opaque pointer fallback.
* @return An iterator to the first element of the container.
* @param end False to get a pointer that is past the last element.
* @return An iterator to the first or past the last element of the
* container.
*/
static iterator begin(const meta_ctx &area, void *container, const void *as_const) {
return container ? iterator{area, std::bool_constant<key_only>{}, static_cast<Type *>(container)->begin()}
: iterator{area, std::bool_constant<key_only>{}, static_cast<const Type *>(as_const)->begin()};
}
/**
* @brief Returns a possibly const iterator to the end.
* @param area The context to pass to the newly created iterator.
* @param container Opaque pointer to a container of the given type.
* @param as_const Const opaque pointer fallback.
* @return An iterator that is past the last element of the container.
*/
static iterator end(const meta_ctx &area, void *container, const void *as_const) {
return container ? iterator{area, std::bool_constant<key_only>{}, static_cast<Type *>(container)->end()}
: iterator{area, std::bool_constant<key_only>{}, static_cast<const Type *>(as_const)->end()};
static iterator iter(const meta_ctx &area, void *container, const void *as_const, const bool end) {
return (container == nullptr)
? iterator{area, std::bool_constant<key_only>{}, end ? static_cast<const Type *>(as_const)->cend() : static_cast<const Type *>(as_const)->cbegin()}
: iterator{area, std::bool_constant<key_only>{}, end ? static_cast<Type *>(container)->end() : static_cast<Type *>(container)->begin()};
}
/**
@@ -293,8 +282,8 @@ struct basic_meta_associative_container_traits {
* @return An iterator to the element with the given key, if any.
*/
static iterator find(const meta_ctx &area, void *container, const void *as_const, const void *key) {
return container ? iterator{area, std::bool_constant<key_only>{}, static_cast<Type *>(container)->find(*static_cast<const typename Type::key_type *>(key))}
: iterator{area, std::bool_constant<key_only>{}, static_cast<const Type *>(as_const)->find(*static_cast<const typename Type::key_type *>(key))};
return (container != nullptr) ? iterator{area, std::bool_constant<key_only>{}, static_cast<Type *>(container)->find(*static_cast<const typename Type::key_type *>(key))}
: iterator{area, std::bool_constant<key_only>{}, static_cast<const Type *>(as_const)->find(*static_cast<const typename Type::key_type *>(key))};
}
};

View File

@@ -1,24 +1,24 @@
#ifndef ENTT_META_CTX_HPP
#define ENTT_META_CTX_HPP
#include <memory>
#include "../container/dense_map.hpp"
#include "../core/fwd.hpp"
#include "../core/utility.hpp"
#include "fwd.hpp"
namespace entt {
class meta_ctx;
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
struct meta_type_node;
struct meta_context {
dense_map<id_type, meta_type_node, identity> value{};
dense_map<id_type, std::unique_ptr<meta_type_node>, identity> value;
[[nodiscard]] inline static meta_context &from(meta_ctx &ctx);
[[nodiscard]] inline static const meta_context &from(const meta_ctx &ctx);
[[nodiscard]] inline static meta_context &from(meta_ctx &);
[[nodiscard]] inline static const meta_context &from(const meta_ctx &);
};
} // namespace internal

View File

@@ -2,17 +2,21 @@
#define ENTT_META_FACTORY_HPP
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/bit.hpp"
#include "../core/fwd.hpp"
#include "../core/hashed_string.hpp"
#include "../core/type_info.hpp"
#include "../core/type_traits.hpp"
#include "../locator/locator.hpp"
#include "context.hpp"
#include "fwd.hpp"
#include "meta.hpp"
#include "node.hpp"
#include "policy.hpp"
@@ -25,64 +29,139 @@ namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
[[nodiscard]] 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()];
}
class basic_meta_factory {
using invoke_type = std::remove_pointer_t<decltype(meta_func_node::invoke)>;
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]));
[[nodiscard]] auto &fetch_node() noexcept {
return *meta_context::from(*ctx).value[parent];
}
return parent.details->func.insert_or_assign(id, std::move(node)).first->second;
}
[[nodiscard]] auto *find_member_or_assert() {
auto *member = find_member<&meta_data_node::id>(fetch_node().details->data, bucket);
ENTT_ASSERT(member != nullptr, "Cannot find member");
return member;
}
[[nodiscard]] auto *find_overload_or_assert() {
auto *overload = find_overload(find_member<&meta_func_node::id>(fetch_node().details->func, bucket), invoke);
ENTT_ASSERT(overload != nullptr, "Cannot find overload");
return overload;
}
void reset_bucket(const id_type id, invoke_type *const ref = nullptr) {
invoke = ref;
bucket = id;
}
protected:
void type(const id_type id, const char *name) noexcept {
reset_bucket(parent);
auto &elem = fetch_node();
ENTT_ASSERT(elem.id == id || !resolve(*ctx, id), "Duplicate identifier");
elem.name = name;
elem.id = id;
}
template<typename Type>
void insert_or_assign(Type node) {
auto &elem = fetch_node();
reset_bucket(parent);
if constexpr(std::is_same_v<Type, meta_base_node>) {
auto *member = find_member<&meta_base_node::type>(elem.details->base, node.type);
member ? (*member = node) : elem.details->base.emplace_back(node);
} else if constexpr(std::is_same_v<Type, meta_conv_node>) {
auto *member = find_member<&meta_conv_node::type>(elem.details->conv, node.type);
member ? (*member = node) : elem.details->conv.emplace_back(node);
} else {
static_assert(std::is_same_v<Type, meta_ctor_node>, "Unexpected type");
auto *member = find_member<&meta_ctor_node::id>(elem.details->ctor, node.id);
member ? (*member = node) : elem.details->ctor.emplace_back(node);
}
}
void data(meta_data_node node) {
auto &elem = fetch_node();
reset_bucket(node.id);
if(auto *member = find_member<&meta_data_node::id>(elem.details->data, node.id); member == nullptr) {
elem.details->data.emplace_back(std::move(node));
} else if(member->set != node.set || member->get != node.get) {
*member = std::move(node);
}
}
void func(meta_func_node node) {
auto &elem = fetch_node();
reset_bucket(node.id, node.invoke);
if(auto *member = find_member<&meta_func_node::id>(elem.details->func, node.id); member == nullptr) {
elem.details->func.emplace_back(std::move(node));
} else if(auto *overload = find_overload(member, node.invoke); overload == nullptr) {
while(member->next != nullptr) { member = member->next.get(); }
member->next = std::make_unique<meta_func_node>(std::move(node));
}
}
void traits(const meta_traits value, const bool unset) {
auto set_or_unset_on = [=](auto &node) {
node.traits = (unset ? (node.traits & ~value) : (node.traits | value));
};
if(bucket == parent) {
set_or_unset_on(fetch_node());
} else if(invoke == nullptr) {
set_or_unset_on(*find_member_or_assert());
} else {
set_or_unset_on(*find_overload_or_assert());
}
}
void custom(meta_custom_node node) {
if(bucket == parent) {
fetch_node().custom = std::move(node);
} else if(invoke == nullptr) {
find_member_or_assert()->custom = std::move(node);
} else {
find_overload_or_assert()->custom = std::move(node);
}
}
public:
basic_meta_factory(meta_ctx &area, meta_type_node node)
: ctx{&area},
parent{node.info->hash()},
bucket{parent} {
if(auto *curr = meta_context::from(*ctx).value.try_emplace(parent, std::make_unique<meta_type_node>(std::move(node))).first->second.get(); curr->details == nullptr) {
curr->details = std::make_unique<meta_type_descriptor>();
}
}
private:
meta_ctx *ctx{};
id_type parent{};
id_type bucket{};
invoke_type *invoke{};
};
} // namespace internal
/*! @endcond */
/**
* @brief Basic meta factory to be used for reflection purposes.
* @tparam Type Reflected type for which the factory was created.
* @brief Meta factory to be used for reflection purposes.
* @tparam Type Type for which the factory was created.
*/
template<typename 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;
}
class meta_factory: private internal::basic_meta_factory {
using base_type = internal::basic_meta_factory;
public:
/*! @brief Type of object for which this factory builds a meta type. */
using element_type = Type;
/*! @brief Default constructor. */
meta_factory() noexcept
: meta_factory{locator<meta_ctx>::value_or()} {}
@@ -92,28 +171,25 @@ public:
* @param area The context into which to construct meta types.
*/
meta_factory(meta_ctx &area) noexcept
: ctx{&area},
bucket{},
info{&type_id<Type>()} {
auto &&elem = internal::owner(*ctx, *info);
: internal::basic_meta_factory{area, internal::setup_node_for<Type>()} {}
if(!elem.details) {
elem.details = std::make_shared<internal::meta_type_descriptor>();
}
bucket = &elem.details->prop;
/**
* @brief Assigns a custom unique identifier to a meta type.
* @param name A custom unique identifier as a **string literal**.
* @return A meta factory for the given type.
*/
meta_factory type(const char *name) noexcept {
return type(hashed_string::value(name), name);
}
/**
* @brief Assigns a custom unique identifier to a meta type.
* @param id A custom unique identifier.
* @param name An optional name for the type as a **string literal**.
* @return A 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;
meta_factory type(const id_type id, const char *name = nullptr) noexcept {
base_type::type(id, name);
return *this;
}
@@ -126,11 +202,10 @@ public:
* @return A meta factory for the parent type.
*/
template<typename Base>
auto base() noexcept {
meta_factory base() noexcept {
static_assert(!std::is_same_v<Type, Base> && std::is_base_of_v<Base, Type>, "Invalid base type");
auto *const op = +[](const void *instance) noexcept { return static_cast<const void *>(static_cast<const Base *>(static_cast<const Type *>(instance))); };
internal::owner(*ctx, *info).details->base.insert_or_assign(type_id<Base>().hash(), internal::meta_base_node{&internal::resolve<Base>, op});
bucket = nullptr;
base_type::insert_or_assign(internal::meta_base_node{type_id<Base>().hash(), &internal::resolve<Base>, op});
return *this;
}
@@ -148,10 +223,9 @@ public:
*/
template<auto Candidate>
auto conv() noexcept {
using conv_type = std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(Candidate), Type &>>>;
using conv_type = std::remove_const_t<std::remove_reference_t<std::invoke_result_t<decltype(Candidate), Type &>>>;
auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, std::invoke(Candidate, *static_cast<const Type *>(instance))); };
internal::owner(*ctx, *info).details->conv.insert_or_assign(type_id<conv_type>().hash(), internal::meta_conv_node{op});
bucket = nullptr;
base_type::insert_or_assign(internal::meta_conv_node{type_id<conv_type>().hash(), op});
return *this;
}
@@ -165,11 +239,10 @@ public:
* @return A meta factory for the parent type.
*/
template<typename To>
auto conv() noexcept {
using conv_type = std::remove_cv_t<std::remove_reference_t<To>>;
meta_factory conv() noexcept {
using conv_type = std::remove_const_t<std::remove_reference_t<To>>;
auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, static_cast<To>(*static_cast<const Type *>(instance))); };
internal::owner(*ctx, *info).details->conv.insert_or_assign(type_id<conv_type>().hash(), internal::meta_conv_node{op});
bucket = nullptr;
base_type::insert_or_assign(internal::meta_conv_node{type_id<conv_type>().hash(), op});
return *this;
}
@@ -186,13 +259,12 @@ public:
* @tparam Policy Optional policy (no policy set by default).
* @return A meta factory for the parent type.
*/
template<auto Candidate, typename Policy = as_is_t>
auto ctor() noexcept {
template<auto Candidate, typename Policy = as_value_t>
meta_factory ctor() noexcept {
using descriptor = meta_function_helper_t<Type, decltype(Candidate)>;
static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy");
static_assert(std::is_same_v<std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>, Type>, "The function doesn't return an object of the required type");
internal::owner(*ctx, *info).details->ctor.insert_or_assign(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>});
bucket = nullptr;
static_assert(std::is_same_v<std::remove_const_t<std::remove_reference_t<typename descriptor::return_type>>, Type>, "The function doesn't return an object of the required type");
base_type::insert_or_assign(internal::meta_ctor_node{type_id<typename descriptor::args_type>().hash(), descriptor::args_type::size, &meta_arg<typename descriptor::args_type>, &meta_construct<Type, Candidate, Policy>});
return *this;
}
@@ -207,42 +279,26 @@ public:
* @return A meta factory for the parent type.
*/
template<typename... Args>
auto ctor() noexcept {
meta_factory 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...)>;
internal::owner(*ctx, *info).details->ctor.insert_or_assign(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...>});
base_type::insert_or_assign(internal::meta_ctor_node{type_id<typename descriptor::args_type>().hash(), descriptor::args_type::size, &meta_arg<typename descriptor::args_type>, &meta_construct<Type, Args...>});
}
bucket = nullptr;
return *this;
}
/**
* @brief Assigns a meta destructor to a meta type.
*
* 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.
*
* @tparam Func The actual function to use as a destructor.
* @return A meta factory for the parent type.
* @brief Assigns a meta data to a meta type.
* @tparam Data The actual variable to attach to the meta type.
* @tparam Policy Optional policy (no policy set by default).
* @param name A custom unique identifier as a **string literal**.
* @return A meta factory for the given type.
*/
template<auto Func>
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 op = +[](void *instance) { std::invoke(Func, *static_cast<Type *>(instance)); };
internal::owner(*ctx, *info).dtor = internal::meta_dtor_node{op};
bucket = nullptr;
return *this;
template<auto Data, typename Policy = as_value_t>
meta_factory data(const char *name) noexcept {
return data<Data, Policy>(hashed_string::value(name), name);
}
/**
@@ -256,27 +312,26 @@ public:
* @tparam Data The actual variable to attach to the meta type.
* @tparam Policy Optional policy (no policy set by default).
* @param id Unique identifier.
* @param name An optional name for the meta data as a **string literal**.
* @return A meta factory for the parent type.
*/
template<auto Data, typename Policy = as_is_t>
auto data(const id_type id) noexcept {
template<auto Data, typename Policy = as_value_t>
meta_factory data(const id_type id, const char *name = nullptr) noexcept {
if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
using data_type = std::invoke_result_t<decltype(Data), 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,
base_type::data(
internal::meta_data_node{
id,
name,
/* this is never static */
std::is_const_v<std::remove_reference_t<data_type>> ? internal::meta_traits::is_const : internal::meta_traits::is_none,
1u,
&internal::resolve<std::remove_cv_t<std::remove_reference_t<data_type>>>,
&meta_arg<type_list<std::remove_cv_t<std::remove_reference_t<data_type>>>>,
&internal::resolve<std::remove_const_t<std::remove_reference_t<data_type>>>,
&meta_arg<type_list<std::remove_const_t<std::remove_reference_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)>;
@@ -286,23 +341,35 @@ public:
static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
}
auto &&elem = internal::meta_extend(
internal::owner(*ctx, *info),
id,
base_type::data(
internal::meta_data_node{
((std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<data_type>>> || std::is_const_v<std::remove_reference_t<data_type>>) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static,
id,
name,
((!std::is_pointer_v<decltype(Data)> || 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<std::remove_reference_t<data_type>>>,
&meta_arg<type_list<std::remove_cv_t<std::remove_reference_t<data_type>>>>,
&internal::resolve<std::remove_const_t<std::remove_reference_t<data_type>>>,
&meta_arg<type_list<std::remove_const_t<std::remove_reference_t<data_type>>>>,
&meta_setter<Type, Data>,
&meta_getter<Type, Data, Policy>});
bucket = &elem.prop;
}
return *this;
}
/**
* @brief Assigns a meta data to a meta type by means of its setter and
* getter.
* @tparam Setter The actual function to use as a setter.
* @tparam Getter The actual function to use as a getter.
* @tparam Policy Optional policy (no policy set by default).
* @param name A custom unique identifier as a **string literal**.
* @return A meta factory for the given type.
*/
template<auto Setter, auto Getter, typename Policy = as_value_t>
meta_factory data(const char *name) noexcept {
return data<Setter, Getter, Policy>(hashed_string::value(name), name);
}
/**
* @brief Assigns a meta data to a meta type by means of its setter and
* getter.
@@ -321,69 +388,55 @@ public:
* @tparam Getter The actual function to use as a getter.
* @tparam Policy Optional policy (no policy set by default).
* @param id Unique identifier.
* @param name An optional name for the meta data as a **string literal**.
* @return A meta factory for the parent type.
*/
template<auto Setter, auto Getter, typename Policy = as_is_t>
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");
template<auto Setter, auto Getter, typename Policy = as_value_t>
meta_factory data(const id_type id, const char *name = nullptr) noexcept {
using descriptor = meta_function_helper_t<Type, decltype(Getter)>;
static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy");
if constexpr(std::is_same_v<decltype(Setter), std::nullptr_t>) {
auto &&elem = internal::meta_extend(
internal::owner(*ctx, *info),
id,
base_type::data(
internal::meta_data_node{
id,
name,
/* this is never static */
internal::meta_traits::is_const,
0u,
&internal::resolve<std::remove_cv_t<std::remove_reference_t<data_type>>>,
&internal::resolve<std::remove_const_t<std::remove_reference_t<typename descriptor::return_type>>>,
&meta_arg<type_list<>>,
&meta_setter<Type, Setter>,
&meta_getter<Type, Getter, Policy>});
bucket = &elem.prop;
} else {
using args_type = typename meta_function_helper_t<Type, decltype(Setter)>::args_type;
auto &&elem = internal::meta_extend(
internal::owner(*ctx, *info),
id,
base_type::data(
internal::meta_data_node{
id,
name,
/* 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>>>,
&internal::resolve<std::remove_const_t<std::remove_reference_t<typename descriptor::return_type>>>,
&meta_arg<type_list<type_list_element_t<static_cast<std::size_t>(args_type::size != 1u), args_type>>>,
&meta_setter<Type, Setter>,
&meta_getter<Type, Getter, Policy>});
bucket = &elem.prop;
}
return *this;
}
/**
* @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.
* @brief Assigns a meta function to a meta type.
* @tparam Candidate The actual function to attach to the meta function.
* @tparam Policy Optional policy (no policy set by default).
* @param id Unique identifier.
* @return A meta factory for the parent type.
* @param name A custom unique identifier as a **string literal**.
* @return A meta factory for the given 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;
template<auto Candidate, typename Policy = as_value_t>
meta_factory func(const char *name) noexcept {
return func<Candidate, Policy>(hashed_string::value(name), name);
}
/**
@@ -397,94 +450,58 @@ public:
* @tparam Candidate The actual function to attach to the meta type.
* @tparam Policy Optional policy (no policy set by default).
* @param id Unique identifier.
* @param name An optional name for the function as a **string literal**.
* @return A meta factory for the parent type.
*/
template<auto Candidate, typename Policy = as_is_t>
auto func(const id_type id) noexcept {
template<auto Candidate, typename Policy = as_value_t>
meta_factory func(const id_type id, const char *name = nullptr) noexcept {
using descriptor = meta_function_helper_t<Type, decltype(Candidate)>;
static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy");
auto &&elem = internal::meta_extend(
internal::owner(*ctx, *info),
id,
base_type::func(
internal::meta_func_node{
id,
name,
(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>>>>,
&internal::resolve<std::conditional_t<std::is_same_v<Policy, as_void_t>, void, std::remove_const_t<std::remove_reference_t<typename descriptor::return_type>>>>,
&meta_arg<typename descriptor::args_type>,
&meta_invoke<Type, Candidate, Policy>});
bucket = &elem.prop;
return *this;
}
/**
* @brief Assigns a property to the last meta object created.
* @brief Sets traits on the last created meta object.
*
* Both the key and the value (if any) must be at least copy constructible.
* The assigned value must be an enum and intended as a bitmask.
*
* @tparam Value Optional type of the property value.
* @param id Property key.
* @param value Optional property value.
* @tparam Value Type of the traits value.
* @param value Traits value.
* @param unset True to unset the given traits, false otherwise.
* @return A meta factory for the parent 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) {
(*bucket)[id] = internal::meta_prop_node{&internal::resolve<void>};
} else {
(*bucket)[id] = internal::meta_prop_node{
&internal::resolve<std::decay_t<Value>>...,
std::make_shared<std::decay_t<Value>>(std::forward<Value>(value))...};
}
template<typename Value>
meta_factory traits(const Value value, const bool unset = false) {
static_assert(std::is_enum_v<Value>, "Invalid enum type");
base_type::traits(internal::user_to_meta_traits(value), unset);
return *this;
}
private:
meta_ctx *ctx;
dense_map<id_type, internal::meta_prop_node, identity> *bucket;
const type_info *info;
/**
* @brief Sets user defined data that will never be used by the library.
* @tparam Value Type of user defined data to store.
* @tparam Args Types of arguments to use to construct the user data.
* @param args Parameters to use to initialize the user data.
* @return A meta factory for the parent type.
*/
template<typename Value, typename... Args>
meta_factory custom(Args &&...args) {
base_type::custom(internal::meta_custom_node{type_id<Value>().hash(), std::make_shared<Value>(std::forward<Args>(args)...)});
return *this;
}
};
/**
* @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.
*
* 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.
* @return A meta factory for the given type.
*/
template<typename Type>
[[nodiscard]] auto meta() noexcept {
return meta<Type>(locator<meta_ctx>::value_or());
}
/**
* @brief Resets a type and all its parts.
*
@@ -498,10 +515,10 @@ template<typename Type>
* @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);
auto &context = internal::meta_context::from(ctx);
for(auto it = context.value.begin(); it != context.value.end();) {
if(it->second.id == id) {
if(it->second->id == id) {
it = context.value.erase(it);
} else {
++it;

View File

@@ -1,24 +1,35 @@
#ifndef ENTT_META_FWD_HPP
#define ENTT_META_FWD_HPP
#include <cstddef>
#include <limits>
namespace entt {
class meta_ctx;
class meta_sequence_container;
class meta_associative_container;
class meta_any;
struct meta_handle;
class meta_handle;
struct meta_prop;
struct meta_custom;
struct meta_data;
class meta_data;
struct meta_func;
class meta_func;
class meta_type;
template<typename>
class meta_factory;
/*! @brief Used to identicate that a sequence container has not a fixed size. */
inline constexpr std::size_t meta_dynamic_extent = (std::numeric_limits<std::size_t>::max)();
} // namespace entt
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,14 @@
#ifndef ENTT_META_NODE_HPP
#define ENTT_META_NODE_HPP
#include <array>
#include <cstddef>
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../container/dense_map.hpp"
#include "../core/attribute.h"
#include "../core/bit.hpp"
#include "../core/enum.hpp"
#include "../core/fwd.hpp"
#include "../core/type_info.hpp"
@@ -20,7 +21,7 @@ namespace entt {
class meta_any;
class meta_type;
struct meta_handle;
class meta_handle;
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
@@ -35,79 +36,99 @@ enum class meta_traits : std::uint32_t {
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
is_pointer = 0x0100,
is_pointer_like = 0x0200,
is_sequence_container = 0x0400,
is_associative_container = 0x0800,
_user_defined_traits = 0xFFFF,
_entt_enum_as_bitmask = 0xFFFF
};
template<typename Type>
[[nodiscard]] auto meta_to_user_traits(const meta_traits traits) noexcept {
static_assert(std::is_enum_v<Type>, "Invalid enum type");
constexpr auto shift = popcount(static_cast<std::underlying_type_t<meta_traits>>(meta_traits::_user_defined_traits));
return Type{static_cast<std::underlying_type_t<Type>>(static_cast<std::underlying_type_t<meta_traits>>(traits) >> shift)};
}
template<typename Type>
[[nodiscard]] auto user_to_meta_traits(const Type value) noexcept {
static_assert(std::is_enum_v<Type>, "Invalid enum type");
constexpr auto shift = popcount(static_cast<std::underlying_type_t<meta_traits>>(meta_traits::_user_defined_traits));
const auto traits = static_cast<std::underlying_type_t<internal::meta_traits>>(static_cast<std::underlying_type_t<Type>>(value));
ENTT_ASSERT(traits < ((~static_cast<std::underlying_type_t<meta_traits>>(meta_traits::_user_defined_traits)) >> shift), "Invalid traits");
return meta_traits{traits << shift};
}
struct meta_type_node;
struct meta_prop_node {
meta_type_node (*type)(const meta_context &) noexcept {};
struct meta_custom_node {
id_type type{};
std::shared_ptr<void> value{};
};
struct meta_base_node {
meta_type_node (*type)(const meta_context &) noexcept {};
id_type type{};
const meta_type_node &(*resolve)(const meta_context &) noexcept {};
const void *(*cast)(const void *) noexcept {};
};
struct meta_conv_node {
id_type type{};
meta_any (*conv)(const meta_ctx &, const void *){};
};
struct meta_ctor_node {
using size_type = std::size_t;
id_type id{};
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 {
using size_type = std::size_t;
id_type id{};
const char *name{};
meta_traits traits{meta_traits::is_none};
size_type arity{0u};
meta_type_node (*type)(const meta_context &) noexcept {};
const 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{};
meta_any (*get)(meta_handle){};
meta_custom_node custom{};
};
struct meta_func_node {
using size_type = std::size_t;
id_type id{};
const char *name{};
meta_traits traits{meta_traits::is_none};
size_type arity{0u};
meta_type_node (*ret)(const meta_context &) noexcept {};
const 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{};
meta_any (*invoke)(meta_handle, meta_any *const){};
std::unique_ptr<meta_func_node> next;
meta_custom_node custom{};
};
struct meta_template_node {
using size_type = std::size_t;
size_type arity{0u};
meta_type_node (*type)(const meta_context &) noexcept {};
meta_type_node (*arg)(const meta_context &, const size_type) noexcept {};
const meta_type_node &(*resolve)(const meta_context &) noexcept {};
const 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{};
std::vector<meta_ctor_node> ctor{};
std::vector<meta_base_node> base{};
std::vector<meta_conv_node> conv{};
std::vector<meta_data_node> data{};
std::vector<meta_func_node> func{};
};
struct meta_type_node {
@@ -115,55 +136,72 @@ struct meta_type_node {
const type_info *info{};
id_type id{};
const char *name{};
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 {};
const 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{};
meta_custom_node custom{};
std::unique_ptr<meta_type_descriptor> details{};
};
template<auto Member, typename Type, typename Value>
[[nodiscard]] auto *find_member(Type &from, const Value value) {
for(auto &&elem: from) {
if((elem.*Member) == value) {
return &elem;
}
}
return static_cast<typename Type::value_type *>(nullptr);
}
[[nodiscard]] inline auto *find_overload(meta_func_node *curr, std::remove_pointer_t<decltype(meta_func_node::invoke)> *const ref) {
while((curr != nullptr) && (curr->invoke != ref)) { curr = curr->next.get(); }
return curr;
}
template<auto Member>
auto *look_for(const meta_context &context, const meta_type_node &node, const id_type id) {
[[nodiscard]] auto *look_for(const meta_context &context, const meta_type_node &node, const id_type id, bool recursive) {
using value_type = typename std::remove_reference_t<decltype((node.details.get()->*Member))>::value_type;
if(node.details) {
if(const auto it = (node.details.get()->*Member).find(id); it != (node.details.get()->*Member).cend()) {
return &it->second;
if(auto *member = find_member<&value_type::id>((node.details.get()->*Member), id); member != nullptr) {
return member;
}
for(auto &&curr: node.details->base) {
if(auto *elem = look_for<Member>(context, curr.second.type(context), id); elem) {
return elem;
if(recursive) {
for(auto &&curr: node.details->base) {
if(auto *elem = look_for<Member>(context, curr.resolve(context), id, recursive); elem) {
return elem;
}
}
}
}
return static_cast<typename std::remove_reference_t<decltype(node.details.get()->*Member)>::mapped_type *>(nullptr);
return static_cast<value_type *>(nullptr);
}
template<typename Type>
meta_type_node resolve(const meta_context &) noexcept;
const meta_type_node &resolve(const meta_context &) noexcept;
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]] const meta_type_node &meta_arg_node(const meta_context &context, type_list<Args...>, const std::size_t index) noexcept {
using resolve_type = const meta_type_node &(*)(const meta_context &) noexcept;
constexpr std::array<resolve_type, sizeof...(Args)> list{&resolve<std::remove_const_t<std::remove_reference_t<Args>>>...};
ENTT_ASSERT(index < sizeof...(Args), "Out of bounds");
return list[index](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;
}
[[nodiscard]] inline const void *try_cast(const meta_context &context, const meta_type_node &from, const id_type to, const void *instance) noexcept {
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) {
if(const void *other = curr.cast(instance); curr.type == to) {
return other;
} else if(const void *elem = try_cast(context, curr.resolve(context), to, other); elem) {
return elem;
}
}
@@ -172,59 +210,24 @@ template<typename... Args>
return nullptr;
}
template<typename Func>
[[nodiscard]] inline auto try_convert(const meta_context &context, const meta_type_node &from, const type_info &to, const bool arithmetic_or_enum, const void *instance, Func func) {
if(from.info && *from.info == to) {
return func(instance, from);
}
if(from.details) {
if(auto it = from.details->conv.find(to.hash()); it != from.details->conv.cend()) {
return func(instance, it->second);
}
for(auto &&curr: from.details->base) {
if(auto other = try_convert(context, curr.second.type(context), to, arithmetic_or_enum, curr.second.cast(instance), func); other) {
return other;
}
}
}
if(from.conversion_helper && arithmetic_or_enum) {
return func(instance, from.conversion_helper);
}
return func(instance);
}
[[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>
[[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");
if(auto *elem = try_resolve(context, type_id<Type>()); elem) {
return *elem;
}
auto setup_node_for() noexcept {
meta_type_node node{
&type_id<Type>(),
type_id<Type>().hash(),
nullptr,
(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),
| (std::is_pointer_v<Type> ? meta_traits::is_pointer : meta_traits::is_none)
| (is_meta_pointer_like_v<Type> ? meta_traits::is_pointer_like : meta_traits::is_none)
| (is_complete_v<meta_sequence_container_traits<Type>> ? meta_traits::is_sequence_container : meta_traits::is_none)
| (is_complete_v<meta_associative_container_traits<Type>> ? meta_traits::is_associative_container : meta_traits::is_none),
size_of_v<Type>,
&resolve<Type>,
&resolve<std::remove_cv_t<std::remove_pointer_t<Type>>>};
&resolve<std::remove_const_t<std::remove_pointer_t<Type>>>};
if constexpr(std::is_default_constructible_v<Type>) {
node.default_constructor = +[](const meta_ctx &ctx) {
@@ -233,22 +236,27 @@ template<typename Type>
}
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));
node.conversion_helper = +[](void *lhs, const void *rhs) {
return lhs ? static_cast<double>(*static_cast<Type *>(lhs) = static_cast<Type>(*static_cast<const double *>(rhs))) : static_cast<double>(*static_cast<const Type *>(rhs));
};
} 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));
node.conversion_helper = +[](void *lhs, const void *rhs) {
return lhs ? static_cast<double>(*static_cast<Type *>(lhs) = static_cast<Type>(static_cast<std::underlying_type_t<Type>>(*static_cast<const double *>(rhs)))) : static_cast<double>(*static_cast<const Type *>(rhs));
};
}
if constexpr(!std::is_void_v<Type> && !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)};
node.from_void = +[](const meta_ctx &ctx, void *elem, const void *celem) {
if(elem && celem) { // ownership construction request
return meta_any{ctx, std::in_place, static_cast<std::decay_t<Type> *>(elem)};
}
return meta_any{ctx, std::in_place_type<const std::decay_t<Type> &>, *static_cast<const std::decay_t<Type> *>(as_const)};
if(elem) { // non-const reference construction request
return meta_any{ctx, std::in_place_type<std::decay_t<Type> &>, *static_cast<std::decay_t<Type> *>(elem)};
}
// const reference construction request
return meta_any{ctx, std::in_place_type<const std::decay_t<Type> &>, *static_cast<const std::decay_t<Type> *>(celem)};
};
}
@@ -256,12 +264,25 @@ template<typename 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); }};
+[](const meta_context &area, const std::size_t index) noexcept -> decltype(auto) { return meta_arg_node(area, typename meta_template_traits<Type>::args_type{}, index); }};
}
return node;
}
[[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.get() : nullptr;
}
template<typename Type>
[[nodiscard]] const 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");
static const meta_type_node node = setup_node_for<Type>();
const auto *elem = try_resolve(context, *node.info);
return (elem == nullptr) ? node : *elem;
}
} // namespace internal
/*! @endcond */

View File

@@ -1,3 +1,5 @@
// IWYU pragma: always_keep
#ifndef ENTT_META_POINTER_HPP
#define ENTT_META_POINTER_HPP
@@ -21,6 +23,7 @@ struct is_meta_pointer_like<Type *>
* @tparam N Number of elements of the array.
*/
template<typename Type, std::size_t N>
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
struct is_meta_pointer_like<Type (*)[N]>
: std::false_type {};
@@ -43,6 +46,14 @@ template<typename Type, typename... Args>
struct is_meta_pointer_like<std::unique_ptr<Type, Args...>>
: std::true_type {};
/**
* @brief Specialization for self-proclaimed meta pointer like types.
* @tparam Type Element type.
*/
template<typename Type>
struct is_meta_pointer_like<Type, std::void_t<typename Type::is_meta_pointer_like>>
: std::true_type {};
} // namespace entt
#endif

View File

@@ -5,24 +5,16 @@
namespace entt {
/*! @brief Empty class type used to request the _as ref_ policy. */
struct as_ref_t final {
/*! @cond TURN_OFF_DOXYGEN */
template<typename Type>
static constexpr bool value = std::is_reference_v<Type> && !std::is_const_v<std::remove_reference_t<Type>>;
/*! @endcond */
};
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
/*! @brief Empty class type used to request the _as cref_ policy. */
struct as_cref_t final {
/*! @cond TURN_OFF_DOXYGEN */
template<typename Type>
static constexpr bool value = std::is_reference_v<Type>;
/*! @endcond */
};
struct meta_policy {};
} // namespace internal
/*! @endcond */
/*! @brief Empty class type used to request the _as-is_ policy. */
struct as_is_t final {
struct as_value_t final: private internal::meta_policy {
/*! @cond TURN_OFF_DOXYGEN */
template<typename>
static constexpr bool value = true;
@@ -30,7 +22,31 @@ struct as_is_t final {
};
/*! @brief Empty class type used to request the _as void_ policy. */
struct as_void_t final {
struct as_void_t final: private internal::meta_policy {
/*! @cond TURN_OFF_DOXYGEN */
template<typename>
static constexpr bool value = true;
/*! @endcond */
};
/*! @brief Empty class type used to request the _as ref_ policy. */
struct as_ref_t final: private internal::meta_policy {
/*! @cond TURN_OFF_DOXYGEN */
template<typename Type>
static constexpr bool value = std::is_reference_v<Type> && !std::is_const_v<std::remove_reference_t<Type>>;
/*! @endcond */
};
/*! @brief Empty class type used to request the _as cref_ policy. */
struct as_cref_t final: private internal::meta_policy {
/*! @cond TURN_OFF_DOXYGEN */
template<typename Type>
static constexpr bool value = std::is_reference_v<Type>;
/*! @endcond */
};
/*! @brief Empty class type used to request the _as auto_ policy. */
struct as_is_t final: private internal::meta_policy {
/*! @cond TURN_OFF_DOXYGEN */
template<typename>
static constexpr bool value = true;
@@ -44,7 +60,7 @@ struct as_void_t final {
*/
template<typename Type>
struct is_meta_policy
: std::bool_constant<std::is_same_v<Type, as_ref_t> || std::is_same_v<Type, as_cref_t> || std::is_same_v<Type, as_is_t> || std::is_same_v<Type, as_void_t>> {};
: std::bool_constant<std::is_base_of_v<internal::meta_policy, Type>> {};
/**
* @brief Helper variable template.

View File

@@ -13,12 +13,14 @@ namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
struct meta_base_node;
template<typename Type, typename It>
struct meta_range_iterator final {
using difference_type = std::ptrdiff_t;
using value_type = std::pair<id_type, 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;
using iterator_concept = std::random_access_iterator_tag;
@@ -35,7 +37,7 @@ struct meta_range_iterator final {
}
constexpr meta_range_iterator operator++(int) noexcept {
meta_range_iterator orig = *this;
const meta_range_iterator orig = *this;
return ++(*this), orig;
}
@@ -44,7 +46,7 @@ struct meta_range_iterator final {
}
constexpr meta_range_iterator operator--(int) noexcept {
meta_range_iterator orig = *this;
const meta_range_iterator orig = *this;
return operator--(), orig;
}
@@ -67,7 +69,13 @@ struct meta_range_iterator final {
}
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
return {it[value].first, Type{*ctx, it[value].second}};
if constexpr(std::is_same_v<It, typename decltype(meta_context::value)::const_iterator>) {
return {it[value].first, Type{*ctx, *it[value].second}};
} else if constexpr(std::is_same_v<typename std::iterator_traits<It>::value_type, meta_base_node>) {
return {it[value].type, Type{*ctx, it[value]}};
} else {
return {it[value].id, Type{*ctx, it[value]}};
}
}
[[nodiscard]] constexpr pointer operator->() const noexcept {
@@ -75,7 +83,7 @@ struct meta_range_iterator final {
}
[[nodiscard]] constexpr reference operator*() const noexcept {
return {it->first, Type{*ctx, it->second}};
return operator[](0);
}
template<typename... Args>

View File

@@ -19,8 +19,8 @@ namespace entt {
*/
template<typename Type>
[[nodiscard]] meta_type resolve(const meta_ctx &ctx) noexcept {
auto &&context = internal::meta_context::from(ctx);
return {ctx, internal::resolve<std::remove_cv_t<std::remove_reference_t<Type>>>(context)};
const auto &context = internal::meta_context::from(ctx);
return {ctx, internal::resolve<std::remove_const_t<std::remove_reference_t<Type>>>(context)};
}
/**
@@ -39,7 +39,7 @@ template<typename Type>
* @return An iterable range to use to visit all meta types.
*/
[[nodiscard]] inline meta_range<meta_type, typename decltype(internal::meta_context::value)::const_iterator> resolve(const meta_ctx &ctx) noexcept {
auto &&context = internal::meta_context::from(ctx);
const auto &context = internal::meta_context::from(ctx);
return {{ctx, context.value.cbegin()}, {ctx, context.value.cend()}};
}
@@ -83,9 +83,9 @@ template<typename Type>
* @return The meta type associated with the given type info object, if any.
*/
[[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const type_info &info) noexcept {
auto &&context = internal::meta_context::from(ctx);
const auto &context = internal::meta_context::from(ctx);
const auto *elem = internal::try_resolve(context, info);
return elem ? meta_type{ctx, *elem} : meta_type{};
return (elem != nullptr) ? meta_type{ctx, *elem} : meta_type{};
}
/**

View File

@@ -1,3 +1,5 @@
// IWYU pragma: always_keep
#ifndef ENTT_META_TEMPLATE_HPP
#define ENTT_META_TEMPLATE_HPP

View File

@@ -31,7 +31,7 @@ struct meta_associative_container_traits;
* @brief Provides the member constant `value` to true if a given type is a
* pointer-like type from the point of view of the meta system, false otherwise.
*/
template<typename>
template<typename, typename = void>
struct is_meta_pointer_like: std::false_type {};
/**

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