Compare commits
857 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4e58bdd36 | ||
|
|
8375ee0910 | ||
|
|
137339f48c | ||
|
|
713bcd50a3 | ||
|
|
03a45d9cca | ||
|
|
cffbf89d9b | ||
|
|
f48d16a6e6 | ||
|
|
4905da2637 | ||
|
|
558109bbe1 | ||
|
|
a505d41266 | ||
|
|
9381900b00 | ||
|
|
f6a3b09ec2 | ||
|
|
51c7533265 | ||
|
|
d502bf18da | ||
|
|
db263d2ff9 | ||
|
|
16fae2d625 | ||
|
|
4e3a1a8428 | ||
|
|
aec8a6588c | ||
|
|
3160d991f7 | ||
|
|
7359018880 | ||
|
|
17fb301636 | ||
|
|
7dac5af05d | ||
|
|
d15e514f10 | ||
|
|
fe8320c66f | ||
|
|
7111549c7c | ||
|
|
e8e5f5ee2b | ||
|
|
9caaf569e6 | ||
|
|
03bf0f90b4 | ||
|
|
5df7142f51 | ||
|
|
4d95793123 | ||
|
|
0aa503e69e | ||
|
|
02323e814a | ||
|
|
440fed990d | ||
|
|
c2dcd7257f | ||
|
|
716e3257b1 | ||
|
|
f781441bcd | ||
|
|
3c0aa4d300 | ||
|
|
a57e1a971d | ||
|
|
dc8da9e42c | ||
|
|
7eeb828980 | ||
|
|
b0d7490829 | ||
|
|
0d678442a0 | ||
|
|
2dff7c9d55 | ||
|
|
3fc5de2916 | ||
|
|
a572671947 | ||
|
|
2a6f769092 | ||
|
|
6a25c0df84 | ||
|
|
72127d699a | ||
|
|
b52d0aa133 | ||
|
|
5d006fefcc | ||
|
|
d7c58d00b4 | ||
|
|
9198a27c25 | ||
|
|
31d5d5ff1c | ||
|
|
f3fa359edf | ||
|
|
cba31e00ea | ||
|
|
d366a5ed92 | ||
|
|
d92bea4afd | ||
|
|
bbfd458d2d | ||
|
|
87a114ccc6 | ||
|
|
6852415ffc | ||
|
|
15f65bd58e | ||
|
|
54c7c47e3d | ||
|
|
5e6ccdedc8 | ||
|
|
dadf493aca | ||
|
|
3e5842a467 | ||
|
|
75ae0a10d0 | ||
|
|
e87a8e4b63 | ||
|
|
41282b0ecc | ||
|
|
030095f48d | ||
|
|
e18061bf86 | ||
|
|
38bcbf41a3 | ||
|
|
602626c840 | ||
|
|
41e359502d | ||
|
|
8e64f8accf | ||
|
|
0afab946da | ||
|
|
20d77024f7 | ||
|
|
b1bb9b4b5b | ||
|
|
978e003dbc | ||
|
|
47c65a20a1 | ||
|
|
31d845a5f5 | ||
|
|
03c5661df9 | ||
|
|
48644e568f | ||
|
|
19c0e8029d | ||
|
|
3562bec4a7 | ||
|
|
e863e4fe54 | ||
|
|
c5d89597fc | ||
|
|
091eef0e4c | ||
|
|
96b56ca630 | ||
|
|
56d78c767a | ||
|
|
1f57b2795f | ||
|
|
de054655ab | ||
|
|
bee74568e5 | ||
|
|
ebb2af1225 | ||
|
|
1487ad292b | ||
|
|
d3b8e92294 | ||
|
|
ab1d7d0126 | ||
|
|
01b21afca0 | ||
|
|
ef0056908a | ||
|
|
b021c78550 | ||
|
|
aedaa7f6a4 | ||
|
|
e15af7b500 | ||
|
|
38b34eb986 | ||
|
|
b4fc627c99 | ||
|
|
c9364d3af5 | ||
|
|
baa0d2c23c | ||
|
|
dc154a4b8a | ||
|
|
ba0e9fab45 | ||
|
|
4ab5a375e8 | ||
|
|
f63f36a411 | ||
|
|
f2b8177aaf | ||
|
|
53fab33423 | ||
|
|
4a252bdb84 | ||
|
|
cece4be1ec | ||
|
|
8f23d743ca | ||
|
|
0fa1bc5999 | ||
|
|
281276e84e | ||
|
|
f73409e9ad | ||
|
|
740cf006d9 | ||
|
|
828ecb57e6 | ||
|
|
8794888c98 | ||
|
|
dd7b96fc9e | ||
|
|
1116c33ada | ||
|
|
38fe05db85 | ||
|
|
b2d4832842 | ||
|
|
4c19d2844e | ||
|
|
6f2e5090e1 | ||
|
|
25e5123128 | ||
|
|
8c3ca20d19 | ||
|
|
ee7f24be63 | ||
|
|
a5f58ef49f | ||
|
|
ba5c4ba121 | ||
|
|
fcf455f7ef | ||
|
|
6836d5b024 | ||
|
|
8142136940 | ||
|
|
e76bbb2015 | ||
|
|
6f57622eff | ||
|
|
0c5cab164a | ||
|
|
c67a62de81 | ||
|
|
80eba8afb4 | ||
|
|
0d6615a767 | ||
|
|
fc4d18ce44 | ||
|
|
ddf28480dc | ||
|
|
f49f2331ba | ||
|
|
6526d67d16 | ||
|
|
4d90521fcf | ||
|
|
9165a1df1e | ||
|
|
a1406baf61 | ||
|
|
645b3563a9 | ||
|
|
91eecc1efd | ||
|
|
0782855880 | ||
|
|
b9bfeb9b3d | ||
|
|
0409aab6bb | ||
|
|
947d89844b | ||
|
|
ff8544f97d | ||
|
|
c777cda857 | ||
|
|
a90584f1ce | ||
|
|
4e47c535c4 | ||
|
|
84fee2848f | ||
|
|
42259fb1ce | ||
|
|
e38e88362f | ||
|
|
14be4141fa | ||
|
|
6a4ca4c4ba | ||
|
|
71e61b5270 | ||
|
|
affe6ca7b8 | ||
|
|
f0a07c4530 | ||
|
|
a003575811 | ||
|
|
be3f022445 | ||
|
|
4b5b3fec41 | ||
|
|
4debb79a60 | ||
|
|
f3b763d973 | ||
|
|
356024ed6e | ||
|
|
e8e62638fc | ||
|
|
6ecf1f35c0 | ||
|
|
05863c6a15 | ||
|
|
1a02b6ad57 | ||
|
|
5ee1f3fec1 | ||
|
|
f44c9a1c05 | ||
|
|
715e5318de | ||
|
|
211e5fddc1 | ||
|
|
9347e91db6 | ||
|
|
bebfbada68 | ||
|
|
c239821d12 | ||
|
|
293cbe2769 | ||
|
|
3587eaed9a | ||
|
|
cb9d8ac3f0 | ||
|
|
e544755bbc | ||
|
|
e2003ff17a | ||
|
|
6232c93907 | ||
|
|
6153cdcbbe | ||
|
|
89af917748 | ||
|
|
28466a7316 | ||
|
|
33efea3f03 | ||
|
|
55f1a47fcc | ||
|
|
e9b3eee6d3 | ||
|
|
b400927f02 | ||
|
|
a6d566ae4a | ||
|
|
93979e13ac | ||
|
|
a77e40093d | ||
|
|
695ac44673 | ||
|
|
1cc078e4fb | ||
|
|
94cd89a72a | ||
|
|
5497291c29 | ||
|
|
3c75f7e2fd | ||
|
|
03d8cccc86 | ||
|
|
47bfffc7cc | ||
|
|
9795f60bc1 | ||
|
|
b3b48db957 | ||
|
|
7d2b4fcf57 | ||
|
|
3af8525eb8 | ||
|
|
3306b72f87 | ||
|
|
91a239a277 | ||
|
|
fdaaeea52d | ||
|
|
d81e57daa1 | ||
|
|
ed84c6e9ff | ||
|
|
fe872f6a0d | ||
|
|
d8aa90234a | ||
|
|
28d8041da6 | ||
|
|
1f53d163b7 | ||
|
|
7f38b7668e | ||
|
|
dc70d97760 | ||
|
|
4d0d48f7d9 | ||
|
|
4ecaf18b20 | ||
|
|
30a66cf513 | ||
|
|
9bbc8ad014 | ||
|
|
626b882f26 | ||
|
|
9b44fa47d0 | ||
|
|
9170fc850b | ||
|
|
973f010d85 | ||
|
|
6241543fe2 | ||
|
|
5e99452cf3 | ||
|
|
d058ccd084 | ||
|
|
7e4694ce69 | ||
|
|
b3b7975290 | ||
|
|
5ee0a39616 | ||
|
|
27b9a25d06 | ||
|
|
9f5a6260ce | ||
|
|
b4b85c8d53 | ||
|
|
4bc11e4297 | ||
|
|
ad291d4280 | ||
|
|
f61fd4584a | ||
|
|
536dba3048 | ||
|
|
4760a09d61 | ||
|
|
47ff98e25b | ||
|
|
a638df6cae | ||
|
|
4f1626fce9 | ||
|
|
9b3965c2c9 | ||
|
|
536a43be90 | ||
|
|
09b0e7f528 | ||
|
|
76b6841c51 | ||
|
|
4507c546c1 | ||
|
|
add999d105 | ||
|
|
9cefbc3c79 | ||
|
|
22347cfbd0 | ||
|
|
6966eabd25 | ||
|
|
a32e004c92 | ||
|
|
49d9385aa4 | ||
|
|
9970ba2284 | ||
|
|
20cc85011e | ||
|
|
7f5d4fb8d2 | ||
|
|
03e046f880 | ||
|
|
436f077e0b | ||
|
|
d76cfb06f0 | ||
|
|
2bb947f6de | ||
|
|
8a8f8dfd1b | ||
|
|
15b8db6f61 | ||
|
|
05e0a22fb3 | ||
|
|
ada11c4be1 | ||
|
|
7dbb59adc9 | ||
|
|
02440e17ae | ||
|
|
fa234af36b | ||
|
|
40fdd26788 | ||
|
|
6fa668900e | ||
|
|
17e88e506c | ||
|
|
3bcbea785a | ||
|
|
3bac3d4bbe | ||
|
|
abdb293106 | ||
|
|
f4b960dc93 | ||
|
|
71c217da28 | ||
|
|
4777ca2980 | ||
|
|
4aabca2f0b | ||
|
|
5c31fda9bd | ||
|
|
eecd1dc636 | ||
|
|
998f4b45da | ||
|
|
fb0b7e2aab | ||
|
|
cbb50c1292 | ||
|
|
fcd265a6ed | ||
|
|
357ee3f994 | ||
|
|
e35eed905b | ||
|
|
7942648035 | ||
|
|
93dca93616 | ||
|
|
9f10d32b73 | ||
|
|
21e82abfa7 | ||
|
|
7b48069bd5 | ||
|
|
5721e62ed0 | ||
|
|
e140399725 | ||
|
|
7f1a2b1887 | ||
|
|
0fd70878b3 | ||
|
|
06e88ce062 | ||
|
|
b5201a56df | ||
|
|
9c05f1d8f3 | ||
|
|
093718a1c6 | ||
|
|
c07cb32ef8 | ||
|
|
04da178800 | ||
|
|
5ab8da50cb | ||
|
|
da684a4159 | ||
|
|
ba20bde250 | ||
|
|
cec550ad11 | ||
|
|
62b56e1b56 | ||
|
|
b045846579 | ||
|
|
665aa3c4cf | ||
|
|
0f49a1fe27 | ||
|
|
ad4ab9dc08 | ||
|
|
1293990605 | ||
|
|
c71bf6e8d9 | ||
|
|
716c67d3a5 | ||
|
|
33b1daafa0 | ||
|
|
2532158860 | ||
|
|
da65e347b3 | ||
|
|
c940bc8015 | ||
|
|
9c4676f4c0 | ||
|
|
3b64dae13e | ||
|
|
3126b3efbf | ||
|
|
01e12fa011 | ||
|
|
1d163ee113 | ||
|
|
f97ecd3ba4 | ||
|
|
3d66676c1b | ||
|
|
bf28bab7ba | ||
|
|
a6c2dc89ac | ||
|
|
e36ff3d50e | ||
|
|
dd4c36333b | ||
|
|
9e328ce232 | ||
|
|
624d0994ff | ||
|
|
6ec1ee6072 | ||
|
|
5642788046 | ||
|
|
e8e77bf011 | ||
|
|
964dc6ec87 | ||
|
|
a0d738215a | ||
|
|
9ae1181ca7 | ||
|
|
3b89567d77 | ||
|
|
09bb10377b | ||
|
|
ffbc5519c1 | ||
|
|
c80b634497 | ||
|
|
db6c6573af | ||
|
|
7257dd50c6 | ||
|
|
5f0af48b4c | ||
|
|
41ebe0e779 | ||
|
|
0cc95c947e | ||
|
|
991f66e1a3 | ||
|
|
d75efb9f16 | ||
|
|
3575f00bb4 | ||
|
|
f532221685 | ||
|
|
f502827e88 | ||
|
|
50c842883f | ||
|
|
2f5b25b189 | ||
|
|
fb0fedf8fc | ||
|
|
45e9c12b29 | ||
|
|
e52957fabb | ||
|
|
209f333252 | ||
|
|
aa5c0d14ed | ||
|
|
ca40a3e62f | ||
|
|
8574335fe6 | ||
|
|
9493756895 | ||
|
|
df5e57f381 | ||
|
|
62e05abc77 | ||
|
|
d7d63b09b7 | ||
|
|
ae7b3d87ee | ||
|
|
e2ef08f129 | ||
|
|
8dfa4aa677 | ||
|
|
31522ce703 | ||
|
|
25cad2543d | ||
|
|
34e50d6550 | ||
|
|
b7a06c535d | ||
|
|
bf5b188b13 | ||
|
|
20af487ea9 | ||
|
|
d037976bcd | ||
|
|
330bf8e120 | ||
|
|
7064415265 | ||
|
|
df07350a46 | ||
|
|
66e80820c9 | ||
|
|
5d3173f1b4 | ||
|
|
3b977186ed | ||
|
|
38caafd999 | ||
|
|
d7232dcf56 | ||
|
|
825f73df6b | ||
|
|
696e084b02 | ||
|
|
fcc7306407 | ||
|
|
1f182a25db | ||
|
|
995018f1a7 | ||
|
|
a6fa6f6122 | ||
|
|
1b6462a327 | ||
|
|
6054e93ecc | ||
|
|
3acf03ae1c | ||
|
|
42f4d28715 | ||
|
|
4ee86d8456 | ||
|
|
3b2adc999d | ||
|
|
87ec44d3ee | ||
|
|
182a6d5fe4 | ||
|
|
49b5b5b2b1 | ||
|
|
f984ae4930 | ||
|
|
e6ef506f2f | ||
|
|
2d1ae3d5eb | ||
|
|
d775841457 | ||
|
|
2f60398892 | ||
|
|
393a347bad | ||
|
|
6a4c594792 | ||
|
|
154c16a9d3 | ||
|
|
9536039932 | ||
|
|
cad91fd0d1 | ||
|
|
7615e55e96 | ||
|
|
0f54af1a11 | ||
|
|
a439cf476c | ||
|
|
121e5a0ca8 | ||
|
|
328e53bd7b | ||
|
|
4dd03da2e2 | ||
|
|
ebab67c314 | ||
|
|
5ddab2b9d9 | ||
|
|
265f00becf | ||
|
|
340888e4e2 | ||
|
|
f5bbc895b5 | ||
|
|
57a6c6b238 | ||
|
|
cb10854bc8 | ||
|
|
1796b18284 | ||
|
|
f505050391 | ||
|
|
b0afed4bd0 | ||
|
|
292cbd3d95 | ||
|
|
756e861346 | ||
|
|
6db98b40a1 | ||
|
|
a9852a2d2f | ||
|
|
7302b1544d | ||
|
|
491956a69e | ||
|
|
2b94080ee5 | ||
|
|
4732bd1bf7 | ||
|
|
2bdb44e3ba | ||
|
|
d805c29b73 | ||
|
|
a9187c1f45 | ||
|
|
4e76ae5cc6 | ||
|
|
54b6ee0f27 | ||
|
|
bf8c83e798 | ||
|
|
4aed49b371 | ||
|
|
c1df295845 | ||
|
|
265057be83 | ||
|
|
021fbb6c69 | ||
|
|
843480fdf6 | ||
|
|
3eb78a175e | ||
|
|
de1ae66966 | ||
|
|
ab9a62bcd8 | ||
|
|
41d9e44a23 | ||
|
|
693b85fe40 | ||
|
|
7a90e2a93b | ||
|
|
11c3ea2fdb | ||
|
|
57d604bcb2 | ||
|
|
63e38e40dd | ||
|
|
c66f116449 | ||
|
|
55fb754614 | ||
|
|
afe687e9d7 | ||
|
|
6ca9c77b26 | ||
|
|
990956980b | ||
|
|
f7f30bc5b6 | ||
|
|
81b8d01e96 | ||
|
|
7ecfe45923 | ||
|
|
5212f0c44a | ||
|
|
fa58cedd2e | ||
|
|
8b1b7a0fc9 | ||
|
|
2a37b972d9 | ||
|
|
df7ebd244b | ||
|
|
b4b1cf38a6 | ||
|
|
292d57a60b | ||
|
|
9d6850636a | ||
|
|
8879a76e88 | ||
|
|
0fa38bf0e0 | ||
|
|
36eba38f62 | ||
|
|
fe8d7d78c4 | ||
|
|
ff32d77eba | ||
|
|
84f60ea2be | ||
|
|
cc6fe33709 | ||
|
|
5b3f04c5ff | ||
|
|
dc0bf7bdd8 | ||
|
|
13f21dd942 | ||
|
|
fbe69ae90b | ||
|
|
dedad6a03e | ||
|
|
86d3d2207a | ||
|
|
9c39f76774 | ||
|
|
8c04222a40 | ||
|
|
d84a18e456 | ||
|
|
e42231fab0 | ||
|
|
d60efe04fd | ||
|
|
6e7ca646ec | ||
|
|
cac3e4c62b | ||
|
|
0e323954e4 | ||
|
|
385f5a4379 | ||
|
|
e80d20a740 | ||
|
|
9d4db9738a | ||
|
|
f54859bedf | ||
|
|
556ff733d5 | ||
|
|
4027bfff69 | ||
|
|
dbcc0baa7b | ||
|
|
d45c2aca8a | ||
|
|
fd015edb98 | ||
|
|
5ccb46383c | ||
|
|
fa6fdc4059 | ||
|
|
d4014c74dc | ||
|
|
21ac61abbc | ||
|
|
9d00adf387 | ||
|
|
35055e8886 | ||
|
|
53da07ebd5 | ||
|
|
41e91688da | ||
|
|
1650d058f6 | ||
|
|
4208eaa4d8 | ||
|
|
5ef09c04c6 | ||
|
|
a1f978da54 | ||
|
|
090ba4ba06 | ||
|
|
aa039b9a0c | ||
|
|
d0711fd7da | ||
|
|
8daacc339f | ||
|
|
43fe07c23f | ||
|
|
b9850320c1 | ||
|
|
5f819c1b25 | ||
|
|
91a6837d6a | ||
|
|
0fafdf65f8 | ||
|
|
217bd07634 | ||
|
|
7e0e3a2196 | ||
|
|
be0e896d25 | ||
|
|
0cb9f7c778 | ||
|
|
52d3fd851e | ||
|
|
2427c09ec6 | ||
|
|
6777f8538b | ||
|
|
4d3e11645b | ||
|
|
f2588c94c6 | ||
|
|
fc01149121 | ||
|
|
4d1501277d | ||
|
|
269bb61f91 | ||
|
|
bbf1b9c1ab | ||
|
|
82b5bcc282 | ||
|
|
8c55c09c3f | ||
|
|
e3e84490e5 | ||
|
|
da1f56d046 | ||
|
|
3bd8807ec7 | ||
|
|
da60ec0a9e | ||
|
|
6efb7dc92d | ||
|
|
904c14095e | ||
|
|
6cf0883769 | ||
|
|
2f35d1cd0e | ||
|
|
91a5f10598 | ||
|
|
780e3d8e3b | ||
|
|
350b10ef53 | ||
|
|
e7e248b526 | ||
|
|
8883281848 | ||
|
|
7784c741c4 | ||
|
|
46e68362f7 | ||
|
|
84c85dc2d2 | ||
|
|
0787f13f7f | ||
|
|
6b95c614bb | ||
|
|
7a7695e603 | ||
|
|
b198b3f087 | ||
|
|
cf51b5bed5 | ||
|
|
777ef5a0bd | ||
|
|
f301738bf4 | ||
|
|
de80e7b910 | ||
|
|
dd70a5833a | ||
|
|
aaf7661f8e | ||
|
|
fdb6214879 | ||
|
|
2df2dd2d6f | ||
|
|
c3853e7a39 | ||
|
|
da69a6f19c | ||
|
|
65b2e5c671 | ||
|
|
dfeac358cc | ||
|
|
52be46965b | ||
|
|
95c142ad65 | ||
|
|
562d25c7ca | ||
|
|
b10ba4da1c | ||
|
|
ba70637eeb | ||
|
|
c2226ee1f4 | ||
|
|
f17d1801d5 | ||
|
|
3ef946802b | ||
|
|
86757df072 | ||
|
|
f0c763b984 | ||
|
|
5fbddb471c | ||
|
|
1fddb2c4c9 | ||
|
|
8156eeb47e | ||
|
|
ccb63bfd80 | ||
|
|
baad567dfa | ||
|
|
b0bd5f641d | ||
|
|
6ae6e038d6 | ||
|
|
9b2a791878 | ||
|
|
fc48670219 | ||
|
|
f7f1e2162f | ||
|
|
9f7be95782 | ||
|
|
1a3e16d7ce | ||
|
|
ddbfaa2dc6 | ||
|
|
38fb30e336 | ||
|
|
24446f2f81 | ||
|
|
0d0373e222 | ||
|
|
86c3c4565b | ||
|
|
12859bb856 | ||
|
|
43a7b452c4 | ||
|
|
f30ca72c69 | ||
|
|
041b5e9882 | ||
|
|
36c1a58bff | ||
|
|
0c73244575 | ||
|
|
8e6bbc4a6c | ||
|
|
303801c23b | ||
|
|
165a048fa1 | ||
|
|
6eb42cd782 | ||
|
|
d726bd761c | ||
|
|
c506fd6f5e | ||
|
|
39aae24c0a | ||
|
|
8b890993a2 | ||
|
|
0a706ec1c5 | ||
|
|
06d5694f7a | ||
|
|
15be7d0301 | ||
|
|
e878406824 | ||
|
|
ee7a33f62f | ||
|
|
68c649794c | ||
|
|
4686094c09 | ||
|
|
476822e6c4 | ||
|
|
7fe1a69d58 | ||
|
|
7bcb4d739e | ||
|
|
08b21b17e2 | ||
|
|
9ae5a3d7d4 | ||
|
|
0aa6cb8301 | ||
|
|
9aaef992ba | ||
|
|
a5d4611d21 | ||
|
|
25dff4bddc | ||
|
|
6936aa951a | ||
|
|
54adabee28 | ||
|
|
85bc3b778a | ||
|
|
c38f1349fc | ||
|
|
f86c325f0b | ||
|
|
38551a5332 | ||
|
|
00365b26d9 | ||
|
|
1eaff4f3d9 | ||
|
|
9e0f8149a4 | ||
|
|
279166cb8b | ||
|
|
b6d3c84595 | ||
|
|
0d944f6547 | ||
|
|
20b99b6b8b | ||
|
|
5e687893de | ||
|
|
915207aa6b | ||
|
|
9ba4fb867a | ||
|
|
e342f3b419 | ||
|
|
3721cf31a4 | ||
|
|
19ca7143c8 | ||
|
|
2aebb12e06 | ||
|
|
b4dfda501b | ||
|
|
eb7c15bee0 | ||
|
|
5636304217 | ||
|
|
588440b487 | ||
|
|
bea0418927 | ||
|
|
b229ca3aba | ||
|
|
41b4acb5b5 | ||
|
|
3b371e9e52 | ||
|
|
eb1e055941 | ||
|
|
93152791da | ||
|
|
7a7cd41b8c | ||
|
|
03f704d767 | ||
|
|
399ca817ec | ||
|
|
2fb2ace002 | ||
|
|
0592c4e4e6 | ||
|
|
48872e7ef8 | ||
|
|
0d3aaef63f | ||
|
|
75bfbd3b0a | ||
|
|
27358e6ab8 | ||
|
|
af193d8523 | ||
|
|
00b6b5c5f5 | ||
|
|
6d03a44246 | ||
|
|
380afaad8f | ||
|
|
7607c0dc4c | ||
|
|
cdfb1828dd | ||
|
|
8bf0f622e6 | ||
|
|
0e15186d42 | ||
|
|
09c11f9324 | ||
|
|
4ac175ca81 | ||
|
|
6304cf6f1e | ||
|
|
8ec65feddd | ||
|
|
d99bb004d7 | ||
|
|
50d23e2413 | ||
|
|
b0a102b9fb | ||
|
|
15c3ac27e9 | ||
|
|
fae3345f79 | ||
|
|
d1abac2adc | ||
|
|
12b0a8650d | ||
|
|
3c5e77e4dd | ||
|
|
073bd70c46 | ||
|
|
65bf5cfe2d | ||
|
|
b834148a76 | ||
|
|
15934056da | ||
|
|
a6306e1736 | ||
|
|
32fbc8a51f | ||
|
|
6f5e9351ed | ||
|
|
0b97fa5036 | ||
|
|
c413ebd628 | ||
|
|
3de8a04ebc | ||
|
|
521f72067c | ||
|
|
c158c2d839 | ||
|
|
2e201c3a60 | ||
|
|
167ec4b310 | ||
|
|
49f4eab570 | ||
|
|
acdd212767 | ||
|
|
7262858a76 | ||
|
|
eccac01899 | ||
|
|
15fe6fed49 | ||
|
|
0d7c437153 | ||
|
|
fa93eb956a | ||
|
|
47d0a4328f | ||
|
|
abbb3bf7ff | ||
|
|
b465e5531e | ||
|
|
3ed80499f7 | ||
|
|
6601ebeb60 | ||
|
|
7a59e147d2 | ||
|
|
86e558b6ce | ||
|
|
326bb9879d | ||
|
|
60a707f9e4 | ||
|
|
5428dfaf69 | ||
|
|
832ff4afcb | ||
|
|
da8e4bfd99 | ||
|
|
b5b3106d35 | ||
|
|
3ae5cbbc3a | ||
|
|
e343a24966 | ||
|
|
c8000f3e87 | ||
|
|
5ef4a2f22a | ||
|
|
abdbddf77f | ||
|
|
295bbb209f | ||
|
|
ec2443e5c4 | ||
|
|
b353f2ed7f | ||
|
|
b879c722f4 | ||
|
|
b9c50bc731 | ||
|
|
41c41e9e30 | ||
|
|
158ff57fdd | ||
|
|
626a8cfb50 | ||
|
|
84583d9a40 | ||
|
|
73ff634a75 | ||
|
|
fe5797e78f | ||
|
|
c3b66bf091 | ||
|
|
f1356db111 | ||
|
|
2dcc9bd0aa | ||
|
|
18e97db6b4 | ||
|
|
b971116c21 | ||
|
|
adcf929fdf | ||
|
|
8e792df7f7 | ||
|
|
71f55e7ab1 | ||
|
|
809a8679a8 | ||
|
|
36e2ac5c0b | ||
|
|
fd4dfbc328 | ||
|
|
5903894032 | ||
|
|
4634130d4c | ||
|
|
0f06314822 | ||
|
|
beaec4c5fe | ||
|
|
b480d8bc14 | ||
|
|
be0d274255 | ||
|
|
ee204b2f1d | ||
|
|
991b5176da | ||
|
|
bd53a126bb | ||
|
|
abc823303c | ||
|
|
9a19f9aa2f | ||
|
|
25e52cc1f8 | ||
|
|
364ff53182 | ||
|
|
f6e1b774b0 | ||
|
|
49ba7ee208 | ||
|
|
42d9628d39 | ||
|
|
f3f9e9a8b9 | ||
|
|
c3914e0181 | ||
|
|
95397a8ea2 | ||
|
|
f330c305bb | ||
|
|
01940a5ee0 | ||
|
|
60289fa846 | ||
|
|
d84ebb4b0c | ||
|
|
9c4bfdef7c | ||
|
|
4bdda6c0cf | ||
|
|
388b39dfac | ||
|
|
ede1dba441 | ||
|
|
41ccbd6700 | ||
|
|
3c6285fd77 | ||
|
|
7baf2aad1e | ||
|
|
bf9ff1a725 | ||
|
|
cadf755d47 | ||
|
|
df78fe739f | ||
|
|
550fe8bdeb | ||
|
|
84543d98c5 | ||
|
|
5a502466aa | ||
|
|
f0aff23469 | ||
|
|
f1bfbaa981 | ||
|
|
7052f2ec72 | ||
|
|
6b26c8f37b | ||
|
|
b337dd616f | ||
|
|
edee42c11a | ||
|
|
823d194217 | ||
|
|
5cf82d9b3b | ||
|
|
e095c339e1 | ||
|
|
527ca23d9b | ||
|
|
cad7f11146 | ||
|
|
5734063de2 | ||
|
|
a7ba2235d6 | ||
|
|
0af6c13e40 | ||
|
|
bf66a6cf0e | ||
|
|
e092ca72f7 | ||
|
|
1997e3d4a1 | ||
|
|
575f06b4f2 | ||
|
|
a3d6e708de | ||
|
|
04e7d104be | ||
|
|
d60801f105 | ||
|
|
9e0ba70f11 | ||
|
|
d10eea8db8 | ||
|
|
ae582973d0 | ||
|
|
68c8fc95cf | ||
|
|
1fd0a6f318 | ||
|
|
e64c66ff77 | ||
|
|
ec9153fab6 | ||
|
|
0097d7b96a | ||
|
|
6d3564ce53 | ||
|
|
1c8b9099fd | ||
|
|
b2110f08e9 | ||
|
|
83ef830329 | ||
|
|
7c920d64f4 | ||
|
|
0177383d5b | ||
|
|
a217be7d44 | ||
|
|
afb6bfc731 | ||
|
|
50216fb896 | ||
|
|
044609a536 | ||
|
|
6b514c3e28 | ||
|
|
0c5ca2f544 | ||
|
|
0181c2c604 | ||
|
|
6cceb9082d | ||
|
|
3b78610a38 | ||
|
|
dbee03f462 | ||
|
|
d3f6b04200 | ||
|
|
d2f03a3051 | ||
|
|
ed7b1ab8a1 | ||
|
|
331b82ed8e | ||
|
|
528302641a | ||
|
|
b900495284 | ||
|
|
8e8c613616 | ||
|
|
a7e294e97f | ||
|
|
faafc30abe | ||
|
|
7bc9579cf1 | ||
|
|
0302caa6b3 | ||
|
|
4e9433542b | ||
|
|
a0f906d6bf | ||
|
|
d893af429f | ||
|
|
f6159bb8d1 | ||
|
|
42bae0d8b3 | ||
|
|
84e6a5fa64 | ||
|
|
6240a26685 | ||
|
|
da1892523e | ||
|
|
0f19c8647b | ||
|
|
fd1b66a38f | ||
|
|
eddb81ff9b | ||
|
|
1085023c51 | ||
|
|
12125e6e60 | ||
|
|
c76fff8649 | ||
|
|
8507164ca3 | ||
|
|
c42a6880e0 | ||
|
|
15b9871205 | ||
|
|
e13c87a447 | ||
|
|
6405aece3a | ||
|
|
bf0bde8a64 | ||
|
|
0a3c2cc400 | ||
|
|
87283afd80 |
10
.clang-tidy
10
.clang-tidy
@@ -1,5 +1,7 @@
|
||||
Checks: >
|
||||
bugprone-*,
|
||||
clang-analyzer-*,
|
||||
-clang-analyzer-optin.core.EnumCastOutOfRange,
|
||||
concurrency-*,
|
||||
cppcoreguidelines-*,
|
||||
-cppcoreguidelines-owning-memory,
|
||||
@@ -16,14 +18,20 @@ Checks: >
|
||||
performance-*,
|
||||
portability-*,
|
||||
readability-*,
|
||||
-readability-else-after-return,
|
||||
-readability-function-cognitive-complexity,
|
||||
-readability-named-parameter,
|
||||
-readability-redundant-member-init,
|
||||
-readability-uppercase-literal-suffix,
|
||||
CheckOptions:
|
||||
- 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
|
||||
@@ -36,6 +44,8 @@ CheckOptions:
|
||||
value: true
|
||||
- key: modernize-avoid-c-arrays.AllowStringArrays
|
||||
value: true
|
||||
- key: performance-enum-size.EnumIgnoreList
|
||||
value: meta_traits
|
||||
- key: readability-function-cognitive-complexity.IgnoreMacros
|
||||
value: true
|
||||
- key: readability-identifier-length.MinimumParameterNameLength
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
name: tools
|
||||
name: analyzer
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- tools
|
||||
- analyzer
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -11,8 +11,8 @@ jobs:
|
||||
timeout-minutes: 60
|
||||
|
||||
env:
|
||||
IWYU: "0.22"
|
||||
LLVM: "18"
|
||||
IWYU: "0.24"
|
||||
LLVM: "20"
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
@@ -23,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
|
||||
@@ -55,6 +55,7 @@ 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
|
||||
@@ -72,10 +73,12 @@ jobs:
|
||||
env:
|
||||
CXX: clang++
|
||||
run: |
|
||||
cmake -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON -DENTT_USE_CLANG_TIDY=ON ..
|
||||
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
|
||||
- name: Run tests
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest -C Debug -j4
|
||||
47
.github/workflows/build.yml
vendored
47
.github/workflows/build.yml
vendored
@@ -5,51 +5,18 @@ on: [push, pull_request]
|
||||
jobs:
|
||||
|
||||
linux:
|
||||
timeout-minutes: 15
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, ubuntu-24.04]
|
||||
compiler:
|
||||
- { 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: g++, exe: 'g++', version: 13 }
|
||||
- { pkg: g++, exe: 'g++', version: 14 }
|
||||
- { pkg: clang, exe: 'clang++', version: 13 }
|
||||
- { pkg: clang, exe: 'clang++', version: 14 }
|
||||
- { pkg: clang, exe: 'clang++', version: 15 }
|
||||
- { pkg: clang, exe: 'clang++', version: 16 }
|
||||
- { pkg: clang, exe: 'clang++', version: 17 }
|
||||
- { pkg: clang, exe: 'clang++', version: 18 }
|
||||
exclude:
|
||||
- os: ubuntu-latest
|
||||
compiler: { pkg: g++, exe: 'g++', version: 12 }
|
||||
- os: ubuntu-latest
|
||||
compiler: { pkg: g++, exe: 'g++', version: 13 }
|
||||
- os: ubuntu-latest
|
||||
compiler: { pkg: g++, exe: 'g++', version: 14 }
|
||||
- os: ubuntu-latest
|
||||
compiler: { pkg: clang, exe: 'clang++', version: 16 }
|
||||
- os: ubuntu-latest
|
||||
compiler: { pkg: clang, exe: 'clang++', version: 17 }
|
||||
- os: ubuntu-latest
|
||||
compiler: { pkg: clang, exe: 'clang++', version: 18 }
|
||||
- os: ubuntu-24.04
|
||||
compiler: { pkg: g++, exe: 'g++', version: 9 }
|
||||
- os: ubuntu-24.04
|
||||
compiler: { pkg: g++, exe: 'g++', version: 10 }
|
||||
- os: ubuntu-24.04
|
||||
compiler: { pkg: g++, exe: 'g++', version: 11 }
|
||||
- os: ubuntu-24.04
|
||||
compiler: { pkg: clang, exe: 'clang++', version: 13 }
|
||||
- os: ubuntu-24.04
|
||||
compiler: { pkg: clang, exe: 'clang++', version: 14 }
|
||||
- os: ubuntu-24.04
|
||||
compiler: { pkg: clang, exe: 'clang++', version: 15 }
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 15
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -71,17 +38,18 @@ jobs:
|
||||
run: ctest -C Debug -j4
|
||||
|
||||
windows:
|
||||
timeout-minutes: 15
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
toolset: [default, v142, clang-cl]
|
||||
toolset: [default, v142, v143, clang-cl]
|
||||
include:
|
||||
- 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:
|
||||
@@ -115,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:
|
||||
|
||||
2
.github/workflows/coverage.yml
vendored
2
.github/workflows/coverage.yml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/deploy.yml
vendored
2
.github/workflows/deploy.yml
vendored
@@ -2,7 +2,7 @@ name: deploy
|
||||
|
||||
on:
|
||||
release:
|
||||
types: published
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
|
||||
|
||||
79
.github/workflows/testbed.yml
vendored
Normal file
79
.github/workflows/testbed.yml
vendored
Normal 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
|
||||
422
CMakeLists.txt
422
CMakeLists.txt
@@ -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-2024 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,105 +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/table.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/bit.hpp>
|
||||
$<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/ranges.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/ranges.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/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/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)
|
||||
@@ -216,109 +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/.*")
|
||||
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}
|
||||
FILES_MATCHING
|
||||
PATTERN "*.h"
|
||||
PATTERN "*.hpp"
|
||||
)
|
||||
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
|
||||
@@ -326,9 +354,5 @@ endif()
|
||||
option(ENTT_BUILD_DOCS "Enable building with documentation." OFF)
|
||||
|
||||
if(ENTT_BUILD_DOCS)
|
||||
find_package(Doxygen 1.10)
|
||||
|
||||
if(DOXYGEN_FOUND)
|
||||
add_subdirectory(docs)
|
||||
endif()
|
||||
add_subdirectory(docs)
|
||||
endif()
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2024 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
|
||||
|
||||
12
README.md
12
README.md
@@ -232,7 +232,8 @@ 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/>
|
||||
@@ -240,12 +241,17 @@ 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
|
||||
@@ -403,7 +409,7 @@ know who has participated so far.
|
||||
|
||||
# License
|
||||
|
||||
Code and documentation Copyright (c) 2017-2024 Michele Caini.<br/>
|
||||
Code and documentation Copyright (c) 2017-2025 Michele Caini.<br/>
|
||||
Colorful logo Copyright (c) 2018-2021 Richard Caseres.
|
||||
|
||||
Code released under
|
||||
|
||||
39
TODO
39
TODO
@@ -10,35 +10,28 @@ DOC:
|
||||
* bump entities, reserved bits on identifiers
|
||||
|
||||
TODO:
|
||||
* 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
|
||||
* self contained entity traits to avoid explicit specializations (ie enum constants)
|
||||
* auto type info data from types if present
|
||||
* test: push sharing types further
|
||||
* after non-continuous generation for entity storage:
|
||||
- get/reset placeholder to position after saving/loading (avoid long lookup)
|
||||
- allow skipping/reserving entity identifiers
|
||||
- documentation for reserved entities
|
||||
* storage entity: no emplace/insert, rename and add a fast range-push from above
|
||||
* view: propagate tombstone check request to iterator
|
||||
* storage entity: fast range-push from above
|
||||
* table: pop back to support swap and pop, single column access, empty type optimization
|
||||
* checkout tools workflow
|
||||
* improve front (no multiple checks) and back (ie no contains) for multi-type view
|
||||
* cleanup common view from tricks to handle single swap-only and in-place, if constexpr branches
|
||||
* entity based component_traits
|
||||
* review cmake warning about FetchContent_Populate (need .28 and EXCLUDE_FROM_ALL for FetchContent)
|
||||
* after removing meta prop vectors, copy meta objects in their handles directly
|
||||
* suppress -Wself-move on CI with g++13
|
||||
* view and view iterator specializations for multi, single and filtered elements
|
||||
* organizer support to groups
|
||||
* meta range: move id to meta objects and return plain types (?), then remove id from meta base and meta ctor too
|
||||
* refine the storage fallback mechanism for views (ie alloc?)
|
||||
* sigh_mixin: automatic signal registration
|
||||
* sigh_mixin: change cb signature from reg/entt to storage/entt (breaking for the good)
|
||||
* don't pass reactive storage by default to callback
|
||||
* 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
|
||||
|
||||
@@ -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}/
|
||||
)
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
/**
|
||||
* @namespace entt
|
||||
*
|
||||
* @brief `EnTT` default namespace.
|
||||
*/
|
||||
551
docs/doxy.in
551
docs/doxy.in
File diff suppressed because it is too large
Load Diff
@@ -22,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.
|
||||
|
||||
@@ -42,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
|
||||
|
||||
@@ -57,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
|
||||
@@ -83,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
|
||||
@@ -109,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.
|
||||
|
||||
@@ -12,16 +12,17 @@
|
||||
# Introduction
|
||||
|
||||
The standard C++ library offers a wide range of containers and adaptors already.
|
||||
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
|
||||
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 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/>
|
||||
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.
|
||||
|
||||
@@ -78,7 +79,7 @@ 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 are returned in the form of a tuple containing
|
||||
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.
|
||||
|
||||
164
docs/md/core.md
164
docs/md/core.md
@@ -10,7 +10,7 @@
|
||||
* [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)
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
`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
|
||||
@@ -49,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.
|
||||
|
||||
@@ -68,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
|
||||
@@ -115,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.
|
||||
|
||||
@@ -135,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
|
||||
@@ -145,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
|
||||
@@ -158,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
|
||||
|
||||
@@ -185,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])>;
|
||||
@@ -231,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/>
|
||||
@@ -273,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
|
||||
@@ -336,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.
|
||||
|
||||
@@ -359,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.
|
||||
@@ -376,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:
|
||||
|
||||
@@ -433,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.
|
||||
@@ -449,13 +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.
|
||||
|
||||
## 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.
|
||||
|
||||
@@ -463,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
|
||||
@@ -506,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.
|
||||
|
||||
@@ -520,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.
|
||||
|
||||
@@ -548,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
|
||||
@@ -571,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
|
||||
@@ -583,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
|
||||
@@ -599,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.
|
||||
@@ -636,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:
|
||||
@@ -648,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:
|
||||
@@ -660,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
|
||||
@@ -673,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)
|
||||
@@ -682,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
|
||||
@@ -692,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.
|
||||
@@ -709,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>;
|
||||
@@ -747,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
|
||||
|
||||
@@ -805,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
|
||||
@@ -827,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
|
||||
@@ -835,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
|
||||
|
||||
@@ -869,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,
|
||||
@@ -883,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
|
||||
|
||||
@@ -905,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
|
||||
@@ -961,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
|
||||
@@ -972,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
@@ -14,10 +14,10 @@
|
||||
|
||||
# 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/>
|
||||
@@ -29,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:
|
||||
@@ -53,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
|
||||
@@ -64,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
|
||||
@@ -83,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.
|
||||
@@ -104,8 +104,8 @@ 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:
|
||||
|
||||
@@ -119,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
|
||||
@@ -163,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.
|
||||
@@ -174,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
|
||||
@@ -184,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:
|
||||
|
||||
@@ -14,20 +14,21 @@
|
||||
|
||||
# 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
|
||||
|
||||
@@ -108,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:
|
||||
@@ -122,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
|
||||
@@ -139,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
|
||||
@@ -165,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
|
||||
@@ -175,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
|
||||
@@ -194,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
|
||||
@@ -208,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:
|
||||
@@ -225,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).
|
||||
@@ -246,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.
|
||||
@@ -272,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
|
||||
@@ -305,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
|
||||
@@ -322,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
|
||||
@@ -361,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.
|
||||
|
||||
@@ -20,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:
|
||||
@@ -53,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.
|
||||
|
||||
@@ -81,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
|
||||
|
||||
@@ -14,16 +14,16 @@
|
||||
`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
|
||||
@@ -48,7 +48,7 @@ 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.
|
||||
@@ -97,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,8 +137,14 @@ I hope the following lists can grow much more in the future.
|
||||
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-learn/mini_games/tree/main/letalka):
|
||||
* [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:
|
||||
|
||||
@@ -190,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.
|
||||
@@ -199,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.
|
||||
@@ -234,6 +240,13 @@ I hope the following lists can grow much more in the future.
|
||||
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:
|
||||
|
||||
@@ -275,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.
|
||||
@@ -307,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
|
||||
@@ -323,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.
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
# 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.
|
||||
@@ -27,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();
|
||||
@@ -45,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/>
|
||||
@@ -69,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.
|
||||
|
||||
363
docs/md/meta.md
363
docs/md/meta.md
@@ -3,10 +3,11 @@
|
||||
# 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)
|
||||
@@ -26,36 +27,36 @@
|
||||
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
|
||||
@@ -65,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
|
||||
@@ -76,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
|
||||
@@ -148,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.
|
||||
|
||||
@@ -163,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
|
||||
@@ -174,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
|
||||
@@ -191,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:
|
||||
|
||||
@@ -205,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
|
||||
@@ -216,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:
|
||||
|
||||
@@ -242,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:
|
||||
|
||||
@@ -256,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:
|
||||
@@ -268,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:
|
||||
|
||||
@@ -282,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()) {
|
||||
@@ -292,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.
|
||||
|
||||
@@ -330,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.
|
||||
@@ -356,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
|
||||
@@ -373,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:
|
||||
@@ -397,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:
|
||||
@@ -412,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.
|
||||
@@ -469,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:
|
||||
@@ -487,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
|
||||
@@ -498,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();
|
||||
@@ -507,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
|
||||
@@ -518,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:
|
||||
@@ -536,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/>
|
||||
@@ -546,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:
|
||||
|
||||
@@ -569,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>
|
||||
@@ -589,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:
|
||||
|
||||
@@ -608,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
|
||||
@@ -629,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:
|
||||
@@ -656,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.
|
||||
|
||||
@@ -669,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>()
|
||||
// ...
|
||||
@@ -683,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>>();
|
||||
```
|
||||
|
||||
@@ -708,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
|
||||
|
||||
@@ -722,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
|
||||
@@ -746,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.
|
||||
@@ -759,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.
|
||||
@@ -776,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/>
|
||||
@@ -792,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
|
||||
@@ -802,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
|
||||
@@ -819,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>();
|
||||
@@ -859,20 +897,17 @@ and meta functions.
|
||||
User-defined traits are set via a meta factory:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>().traits(my_traits::required | my_traits::hidden);
|
||||
entt::meta_factory<my_type>{}.traits(my_traits::required | my_traits::hidden);
|
||||
```
|
||||
|
||||
In the example above, `EnTT` bitmask enum support is used but any integral value
|
||||
is fine, as long as it doesn't exceed 16 bits.<br/>
|
||||
It's not possible to assign traits at different times. Therefore, multiple calls
|
||||
to the `traits` function overwrite previous values. However, traits can be read
|
||||
from meta objects and used to update existing data with a factory, effectively
|
||||
extending them as needed.<br/>
|
||||
Likewise, users can also set traits on meta objects later if needed, as long as
|
||||
the factory is reset to the meta object of interest:
|
||||
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>()
|
||||
entt::meta_factory<my_type>{}
|
||||
.data<&my_type::data_member, entt::as_ref_t>("member"_hs)
|
||||
.traits(my_traits::internal);
|
||||
```
|
||||
@@ -893,12 +928,12 @@ correctly.
|
||||
Custom arbitrary data are set via a meta factory:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>().custom<type_data>("name");
|
||||
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's not possible to assign custom data at different times. Therefore, multiple
|
||||
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/>
|
||||
@@ -906,7 +941,7 @@ 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<my_type>()
|
||||
entt::meta_factory<my_type>{}
|
||||
.func<&my_type::member_function>("member"_hs)
|
||||
.custom<function_data>("tooltip");
|
||||
```
|
||||
@@ -927,7 +962,7 @@ null pointer is returned to inform the user of the failed attempt.
|
||||
|
||||
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:
|
||||
@@ -936,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();
|
||||
@@ -960,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
|
||||
@@ -970,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:
|
||||
|
||||
@@ -992,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.
|
||||
|
||||
@@ -45,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.
|
||||
@@ -63,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
|
||||
@@ -86,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`:
|
||||
|
||||
@@ -104,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:
|
||||
@@ -171,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.
|
||||
|
||||
@@ -182,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<> {
|
||||
@@ -193,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:
|
||||
|
||||
@@ -211,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.
|
||||
@@ -221,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.
|
||||
|
||||
@@ -253,15 +253,15 @@ 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
|
||||
@@ -278,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
|
||||
@@ -312,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/>
|
||||
@@ -342,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.
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [The process](#the-process)
|
||||
* [Adaptor](#adaptor)
|
||||
* [Continuation](#continuation)
|
||||
* [Shared process](#shared-process)
|
||||
* [The scheduler](#the-scheduler)
|
||||
|
||||
# Introduction
|
||||
@@ -17,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();`
|
||||
|
||||
@@ -52,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 *) {
|
||||
@@ -85,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:
|
||||
@@ -137,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
|
||||
@@ -154,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
|
||||
@@ -209,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.
|
||||
|
||||
@@ -14,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.
|
||||
|
||||
@@ -34,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.
|
||||
@@ -49,14 +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.
|
||||
* [Friflo Engine ECS](https://github.com/friflo/Friflo.Json.Fliox/blob/main/Engine/README.md):
|
||||
an archetype ECS with focus on performance, cache locality and DX.
|
||||
* [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
|
||||
@@ -65,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:
|
||||
|
||||
@@ -17,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.
|
||||
@@ -31,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 {
|
||||
@@ -68,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
|
||||
@@ -84,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:
|
||||
@@ -111,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)...);
|
||||
@@ -139,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
|
||||
@@ -158,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);
|
||||
@@ -176,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.
|
||||
|
||||
@@ -19,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.
|
||||
|
||||
@@ -35,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.
|
||||
|
||||
@@ -75,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
|
||||
@@ -106,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
|
||||
@@ -118,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:
|
||||
@@ -136,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
|
||||
@@ -154,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.
|
||||
|
||||
@@ -164,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...)`.
|
||||
@@ -200,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
|
||||
@@ -228,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)`.
|
||||
|
||||
@@ -252,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.
|
||||
|
||||
@@ -271,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
|
||||
@@ -315,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/>
|
||||
@@ -357,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
|
||||
@@ -383,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
|
||||
@@ -430,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.
|
||||
|
||||
@@ -459,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:
|
||||
|
||||
@@ -476,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
|
||||
|
||||
@@ -495,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.
|
||||
@@ -555,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.
|
||||
|
||||
@@ -24,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
|
||||
@@ -37,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.
|
||||
|
||||
@@ -62,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
|
||||
|
||||
1
entt.imp
1
entt.imp
@@ -20,7 +20,6 @@
|
||||
{ "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/mixin.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/observer.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/organizer.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/ranges.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/registry.hpp>", "public" ] },
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
</AutoVisualizer>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,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>
|
||||
@@ -83,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))
|
||||
|
||||
@@ -3,16 +3,16 @@
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
|
||||
// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*)
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 14
|
||||
#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-usage)
|
||||
// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -24,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>;
|
||||
@@ -83,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;
|
||||
}
|
||||
|
||||
@@ -92,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;
|
||||
}
|
||||
|
||||
@@ -190,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},
|
||||
@@ -204,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;
|
||||
}
|
||||
|
||||
@@ -217,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 {
|
||||
@@ -225,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>
|
||||
@@ -259,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>;
|
||||
@@ -273,10 +275,10 @@ class dense_map {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,8 +339,8 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,6 +355,8 @@ 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. */
|
||||
@@ -451,6 +455,17 @@ public:
|
||||
*/
|
||||
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.
|
||||
* @return The associated allocator.
|
||||
@@ -669,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);
|
||||
@@ -681,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()[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;
|
||||
@@ -693,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) noexcept {
|
||||
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.
|
||||
@@ -722,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.
|
||||
@@ -892,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 {};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -910,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 {};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -952,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());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -980,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) {
|
||||
@@ -1004,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())));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,8 +282,8 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,6 +296,8 @@ public:
|
||||
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. */
|
||||
@@ -397,6 +400,17 @@ public:
|
||||
*/
|
||||
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.
|
||||
* @return The associated allocator.
|
||||
@@ -599,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);
|
||||
@@ -611,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()[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;
|
||||
@@ -623,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) noexcept {
|
||||
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.
|
||||
@@ -785,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 {};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -803,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 {};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -845,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());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -873,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) {
|
||||
@@ -897,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())));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
}
|
||||
|
||||
constexpr table_iterator operator++(int) noexcept {
|
||||
table_iterator orig = *this;
|
||||
const table_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
}
|
||||
|
||||
constexpr table_iterator operator--(int) noexcept {
|
||||
table_iterator orig = *this;
|
||||
const table_iterator orig = *this;
|
||||
return operator--(), orig;
|
||||
}
|
||||
|
||||
@@ -152,6 +152,8 @@ class basic_table {
|
||||
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. */
|
||||
@@ -417,7 +419,7 @@ public:
|
||||
*/
|
||||
void erase(const size_type pos) {
|
||||
ENTT_ASSERT(pos < size(), "Index out of bounds");
|
||||
erase(begin() + static_cast<typename const_iterator::difference_type>(pos));
|
||||
erase(begin() + static_cast<difference_type>(pos));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -100,13 +100,14 @@ 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;
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays, misc-const-correctness)
|
||||
std::size_t count[buckets]{};
|
||||
|
||||
for(auto it = from; it != to; ++it) {
|
||||
@@ -121,11 +122,12 @@ struct radix_sort {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -6,156 +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 {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
|
||||
alignas(Align) std::byte data[Len + static_cast<std::size_t>(Len == 0u)];
|
||||
};
|
||||
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 != 0u) && 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 *elem = 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>) {
|
||||
elem = (value.mode == any_policy::owner) ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance);
|
||||
} else {
|
||||
elem = 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>(*elem);
|
||||
}
|
||||
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 *>(elem))};
|
||||
}
|
||||
}
|
||||
|
||||
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>) {
|
||||
// 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 *>(elem) = *static_cast<const Type *>(other);
|
||||
return other;
|
||||
}
|
||||
break;
|
||||
case operation::destroy:
|
||||
if constexpr(in_situ<Type>) {
|
||||
elem->~Type();
|
||||
} else if constexpr(std::is_array_v<Type>) {
|
||||
delete[] elem;
|
||||
} else {
|
||||
delete elem;
|
||||
}
|
||||
break;
|
||||
case operation::compare:
|
||||
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;
|
||||
return (*elem == *static_cast<const Type *>(other)) ? other : nullptr;
|
||||
} else {
|
||||
return (elem == other) ? other : nullptr;
|
||||
}
|
||||
case operation::get:
|
||||
return elem;
|
||||
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 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))};
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
using plain_type = std::remove_cv_t<std::remove_reference_t<Type>>;
|
||||
info = &type_id<plain_type>();
|
||||
using plain_type = std::remove_const_t<std::remove_reference_t<Type>>;
|
||||
|
||||
if constexpr(!std::is_void_v<Type>) {
|
||||
vtable = basic_vtable<plain_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<plain_type>) {
|
||||
if constexpr(std::is_aggregate_v<plain_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<plain_type>)) {
|
||||
::new(&storage) plain_type{std::forward<Args>(args)...};
|
||||
} else {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
|
||||
::new(&storage) plain_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<plain_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<plain_type>)) {
|
||||
instance = new plain_type{std::forward<Args>(args)...};
|
||||
} else if constexpr(std::is_array_v<plain_type>) {
|
||||
static_assert(sizeof...(Args) == 0u, "Invalid arguments");
|
||||
instance = new plain_type[std::extent_v<plain_type>]();
|
||||
} else {
|
||||
instance = new plain_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;
|
||||
@@ -172,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.
|
||||
@@ -194,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -227,10 +266,12 @@ public:
|
||||
*/
|
||||
basic_any &operator=(const basic_any &other) {
|
||||
if(this != &other) {
|
||||
reset();
|
||||
invoke_deleter_if_exists();
|
||||
|
||||
if(other.vtable) {
|
||||
other.vtable(operation::copy, other, this);
|
||||
if(other) {
|
||||
other.vtable(request::copy, other, this);
|
||||
} else {
|
||||
initialize<void>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,14 +284,18 @@ public:
|
||||
* @return This any object.
|
||||
*/
|
||||
basic_any &operator=(basic_any &&other) noexcept {
|
||||
ENTT_ASSERT(this != &other, "Self move assignment");
|
||||
if(this != &other) {
|
||||
invoke_deleter_if_exists();
|
||||
|
||||
reset();
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -270,11 +315,48 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -282,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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -291,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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -299,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());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -308,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>>());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -319,7 +429,7 @@ public:
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
void emplace(Args &&...args) {
|
||||
reset();
|
||||
invoke_deleter_if_exists();
|
||||
initialize<Type>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -329,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);
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -351,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>();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -367,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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -376,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -397,12 +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.
|
||||
*/
|
||||
[[nodiscard]] bool owner() const noexcept {
|
||||
return (mode == any_policy::dynamic || mode == any_policy::embedded);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -414,19 +529,16 @@ 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.
|
||||
@@ -449,8 +561,9 @@ 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>
|
||||
// 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_cv_t<std::remove_reference_t<Type>>>) {
|
||||
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));
|
||||
}
|
||||
@@ -466,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 */
|
||||
@@ -477,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.
|
||||
@@ -498,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.
|
||||
|
||||
@@ -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
|
||||
@@ -41,6 +41,7 @@ template<typename Type>
|
||||
*/
|
||||
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);
|
||||
|
||||
@@ -61,7 +62,7 @@ template<typename Type>
|
||||
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 value & (mod - 1u);
|
||||
return static_cast<Type>(value & (mod - 1u));
|
||||
}
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -22,7 +22,7 @@ struct compressed_pair_element {
|
||||
// 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)} {}
|
||||
|
||||
@@ -52,7 +52,7 @@ struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Ty
|
||||
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)} {}
|
||||
|
||||
@@ -240,7 +240,7 @@ 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,10 @@ namespace entt {
|
||||
*/
|
||||
template<typename...>
|
||||
class family {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
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. */
|
||||
@@ -25,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
|
||||
|
||||
@@ -2,10 +2,25 @@
|
||||
#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;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
@@ -32,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
|
||||
@@ -62,23 +61,12 @@ class basic_hashed_string: internal::basic_hashed_string<Char> {
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
// Fowler–Noll–Vo hash function v. 1a - the good
|
||||
[[nodiscard]] static constexpr auto helper(const std::basic_string_view<Char> view) noexcept {
|
||||
base_type base{view.data(), view.size(), params::offset};
|
||||
|
||||
for(auto &&curr: view) {
|
||||
base.hash = (base.hash ^ static_cast<id_type>(curr)) * params::prime;
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Character type. */
|
||||
using value_type = typename base_type::value_type;
|
||||
@@ -105,7 +93,7 @@ public:
|
||||
*/
|
||||
template<std::size_t N>
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
|
||||
[[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept {
|
||||
[[nodiscard]] static ENTT_CONSTEVAL hash_type value(const value_type (&str)[N]) noexcept {
|
||||
return basic_hashed_string{str};
|
||||
}
|
||||
|
||||
@@ -128,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.
|
||||
@@ -137,8 +132,13 @@ public:
|
||||
*/
|
||||
template<std::size_t N>
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
|
||||
constexpr basic_hashed_string(const value_type (&str)[N]) noexcept
|
||||
: base_type{helper({static_cast<const value_type *>(str)})} {}
|
||||
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
|
||||
@@ -150,10 +150,16 @@ 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 {
|
||||
@@ -177,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();
|
||||
}
|
||||
|
||||
@@ -291,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};
|
||||
}
|
||||
|
||||
@@ -300,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};
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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{static_cast<const char *>(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
|
||||
}
|
||||
|
||||
@@ -138,9 +146,9 @@ struct type_info final {
|
||||
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)
|
||||
|
||||
/**
|
||||
@@ -179,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();
|
||||
}
|
||||
|
||||
@@ -189,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);
|
||||
}
|
||||
|
||||
@@ -199,7 +207,7 @@ private:
|
||||
* @param rhs A valid type info object.
|
||||
* @return True if the first element is less than the second, 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.index() < rhs.index();
|
||||
}
|
||||
|
||||
@@ -210,7 +218,7 @@ private:
|
||||
* @return True if the first element is less than or equal to the second, 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 !(rhs < lhs);
|
||||
}
|
||||
|
||||
@@ -221,7 +229,7 @@ private:
|
||||
* @return True if the first element is greater than the second, 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 rhs < lhs;
|
||||
}
|
||||
|
||||
@@ -232,7 +240,7 @@ private:
|
||||
* @return True if the first element is greater than or equal to the second,
|
||||
* 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);
|
||||
}
|
||||
|
||||
@@ -249,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
|
||||
|
||||
@@ -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...>;
|
||||
};
|
||||
|
||||
@@ -689,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,18 +774,18 @@ template<typename Type>
|
||||
// NOLINTBEGIN(modernize-use-transparent-functors)
|
||||
if constexpr(std::is_array_v<Type>) {
|
||||
return false;
|
||||
} else if constexpr(!is_iterator_v<Type> && has_value_type<Type>::value) {
|
||||
if constexpr(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 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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -20,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;
|
||||
|
||||
@@ -47,9 +52,6 @@ class basic_runtime_view;
|
||||
template<typename, typename, typename>
|
||||
class basic_group;
|
||||
|
||||
template<typename, typename = std::allocator<void>>
|
||||
class basic_observer;
|
||||
|
||||
template<typename>
|
||||
class basic_organizer;
|
||||
|
||||
@@ -92,9 +94,6 @@ using reactive_mixin = basic_reactive_mixin<Type, basic_registry<typename Type::
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using registry = basic_registry<>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using observer = basic_observer<registry>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using organizer = basic_organizer<registry>;
|
||||
|
||||
@@ -283,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
|
||||
|
||||
@@ -57,7 +57,7 @@ public:
|
||||
}
|
||||
|
||||
extended_group_iterator operator++(int) noexcept {
|
||||
extended_group_iterator orig = *this;
|
||||
const extended_group_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ class group_handler final: public group_descriptor {
|
||||
|
||||
void common_setup() {
|
||||
// we cannot iterate backwards because we want to leave behind valid entities in case of owned types
|
||||
for(auto first = pools[0u]->rbegin(), last = first + pools[0u]->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);
|
||||
}
|
||||
}
|
||||
@@ -151,7 +151,7 @@ public:
|
||||
|
||||
[[nodiscard]] bool owned(const id_type hash) const noexcept override {
|
||||
for(size_type pos{}; pos < Owned; ++pos) {
|
||||
if(pools[pos]->type().hash() == hash) {
|
||||
if(pools[pos]->info().hash() == hash) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -297,6 +297,8 @@ 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. */
|
||||
@@ -465,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)];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -711,6 +713,8 @@ 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. */
|
||||
@@ -794,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{};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -824,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{};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -864,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)];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -47,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ template<typename Registry>
|
||||
class as_view {
|
||||
template<typename... Get, typename... 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>...>{});
|
||||
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 ®
|
||||
registry_type *reg;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -62,9 +62,9 @@ class as_group {
|
||||
template<typename... Owned, typename... Get, typename... Exclude>
|
||||
[[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::element_type...>(get_t<typename Get::element_type...>{}, exclude_t<typename Exclude::element_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::element_type, Owned>...>(get_t<constness_as_t<typename Get::element_type, Get>...>{}, exclude_t<constness_as_t<typename Exclude::element_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,7 +94,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
registry_type ®
|
||||
registry_type *reg;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -124,16 +124,17 @@ void invoke(Registry ®, const typename Registry::entity_type entt) {
|
||||
*/
|
||||
template<typename... Args>
|
||||
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>;
|
||||
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 typename basic_storage<Args...>::base_type &base = storage;
|
||||
const auto *addr = std::addressof(instance);
|
||||
const auto *page = storage.raw();
|
||||
|
||||
for(auto it = base.rbegin(), last = base.rend(); it < last; it += traits_type::page_size) {
|
||||
if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < static_cast<decltype(dist)>(traits_type::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);
|
||||
}
|
||||
}
|
||||
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -36,20 +36,6 @@ 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 {};
|
||||
|
||||
template<typename Type>
|
||||
auto *any_to_owner(any &value) noexcept {
|
||||
using base_type = basic_registry<typename Type::entity_type, typename Type::allocator_type>;
|
||||
auto *reg = any_cast<base_type>(&value);
|
||||
|
||||
if constexpr(!std::is_same_v<Type, base_type>) {
|
||||
if(!reg) {
|
||||
reg = any_cast<Type>(&value);
|
||||
}
|
||||
}
|
||||
|
||||
return reg;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
@@ -130,7 +116,14 @@ private:
|
||||
}
|
||||
|
||||
void bind_any(any value) noexcept final {
|
||||
owner = internal::any_to_owner<registry_type>(value);
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -157,15 +150,15 @@ public:
|
||||
destruction{allocator},
|
||||
update{allocator} {
|
||||
if constexpr(internal::has_on_construct<typename underlying_type::element_type, Registry>::value) {
|
||||
entt::sink{construction}.template connect<&underlying_type::element_type::on_construct>();
|
||||
sink{construction}.template connect<&underlying_type::element_type::on_construct>();
|
||||
}
|
||||
|
||||
if constexpr(internal::has_on_update<typename underlying_type::element_type, Registry>::value) {
|
||||
entt::sink{update}.template connect<&underlying_type::element_type::on_update>();
|
||||
sink{update}.template connect<&underlying_type::element_type::on_update>();
|
||||
}
|
||||
|
||||
if constexpr(internal::has_on_destroy<typename underlying_type::element_type, Registry>::value) {
|
||||
entt::sink{destruction}.template connect<&underlying_type::element_type::on_destroy>();
|
||||
sink{destruction}.template connect<&underlying_type::element_type::on_destroy>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +170,7 @@ public:
|
||||
* @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)},
|
||||
@@ -189,7 +182,7 @@ public:
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
basic_sigh_mixin(basic_sigh_mixin &&other, const allocator_type &allocator)
|
||||
: underlying_type{std::move(other), allocator},
|
||||
: underlying_type{static_cast<underlying_type &&>(other), allocator},
|
||||
owner{other.owner},
|
||||
construction{std::move(other.construction), allocator},
|
||||
destruction{std::move(other.destruction), allocator},
|
||||
@@ -294,48 +287,59 @@ 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 Whatever the underlying storage returns.
|
||||
* @brief Creates a new identifier or recycles a destroyed one.
|
||||
* @return A valid identifier.
|
||||
*/
|
||||
auto emplace() {
|
||||
const auto entt = underlying_type::emplace();
|
||||
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 Whatever the underlying storage returns.
|
||||
* @brief Creates a new identifier or recycles a destroyed one.
|
||||
* @param hint Required identifier.
|
||||
* @return A valid identifier.
|
||||
*/
|
||||
template<typename... Args>
|
||||
std::conditional_t<std::is_same_v<typename underlying_type::element_type, entity_type>, entity_type, decltype(std::declval<underlying_type>().get({}))>
|
||||
emplace(const entity_type hint, Args &&...args) {
|
||||
if constexpr(std::is_same_v<typename underlying_type::element_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 ® = 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.
|
||||
@@ -349,16 +353,12 @@ 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>
|
||||
@@ -367,6 +367,7 @@ public:
|
||||
underlying_type::insert(first, last, std::forward<Args>(args)...);
|
||||
|
||||
if(auto ® = owner_or_assert(); !construction.empty()) {
|
||||
// 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));
|
||||
}
|
||||
@@ -390,7 +391,9 @@ 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");
|
||||
|
||||
@@ -407,7 +410,14 @@ class basic_reactive_mixin final: public Type {
|
||||
|
||||
private:
|
||||
void bind_any(any value) noexcept final {
|
||||
owner = internal::any_to_owner<registry_type>(value);
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -429,7 +439,8 @@ public:
|
||||
*/
|
||||
explicit basic_reactive_mixin(const allocator_type &allocator)
|
||||
: underlying_type{allocator},
|
||||
owner{} {
|
||||
owner{},
|
||||
conn{allocator} {
|
||||
}
|
||||
|
||||
/*! @brief Default copy constructor, deleted on purpose. */
|
||||
@@ -440,8 +451,10 @@ public:
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
basic_reactive_mixin(basic_reactive_mixin &&other) noexcept
|
||||
: underlying_type{std::move(other)},
|
||||
owner{other.owner} {}
|
||||
: underlying_type{static_cast<underlying_type &&>(other)},
|
||||
owner{other.owner},
|
||||
conn{std::move(other.conn)} {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
@@ -449,8 +462,10 @@ public:
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
basic_reactive_mixin(basic_reactive_mixin &&other, const allocator_type &allocator)
|
||||
: underlying_type{std::move(other), allocator},
|
||||
owner{other.owner} {}
|
||||
: 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;
|
||||
@@ -467,18 +482,8 @@ public:
|
||||
* @return This mixin.
|
||||
*/
|
||||
basic_reactive_mixin &operator=(basic_reactive_mixin &&other) noexcept {
|
||||
swap(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exchanges the contents with those of a given storage.
|
||||
* @param other Storage to exchange the content with.
|
||||
*/
|
||||
void swap(basic_reactive_mixin &other) noexcept {
|
||||
using std::swap;
|
||||
swap(owner, other.owner);
|
||||
underlying_type::swap(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -490,7 +495,8 @@ public:
|
||||
*/
|
||||
template<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
|
||||
basic_reactive_mixin &on_construct(const id_type id = type_hash<Clazz>::value()) {
|
||||
owner_or_assert().template storage<Clazz>(id).on_construct().template connect<Candidate>(*this);
|
||||
auto curr = owner_or_assert().template storage<Clazz>(id).on_construct().template connect<Candidate>(*this);
|
||||
conn.push_back(std::move(curr));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -503,7 +509,8 @@ public:
|
||||
*/
|
||||
template<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
|
||||
basic_reactive_mixin &on_update(const id_type id = type_hash<Clazz>::value()) {
|
||||
owner_or_assert().template storage<Clazz>(id).on_update().template connect<Candidate>(*this);
|
||||
auto curr = owner_or_assert().template storage<Clazz>(id).on_update().template connect<Candidate>(*this);
|
||||
conn.push_back(std::move(curr));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -516,7 +523,8 @@ public:
|
||||
*/
|
||||
template<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
|
||||
basic_reactive_mixin &on_destroy(const id_type id = type_hash<Clazz>::value()) {
|
||||
owner_or_assert().template storage<Clazz>(id).on_destroy().template connect<Candidate>(*this);
|
||||
auto curr = owner_or_assert().template storage<Clazz>(id).on_destroy().template connect<Candidate>(*this);
|
||||
conn.push_back(std::move(curr));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -560,12 +568,22 @@ public:
|
||||
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{}) {
|
||||
owner_type &parent = owner_or_assert();
|
||||
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
|
||||
|
||||
@@ -1,438 +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 "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 elements tracked by the matcher.
|
||||
* @tparam NoneOf Types of elements 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 element 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 [[deprecated("use reactive mixin instead")]] 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 elements tracked by the matcher.
|
||||
* @tparam NoneOf Types of elements 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 element 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 elements required by the matcher.
|
||||
* @tparam NoneOf Types of elements 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 elements.
|
||||
*
|
||||
* 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 elements 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 elements are created and assigned to entities.
|
||||
* * The entity currently pointed is modified (as an example, if one of the
|
||||
* given elements 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 elements 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 Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Registry, typename Allocator>
|
||||
class basic_observer {
|
||||
using mask_type = std::uint64_t;
|
||||
using storage_type = basic_storage<mask_type, typename Registry::entity_type, typename std::allocator_traits<Allocator>::template rebind_alloc<mask_type>>;
|
||||
|
||||
template<std::size_t Index>
|
||||
static void discard_if(storage_type &storage, Registry &, const typename Registry::entity_type entt) {
|
||||
if(storage.contains(entt) && !(storage.get(entt) &= (~(1 << Index)))) {
|
||||
storage.erase(entt);
|
||||
}
|
||||
}
|
||||
|
||||
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(storage_type &storage, Registry &parent, const typename Registry::entity_type entt) {
|
||||
if(parent.template all_of<Require...>(entt) && !parent.template any_of<Reject...>(entt)) {
|
||||
if(!storage.contains(entt)) {
|
||||
storage.emplace(entt);
|
||||
}
|
||||
|
||||
storage.get(entt) |= (1 << Index);
|
||||
}
|
||||
}
|
||||
|
||||
template<std::size_t Index>
|
||||
static void connect(storage_type &storage, Registry &parent) {
|
||||
(parent.template on_destroy<Require>().template connect<&discard_if<Index>>(storage), ...);
|
||||
(parent.template on_construct<Reject>().template connect<&discard_if<Index>>(storage), ...);
|
||||
parent.template on_update<AnyOf>().template connect<&maybe_valid_if<Index>>(storage);
|
||||
parent.template on_destroy<AnyOf>().template connect<&discard_if<Index>>(storage);
|
||||
}
|
||||
|
||||
static void disconnect(storage_type &storage, Registry &parent) {
|
||||
(parent.template on_destroy<Require>().disconnect(&storage), ...);
|
||||
(parent.template on_construct<Reject>().disconnect(&storage), ...);
|
||||
parent.template on_update<AnyOf>().disconnect(&storage);
|
||||
parent.template on_destroy<AnyOf>().disconnect(&storage);
|
||||
}
|
||||
};
|
||||
|
||||
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(storage_type &storage, Registry &parent, const typename Registry::entity_type entt) {
|
||||
bool guard{};
|
||||
|
||||
if constexpr(sizeof...(Ignore) == 0) {
|
||||
guard = parent.template all_of<AllOf..., Require...>(entt) && !parent.template any_of<NoneOf..., Reject...>(entt);
|
||||
} else {
|
||||
guard = parent.template all_of<AllOf..., Require...>(entt) && ((std::is_same_v<Ignore..., NoneOf> || !parent.template any_of<NoneOf>(entt)) && ...) && !parent.template any_of<Reject...>(entt);
|
||||
}
|
||||
|
||||
if(guard) {
|
||||
if(!storage.contains(entt)) {
|
||||
storage.emplace(entt);
|
||||
}
|
||||
|
||||
storage.get(entt) |= (1 << Index);
|
||||
}
|
||||
}
|
||||
|
||||
template<std::size_t Index>
|
||||
static void connect(storage_type &storage, Registry &parent) {
|
||||
(parent.template on_destroy<Require>().template connect<&discard_if<Index>>(storage), ...);
|
||||
(parent.template on_construct<Reject>().template connect<&discard_if<Index>>(storage), ...);
|
||||
(parent.template on_construct<AllOf>().template connect<&maybe_valid_if<Index>>(storage), ...);
|
||||
(parent.template on_destroy<NoneOf>().template connect<&maybe_valid_if<Index, NoneOf>>(storage), ...);
|
||||
(parent.template on_destroy<AllOf>().template connect<&discard_if<Index>>(storage), ...);
|
||||
(parent.template on_construct<NoneOf>().template connect<&discard_if<Index>>(storage), ...);
|
||||
}
|
||||
|
||||
static void disconnect(storage_type &storage, Registry &parent) {
|
||||
(parent.template on_destroy<Require>().disconnect(&storage), ...);
|
||||
(parent.template on_construct<Reject>().disconnect(&storage), ...);
|
||||
(parent.template on_construct<AllOf>().disconnect(&storage), ...);
|
||||
(parent.template on_destroy<NoneOf>().disconnect(&storage), ...);
|
||||
(parent.template on_destroy<AllOf>().disconnect(&storage), ...);
|
||||
(parent.template on_construct<NoneOf>().disconnect(&storage), ...);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Matcher>
|
||||
static void disconnect(Registry &parent, storage_type &storage) {
|
||||
(matcher_handler<Matcher>::disconnect(storage, parent), ...);
|
||||
}
|
||||
|
||||
template<typename... Matcher, std::size_t... Index>
|
||||
static void connect(Registry &parent, storage_type &storage, std::index_sequence<Index...>) {
|
||||
static_assert(sizeof...(Matcher) < std::numeric_limits<mask_type>::digits, "Too many matchers");
|
||||
(matcher_handler<Matcher>::template connect<Index>(storage, parent), ...);
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! 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 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)
|
||||
: release{},
|
||||
parent{},
|
||||
storage{allocator} {}
|
||||
|
||||
/*! @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 ®, basic_collector<Matcher...>, const allocator_type &allocator = allocator_type{})
|
||||
: release{&basic_observer::disconnect<Matcher...>},
|
||||
parent{®},
|
||||
storage{allocator} {
|
||||
connect<Matcher...>(reg, storage, std::index_sequence_for<Matcher...>{});
|
||||
}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~basic_observer() = default;
|
||||
|
||||
/**
|
||||
* @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 ®, basic_collector<Matcher...>) {
|
||||
disconnect();
|
||||
storage.clear();
|
||||
|
||||
parent = ®
|
||||
release = &basic_observer::disconnect<Matcher...>;
|
||||
connect<Matcher...>(reg, storage, std::index_sequence_for<Matcher...>{});
|
||||
}
|
||||
|
||||
/*! @brief Disconnects an observer from the registry it keeps track of. */
|
||||
void disconnect() {
|
||||
if(release) {
|
||||
release(*parent, storage);
|
||||
release = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements in an observer.
|
||||
* @return Number of elements.
|
||||
*/
|
||||
[[nodiscard]] size_type size() const noexcept {
|
||||
return storage.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether an observer is empty.
|
||||
* @return True if the observer is empty, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool empty() const noexcept {
|
||||
return storage.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 storage.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 storage.storage_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 storage.storage_type::base_type::end();
|
||||
}
|
||||
|
||||
/*! @brief Clears the underlying container. */
|
||||
void clear() noexcept {
|
||||
storage.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:
|
||||
void (*release)(registry_type &, storage_type &);
|
||||
registry_type *parent;
|
||||
storage_type storage;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
@@ -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<
|
||||
@@ -60,27 +69,38 @@ 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 */
|
||||
@@ -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>>();
|
||||
}
|
||||
@@ -148,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()), ...);
|
||||
}
|
||||
@@ -272,14 +294,6 @@ public:
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the list of nodes reachable from a given vertex.
|
||||
* @return The list of nodes reachable from the vertex.
|
||||
*/
|
||||
[[deprecated("use ::out_edges")]] [[nodiscard]] const std::vector<std::size_t> &children() const noexcept {
|
||||
return out_edges();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prepares a registry and assures that all required resources
|
||||
* are properly instantiated before using them.
|
||||
@@ -303,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 ®) {
|
||||
std::apply(Candidate, to_args(reg, typename resource_type::args{}));
|
||||
@@ -320,7 +333,7 @@ public:
|
||||
+[](registry_type ®) { 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));
|
||||
}
|
||||
|
||||
@@ -335,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 ®) {
|
||||
Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
|
||||
@@ -353,7 +365,7 @@ public:
|
||||
+[](registry_type ®) { 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));
|
||||
}
|
||||
|
||||
@@ -367,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{
|
||||
@@ -387,7 +399,7 @@ public:
|
||||
* @brief Generates a task graph for the current content.
|
||||
* @return The adjacency list of the task graph.
|
||||
*/
|
||||
[[nodiscard]] 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();
|
||||
|
||||
@@ -64,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;
|
||||
}
|
||||
|
||||
@@ -73,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;
|
||||
}
|
||||
|
||||
@@ -164,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);
|
||||
@@ -176,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>
|
||||
@@ -187,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>
|
||||
@@ -215,7 +219,7 @@ 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:
|
||||
@@ -251,7 +255,7 @@ class basic_registry {
|
||||
using storage_type = storage_for_type<Type>;
|
||||
|
||||
if(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<storage_type &>(*it->second);
|
||||
}
|
||||
|
||||
@@ -281,7 +285,7 @@ class basic_registry {
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -449,7 +453,7 @@ public:
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -460,7 +464,7 @@ public:
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] const storage_for_type<Type> *storage(const id_type id = type_hash<Type>::value()) const {
|
||||
return assure<Type>(id);
|
||||
return assure<std::remove_const_t<Type>>(id);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -497,7 +501,7 @@ public:
|
||||
* @return A valid identifier.
|
||||
*/
|
||||
[[nodiscard]] entity_type create() {
|
||||
return entities.emplace();
|
||||
return entities.generate();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -510,7 +514,7 @@ public:
|
||||
* @return A valid identifier.
|
||||
*/
|
||||
[[nodiscard]] entity_type create(const entity_type hint) {
|
||||
return entities.emplace(hint);
|
||||
return entities.generate(hint);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -524,7 +528,7 @@ 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));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -539,7 +543,7 @@ public:
|
||||
*/
|
||||
version_type destroy(const entity_type entt) {
|
||||
for(size_type pos = pools.size(); pos != 0u; --pos) {
|
||||
pools.begin()[pos - 1u].second->remove(entt);
|
||||
pools.begin()[static_cast<typename pool_container_type::difference_type>(pos - 1u)].second->remove(entt);
|
||||
}
|
||||
|
||||
entities.erase(entt);
|
||||
@@ -576,7 +580,7 @@ public:
|
||||
template<typename It>
|
||||
void destroy(It first, It last) {
|
||||
const auto to = entities.sort_as(first, last);
|
||||
const auto from = entities.cend() - entities.free_list();
|
||||
const auto from = entities.cend() - static_cast<typename common_type::difference_type>(entities.free_list());
|
||||
|
||||
for(auto &&curr: pools) {
|
||||
curr.second->remove(from, to);
|
||||
@@ -606,6 +610,22 @@ public:
|
||||
return assure<Type>().emplace(entt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
template<typename Type, typename It>
|
||||
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.
|
||||
*
|
||||
@@ -618,7 +638,7 @@ public:
|
||||
* @param value An instance of the element to assign.
|
||||
*/
|
||||
template<typename Type, typename It>
|
||||
void insert(It first, It last, const Type &value = {}) {
|
||||
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);
|
||||
}
|
||||
@@ -960,7 +980,7 @@ public:
|
||||
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();
|
||||
|
||||
@@ -38,8 +38,7 @@ public:
|
||||
it{},
|
||||
tombstone_check{} {}
|
||||
|
||||
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
|
||||
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},
|
||||
@@ -56,7 +55,7 @@ public:
|
||||
}
|
||||
|
||||
runtime_view_iterator operator++(int) {
|
||||
runtime_view_iterator orig = *this;
|
||||
const runtime_view_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
@@ -67,7 +66,7 @@ public:
|
||||
}
|
||||
|
||||
runtime_view_iterator operator--(int) {
|
||||
runtime_view_iterator orig = *this;
|
||||
const runtime_view_iterator orig = *this;
|
||||
return operator--(), orig;
|
||||
}
|
||||
|
||||
@@ -126,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;
|
||||
@@ -133,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. */
|
||||
@@ -219,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;
|
||||
@@ -243,7 +250,7 @@ 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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -255,7 +262,7 @@ public:
|
||||
* @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};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -265,7 +272,7 @@ public:
|
||||
* 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};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -284,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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -231,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>();
|
||||
@@ -247,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) {
|
||||
|
||||
@@ -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,7 +73,7 @@ struct sparse_set_iterator final {
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
|
||||
return (*packed)[index() - value];
|
||||
return (*packed)[static_cast<typename Container::size_type>(index() - value)];
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr pointer operator->() const noexcept {
|
||||
@@ -164,29 +164,38 @@ class basic_sparse_set {
|
||||
|
||||
static constexpr auto max_size = static_cast<std::size_t>(traits_type::to_entity(null));
|
||||
|
||||
[[nodiscard]] auto policy_to_head() const noexcept {
|
||||
return static_cast<size_type>(max_size * static_cast<decltype(max_size)>(mode != deletion_policy::swap_only));
|
||||
// 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);
|
||||
@@ -259,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;
|
||||
@@ -271,7 +281,7 @@ protected:
|
||||
*/
|
||||
void in_place_pop(const basic_iterator it) {
|
||||
ENTT_ASSERT(mode == deletion_policy::in_place, "Deletion policy mismatch");
|
||||
const auto pos = static_cast<size_type>(traits_type::to_entity(std::exchange(sparse_ref(*it), null)));
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -305,9 +315,9 @@ protected:
|
||||
switch(mode) {
|
||||
case deletion_policy::in_place:
|
||||
if(head != max_size) {
|
||||
for(auto first = begin(); !(first.index() < 0); ++first) {
|
||||
if(*first != tombstone) {
|
||||
sparse_ref(*first) = null;
|
||||
for(auto &&elem: packed) {
|
||||
if(elem != tombstone) {
|
||||
sparse_ref(elem) = null;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -315,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;
|
||||
}
|
||||
@@ -342,7 +352,7 @@ protected:
|
||||
pos = head;
|
||||
ENTT_ASSERT(elem == null, "Slot not available");
|
||||
elem = traits_type::combine(static_cast<typename traits_type::entity_type>(head), traits_type::to_integral(entt));
|
||||
head = static_cast<size_type>(traits_type::to_entity(std::exchange(packed[pos], entt)));
|
||||
head = entity_to_pos(std::exchange(packed[pos], entt));
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
@@ -356,16 +366,16 @@ 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(!(static_cast<size_type>(traits_type::to_entity(elem)) < head), "Slot not available");
|
||||
ENTT_ASSERT(!(entity_to_pos(elem) < head), "Slot not available");
|
||||
bump(entt);
|
||||
}
|
||||
|
||||
pos = head++;
|
||||
swap_at(static_cast<size_type>(traits_type::to_entity(elem)), pos);
|
||||
swap_at(entity_to_pos(elem), pos);
|
||||
break;
|
||||
}
|
||||
|
||||
return --(end() - static_cast<typename iterator::difference_type>(pos));
|
||||
return iterator{packed, static_cast<difference_type>(++pos)};
|
||||
}
|
||||
|
||||
/*! @brief Forwards variables to derived classes, if any. */
|
||||
@@ -381,6 +391,8 @@ public:
|
||||
using version_type = typename traits_type::version_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @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. */
|
||||
@@ -421,7 +433,7 @@ 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()} {
|
||||
ENTT_ASSERT(traits_type::version_mask || mode != deletion_policy::in_place, "Policy does not support zero-sized versions");
|
||||
@@ -437,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())} {}
|
||||
|
||||
@@ -449,7 +461,7 @@ public:
|
||||
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 || get_allocator() == other.get_allocator(), "Copying a sparse set is not allowed");
|
||||
@@ -485,7 +497,7 @@ public:
|
||||
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);
|
||||
}
|
||||
@@ -546,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();
|
||||
}
|
||||
|
||||
@@ -553,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.
|
||||
*/
|
||||
@@ -610,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};
|
||||
}
|
||||
|
||||
@@ -712,7 +750,7 @@ 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));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -798,7 +836,7 @@ public:
|
||||
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[static_cast<size_type>(traits_type::to_entity(elem))] = entt;
|
||||
packed[entity_to_pos(elem)] = entt;
|
||||
return traits_type::to_version(entt);
|
||||
}
|
||||
|
||||
@@ -868,7 +906,7 @@ public:
|
||||
++first;
|
||||
}
|
||||
|
||||
count += std::distance(it, first);
|
||||
count += static_cast<size_type>(std::distance(it, first));
|
||||
erase(it, first);
|
||||
}
|
||||
} else {
|
||||
@@ -889,7 +927,7 @@ public:
|
||||
for(; from && packed[from - 1u] == tombstone; --from) {}
|
||||
|
||||
while(pos != max_size) {
|
||||
if(const auto to = std::exchange(pos, static_cast<size_type>(traits_type::to_entity(packed[pos]))); to < from) {
|
||||
if(const auto to = std::exchange(pos, entity_to_pos(packed[pos])); to < from) {
|
||||
--from;
|
||||
swap_or_move(from, to);
|
||||
|
||||
@@ -901,7 +939,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
packed.erase(packed.begin() + from, packed.end());
|
||||
packed.erase(packed.begin() + static_cast<difference_type>(from), packed.end());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -962,7 +1000,7 @@ public:
|
||||
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;
|
||||
@@ -1015,7 +1053,7 @@ public:
|
||||
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<typename iterator::difference_type>(len);
|
||||
auto it = end() - static_cast<difference_type>(len);
|
||||
|
||||
for(const auto other = end(); (it != other) && (first != last); ++first) {
|
||||
if(const auto curr = *first; contains(curr)) {
|
||||
@@ -1041,42 +1079,32 @@ 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.
|
||||
*/
|
||||
[[nodiscard]] const type_info &type() const noexcept {
|
||||
return *info;
|
||||
[[nodiscard]] const type_info &info() const noexcept {
|
||||
return *descriptor;
|
||||
}
|
||||
|
||||
/*! @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.
|
||||
* @return Nothing.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[deprecated("avoid wrapping elements with basic_any")]] std::enable_if_t<std::is_same_v<std::remove_const_t<std::remove_reference_t<Type>>, basic_any<>>>
|
||||
bind(Type &&value) noexcept {
|
||||
// backward compatibility
|
||||
bind_any(std::forward<Type>(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Forwards variables to derived classes, if any.
|
||||
* @tparam Type Type of the element to forward.
|
||||
* @param value The element to forward.
|
||||
* @return Nothing.
|
||||
*/
|
||||
template<typename Type>
|
||||
std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_reference_t<Type>>, basic_any<>>>
|
||||
bind(Type &&value) noexcept {
|
||||
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;
|
||||
size_type head;
|
||||
};
|
||||
|
||||
@@ -23,9 +23,9 @@ namespace entt {
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename Container>
|
||||
template<typename Container, auto Page>
|
||||
class storage_iterator final {
|
||||
friend storage_iterator<const Container>;
|
||||
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>;
|
||||
@@ -49,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>> &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 {
|
||||
@@ -57,7 +57,7 @@ public:
|
||||
}
|
||||
|
||||
constexpr storage_iterator operator++(int) noexcept {
|
||||
storage_iterator orig = *this;
|
||||
const storage_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ public:
|
||||
}
|
||||
|
||||
constexpr storage_iterator operator--(int) noexcept {
|
||||
storage_iterator orig = *this;
|
||||
const storage_iterator orig = *this;
|
||||
return operator--(), orig;
|
||||
}
|
||||
|
||||
@@ -89,9 +89,8 @@ public:
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
|
||||
const auto pos = index() - value;
|
||||
constexpr auto page_size = component_traits<value_type>::page_size;
|
||||
return (*payload)[pos / page_size][fast_mod(static_cast<std::size_t>(pos), page_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 {
|
||||
@@ -111,38 +110,38 @@ private:
|
||||
difference_type offset;
|
||||
};
|
||||
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &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>
|
||||
[[nodiscard]] constexpr bool operator==(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &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>
|
||||
[[nodiscard]] constexpr bool operator!=(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &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>
|
||||
[[nodiscard]] constexpr bool operator<(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &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>
|
||||
[[nodiscard]] constexpr bool operator>(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &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>
|
||||
[[nodiscard]] constexpr bool operator<=(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &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>
|
||||
[[nodiscard]] constexpr bool operator>=(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &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);
|
||||
}
|
||||
|
||||
@@ -175,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;
|
||||
}
|
||||
|
||||
@@ -233,7 +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>;
|
||||
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)];
|
||||
@@ -296,6 +295,19 @@ 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:
|
||||
@@ -304,24 +316,16 @@ private:
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,14 +406,16 @@ public:
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @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. */
|
||||
using const_pointer = typename alloc_traits::template rebind_traits<typename alloc_traits::const_pointer>::const_pointer;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator = internal::storage_iterator<container_type>;
|
||||
using iterator = internal::storage_iterator<container_type, traits_type::page_size>;
|
||||
/*! @brief Constant random access iterator type. */
|
||||
using const_iterator = internal::storage_iterator<const container_type>;
|
||||
using const_iterator = internal::storage_iterator<const container_type, traits_type::page_size>;
|
||||
/*! @brief Reverse iterator type. */
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
/*! @brief Constant reverse iterator type. */
|
||||
@@ -445,7 +451,7 @@ public:
|
||||
* @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)} {}
|
||||
|
||||
/**
|
||||
@@ -454,9 +460,8 @@ public:
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
basic_storage(basic_storage &&other, const allocator_type &allocator)
|
||||
: base_type{std::move(other), allocator},
|
||||
: base_type{static_cast<base_type &&>(other), allocator},
|
||||
payload{std::move(other.payload), allocator} {
|
||||
// NOLINTNEXTLINE(bugprone-use-after-move)
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a storage is not allowed");
|
||||
}
|
||||
|
||||
@@ -552,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};
|
||||
}
|
||||
|
||||
@@ -563,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};
|
||||
}
|
||||
|
||||
@@ -781,11 +786,11 @@ 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>;
|
||||
using traits_type = component_traits<Type, Entity>;
|
||||
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
@@ -800,6 +805,8 @@ public:
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @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. */
|
||||
@@ -898,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);
|
||||
}
|
||||
|
||||
@@ -921,12 +926,11 @@ public:
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
@@ -978,13 +982,19 @@ class basic_storage<Entity, Entity, Allocator>
|
||||
using underlying_iterator = typename basic_sparse_set<Entity, Allocator>::basic_iterator;
|
||||
using traits_type = entt_traits<Entity>;
|
||||
|
||||
auto next() noexcept {
|
||||
entity_type entt = null;
|
||||
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;
|
||||
}
|
||||
|
||||
do {
|
||||
ENTT_ASSERT(placeholder < traits_type::to_entity(null), "No more entities available");
|
||||
entt = traits_type::combine(static_cast<typename traits_type::entity_type>(placeholder++), {});
|
||||
} while(base_type::current(entt) != traits_type::to_version(tombstone) && entt != null);
|
||||
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;
|
||||
}
|
||||
@@ -1002,7 +1012,7 @@ protected:
|
||||
* @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:
|
||||
@@ -1018,6 +1028,8 @@ public:
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @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. */
|
||||
@@ -1048,8 +1060,9 @@ public:
|
||||
* @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} {}
|
||||
|
||||
/**
|
||||
@@ -1057,8 +1070,9 @@ public:
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
|
||||
basic_storage(basic_storage &&other, const allocator_type &allocator)
|
||||
: base_type{std::move(other), allocator},
|
||||
: base_type{static_cast<base_type &&>(other), allocator},
|
||||
placeholder{other.placeholder} {}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
@@ -1081,6 +1095,16 @@ public:
|
||||
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`.
|
||||
*
|
||||
@@ -1108,7 +1132,7 @@ 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()) ? next() : base_type::data()[len];
|
||||
return *base_type::try_emplace(entt, true);
|
||||
@@ -1123,14 +1147,31 @@ public:
|
||||
* @param hint Required identifier.
|
||||
* @return A valid identifier.
|
||||
*/
|
||||
entity_type emplace(const entity_type hint) {
|
||||
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 emplace();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1145,23 +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(next(), true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterable object to use to _visit_ a storage.
|
||||
*
|
||||
@@ -1176,7 +1200,8 @@ public:
|
||||
/*! @copydoc each */
|
||||
[[nodiscard]] const_iterable each() const noexcept {
|
||||
const auto it = base_type::cend();
|
||||
return const_iterable{it - base_type::free_list(), it};
|
||||
const auto offset = static_cast<difference_type>(base_type::free_list());
|
||||
return const_iterable{it - offset, it};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1193,7 +1218,19 @@ public:
|
||||
/*! @copydoc reach */
|
||||
[[nodiscard]] const_reverse_iterable reach() const noexcept {
|
||||
const auto it = base_type::crbegin();
|
||||
return const_reverse_iterable{it, it + base_type::free_list()};
|
||||
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:
|
||||
|
||||
@@ -18,6 +18,10 @@ namespace entt {
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename... Type>
|
||||
// NOLINTNEXTLINE(misc-redundant-expression)
|
||||
static constexpr bool tombstone_check_v = ((sizeof...(Type) == 1u) && ... && (Type::storage_policy == deletion_policy::in_place));
|
||||
|
||||
template<typename Type>
|
||||
const Type *view_placeholder() {
|
||||
static_assert(std::is_same_v<std::remove_const_t<std::remove_reference_t<Type>>, Type>, "Unexpected type");
|
||||
@@ -38,8 +42,8 @@ template<typename It, typename Entity>
|
||||
}
|
||||
|
||||
template<typename It>
|
||||
[[nodiscard]] bool fully_initialized(It first, const It last) noexcept {
|
||||
for(const auto *placeholder = view_placeholder<std::remove_const_t<std::remove_pointer_t<typename std::iterator_traits<It>::value_type>>>(); (first != last) && *first != placeholder; ++first) {}
|
||||
[[nodiscard]] bool fully_initialized(It first, const It last, const std::remove_pointer_t<typename std::iterator_traits<It>::value_type> *placeholder) noexcept {
|
||||
for(; (first != last) && *first != placeholder; ++first) {}
|
||||
return first == last;
|
||||
}
|
||||
|
||||
@@ -48,12 +52,13 @@ template<typename Result, typename View, typename Other, std::size_t... GLhs, st
|
||||
Result elem{};
|
||||
// friend-initialization, avoid multiple calls to refresh
|
||||
elem.pools = {view.template storage<GLhs>()..., other.template storage<GRhs>()...};
|
||||
elem.filter = {view.template storage<sizeof...(GLhs) + ELhs>()..., other.template storage<sizeof...(GRhs) + ERhs>()...};
|
||||
auto filter_or_placeholder = [placeholder = elem.placeholder](auto *value) { return (value == nullptr) ? placeholder : value; };
|
||||
elem.filter = {filter_or_placeholder(view.template storage<sizeof...(GLhs) + ELhs>())..., filter_or_placeholder(other.template storage<sizeof...(GRhs) + ERhs>())...};
|
||||
elem.refresh();
|
||||
return elem;
|
||||
}
|
||||
|
||||
template<typename Type, std::size_t Get, std::size_t Exclude>
|
||||
template<typename Type, bool Checked, std::size_t Get, std::size_t Exclude>
|
||||
class view_iterator final {
|
||||
template<typename, typename...>
|
||||
friend class extended_view_iterator;
|
||||
@@ -62,9 +67,9 @@ class view_iterator final {
|
||||
using iterator_traits = std::iterator_traits<iterator_type>;
|
||||
|
||||
[[nodiscard]] bool valid(const typename iterator_traits::value_type entt) const noexcept {
|
||||
return ((Get != 1u) || (entt != tombstone))
|
||||
&& internal::all_of(pools.begin(), pools.begin() + index, entt) && internal::all_of(pools.begin() + index + 1, pools.end(), entt)
|
||||
&& internal::none_of(filter.begin(), filter.end(), entt);
|
||||
return (!Checked || (entt != tombstone))
|
||||
&& ((Get == 1u) || (internal::all_of(pools.begin(), pools.begin() + index, entt) && internal::all_of(pools.begin() + index + 1, pools.end(), entt)))
|
||||
&& ((Exclude == 0u) || internal::none_of(filter.begin(), filter.end(), entt));
|
||||
}
|
||||
|
||||
void seek_next() {
|
||||
@@ -88,7 +93,8 @@ public:
|
||||
: it{first},
|
||||
pools{value},
|
||||
filter{excl},
|
||||
index{idx} {
|
||||
index{static_cast<difference_type>(idx)} {
|
||||
ENTT_ASSERT((Get != 1u) || (Exclude != 0u) || pools[0u]->policy() == deletion_policy::in_place, "Non in-place storage view iterator");
|
||||
seek_next();
|
||||
}
|
||||
|
||||
@@ -99,7 +105,7 @@ public:
|
||||
}
|
||||
|
||||
view_iterator operator++(int) noexcept {
|
||||
view_iterator orig = *this;
|
||||
const view_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
@@ -118,7 +124,7 @@ private:
|
||||
iterator_type it;
|
||||
std::array<const Type *, Get> pools;
|
||||
std::array<const Type *, Exclude> filter;
|
||||
std::size_t index;
|
||||
difference_type index;
|
||||
};
|
||||
|
||||
template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs>
|
||||
@@ -158,7 +164,7 @@ public:
|
||||
}
|
||||
|
||||
extended_view_iterator operator++(int) noexcept {
|
||||
extended_view_iterator orig = *this;
|
||||
const extended_view_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
@@ -219,10 +225,11 @@ class basic_view;
|
||||
* @brief Basic storage view implementation.
|
||||
* @warning For internal use only, backward compatibility not guaranteed.
|
||||
* @tparam Type Common type among all storage types.
|
||||
* @tparam Checked True to enable the tombstone check, false otherwise.
|
||||
* @tparam Get Number of storage iterated by the view.
|
||||
* @tparam Exclude Number of storage used to filter the view.
|
||||
*/
|
||||
template<typename Type, std::size_t Get, std::size_t Exclude>
|
||||
template<typename Type, bool Checked, std::size_t Get, std::size_t Exclude>
|
||||
class basic_common_view {
|
||||
static_assert(std::is_same_v<std::remove_const_t<std::remove_reference_t<Type>>, Type>, "Unexpected type");
|
||||
|
||||
@@ -249,8 +256,8 @@ class basic_common_view {
|
||||
protected:
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
basic_common_view() noexcept {
|
||||
for(size_type pos{}; pos < Exclude; ++pos) {
|
||||
filter[pos] = internal::view_placeholder<Type>();
|
||||
for(size_type pos{}, last = filter.size(); pos < last; ++pos) {
|
||||
filter[pos] = placeholder;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,27 +272,19 @@ protected:
|
||||
return pools[pos];
|
||||
}
|
||||
|
||||
[[nodiscard]] const Type *storage(const std::size_t pos) const noexcept {
|
||||
if(pos < Get) {
|
||||
return pools[pos];
|
||||
}
|
||||
|
||||
if(const auto idx = pos - Get; filter[idx] != internal::view_placeholder<Type>()) {
|
||||
return filter[idx];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
void pool_at(const std::size_t pos, const Type *elem) noexcept {
|
||||
ENTT_ASSERT(elem != nullptr, "Unexpected element");
|
||||
pools[pos] = elem;
|
||||
refresh();
|
||||
}
|
||||
|
||||
void storage(const std::size_t pos, const Type *elem) noexcept {
|
||||
ENTT_ASSERT(elem != nullptr, "Unexpected element");
|
||||
[[nodiscard]] const Type *filter_at(const std::size_t pos) const noexcept {
|
||||
return (filter[pos] == placeholder) ? nullptr : filter[pos];
|
||||
}
|
||||
|
||||
if(pos < Get) {
|
||||
pools[pos] = elem;
|
||||
refresh();
|
||||
} else {
|
||||
filter[pos - Get] = elem;
|
||||
}
|
||||
void filter_at(const std::size_t pos, const Type *elem) noexcept {
|
||||
ENTT_ASSERT(elem != nullptr, "Unexpected element");
|
||||
filter[pos] = elem;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool none_of(const typename Type::entity_type entt) const noexcept {
|
||||
@@ -304,8 +303,10 @@ 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 Forward iterator type. */
|
||||
using iterator = internal::view_iterator<common_type, Get, Exclude>;
|
||||
using iterator = internal::view_iterator<common_type, Checked, Get, Exclude>;
|
||||
|
||||
/*! @brief Updates the internal leading view if required. */
|
||||
void refresh() noexcept {
|
||||
@@ -341,7 +342,7 @@ public:
|
||||
* @return An iterator to the first entity of the view.
|
||||
*/
|
||||
[[nodiscard]] iterator begin() const noexcept {
|
||||
return (index != Get) ? iterator{pools[index]->end() - static_cast<typename iterator::difference_type>(offset()), pools, filter, index} : iterator{};
|
||||
return (index != Get) ? iterator{pools[index]->end() - static_cast<difference_type>(offset()), pools, filter, index} : iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -370,8 +371,8 @@ public:
|
||||
[[nodiscard]] entity_type back() const noexcept {
|
||||
if(index != Get) {
|
||||
auto it = pools[index]->rbegin();
|
||||
const auto last = it + static_cast<typename iterator::difference_type>(offset());
|
||||
for(; it != last && !contains(*it); ++it) {}
|
||||
const auto last = it + static_cast<difference_type>(offset());
|
||||
for(const auto idx = static_cast<difference_type>(index); it != last && !(internal::all_of(pools.begin(), pools.begin() + idx, *it) && internal::all_of(pools.begin() + idx + 1, pools.end(), *it) && internal::none_of(filter.begin(), filter.end(), *it)); ++it) {}
|
||||
return it == last ? null : *it;
|
||||
}
|
||||
|
||||
@@ -393,7 +394,7 @@ public:
|
||||
* @return True if the view is fully initialized, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] explicit operator bool() const noexcept {
|
||||
return (index != Get) && internal::fully_initialized(filter.begin(), filter.end());
|
||||
return (index != Get) && internal::fully_initialized(filter.begin(), filter.end(), placeholder);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -411,6 +412,7 @@ public:
|
||||
private:
|
||||
std::array<const common_type *, Get> pools{};
|
||||
std::array<const common_type *, Exclude> filter{};
|
||||
const common_type *placeholder{internal::view_placeholder<common_type>()};
|
||||
size_type index{Get};
|
||||
};
|
||||
|
||||
@@ -428,8 +430,11 @@ private:
|
||||
*/
|
||||
template<typename... Get, typename... Exclude>
|
||||
class basic_view<get_t<Get...>, exclude_t<Exclude...>, std::enable_if_t<(sizeof...(Get) != 0u)>>
|
||||
: public basic_common_view<std::common_type_t<typename Get::base_type..., typename Exclude::base_type...>, sizeof...(Get), sizeof...(Exclude)> {
|
||||
using base_type = basic_common_view<std::common_type_t<typename Get::base_type..., typename Exclude::base_type...>, sizeof...(Get), sizeof...(Exclude)>;
|
||||
: public basic_common_view<std::common_type_t<typename Get::base_type...>, internal::tombstone_check_v<Get...>, sizeof...(Get), sizeof...(Exclude)> {
|
||||
using base_type = basic_common_view<std::common_type_t<typename Get::base_type...>, internal::tombstone_check_v<Get...>, sizeof...(Get), sizeof...(Exclude)>;
|
||||
|
||||
template<std::size_t Index>
|
||||
using element_at = type_list_element_t<Index, type_list<Get..., Exclude...>>;
|
||||
|
||||
template<typename 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...>>;
|
||||
@@ -450,10 +455,8 @@ class basic_view<get_t<Get...>, exclude_t<Exclude...>, std::enable_if_t<(sizeof.
|
||||
|
||||
template<std::size_t Curr, typename Func, std::size_t... Index>
|
||||
void each(Func &func, std::index_sequence<Index...>) const {
|
||||
static constexpr bool tombstone_check_required = ((sizeof...(Get) == 1u) && ... && (Get::storage_policy == deletion_policy::in_place));
|
||||
|
||||
for(const auto curr: storage<Curr>()->each()) {
|
||||
if(const auto entt = std::get<0>(curr); (!tombstone_check_required || (entt != tombstone)) && ((Curr == Index || base_type::pool_at(Index)->contains(entt)) && ...) && base_type::none_of(entt)) {
|
||||
if(const auto entt = std::get<0>(curr); (!internal::tombstone_check_v<Get...> || (entt != tombstone)) && ((Curr == Index || base_type::pool_at(Index)->contains(entt)) && ...) && base_type::none_of(entt)) {
|
||||
if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_view>().get({})))>) {
|
||||
std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get<Curr, Index>(curr)...));
|
||||
} else {
|
||||
@@ -477,6 +480,8 @@ public:
|
||||
using entity_type = typename base_type::entity_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename base_type::size_type;
|
||||
/*! @brief Signed integer type. */
|
||||
using difference_type = std::ptrdiff_t;
|
||||
/*! @brief Forward iterator type. */
|
||||
using iterator = typename base_type::iterator;
|
||||
/*! @brief Iterable view type. */
|
||||
@@ -538,8 +543,11 @@ public:
|
||||
*/
|
||||
template<std::size_t Index>
|
||||
[[nodiscard]] auto *storage() const noexcept {
|
||||
using type = type_list_element_t<Index, type_list<Get..., Exclude...>>;
|
||||
return static_cast<type *>(const_cast<constness_as_t<common_type, type> *>(base_type::storage(Index)));
|
||||
if constexpr(Index < sizeof...(Get)) {
|
||||
return static_cast<element_at<Index> *>(const_cast<constness_as_t<common_type, element_at<Index>> *>(base_type::pool_at(Index)));
|
||||
} else {
|
||||
return static_cast<element_at<Index> *>(const_cast<constness_as_t<common_type, element_at<Index>> *>(base_type::filter_at(Index - sizeof...(Get))));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -560,8 +568,13 @@ public:
|
||||
*/
|
||||
template<std::size_t Index, typename Type>
|
||||
void storage(Type &elem) noexcept {
|
||||
static_assert(std::is_convertible_v<Type &, type_list_element_t<Index, type_list<Get..., Exclude...>> &>, "Unexpected type");
|
||||
base_type::storage(Index, &elem);
|
||||
static_assert(std::is_convertible_v<Type &, element_at<Index> &>, "Unexpected type");
|
||||
|
||||
if constexpr(Index < sizeof...(Get)) {
|
||||
base_type::pool_at(Index, &elem);
|
||||
} else {
|
||||
base_type::filter_at(Index - sizeof...(Get), &elem);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -635,6 +648,17 @@ public:
|
||||
return iterable{base_type::begin(), base_type::end()};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Combines a view and a storage in _more specific_ view.
|
||||
* @tparam OGet Type of storage to combine the view with.
|
||||
* @param other The storage for the type to combine the view with.
|
||||
* @return A more specific view.
|
||||
*/
|
||||
template<typename OGet>
|
||||
[[nodiscard]] std::enable_if_t<std::is_base_of_v<common_type, OGet>, basic_view<get_t<Get..., OGet>, exclude_t<Exclude...>>> operator|(OGet &other) const noexcept {
|
||||
return *this | basic_view<get_t<OGet>, exclude_t<>>{other};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Combines two views in a _more specific_ one.
|
||||
* @tparam OGet Element list of the view to combine with.
|
||||
@@ -676,8 +700,10 @@ public:
|
||||
using entity_type = typename common_type::entity_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Signed integer type. */
|
||||
using difference_type = std::ptrdiff_t;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator = std::conditional_t<Policy == deletion_policy::in_place, internal::view_iterator<common_type, 1u, 0u>, typename common_type::iterator>;
|
||||
using iterator = std::conditional_t<Policy == deletion_policy::in_place, internal::view_iterator<common_type, true, 1u, 0u>, typename common_type::iterator>;
|
||||
/*! @brief Reverse iterator type. */
|
||||
using reverse_iterator = std::conditional_t<Policy == deletion_policy::in_place, void, typename common_type::reverse_iterator>;
|
||||
|
||||
@@ -740,7 +766,7 @@ public:
|
||||
if constexpr(Policy == deletion_policy::swap_and_pop) {
|
||||
return leading ? leading->begin() : iterator{};
|
||||
} else if constexpr(Policy == deletion_policy::swap_only) {
|
||||
return leading ? (leading->end() - leading->free_list()) : iterator{};
|
||||
return leading ? (leading->end() - static_cast<difference_type>(leading->free_list())) : iterator{};
|
||||
} else {
|
||||
static_assert(Policy == deletion_policy::in_place, "Unexpected storage policy");
|
||||
return leading ? iterator{leading->begin(), {leading}, {}, 0u} : iterator{};
|
||||
@@ -786,7 +812,7 @@ public:
|
||||
return leading ? leading->rend() : reverse_iterator{};
|
||||
} else {
|
||||
static_assert(Policy == deletion_policy::swap_only, "Unexpected storage policy");
|
||||
return leading ? (leading->rbegin() + leading->free_list()) : reverse_iterator{};
|
||||
return leading ? (leading->rbegin() + static_cast<difference_type>(leading->free_list())) : reverse_iterator{};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -799,7 +825,7 @@ public:
|
||||
if constexpr(Policy == deletion_policy::swap_and_pop) {
|
||||
return empty() ? null : *leading->begin();
|
||||
} else if constexpr(Policy == deletion_policy::swap_only) {
|
||||
return empty() ? null : *(leading->end() - leading->free_list());
|
||||
return empty() ? null : *(leading->end() - static_cast<difference_type>(leading->free_list()));
|
||||
} else {
|
||||
static_assert(Policy == deletion_policy::in_place, "Unexpected storage policy");
|
||||
const auto it = begin();
|
||||
@@ -842,8 +868,7 @@ public:
|
||||
const auto it = leading ? leading->find(entt) : iterator{};
|
||||
return leading && (static_cast<size_type>(it.index()) < leading->free_list()) ? it : iterator{};
|
||||
} else {
|
||||
const auto it = leading ? leading->find(entt) : typename common_type::iterator{};
|
||||
return iterator{it, {leading}, {}, 0u};
|
||||
return leading ? iterator{leading->find(entt), {leading}, {}, 0u} : iterator{};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -895,6 +920,8 @@ public:
|
||||
using entity_type = typename base_type::entity_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename base_type::size_type;
|
||||
/*! @brief Signed integer type. */
|
||||
using difference_type = std::ptrdiff_t;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator = typename base_type::iterator;
|
||||
/*! @brief Reverse iterator type. */
|
||||
@@ -1033,7 +1060,7 @@ public:
|
||||
func();
|
||||
}
|
||||
} else {
|
||||
if(const auto len = base_type::size(); len != 0u) {
|
||||
if(const auto len = static_cast<difference_type>(base_type::size()); len != 0) {
|
||||
for(auto last = storage()->end(), first = last - len; first != last; ++first) {
|
||||
func(*first);
|
||||
}
|
||||
@@ -1066,6 +1093,17 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Combines a view and a storage in _more specific_ view.
|
||||
* @tparam OGet Type of storage to combine the view with.
|
||||
* @param other The storage for the type to combine the view with.
|
||||
* @return A more specific view.
|
||||
*/
|
||||
template<typename OGet>
|
||||
[[nodiscard]] std::enable_if_t<std::is_base_of_v<common_type, OGet>, basic_view<get_t<Get, OGet>, exclude_t<>>> operator|(OGet &other) const noexcept {
|
||||
return *this | basic_view<get_t<OGet>, exclude_t<>>{other};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Combines two views in a _more specific_ one.
|
||||
* @tparam OGet Element list of the view to combine with.
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
/*! @brief `EnTT` default namespace. */
|
||||
namespace entt {}
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include "config/config.h"
|
||||
#include "config/macro.h"
|
||||
@@ -7,7 +10,6 @@
|
||||
#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"
|
||||
@@ -28,7 +30,6 @@
|
||||
#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"
|
||||
|
||||
@@ -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>;
|
||||
@@ -37,16 +41,17 @@ public:
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -70,12 +75,12 @@ private:
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -175,6 +180,16 @@ public:
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the associated allocator.
|
||||
* @return The associated allocator.
|
||||
@@ -189,16 +204,6 @@ public:
|
||||
vert = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exchanges the contents with those of a given adjacency matrix.
|
||||
* @param other Adjacency matrix to exchange the content with.
|
||||
*/
|
||||
void swap(adjacency_matrix &other) noexcept {
|
||||
using std::swap;
|
||||
swap(matrix, other.matrix);
|
||||
swap(vert, other.vert);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true if an adjacency matrix is empty, false otherwise.
|
||||
*
|
||||
|
||||
@@ -180,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.
|
||||
@@ -194,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. */
|
||||
@@ -205,18 +217,6 @@ public:
|
||||
sync_on = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 true if a flow builder contains no tasks, false otherwise.
|
||||
* @return True if the flow builder contains no tasks, false otherwise.
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#define ENTT_META_CONTAINER_HPP
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <deque>
|
||||
#include <iterator>
|
||||
#include <list>
|
||||
@@ -15,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"
|
||||
|
||||
@@ -24,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 {};
|
||||
@@ -60,15 +63,17 @@ 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");
|
||||
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 True in case of key-only containers, false otherwise. */
|
||||
static constexpr bool fixed_size = internal::fixed_size_sequence_container_v<Type>;
|
||||
/*! @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.
|
||||
@@ -85,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,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 != nullptr) ? 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 != nullptr) ? 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()};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,13 +156,13 @@ struct basic_meta_sequence_container_traits {
|
||||
* @return A possibly invalid iterator to the inserted element.
|
||||
*/
|
||||
[[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(fixed_size) {
|
||||
return iterator{};
|
||||
} else {
|
||||
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 != nullptr) ? *static_cast<const typename Type::value_type *>(value) : *static_cast<const std::remove_reference_t<typename Type::const_reference> *>(cref))};
|
||||
} else {
|
||||
return iterator{};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,11 +174,11 @@ struct basic_meta_sequence_container_traits {
|
||||
* @return A possibly invalid iterator following the last removed element.
|
||||
*/
|
||||
[[nodiscard]] static iterator erase([[maybe_unused]] const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const iterator &it) {
|
||||
if constexpr(fixed_size) {
|
||||
return iterator{};
|
||||
} else {
|
||||
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{};
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -193,7 +189,7 @@ 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");
|
||||
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;
|
||||
@@ -238,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 != nullptr) ? 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 != nullptr) ? 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()};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -11,10 +11,12 @@
|
||||
#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"
|
||||
@@ -30,14 +32,18 @@ namespace internal {
|
||||
class basic_meta_factory {
|
||||
using invoke_type = std::remove_pointer_t<decltype(meta_func_node::invoke)>;
|
||||
|
||||
auto *find_member_or_assert() {
|
||||
auto *member = find_member<&meta_data_node::id>(details->data, bucket);
|
||||
[[nodiscard]] auto &fetch_node() noexcept {
|
||||
return *meta_context::from(*ctx).value[parent];
|
||||
}
|
||||
|
||||
[[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;
|
||||
}
|
||||
|
||||
auto *find_overload_or_assert() {
|
||||
auto *overload = find_overload(find_member<&meta_func_node::id>(details->func, bucket), invoke);
|
||||
[[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;
|
||||
}
|
||||
@@ -48,84 +54,75 @@ class basic_meta_factory {
|
||||
}
|
||||
|
||||
protected:
|
||||
void type(const id_type id) noexcept {
|
||||
void type(const id_type id, const char *name) noexcept {
|
||||
reset_bucket(parent);
|
||||
auto &&elem = meta_context::from(*ctx).value[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>(details->base, node.type);
|
||||
member ? (*member = node) : details->base.emplace_back(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>(details->conv, node.type);
|
||||
member ? (*member = node) : details->conv.emplace_back(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>(details->ctor, node.id);
|
||||
member ? (*member = node) : details->ctor.emplace_back(node);
|
||||
auto *member = find_member<&meta_ctor_node::id>(elem.details->ctor, node.id);
|
||||
member ? (*member = node) : elem.details->ctor.emplace_back(node);
|
||||
}
|
||||
}
|
||||
|
||||
void dtor(meta_dtor_node node) {
|
||||
reset_bucket(parent);
|
||||
meta_context::from(*ctx).value[parent].dtor = node;
|
||||
}
|
||||
|
||||
void data(meta_data_node node) {
|
||||
auto &elem = fetch_node();
|
||||
|
||||
reset_bucket(node.id);
|
||||
|
||||
if(auto *member = find_member<&meta_data_node::id>(details->data, node.id); member == nullptr) {
|
||||
details->data.emplace_back(std::move(node));
|
||||
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>(details->func, node.id); member == nullptr) {
|
||||
details->func.emplace_back(std::move(node));
|
||||
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_shared<meta_func_node>(std::move(node));
|
||||
member->next = std::make_unique<meta_func_node>(std::move(node));
|
||||
}
|
||||
}
|
||||
|
||||
void prop(meta_prop_node node) {
|
||||
std::vector<meta_prop_node> *container = nullptr;
|
||||
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) {
|
||||
container = &details->prop;
|
||||
set_or_unset_on(fetch_node());
|
||||
} else if(invoke == nullptr) {
|
||||
container = &find_member_or_assert()->prop;
|
||||
set_or_unset_on(*find_member_or_assert());
|
||||
} else {
|
||||
container = &find_overload_or_assert()->prop;
|
||||
}
|
||||
|
||||
auto *member = find_member<&meta_prop_node::id>(*container, node.id);
|
||||
(member != nullptr) ? (*member = std::move(node)) : container->emplace_back(std::move(node));
|
||||
}
|
||||
|
||||
void traits(const meta_traits value) {
|
||||
if(bucket == parent) {
|
||||
meta_context::from(*ctx).value[bucket].traits |= value;
|
||||
} else if(invoke == nullptr) {
|
||||
find_member_or_assert()->traits |= value;
|
||||
} else {
|
||||
find_overload_or_assert()->traits |= value;
|
||||
set_or_unset_on(*find_overload_or_assert());
|
||||
}
|
||||
}
|
||||
|
||||
void custom(meta_custom_node node) {
|
||||
if(bucket == parent) {
|
||||
meta_context::from(*ctx).value[bucket].custom = std::move(node);
|
||||
fetch_node().custom = std::move(node);
|
||||
} else if(invoke == nullptr) {
|
||||
find_member_or_assert()->custom = std::move(node);
|
||||
} else {
|
||||
@@ -134,17 +131,13 @@ protected:
|
||||
}
|
||||
|
||||
public:
|
||||
basic_meta_factory(const id_type id, meta_ctx &area)
|
||||
basic_meta_factory(meta_ctx &area, meta_type_node node)
|
||||
: ctx{&area},
|
||||
parent{id},
|
||||
bucket{id} {
|
||||
auto &&elem = meta_context::from(*ctx).value[parent];
|
||||
|
||||
if(!elem.details) {
|
||||
elem.details = std::make_shared<meta_type_descriptor>();
|
||||
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>();
|
||||
}
|
||||
|
||||
details = elem.details.get();
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -152,7 +145,6 @@ private:
|
||||
id_type parent{};
|
||||
id_type bucket{};
|
||||
invoke_type *invoke{};
|
||||
meta_type_descriptor *details{};
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
@@ -160,49 +152,44 @@ private:
|
||||
|
||||
/**
|
||||
* @brief Meta factory to be used for reflection purposes.
|
||||
* @tparam Type Reflected type for which the factory was created.
|
||||
* @tparam Type Type for which the factory was created.
|
||||
*/
|
||||
template<typename Type>
|
||||
class meta_factory: private internal::basic_meta_factory {
|
||||
using base_type = internal::basic_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");
|
||||
|
||||
base_type::data(
|
||||
internal::meta_data_node{
|
||||
id,
|
||||
/* 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>});
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Type of object for which this factory builds a meta type. */
|
||||
using element_type = Type;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
meta_factory() noexcept
|
||||
: internal::basic_meta_factory{type_id<Type>(), locator<meta_ctx>::value_or()} {}
|
||||
: meta_factory{locator<meta_ctx>::value_or()} {}
|
||||
|
||||
/**
|
||||
* @brief Context aware constructor.
|
||||
* @param area The context into which to construct meta types.
|
||||
*/
|
||||
meta_factory(meta_ctx &area) noexcept
|
||||
: internal::basic_meta_factory{type_id<Type>().hash(), area} {}
|
||||
: internal::basic_meta_factory{area, internal::setup_node_for<Type>()} {}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
meta_factory type(const id_type id) noexcept {
|
||||
base_type::type(id);
|
||||
meta_factory type(const id_type id, const char *name = nullptr) noexcept {
|
||||
base_type::type(id, name);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -236,7 +223,7 @@ 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))); };
|
||||
base_type::insert_or_assign(internal::meta_conv_node{type_id<conv_type>().hash(), op});
|
||||
return *this;
|
||||
@@ -253,7 +240,7 @@ public:
|
||||
*/
|
||||
template<typename To>
|
||||
meta_factory conv() noexcept {
|
||||
using conv_type = std::remove_cv_t<std::remove_reference_t<To>>;
|
||||
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))); };
|
||||
base_type::insert_or_assign(internal::meta_conv_node{type_id<conv_type>().hash(), op});
|
||||
return *this;
|
||||
@@ -272,11 +259,11 @@ 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>
|
||||
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");
|
||||
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;
|
||||
}
|
||||
@@ -303,29 +290,15 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @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>
|
||||
meta_factory 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)); };
|
||||
base_type::dtor(internal::meta_dtor_node{op});
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -339,10 +312,11 @@ 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>
|
||||
meta_factory 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");
|
||||
@@ -350,11 +324,12 @@ public:
|
||||
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>});
|
||||
} else {
|
||||
@@ -369,10 +344,11 @@ public:
|
||||
base_type::data(
|
||||
internal::meta_data_node{
|
||||
id,
|
||||
((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,
|
||||
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>});
|
||||
}
|
||||
@@ -380,6 +356,20 @@ public:
|
||||
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.
|
||||
@@ -398,21 +388,23 @@ 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>
|
||||
meta_factory 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>) {
|
||||
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>});
|
||||
@@ -422,11 +414,12 @@ public:
|
||||
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>});
|
||||
}
|
||||
@@ -435,26 +428,15 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @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>
|
||||
meta_factory 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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -468,46 +450,27 @@ 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>
|
||||
meta_factory 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");
|
||||
|
||||
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>});
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns a property to the last created meta object.
|
||||
*
|
||||
* Both the key and the value (if any) must be at least copy constructible.
|
||||
*
|
||||
* @tparam Value Optional type of the property value.
|
||||
* @param id Property key.
|
||||
* @param value Optional property value.
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template<typename... Value>
|
||||
[[deprecated("use ::custom() instead")]] meta_factory prop(id_type id, [[maybe_unused]] Value &&...value) {
|
||||
if constexpr(sizeof...(Value) == 0u) {
|
||||
base_type::prop(internal::meta_prop_node{id, &internal::resolve<void>});
|
||||
} else {
|
||||
base_type::prop(internal::meta_prop_node{id, &internal::resolve<std::decay_t<Value>>..., std::make_shared<std::decay_t<Value>>(std::forward<Value>(value))...});
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets traits on the last created meta object.
|
||||
*
|
||||
@@ -515,12 +478,13 @@ public:
|
||||
*
|
||||
* @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 traits(const Value 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));
|
||||
base_type::traits(internal::user_to_meta_traits(value), unset);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -538,42 +502,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*
|
||||
@@ -587,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;
|
||||
|
||||
@@ -1,26 +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;
|
||||
|
||||
struct meta_prop;
|
||||
class meta_handle;
|
||||
|
||||
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
@@ -1,13 +1,13 @@
|
||||
#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 "../core/attribute.h"
|
||||
#include "../core/bit.hpp"
|
||||
#include "../core/enum.hpp"
|
||||
#include "../core/fwd.hpp"
|
||||
@@ -21,7 +21,7 @@ namespace entt {
|
||||
|
||||
class meta_any;
|
||||
class meta_type;
|
||||
struct meta_handle;
|
||||
class meta_handle;
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
@@ -37,9 +37,9 @@ enum class meta_traits : std::uint32_t {
|
||||
is_enum = 0x0040,
|
||||
is_class = 0x0080,
|
||||
is_pointer = 0x0100,
|
||||
is_meta_pointer_like = 0x0200,
|
||||
is_meta_sequence_container = 0x0400,
|
||||
is_meta_associative_container = 0x0800,
|
||||
is_pointer_like = 0x0200,
|
||||
is_sequence_container = 0x0400,
|
||||
is_associative_container = 0x0800,
|
||||
_user_defined_traits = 0xFFFF,
|
||||
_entt_enum_as_bitmask = 0xFFFF
|
||||
};
|
||||
@@ -67,15 +67,9 @@ struct meta_custom_node {
|
||||
std::shared_ptr<void> value{};
|
||||
};
|
||||
|
||||
struct meta_prop_node {
|
||||
id_type id{};
|
||||
meta_type_node (*type)(const meta_context &) noexcept {};
|
||||
std::shared_ptr<void> value{};
|
||||
};
|
||||
|
||||
struct meta_base_node {
|
||||
id_type type{};
|
||||
meta_type_node (*resolve)(const meta_context &) noexcept {};
|
||||
const meta_type_node &(*resolve)(const meta_context &) noexcept {};
|
||||
const void *(*cast)(const void *) noexcept {};
|
||||
};
|
||||
|
||||
@@ -93,44 +87,40 @@ struct meta_ctor_node {
|
||||
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){};
|
||||
meta_any (*get)(meta_handle){};
|
||||
meta_custom_node custom{};
|
||||
std::vector<meta_prop_node> prop{};
|
||||
};
|
||||
|
||||
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{};
|
||||
meta_any (*invoke)(meta_handle, meta_any *const){};
|
||||
std::unique_ptr<meta_func_node> next;
|
||||
meta_custom_node custom{};
|
||||
std::vector<meta_prop_node> prop{};
|
||||
};
|
||||
|
||||
struct meta_template_node {
|
||||
using size_type = std::size_t;
|
||||
|
||||
size_type arity{0u};
|
||||
meta_type_node (*resolve)(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 {
|
||||
@@ -139,7 +129,6 @@ struct meta_type_descriptor {
|
||||
std::vector<meta_conv_node> conv{};
|
||||
std::vector<meta_data_node> data{};
|
||||
std::vector<meta_func_node> func{};
|
||||
std::vector<meta_prop_node> prop{};
|
||||
};
|
||||
|
||||
struct meta_type_node {
|
||||
@@ -147,17 +136,16 @@ 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{};
|
||||
meta_custom_node custom{};
|
||||
std::shared_ptr<meta_type_descriptor> details{};
|
||||
std::unique_ptr<meta_type_descriptor> details{};
|
||||
};
|
||||
|
||||
template<auto Member, typename Type, typename Value>
|
||||
@@ -177,7 +165,7 @@ template<auto Member, typename Type, typename Value>
|
||||
}
|
||||
|
||||
template<auto Member>
|
||||
[[nodiscard]] 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) {
|
||||
@@ -185,9 +173,11 @@ template<auto Member>
|
||||
return member;
|
||||
}
|
||||
|
||||
for(auto &&curr: node.details->base) {
|
||||
if(auto *elem = look_for<Member>(context, curr.resolve(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -196,25 +186,22 @@ template<auto Member>
|
||||
}
|
||||
|
||||
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 != nullptr) && (to.info != nullptr) && *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.resolve(context), to, curr.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;
|
||||
}
|
||||
}
|
||||
@@ -223,49 +210,12 @@ 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) {
|
||||
for(auto &&elem: from.details->conv) {
|
||||
if(elem.type == to.hash()) {
|
||||
return func(instance, elem);
|
||||
}
|
||||
}
|
||||
|
||||
for(auto &&curr: from.details->base) {
|
||||
if(auto other = try_convert(context, curr.resolve(context), to, arithmetic_or_enum, curr.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)
|
||||
@@ -273,12 +223,11 @@ template<typename Type>
|
||||
| (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)
|
||||
| (std::is_pointer_v<Type> ? meta_traits::is_pointer : 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),
|
||||
| (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) {
|
||||
@@ -298,10 +247,15 @@ template<typename Type>
|
||||
|
||||
if constexpr(!std::is_void_v<Type> && !std::is_function_v<Type>) {
|
||||
node.from_void = +[](const meta_ctx &ctx, void *elem, const void *celem) {
|
||||
if(elem) {
|
||||
if(elem && celem) { // ownership construction request
|
||||
return meta_any{ctx, std::in_place, static_cast<std::decay_t<Type> *>(elem)};
|
||||
}
|
||||
|
||||
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)};
|
||||
};
|
||||
}
|
||||
@@ -310,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 */
|
||||
|
||||
|
||||
@@ -46,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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -37,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;
|
||||
}
|
||||
|
||||
@@ -46,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;
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ struct meta_range_iterator final {
|
||||
|
||||
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
|
||||
if constexpr(std::is_same_v<It, typename decltype(meta_context::value)::const_iterator>) {
|
||||
return {it[value].first, Type{*ctx, it[value].second}};
|
||||
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 {
|
||||
|
||||
@@ -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,7 +83,7 @@ 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 != nullptr) ? meta_type{ctx, *elem} : meta_type{};
|
||||
}
|
||||
|
||||
@@ -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 {};
|
||||
|
||||
/**
|
||||
|
||||
@@ -93,11 +93,11 @@ struct meta_function_descriptor<Type, Ret (*)(MaybeType, Args...)>
|
||||
: meta_function_descriptor_traits<
|
||||
Ret,
|
||||
std::conditional_t<
|
||||
std::is_same_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>,
|
||||
std::is_same_v<std::remove_const_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_const_t<std::remove_reference_t<MaybeType>>, Type>,
|
||||
type_list<Args...>,
|
||||
type_list<MaybeType, Args...>>,
|
||||
!(std::is_same_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>),
|
||||
std::is_const_v<std::remove_reference_t<MaybeType>> && (std::is_same_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>)> {};
|
||||
!(std::is_same_v<std::remove_const_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_const_t<std::remove_reference_t<MaybeType>>, Type>),
|
||||
std::is_const_v<std::remove_reference_t<MaybeType>> && (std::is_same_v<std::remove_const_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_const_t<std::remove_reference_t<MaybeType>>, Type>)> {};
|
||||
|
||||
/**
|
||||
* @brief Meta function descriptor.
|
||||
@@ -164,15 +164,15 @@ using meta_function_helper_t = typename meta_function_helper<Type, Candidate>::t
|
||||
* @param value Value to wrap.
|
||||
* @return A meta any containing the returned value, if any.
|
||||
*/
|
||||
template<typename Policy = as_is_t, typename Type>
|
||||
template<typename Policy = as_value_t, typename Type>
|
||||
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) {
|
||||
if constexpr(std::is_same_v<Policy, as_void_t>) {
|
||||
return meta_any{ctx, std::in_place_type<void>};
|
||||
} else if constexpr(std::is_same_v<Policy, as_ref_t>) {
|
||||
return meta_any{ctx, std::in_place_type<Type>, value};
|
||||
} else if constexpr(std::is_same_v<Policy, as_cref_t>) {
|
||||
if constexpr(std::is_same_v<Policy, as_cref_t>) {
|
||||
static_assert(std::is_lvalue_reference_v<Type>, "Invalid type");
|
||||
return meta_any{ctx, std::in_place_type<const std::remove_reference_t<Type> &>, std::as_const(value)};
|
||||
} else if constexpr(std::is_same_v<Policy, as_ref_t> || (std::is_same_v<Policy, as_is_t> && std::is_lvalue_reference_v<Type>)) {
|
||||
return meta_any{ctx, std::in_place_type<Type>, value};
|
||||
} else if constexpr(std::is_same_v<Policy, as_void_t>) {
|
||||
return meta_any{ctx, std::in_place_type<void>};
|
||||
} else {
|
||||
return meta_any{ctx, std::forward<Type>(value)};
|
||||
}
|
||||
@@ -185,134 +185,11 @@ template<typename Policy = as_is_t, typename Type>
|
||||
* @param value Value to wrap.
|
||||
* @return A meta any containing the returned value, if any.
|
||||
*/
|
||||
template<typename Policy = as_is_t, typename Type>
|
||||
template<typename Policy = as_value_t, typename Type>
|
||||
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(Type &&value) {
|
||||
return meta_dispatch<Policy, Type>(locator<meta_ctx>::value_or(), std::forward<Type>(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the meta type of the i-th element of a list of arguments.
|
||||
* @tparam Type Type list of the actual types of arguments.
|
||||
* @param ctx The context from which to search for meta types.
|
||||
* @param index The index of the element for which to return the meta type.
|
||||
* @return The meta type of the i-th element of the list of arguments.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] static meta_type meta_arg(const meta_ctx &ctx, const std::size_t index) noexcept {
|
||||
auto &&context = internal::meta_context::from(ctx);
|
||||
return {ctx, internal::meta_arg_node(context, Type{}, index)};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the meta type of the i-th element of a list of arguments.
|
||||
* @tparam Type Type list of the actual types of arguments.
|
||||
* @param index The index of the element for which to return the meta type.
|
||||
* @return The meta type of the i-th element of the list of arguments.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] static meta_type meta_arg(const std::size_t index) noexcept {
|
||||
return meta_arg<Type>(locator<meta_ctx>::value_or(), index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the value of a given variable.
|
||||
* @tparam Type Reflected type to which the variable is associated.
|
||||
* @tparam Data The actual variable to set.
|
||||
* @param instance An opaque instance of the underlying type, if required.
|
||||
* @param value Parameter to use to set the variable.
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
template<typename Type, auto Data>
|
||||
[[nodiscard]] bool meta_setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) {
|
||||
if constexpr(!std::is_same_v<decltype(Data), Type> && !std::is_same_v<decltype(Data), std::nullptr_t>) {
|
||||
if constexpr(std::is_member_function_pointer_v<decltype(Data)> || std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>>) {
|
||||
using descriptor = meta_function_helper_t<Type, decltype(Data)>;
|
||||
using data_type = type_list_element_t<descriptor::is_static, typename descriptor::args_type>;
|
||||
|
||||
if(auto *const clazz = instance->try_cast<Type>(); clazz && value.allow_cast<data_type>()) {
|
||||
std::invoke(Data, *clazz, value.cast<data_type>());
|
||||
return true;
|
||||
}
|
||||
} else if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
|
||||
using data_type = std::remove_reference_t<typename meta_function_helper_t<Type, decltype(Data)>::return_type>;
|
||||
|
||||
if constexpr(!std::is_array_v<data_type> && !std::is_const_v<data_type>) {
|
||||
if(auto *const clazz = instance->try_cast<Type>(); clazz && value.allow_cast<data_type>()) {
|
||||
std::invoke(Data, *clazz) = value.cast<data_type>();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
using data_type = std::remove_reference_t<decltype(*Data)>;
|
||||
|
||||
if constexpr(!std::is_array_v<data_type> && !std::is_const_v<data_type>) {
|
||||
if(value.allow_cast<data_type>()) {
|
||||
*Data = value.cast<data_type>();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the value of a given variable.
|
||||
*
|
||||
* @warning
|
||||
* The context provided is used only for the return type.<br/>
|
||||
* It's up to the caller to bind the arguments to the right context(s).
|
||||
*
|
||||
* @tparam Type Reflected type to which the variable is associated.
|
||||
* @tparam Data The actual variable to get.
|
||||
* @tparam Policy Optional policy (no policy set by default).
|
||||
* @param ctx The context from which to search for meta types.
|
||||
* @param instance An opaque instance of the underlying type, if required.
|
||||
* @return A meta any containing the value of the underlying variable.
|
||||
*/
|
||||
template<typename Type, auto Data, typename Policy = as_is_t>
|
||||
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_getter(const meta_ctx &ctx, [[maybe_unused]] meta_handle instance) {
|
||||
if constexpr(std::is_member_pointer_v<decltype(Data)> || std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>>) {
|
||||
if constexpr(!std::is_array_v<std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(Data), Type &>>>>) {
|
||||
if constexpr(std::is_invocable_v<decltype(Data), Type &>) {
|
||||
if(auto *clazz = instance->try_cast<Type>(); clazz) {
|
||||
return meta_dispatch<Policy>(ctx, std::invoke(Data, *clazz));
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr(std::is_invocable_v<decltype(Data), const Type &>) {
|
||||
if(auto *fallback = instance->try_cast<const Type>(); fallback) {
|
||||
return meta_dispatch<Policy>(ctx, std::invoke(Data, *fallback));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return meta_any{meta_ctx_arg, ctx};
|
||||
} else if constexpr(std::is_pointer_v<decltype(Data)>) {
|
||||
if constexpr(std::is_array_v<std::remove_pointer_t<decltype(Data)>>) {
|
||||
return meta_any{meta_ctx_arg, ctx};
|
||||
} else {
|
||||
return meta_dispatch<Policy>(ctx, *Data);
|
||||
}
|
||||
} else {
|
||||
return meta_dispatch<Policy>(ctx, Data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the value of a given variable.
|
||||
* @tparam Type Reflected type to which the variable is associated.
|
||||
* @tparam Data The actual variable to get.
|
||||
* @tparam Policy Optional policy (no policy set by default).
|
||||
* @param instance An opaque instance of the underlying type, if required.
|
||||
* @return A meta any containing the value of the underlying variable.
|
||||
*/
|
||||
template<typename Type, auto Data, typename Policy = as_is_t>
|
||||
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_getter(meta_handle instance) {
|
||||
return meta_getter<Type, Data, Policy>(locator<meta_ctx>::value_or(), std::move(instance));
|
||||
}
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
@@ -327,26 +204,26 @@ template<typename Policy, typename Candidate, typename... Args>
|
||||
}
|
||||
|
||||
template<typename Type, typename Policy, typename Candidate, std::size_t... Index>
|
||||
[[nodiscard]] meta_any meta_invoke(const meta_ctx &ctx, [[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *const args, std::index_sequence<Index...>) {
|
||||
[[nodiscard]] meta_any meta_invoke(meta_any &instance, Candidate &&candidate, [[maybe_unused]] meta_any *const args, std::index_sequence<Index...>) {
|
||||
using descriptor = meta_function_helper_t<Type, std::remove_reference_t<Candidate>>;
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) - waiting for C++20 (and std::span)
|
||||
if constexpr(std::is_invocable_v<std::remove_reference_t<Candidate>, const Type &, type_list_element_t<Index, typename descriptor::args_type>...>) {
|
||||
if(const auto *const clazz = instance->try_cast<const Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
|
||||
return meta_invoke_with_args<Policy>(ctx, std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
|
||||
if(const auto *const clazz = instance.try_cast<const Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
|
||||
return meta_invoke_with_args<Policy>(instance.context(), std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
|
||||
}
|
||||
} else if constexpr(std::is_invocable_v<std::remove_reference_t<Candidate>, Type &, type_list_element_t<Index, typename descriptor::args_type>...>) {
|
||||
if(auto *const clazz = instance->try_cast<Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
|
||||
return meta_invoke_with_args<Policy>(ctx, std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
|
||||
if(auto *const clazz = instance.try_cast<Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
|
||||
return meta_invoke_with_args<Policy>(instance.context(), std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
|
||||
}
|
||||
} else {
|
||||
if(((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
|
||||
return meta_invoke_with_args<Policy>(ctx, std::forward<Candidate>(candidate), (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
|
||||
return meta_invoke_with_args<Policy>(instance.context(), std::forward<Candidate>(candidate), (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
|
||||
}
|
||||
}
|
||||
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
|
||||
return meta_any{meta_ctx_arg, ctx};
|
||||
return meta_any{meta_ctx_arg, instance.context()};
|
||||
}
|
||||
|
||||
template<typename Type, typename... Args, std::size_t... Index>
|
||||
@@ -364,24 +241,105 @@ template<typename Type, typename... Args, std::size_t... Index>
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Tries to _invoke_ an object given a list of erased parameters.
|
||||
*
|
||||
* @warning
|
||||
* The context provided is used only for the return type.<br/>
|
||||
* It's up to the caller to bind the arguments to the right context(s).
|
||||
*
|
||||
* @tparam Type Reflected type to which the object to _invoke_ is associated.
|
||||
* @tparam Policy Optional policy (no policy set by default).
|
||||
* @brief Returns the meta type of the i-th element of a list of arguments.
|
||||
* @tparam Type Type list of the actual types of arguments.
|
||||
* @param ctx The context from which to search for meta types.
|
||||
* @tparam Candidate The type of the actual object to _invoke_.
|
||||
* @param instance An opaque instance of the underlying type, if required.
|
||||
* @param candidate The actual object to _invoke_.
|
||||
* @param args Parameters to use to _invoke_ the object.
|
||||
* @return A meta any containing the returned value, if any.
|
||||
* @param index The index of the element for which to return the meta type.
|
||||
* @return The meta type of the i-th element of the list of arguments.
|
||||
*/
|
||||
template<typename Type, typename Policy = as_is_t, typename Candidate>
|
||||
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_invoke(const meta_ctx &ctx, meta_handle instance, Candidate &&candidate, meta_any *const args) {
|
||||
return internal::meta_invoke<Type, Policy>(ctx, std::move(instance), std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
|
||||
template<typename Type>
|
||||
[[nodiscard]] meta_type meta_arg(const meta_ctx &ctx, const std::size_t index) noexcept {
|
||||
const auto &context = internal::meta_context::from(ctx);
|
||||
return {ctx, internal::meta_arg_node(context, Type{}, index)};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the meta type of the i-th element of a list of arguments.
|
||||
* @tparam Type Type list of the actual types of arguments.
|
||||
* @param index The index of the element for which to return the meta type.
|
||||
* @return The meta type of the i-th element of the list of arguments.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] meta_type meta_arg(const std::size_t index) noexcept {
|
||||
return meta_arg<Type>(locator<meta_ctx>::value_or(), index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the value of a given variable.
|
||||
* @tparam Type Reflected type to which the variable is associated.
|
||||
* @tparam Data The actual variable to set.
|
||||
* @param instance An opaque instance of the underlying type, if required.
|
||||
* @param value Parameter to use to set the variable.
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
template<typename Type, auto Data>
|
||||
[[nodiscard]] bool meta_setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) {
|
||||
if constexpr(std::is_member_function_pointer_v<decltype(Data)> || std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>>) {
|
||||
using descriptor = meta_function_helper_t<Type, decltype(Data)>;
|
||||
using data_type = type_list_element_t<descriptor::is_static, typename descriptor::args_type>;
|
||||
|
||||
if(auto *const clazz = instance->try_cast<Type>(); clazz && value.allow_cast<data_type>()) {
|
||||
std::invoke(Data, *clazz, value.cast<data_type>());
|
||||
return true;
|
||||
}
|
||||
} else if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
|
||||
using data_type = std::remove_reference_t<typename meta_function_helper_t<Type, decltype(Data)>::return_type>;
|
||||
|
||||
if constexpr(!std::is_array_v<data_type> && !std::is_const_v<data_type>) {
|
||||
if(auto *const clazz = instance->try_cast<Type>(); clazz && value.allow_cast<data_type>()) {
|
||||
std::invoke(Data, *clazz) = value.cast<data_type>();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if constexpr(std::is_pointer_v<decltype(Data)>) {
|
||||
using data_type = std::remove_reference_t<decltype(*Data)>;
|
||||
|
||||
if constexpr(!std::is_array_v<data_type> && !std::is_const_v<data_type>) {
|
||||
if(value.allow_cast<data_type>()) {
|
||||
*Data = value.cast<data_type>();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the value of a given variable.
|
||||
* @tparam Type Reflected type to which the variable is associated.
|
||||
* @tparam Data The actual variable to get.
|
||||
* @tparam Policy Optional policy (no policy set by default).
|
||||
* @param instance An opaque instance of the underlying type, if required.
|
||||
* @return A meta any containing the value of the underlying variable.
|
||||
*/
|
||||
template<typename Type, auto Data, typename Policy = as_value_t>
|
||||
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_getter(meta_handle instance) {
|
||||
if constexpr(std::is_member_pointer_v<decltype(Data)> || std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>>) {
|
||||
if constexpr(!std::is_array_v<std::remove_const_t<std::remove_reference_t<std::invoke_result_t<decltype(Data), Type &>>>>) {
|
||||
if constexpr(std::is_invocable_v<decltype(Data), Type &>) {
|
||||
if(auto *clazz = instance->try_cast<Type>(); clazz) {
|
||||
return meta_dispatch<Policy>(instance->context(), std::invoke(Data, *clazz));
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr(std::is_invocable_v<decltype(Data), const Type &>) {
|
||||
if(auto *fallback = instance->try_cast<const Type>(); fallback) {
|
||||
return meta_dispatch<Policy>(instance->context(), std::invoke(Data, *fallback));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return meta_any{meta_ctx_arg, instance->context()};
|
||||
} else if constexpr(std::is_pointer_v<decltype(Data)>) {
|
||||
if constexpr(std::is_array_v<std::remove_pointer_t<decltype(Data)>>) {
|
||||
return meta_any{meta_ctx_arg, instance->context()};
|
||||
} else {
|
||||
return meta_dispatch<Policy>(instance->context(), *Data);
|
||||
}
|
||||
} else {
|
||||
return meta_dispatch<Policy>(instance->context(), Data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -394,29 +352,9 @@ template<typename Type, typename Policy = as_is_t, typename Candidate>
|
||||
* @param args Parameters to use to _invoke_ the object.
|
||||
* @return A meta any containing the returned value, if any.
|
||||
*/
|
||||
template<typename Type, typename Policy = as_is_t, typename Candidate>
|
||||
template<typename Type, typename Policy = as_value_t, typename Candidate>
|
||||
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_invoke(meta_handle instance, Candidate &&candidate, meta_any *const args) {
|
||||
return meta_invoke<Type, Policy>(locator<meta_ctx>::value_or(), std::move(instance), std::forward<Candidate>(candidate), args);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Tries to invoke a function given a list of erased parameters.
|
||||
*
|
||||
* @warning
|
||||
* The context provided is used only for the return type.<br/>
|
||||
* It's up to the caller to bind the arguments to the right context(s).
|
||||
*
|
||||
* @tparam Type Reflected type to which the function is associated.
|
||||
* @tparam Candidate The actual function to invoke.
|
||||
* @tparam Policy Optional policy (no policy set by default).
|
||||
* @param ctx The context from which to search for meta types.
|
||||
* @param instance An opaque instance of the underlying type, if required.
|
||||
* @param args Parameters to use to invoke the function.
|
||||
* @return A meta any containing the returned value, if any.
|
||||
*/
|
||||
template<typename Type, auto Candidate, typename Policy = as_is_t>
|
||||
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_invoke(const meta_ctx &ctx, meta_handle instance, meta_any *const args) {
|
||||
return internal::meta_invoke<Type, Policy>(ctx, std::move(instance), Candidate, args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<decltype(Candidate)>>::args_type::size>{});
|
||||
return internal::meta_invoke<Type, Policy>(*instance.operator->(), std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -428,9 +366,9 @@ template<typename Type, auto Candidate, typename Policy = as_is_t>
|
||||
* @param args Parameters to use to invoke the function.
|
||||
* @return A meta any containing the returned value, if any.
|
||||
*/
|
||||
template<typename Type, auto Candidate, typename Policy = as_is_t>
|
||||
template<typename Type, auto Candidate, typename Policy = as_value_t>
|
||||
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_invoke(meta_handle instance, meta_any *const args) {
|
||||
return meta_invoke<Type, Candidate, Policy>(locator<meta_ctx>::value_or(), std::move(instance), args);
|
||||
return internal::meta_invoke<Type, Policy>(*instance.operator->(), Candidate, args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<decltype(Candidate)>>::args_type::size>{});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -478,13 +416,14 @@ template<typename Type, typename... Args>
|
||||
* @param args Parameters to use to _invoke_ the object.
|
||||
* @return A meta any containing the returned value, if any.
|
||||
*/
|
||||
template<typename Type, typename Policy = as_is_t, typename Candidate>
|
||||
template<typename Type, typename Policy = as_value_t, typename Candidate>
|
||||
[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, Candidate &&candidate, meta_any *const args) {
|
||||
if constexpr(meta_function_helper_t<Type, Candidate>::is_static || std::is_class_v<std::remove_cv_t<std::remove_reference_t<Candidate>>>) {
|
||||
return internal::meta_invoke<Type, Policy>(ctx, {}, std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
|
||||
if constexpr(meta_function_helper_t<Type, Candidate>::is_static || std::is_class_v<std::remove_const_t<std::remove_reference_t<Candidate>>>) {
|
||||
meta_any placeholder{meta_ctx_arg, ctx};
|
||||
return internal::meta_invoke<Type, Policy>(placeholder, std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
|
||||
} else {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - waiting for C++20 (and std::span)
|
||||
return internal::meta_invoke<Type, Policy>(ctx, *args, std::forward<Candidate>(candidate), args + 1u, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
|
||||
return internal::meta_invoke<Type, Policy>(*args, std::forward<Candidate>(candidate), args + 1u, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -497,7 +436,7 @@ template<typename Type, typename Policy = as_is_t, typename Candidate>
|
||||
* @param args Parameters to use to _invoke_ the object.
|
||||
* @return A meta any containing the returned value, if any.
|
||||
*/
|
||||
template<typename Type, typename Policy = as_is_t, typename Candidate>
|
||||
template<typename Type, typename Policy = as_value_t, typename Candidate>
|
||||
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_construct(Candidate &&candidate, meta_any *const args) {
|
||||
return meta_construct<Type, Policy>(locator<meta_ctx>::value_or(), std::forward<Candidate>(candidate), args);
|
||||
}
|
||||
@@ -516,7 +455,7 @@ template<typename Type, typename Policy = as_is_t, typename Candidate>
|
||||
* @param args Parameters to use to invoke the function.
|
||||
* @return A meta any containing the returned value, if any.
|
||||
*/
|
||||
template<typename Type, auto Candidate, typename Policy = as_is_t>
|
||||
template<typename Type, auto Candidate, typename Policy = as_value_t>
|
||||
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_construct(const meta_ctx &ctx, meta_any *const args) {
|
||||
return meta_construct<Type, Policy>(ctx, Candidate, args);
|
||||
}
|
||||
@@ -529,7 +468,7 @@ template<typename Type, auto Candidate, typename Policy = as_is_t>
|
||||
* @param args Parameters to use to invoke the function.
|
||||
* @return A meta any containing the returned value, if any.
|
||||
*/
|
||||
template<typename Type, auto Candidate, typename Policy = as_is_t>
|
||||
template<typename Type, auto Candidate, typename Policy = as_value_t>
|
||||
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_construct(meta_any *const args) {
|
||||
return meta_construct<Type, Candidate, Policy>(locator<meta_ctx>::value_or(), args);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
<Type Name="entt::basic_any<*>">
|
||||
<DisplayString>{{ type={ info->alias,na }, policy={ mode,en } }}</DisplayString>
|
||||
<DisplayString>{{ policy={ mode,en } }}</DisplayString>
|
||||
</Type>
|
||||
<Type Name="entt::compressed_pair<*>">
|
||||
<Intrinsic Name="first" Optional="true" Expression="((first_base*)this)->value"/>
|
||||
@@ -47,7 +47,7 @@
|
||||
<Intrinsic Name="is_valid_entity" Expression="!traits_type::version_mask || (*((traits_type::entity_type *)&entity) < (traits_type::version_mask << traits_type::length))">
|
||||
<Parameter Name="entity" Type="const traits_type::value_type &"/>
|
||||
</Intrinsic>
|
||||
<DisplayString>{{ size={ packed.size() }, type={ info->alias,na } }}</DisplayString>
|
||||
<DisplayString>{{ size={ packed.size() }, type={ descriptor->alias,na } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[capacity]" ExcludeView="simple">packed.capacity()</Item>
|
||||
<Item Name="[policy]" ExcludeView="simple">mode,en</Item>
|
||||
@@ -94,7 +94,7 @@
|
||||
<Intrinsic Name="is_valid_entity" Expression="!base_type::traits_type::version_mask || (*((base_type::traits_type::entity_type *)&entity) < (base_type::traits_type::version_mask << base_type::traits_type::length))">
|
||||
<Parameter Name="entity" Type="const base_type::traits_type::value_type &"/>
|
||||
</Intrinsic>
|
||||
<DisplayString>{{ size={ base_type::packed.size() }, type={ base_type::info->alias,na } }}</DisplayString>
|
||||
<DisplayString>{{ size={ base_type::packed.size() }, type={ base_type::descriptor->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>
|
||||
@@ -163,7 +163,7 @@
|
||||
<Loop>
|
||||
<Break Condition="pos == last"/>
|
||||
<If Condition="pool_at(pos)->sparse.size() > page && pool_at(pos)->sparse[page] != nullptr && ((*((traits_type::entity_type *)&pool_at(pos)->sparse[page][offset])) & entity_mask) != entity_mask">
|
||||
<Item Name="[{ pool_at(pos)->info->alias,na }:{ ((*((traits_type::entity_type *)&pool_at(pos)->sparse[page][offset])) & entity_mask) != entity_mask }]">pool_at(pos),view(simple)nanr</Item>
|
||||
<Item Name="[{ pool_at(pos)->descriptor->alias,na }:{ ((*((traits_type::entity_type *)&pool_at(pos)->sparse[page][offset])) & entity_mask) != entity_mask }]">pool_at(pos),view(simple)nanr</Item>
|
||||
</If>
|
||||
<Exec>++pos</Exec>
|
||||
</Loop>
|
||||
@@ -34,43 +34,33 @@
|
||||
<Intrinsic Name="has_trait" Expression="!!(traits & property)">
|
||||
<Parameter Name="property" Type="int"/>
|
||||
</Intrinsic>
|
||||
<DisplayString Condition="name != nullptr">{{ id={ name,na } }}</DisplayString>
|
||||
<DisplayString Condition="get != nullptr">{{ id={ id } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[id]">id</Item>
|
||||
<Item Name="[name]" Condition="name != nullptr">name,na</Item>
|
||||
<Item Name="[arity]">arity</Item>
|
||||
<Item Name="[is_const]">has_trait(entt::internal::meta_traits::is_const)</Item>
|
||||
<Item Name="[is_static]">has_trait(entt::internal::meta_traits::is_static)</Item>
|
||||
<Item Name="[prop]">prop,view(simple)</Item>
|
||||
<Item Name="[custom]">custom</Item>
|
||||
<Item Name="[custom]" Condition="custom.value != nullptr">custom</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_dtor_node">
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand/>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_func_node" >
|
||||
<Intrinsic Name="has_trait" Expression="!!(traits & property)">
|
||||
<Parameter Name="property" Type="int"/>
|
||||
</Intrinsic>
|
||||
<DisplayString Condition="name != nullptr">{{ id={ name,na } }}</DisplayString>
|
||||
<DisplayString Condition="invoke != nullptr">{{ id={ id } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[id]">id</Item>
|
||||
<Item Name="[name]" Condition="name != nullptr">name,na</Item>
|
||||
<Item Name="[arity]">arity</Item>
|
||||
<Item Name="[is_const]">has_trait(entt::internal::meta_traits::is_const)</Item>
|
||||
<Item Name="[is_static]">has_trait(entt::internal::meta_traits::is_static)</Item>
|
||||
<Item Name="[next]" Condition="next != nullptr">*next</Item>
|
||||
<Item Name="[prop]">prop,view(simple)</Item>
|
||||
<Item Name="[custom]">custom</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_prop_node">
|
||||
<DisplayString Condition="value != nullptr">{{ key={ id } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[key]">id</Item>
|
||||
<Item Name="[value]">value</Item>
|
||||
<Item Name="[custom]" Condition="custom.value != nullptr">custom</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_template_node">
|
||||
@@ -88,17 +78,18 @@
|
||||
<Item Name="[conv]">conv,view(simple)</Item>
|
||||
<Item Name="[data]">data,view(simple)</Item>
|
||||
<Item Name="[func]">func,view(simple)</Item>
|
||||
<Item Name="[prop]">prop,view(simple)</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_type_node">
|
||||
<Intrinsic Name="has_trait" Expression="!!(traits & property)">
|
||||
<Parameter Name="property" Type="int"/>
|
||||
</Intrinsic>
|
||||
<DisplayString Condition="name != nullptr">{{ type={ name,na } }}</DisplayString>
|
||||
<DisplayString Condition="info != nullptr">{{ type={ info->alias,na } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[id]">id</Item>
|
||||
<Item Name="[name]" Condition="name != nullptr">name,na</Item>
|
||||
<Item Name="[sizeof]">size_of</Item>
|
||||
<Item Name="[is_arithmetic]">has_trait(entt::internal::meta_traits::is_arithmetic)</Item>
|
||||
<Item Name="[is_integral]">has_trait(entt::internal::meta_traits::is_integral)</Item>
|
||||
@@ -107,23 +98,23 @@
|
||||
<Item Name="[is_enum]">has_trait(entt::internal::meta_traits::is_enum)</Item>
|
||||
<Item Name="[is_class]">has_trait(entt::internal::meta_traits::is_class)</Item>
|
||||
<Item Name="[is_pointer]">has_trait(entt::internal::meta_traits::is_pointer)</Item>
|
||||
<Item Name="[is_meta_pointer_like]">has_trait(entt::internal::meta_traits::is_meta_pointer_like)</Item>
|
||||
<Item Name="[is_meta_sequence_container]">has_trait(entt::internal::meta_traits::is_meta_sequence_container)</Item>
|
||||
<Item Name="[is_meta_associative_container]">has_trait(entt::internal::meta_traits::is_meta_associative_container)</Item>
|
||||
<Item Name="[is_pointer_like]">has_trait(entt::internal::meta_traits::is_pointer_like)</Item>
|
||||
<Item Name="[is_sequence_container]">has_trait(entt::internal::meta_traits::is_sequence_container)</Item>
|
||||
<Item Name="[is_associative_container]">has_trait(entt::internal::meta_traits::is_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="[custom]">custom</Item>
|
||||
<Item Name="[details]" Condition="!(details == nullptr)">*details</Item>
|
||||
<Item Name="[template_info]" Condition="templ.arity != 0u">templ</Item>
|
||||
<Item Name="[custom]" Condition="custom.value != nullptr">custom</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>{ storage }</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem>node</ExpandedItem>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
|
||||
<ExpandedItem Condition="node != nullptr">node,na</ExpandedItem>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx,na</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_handle">
|
||||
@@ -136,7 +127,7 @@
|
||||
<DisplayString Condition="data != nullptr">{{ const={ const_only } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx,na</Item>
|
||||
<Item Name="[const]">const_only</Item>
|
||||
<Item Name="[data]">data</Item>
|
||||
</Expand>
|
||||
@@ -145,40 +136,40 @@
|
||||
<DisplayString Condition="data != nullptr">{{ const={ const_only } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx,na</Item>
|
||||
<Item Name="[const]">const_only</Item>
|
||||
<Item Name="[data]">data</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_data">
|
||||
<DisplayString Condition="node != nullptr">{ *node }</DisplayString>
|
||||
<Type Name="entt::meta_custom">
|
||||
<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_data">
|
||||
<DisplayString Condition="node != nullptr">{ node }</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx,na</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_func">
|
||||
<DisplayString Condition="node != nullptr">{ *node }</DisplayString>
|
||||
<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.type != nullptr">{ node }</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem Condition="node.type != nullptr">node</ExpandedItem>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx,na</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_type">
|
||||
<DisplayString>{ node }</DisplayString>
|
||||
<DisplayString Condition="node != nullptr">{ node }</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem>node</ExpandedItem>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
|
||||
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx,na</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_ctx">
|
||||
21
src/entt/natvis/process.natvis
Normal file
21
src/entt/natvis/process.natvis
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
<Type Name="entt::basic_process<*>">
|
||||
<DisplayString>{{ state={ current,en } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[state]">current,en</Item>
|
||||
<Item Name="[child]" Condition="next.first_base::value != nullptr">*next.first_base::value</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_scheduler<*>">
|
||||
<Intrinsic Name="size" Expression="handlers.first_base::value.size()"/>
|
||||
<DisplayString>{{ size={ size() } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[capacity]" ExcludeView="simple">handlers.first_base::value.capacity()</Item>
|
||||
<IndexListItems>
|
||||
<Size>size()</Size>
|
||||
<ValueNode>*handlers.first_base::value[$i]</ValueNode>
|
||||
</IndexListItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
</AutoVisualizer>
|
||||
@@ -97,11 +97,11 @@ class poly_vtable {
|
||||
}
|
||||
|
||||
using vtable_type = decltype(make_vtable(Concept{}));
|
||||
static constexpr bool is_mono_v = std::tuple_size_v<vtable_type> == 1u;
|
||||
static constexpr bool is_mono = std::tuple_size_v<vtable_type> == 1u;
|
||||
|
||||
public:
|
||||
/*! @brief Virtual table type. */
|
||||
using type = std::conditional_t<is_mono_v, std::tuple_element_t<0u, vtable_type>, const vtable_type *>;
|
||||
using type = std::conditional_t<is_mono, std::tuple_element_t<0u, vtable_type>, const vtable_type *>;
|
||||
|
||||
/**
|
||||
* @brief Returns a static virtual table for a specific concept and type.
|
||||
@@ -113,7 +113,7 @@ public:
|
||||
static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Type differs from its decayed form");
|
||||
static const vtable_type vtable = fill_vtable<Type>(std::make_index_sequence<Concept::template impl<Type>::size>{});
|
||||
|
||||
if constexpr(is_mono_v) {
|
||||
if constexpr(is_mono) {
|
||||
return std::get<0>(vtable);
|
||||
} else {
|
||||
return &vtable;
|
||||
@@ -211,23 +211,28 @@ public:
|
||||
template<typename Type, typename... Args>
|
||||
explicit basic_poly(std::in_place_type_t<Type>, Args &&...args)
|
||||
: storage{std::in_place_type<Type>, std::forward<Args>(args)...},
|
||||
vtable{poly_vtable<Concept, Len, Align>::template instance<std::remove_cv_t<std::remove_reference_t<Type>>>()} {}
|
||||
vtable{poly_vtable<Concept, Len, Align>::template instance<std::remove_const_t<std::remove_reference_t<Type>>>()} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a poly from a given value.
|
||||
* @tparam Type Type of object to use to initialize the poly.
|
||||
* @param value An instance of an object to use to initialize the poly.
|
||||
*/
|
||||
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, basic_poly>>>
|
||||
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_reference_t<Type>>, basic_poly>>>
|
||||
basic_poly(Type &&value) noexcept
|
||||
: basic_poly{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)} {}
|
||||
: basic_poly{std::in_place_type<std::remove_const_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)} {}
|
||||
|
||||
/**
|
||||
* @brief Returns the object type if any, `type_id<void>()` otherwise.
|
||||
* @return The object type if any, `type_id<void>()` otherwise.
|
||||
* @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 &type() const noexcept {
|
||||
return storage.type();
|
||||
[[nodiscard]] const type_info &info() const noexcept {
|
||||
return storage.info();
|
||||
}
|
||||
|
||||
/*! @copydoc info */
|
||||
[[deprecated("use ::info instead")]] [[nodiscard]] const type_info &type() const noexcept {
|
||||
return info();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -252,7 +257,7 @@ public:
|
||||
template<typename Type, typename... Args>
|
||||
void emplace(Args &&...args) {
|
||||
storage.template emplace<Type>(std::forward<Args>(args)...);
|
||||
vtable = poly_vtable<Concept, Len, Align>::template instance<std::remove_cv_t<std::remove_reference_t<Type>>>();
|
||||
vtable = poly_vtable<Concept, Len, Align>::template instance<std::remove_const_t<std::remove_reference_t<Type>>>();
|
||||
}
|
||||
|
||||
/*! @brief Destroys contained object */
|
||||
|
||||
@@ -6,14 +6,17 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
template<typename, typename>
|
||||
class process;
|
||||
template<typename, typename = std::allocator<void>>
|
||||
class basic_process;
|
||||
|
||||
template<typename = std::uint32_t, typename = std::allocator<void>>
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using process = basic_process<std::uint32_t>;
|
||||
|
||||
template<typename, typename = std::allocator<void>>
|
||||
class basic_scheduler;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using scheduler = basic_scheduler<>;
|
||||
using scheduler = basic_scheduler<std::uint32_t>;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
|
||||
@@ -2,22 +2,32 @@
|
||||
#define ENTT_PROCESS_PROCESS_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../core/compressed_pair.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename, typename, typename>
|
||||
struct process_adaptor;
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Base class for processes.
|
||||
*
|
||||
* This class stays true to the CRTP idiom. Derived classes must specify what's
|
||||
* the intended type for elapsed times.<br/>
|
||||
* A process should expose publicly the following member functions whether
|
||||
* required:
|
||||
* Derived classes must specify what's the intended type for elapsed times.<br/>
|
||||
* A process can implement the following member functions whether required:
|
||||
*
|
||||
* * @code{.cpp}
|
||||
* void update(Delta, void *);
|
||||
* void update(Delta, void *) override;
|
||||
* @endcode
|
||||
*
|
||||
* It's invoked once per tick until a process is explicitly aborted or it
|
||||
@@ -28,30 +38,21 @@ namespace entt {
|
||||
* update.
|
||||
*
|
||||
* * @code{.cpp}
|
||||
* void init();
|
||||
* @endcode
|
||||
*
|
||||
* It's invoked when the process joins the running queue of a scheduler. This
|
||||
* happens as soon as it's attached to the scheduler if the process is a top
|
||||
* level one, otherwise when it replaces its parent if the process is a
|
||||
* continuation.
|
||||
*
|
||||
* * @code{.cpp}
|
||||
* void succeeded();
|
||||
* void succeeded() override;
|
||||
* @endcode
|
||||
*
|
||||
* It's invoked in case of success, immediately after an update and during the
|
||||
* same tick.
|
||||
*
|
||||
* * @code{.cpp}
|
||||
* void failed();
|
||||
* void failed() override;
|
||||
* @endcode
|
||||
*
|
||||
* It's invoked in case of errors, immediately after an update and during the
|
||||
* same tick.
|
||||
*
|
||||
* * @code{.cpp}
|
||||
* void aborted();
|
||||
* void aborted() override;
|
||||
* @endcode
|
||||
*
|
||||
* It's invoked only if a process is explicitly aborted. There is no guarantee
|
||||
@@ -59,18 +60,18 @@ namespace entt {
|
||||
* process is aborted immediately or not.
|
||||
*
|
||||
* Derived classes can change the internal state of a process by invoking the
|
||||
* `succeed` and `fail` protected member functions and even pause or unpause the
|
||||
* process itself.
|
||||
* `succeed` and `fail` member functions and even pause or unpause the process
|
||||
* itself.
|
||||
*
|
||||
* @sa scheduler
|
||||
*
|
||||
* @tparam Derived Actual type of process that extends the class template.
|
||||
* @tparam Delta Type to use to provide elapsed time.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Derived, typename Delta>
|
||||
class process {
|
||||
template<typename Delta, typename Allocator>
|
||||
class basic_process: public std::enable_shared_from_this<basic_process<Delta, Allocator>> {
|
||||
enum class state : std::uint8_t {
|
||||
uninitialized = 0,
|
||||
idle = 0,
|
||||
running,
|
||||
paused,
|
||||
succeeded,
|
||||
@@ -80,45 +81,73 @@ class process {
|
||||
rejected
|
||||
};
|
||||
|
||||
template<typename Target = Derived>
|
||||
auto next(std::integral_constant<state, state::uninitialized>)
|
||||
-> decltype(std::declval<Target>().init(), void()) {
|
||||
static_cast<Target *>(this)->init();
|
||||
virtual void update(const Delta, void *) {
|
||||
abort();
|
||||
}
|
||||
|
||||
template<typename Target = Derived>
|
||||
auto next(std::integral_constant<state, state::running>, Delta delta, void *data)
|
||||
-> decltype(std::declval<Target>().update(delta, data), void()) {
|
||||
static_cast<Target *>(this)->update(delta, data);
|
||||
}
|
||||
virtual void succeeded() {}
|
||||
virtual void failed() {}
|
||||
virtual void aborted() {}
|
||||
|
||||
template<typename Target = Derived>
|
||||
auto next(std::integral_constant<state, state::succeeded>)
|
||||
-> decltype(std::declval<Target>().succeeded(), void()) {
|
||||
static_cast<Target *>(this)->succeeded();
|
||||
}
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Type used to provide elapsed time. */
|
||||
using delta_type = Delta;
|
||||
/*! @brief Handle type. */
|
||||
using handle_type = std::shared_ptr<basic_process>;
|
||||
|
||||
template<typename Target = Derived>
|
||||
auto next(std::integral_constant<state, state::failed>)
|
||||
-> decltype(std::declval<Target>().failed(), void()) {
|
||||
static_cast<Target *>(this)->failed();
|
||||
}
|
||||
/*! @brief Default constructor. */
|
||||
basic_process()
|
||||
: basic_process{allocator_type{}} {}
|
||||
|
||||
template<typename Target = Derived>
|
||||
auto next(std::integral_constant<state, state::aborted>)
|
||||
-> decltype(std::declval<Target>().aborted(), void()) {
|
||||
static_cast<Target *>(this)->aborted();
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void next(Args &&...) const noexcept {}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Terminates a process with success if it's still alive.
|
||||
*
|
||||
* The function is idempotent and it does nothing if the process isn't
|
||||
* alive.
|
||||
* @brief Constructs a scheduler with a given allocator.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit basic_process(const allocator_type &allocator)
|
||||
: next{nullptr, allocator},
|
||||
current{state::idle} {}
|
||||
|
||||
/*! @brief Default copy constructor, deleted on purpose. */
|
||||
basic_process(const basic_process &) = delete;
|
||||
|
||||
/*! @brief Default move constructor, deleted on purpose. */
|
||||
basic_process(basic_process &&) = delete;
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
virtual ~basic_process() = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator, deleted on purpose.
|
||||
* @return This process scheduler.
|
||||
*/
|
||||
basic_process &operator=(const basic_process &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Default move assignment operator, deleted on purpose.
|
||||
* @return This process scheduler.
|
||||
*/
|
||||
basic_process &operator=(basic_process &&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Returns the associated allocator.
|
||||
* @return The associated allocator.
|
||||
*/
|
||||
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
|
||||
return next.second();
|
||||
}
|
||||
|
||||
/*! @brief Aborts a process if it's still alive, otherwise does nothing. */
|
||||
void abort() {
|
||||
if(alive()) {
|
||||
current = state::aborted;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Terminates a process with success if it's still alive, otherwise
|
||||
* does nothing.
|
||||
*/
|
||||
void succeed() noexcept {
|
||||
if(alive()) {
|
||||
@@ -127,10 +156,8 @@ protected:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Terminates a process with errors if it's still alive.
|
||||
*
|
||||
* The function is idempotent and it does nothing if the process isn't
|
||||
* alive.
|
||||
* @brief Terminates a process with errors if it's still alive, otherwise
|
||||
* does nothing.
|
||||
*/
|
||||
void fail() noexcept {
|
||||
if(alive()) {
|
||||
@@ -138,75 +165,17 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stops a process if it's in a running state.
|
||||
*
|
||||
* The function is idempotent and it does nothing if the process isn't
|
||||
* running.
|
||||
*/
|
||||
/*! @brief Stops a process if it's running, otherwise does nothing. */
|
||||
void pause() noexcept {
|
||||
if(current == state::running) {
|
||||
if(alive()) {
|
||||
current = state::paused;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Restarts a process if it's paused.
|
||||
*
|
||||
* The function is idempotent and it does nothing if the process isn't
|
||||
* paused.
|
||||
*/
|
||||
/*! @brief Restarts a process if it's paused, otherwise does nothing. */
|
||||
void unpause() noexcept {
|
||||
if(current == state::paused) {
|
||||
current = state::running;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Type used to provide elapsed time. */
|
||||
using delta_type = Delta;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
constexpr process() = default;
|
||||
|
||||
/*! @brief Default copy constructor. */
|
||||
process(const process &) = default;
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
process(process &&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator.
|
||||
* @return This process.
|
||||
*/
|
||||
process &operator=(const process &) = default;
|
||||
|
||||
/**
|
||||
* @brief Default move assignment operator.
|
||||
* @return This process.
|
||||
*/
|
||||
process &operator=(process &&) noexcept = default;
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
virtual ~process() {
|
||||
static_assert(std::is_base_of_v<process, Derived>, "Incorrect use of the class template");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Aborts a process if it's still alive.
|
||||
*
|
||||
* The function is idempotent and it does nothing if the process isn't
|
||||
* alive.
|
||||
*
|
||||
* @param immediate Requests an immediate operation.
|
||||
*/
|
||||
void abort(const bool immediate = false) {
|
||||
if(alive()) {
|
||||
current = state::aborted;
|
||||
|
||||
if(immediate) {
|
||||
tick({});
|
||||
}
|
||||
current = state::running;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,18 +212,50 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates a process and its internal state if required.
|
||||
* @brief Assigns a child process to run in case of success.
|
||||
* @tparam Type Type of child process to create.
|
||||
* @tparam Args Types of arguments to use to initialize the child process.
|
||||
* @param args Parameters to use to initialize the child process.
|
||||
* @return A reference to the newly created child process.
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
basic_process &then(Args &&...args) {
|
||||
const auto &allocator = next.second();
|
||||
return *(next.first() = std::allocate_shared<Type>(allocator, allocator, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns a child process to run in case of success.
|
||||
* @tparam Func Type of child process to create.
|
||||
* @param func Either a lambda or a functor to use as a child process.
|
||||
* @return A reference to the newly created child process.
|
||||
*/
|
||||
template<typename Func>
|
||||
basic_process &then(Func func) {
|
||||
const auto &allocator = next.second();
|
||||
using process_type = internal::process_adaptor<delta_type, Func, allocator_type>;
|
||||
return *(next.first() = std::allocate_shared<process_type>(allocator, allocator, std::move(func)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the child process without releasing ownership, if any.
|
||||
* @return The child process attached to the object, if any.
|
||||
*/
|
||||
handle_type peek() {
|
||||
return next.first();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates a process and its internal state, if required.
|
||||
* @param delta Elapsed time.
|
||||
* @param data Optional data.
|
||||
*/
|
||||
void tick(const Delta delta, void *data = nullptr) {
|
||||
switch(current) {
|
||||
case state::uninitialized:
|
||||
next(std::integral_constant<state, state::uninitialized>{});
|
||||
current = state::running;
|
||||
break;
|
||||
case state::idle:
|
||||
case state::running:
|
||||
next(std::integral_constant<state, state::running>{}, delta, data);
|
||||
current = state::running;
|
||||
update(delta, data);
|
||||
break;
|
||||
default:
|
||||
// suppress warnings
|
||||
@@ -264,15 +265,15 @@ public:
|
||||
// if it's dead, it must be notified and removed immediately
|
||||
switch(current) {
|
||||
case state::succeeded:
|
||||
next(std::integral_constant<state, state::succeeded>{});
|
||||
succeeded();
|
||||
current = state::finished;
|
||||
break;
|
||||
case state::failed:
|
||||
next(std::integral_constant<state, state::failed>{});
|
||||
failed();
|
||||
current = state::rejected;
|
||||
break;
|
||||
case state::aborted:
|
||||
next(std::integral_constant<state, state::aborted>{});
|
||||
aborted();
|
||||
current = state::rejected;
|
||||
break;
|
||||
default:
|
||||
@@ -282,73 +283,34 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
state current{state::uninitialized};
|
||||
compressed_pair<handle_type, allocator_type> next;
|
||||
state current;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Adaptor for lambdas and functors to turn them into processes.
|
||||
*
|
||||
* Lambdas and functors can't be used directly with a scheduler for they are not
|
||||
* properly defined processes with managed life cycles.<br/>
|
||||
* This class helps in filling the gap and turning lambdas and functors into
|
||||
* full featured processes usable by a scheduler.
|
||||
*
|
||||
* The signature of the function call operator should be equivalent to the
|
||||
* following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(Delta delta, void *data, auto succeed, auto fail);
|
||||
* @endcode
|
||||
*
|
||||
* Where:
|
||||
*
|
||||
* * `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 signature of the function call operator of both `succeed` and `fail`
|
||||
* is equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void();
|
||||
* @endcode
|
||||
*
|
||||
* Usually users shouldn't worry about creating adaptors. A scheduler will
|
||||
* create them internally each and avery time a lambda or a functor is used as
|
||||
* a process.
|
||||
*
|
||||
* @sa process
|
||||
* @sa scheduler
|
||||
*
|
||||
* @tparam Func Actual type of process.
|
||||
* @tparam Delta Type to use to provide elapsed time.
|
||||
*/
|
||||
template<typename Func, typename Delta>
|
||||
struct process_adaptor: process<process_adaptor<Func, Delta>, Delta>, private Func {
|
||||
/**
|
||||
* @brief Constructs a process adaptor from a lambda or a functor.
|
||||
* @tparam Args Types of arguments to use to initialize the actual process.
|
||||
* @param args Parameters to use to initialize the actual process.
|
||||
*/
|
||||
template<typename... Args>
|
||||
process_adaptor(Args &&...args)
|
||||
: Func{std::forward<Args>(args)...} {}
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
/**
|
||||
* @brief Updates a process and its internal state if required.
|
||||
* @param delta Elapsed time.
|
||||
* @param data Optional data.
|
||||
*/
|
||||
void update(const Delta delta, void *data) {
|
||||
Func::operator()(
|
||||
delta,
|
||||
data,
|
||||
[this]() { this->succeed(); },
|
||||
[this]() { this->fail(); });
|
||||
template<typename Delta, typename Func, typename Allocator>
|
||||
struct process_adaptor: public basic_process<Delta, Allocator> {
|
||||
using allocator_type = Allocator;
|
||||
using base_type = basic_process<Delta, Allocator>;
|
||||
using delta_type = typename base_type::delta_type;
|
||||
|
||||
process_adaptor(const allocator_type &allocator, Func proc)
|
||||
: base_type{allocator},
|
||||
func{std::move(proc)} {}
|
||||
|
||||
void update(const delta_type delta, void *data) override {
|
||||
func(*this, delta, data);
|
||||
}
|
||||
|
||||
private:
|
||||
Func func;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
|
||||
@@ -13,44 +13,6 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename Delta>
|
||||
struct basic_process_handler {
|
||||
virtual ~basic_process_handler() = default;
|
||||
|
||||
virtual bool update(Delta, void *) = 0;
|
||||
virtual void abort(bool) = 0;
|
||||
|
||||
// std::shared_ptr because of its type erased allocator which is useful here
|
||||
std::shared_ptr<basic_process_handler> next;
|
||||
};
|
||||
|
||||
template<typename Delta, typename Type>
|
||||
struct process_handler final: basic_process_handler<Delta> {
|
||||
template<typename... Args>
|
||||
process_handler(Args &&...args)
|
||||
: process{std::forward<Args>(args)...} {}
|
||||
|
||||
bool update(const Delta delta, void *data) override {
|
||||
if(process.tick(delta, data); process.rejected()) {
|
||||
this->next.reset();
|
||||
}
|
||||
|
||||
return (process.rejected() || process.finished());
|
||||
}
|
||||
|
||||
void abort(const bool immediate) override {
|
||||
process.abort(immediate);
|
||||
}
|
||||
|
||||
Type process;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Cooperative scheduler for processes.
|
||||
*
|
||||
@@ -62,14 +24,6 @@ struct process_handler final: basic_process_handler<Delta> {
|
||||
* its child when it terminates if it returns with success. In case of errors,
|
||||
* both the process and its child are discarded.
|
||||
*
|
||||
* Example of use (pseudocode):
|
||||
*
|
||||
* @code{.cpp}
|
||||
* scheduler.attach([](auto delta, void *, auto succeed, auto fail) {
|
||||
* // code
|
||||
* }).then<my_process>(arguments...);
|
||||
* @endcode
|
||||
*
|
||||
* In order to invoke all scheduled processes, call the `update` member function
|
||||
* passing it the elapsed time to forward to the tasks.
|
||||
*
|
||||
@@ -80,17 +34,14 @@ struct process_handler final: basic_process_handler<Delta> {
|
||||
*/
|
||||
template<typename Delta, typename Allocator>
|
||||
class basic_scheduler {
|
||||
template<typename Type>
|
||||
using handler_type = internal::process_handler<Delta, Type>;
|
||||
|
||||
// std::shared_ptr because of its type erased allocator which is useful here
|
||||
using process_type = std::shared_ptr<internal::basic_process_handler<Delta>>;
|
||||
|
||||
using base_type = basic_process<Delta, Allocator>;
|
||||
using alloc_traits = std::allocator_traits<Allocator>;
|
||||
using container_allocator = typename alloc_traits::template rebind_alloc<process_type>;
|
||||
using container_type = std::vector<process_type, container_allocator>;
|
||||
using container_allocator = typename alloc_traits::template rebind_alloc<std::shared_ptr<base_type>>;
|
||||
using container_type = std::vector<std::shared_ptr<base_type>, container_allocator>;
|
||||
|
||||
public:
|
||||
/*! @brief Process type. */
|
||||
using type = base_type;
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Unsigned integer type. */
|
||||
@@ -194,122 +145,28 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Schedules a process for the next tick.
|
||||
*
|
||||
* Returned value can be used to attach a continuation for the last process.
|
||||
* The continutation is scheduled automatically when the process terminates
|
||||
* and only if the process returns with success.
|
||||
*
|
||||
* Example of use (pseudocode):
|
||||
*
|
||||
* @code{.cpp}
|
||||
* // schedules a task in the form of a process class
|
||||
* scheduler.attach<my_process>(arguments...)
|
||||
* // appends a child in the form of a lambda function
|
||||
* .then([](auto delta, void *, auto succeed, auto fail) {
|
||||
* // code
|
||||
* })
|
||||
* // appends a child in the form of another process class
|
||||
* .then<my_other_process>();
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Proc Type of process to schedule.
|
||||
* @tparam Type Type of process to create.
|
||||
* @tparam Args Types of arguments to use to initialize the process.
|
||||
* @param args Parameters to use to initialize the process.
|
||||
* @return This process scheduler.
|
||||
* @return A reference to the newly created process.
|
||||
*/
|
||||
template<typename Proc, typename... Args>
|
||||
basic_scheduler &attach(Args &&...args) {
|
||||
static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type");
|
||||
auto &ref = handlers.first().emplace_back(std::allocate_shared<handler_type<Proc>>(handlers.second(), std::forward<Args>(args)...));
|
||||
// forces the process to exit the uninitialized state
|
||||
ref->update({}, nullptr);
|
||||
return *this;
|
||||
template<typename Type, typename... Args>
|
||||
type &attach(Args &&...args) {
|
||||
const auto &allocator = handlers.second();
|
||||
return *handlers.first().emplace_back(std::allocate_shared<Type>(allocator, allocator, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Schedules a process for the next tick.
|
||||
*
|
||||
* A process can be either a lambda or a functor. The scheduler wraps both
|
||||
* of them in a process adaptor internally.<br/>
|
||||
* The signature of the function call operator should be equivalent to the
|
||||
* following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(Delta delta, void *data, auto succeed, auto fail);
|
||||
* @endcode
|
||||
*
|
||||
* Where:
|
||||
*
|
||||
* * `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 signature of the function call operator of both `succeed` and `fail`
|
||||
* is equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void();
|
||||
* @endcode
|
||||
*
|
||||
* Returned value can be used to attach a continuation for the last process.
|
||||
* The continutation is scheduled automatically when the process terminates
|
||||
* and only if the process returns with success.
|
||||
*
|
||||
* Example of use (pseudocode):
|
||||
*
|
||||
* @code{.cpp}
|
||||
* // schedules a task in the form of a lambda function
|
||||
* scheduler.attach([](auto delta, void *, auto succeed, auto fail) {
|
||||
* // code
|
||||
* })
|
||||
* // appends a child in the form of another lambda function
|
||||
* .then([](auto delta, void *, auto succeed, auto fail) {
|
||||
* // code
|
||||
* })
|
||||
* // appends a child in the form of a process class
|
||||
* .then<my_process>(arguments...);
|
||||
* @endcode
|
||||
*
|
||||
* @sa process_adaptor
|
||||
*
|
||||
* @tparam Func Type of process to schedule.
|
||||
* @tparam Func Type of process to create.
|
||||
* @param func Either a lambda or a functor to use as a process.
|
||||
* @return This process scheduler.
|
||||
* @return A reference to the newly created process.
|
||||
*/
|
||||
template<typename Func>
|
||||
basic_scheduler &attach(Func &&func) {
|
||||
using Proc = process_adaptor<std::decay_t<Func>, Delta>;
|
||||
return attach<Proc>(std::forward<Func>(func));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a process as a continuation of the last scheduled process.
|
||||
* @tparam Proc Type of process to use as a continuation.
|
||||
* @tparam Args Types of arguments to use to initialize the process.
|
||||
* @param args Parameters to use to initialize the process.
|
||||
* @return This process scheduler.
|
||||
*/
|
||||
template<typename Proc, typename... Args>
|
||||
basic_scheduler &then(Args &&...args) {
|
||||
static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type");
|
||||
ENTT_ASSERT(!handlers.first().empty(), "Process not available");
|
||||
auto *curr = handlers.first().back().get();
|
||||
for(; curr->next; curr = curr->next.get()) {}
|
||||
curr->next = std::allocate_shared<handler_type<Proc>>(handlers.second(), std::forward<Args>(args)...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a process as a continuation of the last scheduled process.
|
||||
* @tparam Func Type of process to use as a continuation.
|
||||
* @param func Either a lambda or a functor to use as a process.
|
||||
* @return This process scheduler.
|
||||
*/
|
||||
template<typename Func>
|
||||
basic_scheduler &then(Func &&func) {
|
||||
using Proc = process_adaptor<std::decay_t<Func>, Delta>;
|
||||
return then<Proc>(std::forward<Func>(func));
|
||||
type &attach(Func func) {
|
||||
const auto &allocator = handlers.second();
|
||||
using process_type = internal::process_adaptor<delta_type, Func, allocator_type>;
|
||||
return *handlers.first().emplace_back(std::allocate_shared<process_type>(allocator, allocator, std::move(func)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -325,16 +182,18 @@ public:
|
||||
*/
|
||||
void update(const delta_type delta, void *data = nullptr) {
|
||||
for(auto next = handlers.first().size(); next; --next) {
|
||||
if(const auto pos = next - 1u; handlers.first()[pos]->update(delta, data)) {
|
||||
// updating might spawn/reallocate, cannot hold refs until here
|
||||
if(auto &curr = handlers.first()[pos]; curr->next) {
|
||||
curr = std::move(curr->next);
|
||||
// forces the process to exit the uninitialized state
|
||||
curr->update({}, nullptr);
|
||||
} else {
|
||||
curr = std::move(handlers.first().back());
|
||||
handlers.first().pop_back();
|
||||
}
|
||||
const auto pos = next - 1u;
|
||||
handlers.first()[pos]->tick(delta, data);
|
||||
// updating might spawn/reallocate, cannot hold refs until here
|
||||
auto &elem = handlers.first()[pos];
|
||||
|
||||
if(elem->finished()) {
|
||||
elem = elem->peek();
|
||||
}
|
||||
|
||||
if(!elem || elem->rejected()) {
|
||||
elem = std::move(handlers.first().back());
|
||||
handlers.first().pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -351,7 +210,11 @@ public:
|
||||
*/
|
||||
void abort(const bool immediate = false) {
|
||||
for(auto &&curr: handlers.first()) {
|
||||
curr->abort(immediate);
|
||||
curr->abort();
|
||||
|
||||
if(immediate) {
|
||||
curr->tick({});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
}
|
||||
|
||||
constexpr resource_cache_iterator operator++(int) noexcept {
|
||||
resource_cache_iterator orig = *this;
|
||||
const resource_cache_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
}
|
||||
|
||||
constexpr resource_cache_iterator operator--(int) noexcept {
|
||||
resource_cache_iterator orig = *this;
|
||||
const resource_cache_iterator orig = *this;
|
||||
return operator--(), orig;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ class resource {
|
||||
friend class resource;
|
||||
|
||||
template<typename Other>
|
||||
static constexpr bool is_acceptable_v = !std::is_same_v<Type, Other> && std::is_constructible_v<Type &, Other &>;
|
||||
static constexpr bool is_acceptable = !std::is_same_v<Type, Other> && std::is_constructible_v<Type &, Other &>;
|
||||
|
||||
public:
|
||||
/*! @brief Resource type. */
|
||||
@@ -64,7 +64,7 @@ public:
|
||||
* @tparam Other Type of resource managed by the received handle.
|
||||
* @param other The handle to copy from.
|
||||
*/
|
||||
template<typename Other, typename = std::enable_if_t<is_acceptable_v<Other>>>
|
||||
template<typename Other, typename = std::enable_if_t<is_acceptable<Other>>>
|
||||
resource(const resource<Other> &other) noexcept
|
||||
: value{other.value} {}
|
||||
|
||||
@@ -73,7 +73,7 @@ public:
|
||||
* @tparam Other Type of resource managed by the received handle.
|
||||
* @param other The handle to move from.
|
||||
*/
|
||||
template<typename Other, typename = std::enable_if_t<is_acceptable_v<Other>>>
|
||||
template<typename Other, typename = std::enable_if_t<is_acceptable<Other>>>
|
||||
resource(resource<Other> &&other) noexcept
|
||||
: value{std::move(other.value)} {}
|
||||
|
||||
@@ -98,7 +98,7 @@ public:
|
||||
* @param other The handle to copy from.
|
||||
* @return This resource handle.
|
||||
*/
|
||||
template<typename Other, typename = std::enable_if_t<is_acceptable_v<Other>>>
|
||||
template<typename Other, typename = std::enable_if_t<is_acceptable<Other>>>
|
||||
resource &operator=(const resource<Other> &other) noexcept {
|
||||
value = other.value;
|
||||
return *this;
|
||||
@@ -110,7 +110,7 @@ public:
|
||||
* @param other The handle to move from.
|
||||
* @return This resource handle.
|
||||
*/
|
||||
template<typename Other, typename = std::enable_if_t<is_acceptable_v<Other>>>
|
||||
template<typename Other, typename = std::enable_if_t<is_acceptable<Other>>>
|
||||
resource &operator=(resource<Other> &&other) noexcept {
|
||||
value = std::move(other.value);
|
||||
return *this;
|
||||
@@ -175,7 +175,7 @@ public:
|
||||
* @brief Returns the underlying resource handle.
|
||||
* @return The underlying resource handle.
|
||||
*/
|
||||
[[nodiscard]] const handle_type &handle() const noexcept {
|
||||
[[nodiscard]] handle_type handle() const noexcept {
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ public:
|
||||
signal.publish(events[pos]);
|
||||
}
|
||||
|
||||
events.erase(events.cbegin(), events.cbegin() + length);
|
||||
events.erase(events.cbegin(), events.cbegin() + static_cast<typename container_type::difference_type>(length));
|
||||
}
|
||||
|
||||
void disconnect(void *instance) override {
|
||||
|
||||
@@ -123,7 +123,7 @@ public:
|
||||
* @param value An instance of the given type of event.
|
||||
*/
|
||||
template<typename Type>
|
||||
void publish(Type &&value) {
|
||||
void publish(Type value) {
|
||||
if(const auto id = type_id<Type>().hash(); handlers.first().contains(id)) {
|
||||
handlers.first()[id](&value);
|
||||
}
|
||||
@@ -147,7 +147,7 @@ public:
|
||||
*/
|
||||
template<typename Type>
|
||||
void erase() {
|
||||
handlers.first().erase(type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value());
|
||||
handlers.first().erase(type_hash<std::remove_const_t<std::remove_reference_t<Type>>>::value());
|
||||
}
|
||||
|
||||
/*! @brief Disconnects all the listeners. */
|
||||
@@ -162,7 +162,7 @@ public:
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] bool contains() const {
|
||||
return handlers.first().contains(type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value());
|
||||
return handlers.first().contains(type_hash<std::remove_const_t<std::remove_reference_t<Type>>>::value());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -374,15 +374,26 @@ class sink<sigh<Ret(Args...), Allocator>> {
|
||||
|
||||
template<typename Func>
|
||||
void disconnect_if(Func callback) {
|
||||
for(auto pos = signal->calls.size(); pos; --pos) {
|
||||
if(auto &elem = signal->calls[pos - 1u]; callback(elem)) {
|
||||
elem = std::move(signal->calls.back());
|
||||
signal->calls.pop_back();
|
||||
auto &ref = signal_or_assert();
|
||||
|
||||
for(auto pos = ref.calls.size(); pos; --pos) {
|
||||
if(auto &elem = ref.calls[pos - 1u]; callback(elem)) {
|
||||
elem = std::move(ref.calls.back());
|
||||
ref.calls.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] auto &signal_or_assert() const noexcept {
|
||||
ENTT_ASSERT(signal != nullptr, "Invalid pointer to signal");
|
||||
return *signal;
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Constructs an invalid sink. */
|
||||
sink() noexcept
|
||||
: signal{} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a sink that is allowed to modify a given signal.
|
||||
* @param ref A valid reference to a signal object.
|
||||
@@ -395,41 +406,127 @@ public:
|
||||
* @return True if the sink has no listeners connected, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool empty() const noexcept {
|
||||
return signal->calls.empty();
|
||||
return signal_or_assert().calls.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connects a free function (with or without payload), a bound or an
|
||||
* unbound member to a signal.
|
||||
* @brief Connects a free function or an unbound member to a signal.
|
||||
* @tparam Candidate Function or member to connect to the signal.
|
||||
* @tparam Type Type of class or type of payload, if any.
|
||||
* @param value_or_instance A valid object that fits the purpose, if any.
|
||||
* @return A properly initialized connection object.
|
||||
*/
|
||||
template<auto Candidate, typename... Type>
|
||||
connection connect(Type &&...value_or_instance) {
|
||||
disconnect<Candidate>(value_or_instance...);
|
||||
template<auto Candidate>
|
||||
connection connect() {
|
||||
disconnect<Candidate>();
|
||||
|
||||
delegate_type call{};
|
||||
call.template connect<Candidate>(value_or_instance...);
|
||||
signal->calls.push_back(std::move(call));
|
||||
call.template connect<Candidate>();
|
||||
signal_or_assert().calls.push_back(std::move(call));
|
||||
|
||||
delegate<void(void *)> conn{};
|
||||
conn.template connect<&release<Candidate, Type...>>(value_or_instance...);
|
||||
conn.template connect<&release<Candidate>>();
|
||||
return {conn, signal};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disconnects a free function (with or without payload), a bound or
|
||||
* an unbound member from a signal.
|
||||
* @brief Connects a free function with payload or a bound member to a
|
||||
* signal.
|
||||
*
|
||||
* The signal isn't responsible for the connected object or the payload.
|
||||
* Users must always guarantee that the lifetime of the instance overcomes
|
||||
* the one of the signal.<br/>
|
||||
* When used to connect a free function with payload, its signature must be
|
||||
* such that the instance is the first argument before the ones used to
|
||||
* define the signal itself.
|
||||
*
|
||||
* @tparam Candidate Function or member to connect to the signal.
|
||||
* @tparam Type Type of class or type of payload.
|
||||
* @param value_or_instance A valid reference that fits the purpose.
|
||||
* @return A properly initialized connection object.
|
||||
*/
|
||||
template<auto Candidate, typename Type>
|
||||
connection connect(Type &value_or_instance) {
|
||||
disconnect<Candidate>(value_or_instance);
|
||||
|
||||
delegate_type call{};
|
||||
call.template connect<Candidate>(value_or_instance);
|
||||
signal_or_assert().calls.push_back(std::move(call));
|
||||
|
||||
delegate<void(void *)> conn{};
|
||||
conn.template connect<&release<Candidate, Type &>>(value_or_instance);
|
||||
return {conn, signal};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connects a free function with payload or a bound member to a
|
||||
* signal.
|
||||
*
|
||||
* @sa connect(Type &)
|
||||
*
|
||||
* @tparam Candidate Function or member to connect to the signal.
|
||||
* @tparam Type Type of class or type of payload.
|
||||
* @param value_or_instance A valid pointer that fits the purpose.
|
||||
* @return A properly initialized connection object.
|
||||
*/
|
||||
template<auto Candidate, typename Type>
|
||||
connection connect(Type *value_or_instance) {
|
||||
disconnect<Candidate>(value_or_instance);
|
||||
|
||||
delegate_type call{};
|
||||
call.template connect<Candidate>(value_or_instance);
|
||||
signal_or_assert().calls.push_back(std::move(call));
|
||||
|
||||
delegate<void(void *)> conn{};
|
||||
conn.template connect<&release<Candidate, Type *>>(value_or_instance);
|
||||
return {conn, signal};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disconnects a free function or an unbound member from a signal.
|
||||
* @tparam Candidate Function or member to disconnect from the signal.
|
||||
*/
|
||||
template<auto Candidate>
|
||||
void disconnect() {
|
||||
delegate_type call{};
|
||||
call.template connect<Candidate>();
|
||||
disconnect_if([&call](const auto &elem) { return elem == call; });
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disconnects a free function with payload or a bound member from a
|
||||
* signal.
|
||||
*
|
||||
* The signal isn't responsible for the connected object or the payload.
|
||||
* Users must always guarantee that the lifetime of the instance overcomes
|
||||
* the one of the signal.<br/>
|
||||
* When used to connect a free function with payload, its signature must be
|
||||
* such that the instance is the first argument before the ones used to
|
||||
* define the signal itself.
|
||||
*
|
||||
* @tparam Candidate Function or member to disconnect from the signal.
|
||||
* @tparam Type Type of class or type of payload, if any.
|
||||
* @param value_or_instance A valid object that fits the purpose, if any.
|
||||
* @param value_or_instance A valid reference that fits the purpose.
|
||||
*/
|
||||
template<auto Candidate, typename... Type>
|
||||
void disconnect(Type &&...value_or_instance) {
|
||||
template<auto Candidate, typename Type>
|
||||
void disconnect(Type &value_or_instance) {
|
||||
delegate_type call{};
|
||||
call.template connect<Candidate>(value_or_instance...);
|
||||
call.template connect<Candidate>(value_or_instance);
|
||||
disconnect_if([&call](const auto &elem) { return elem == call; });
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disconnects a free function with payload or a bound member from a
|
||||
* signal.
|
||||
*
|
||||
* @sa disconnect(Type &)
|
||||
*
|
||||
* @tparam Candidate Function or member to disconnect from the signal.
|
||||
* @tparam Type Type of class or type of payload, if any.
|
||||
* @param value_or_instance A valid pointer that fits the purpose.
|
||||
*/
|
||||
template<auto Candidate, typename Type>
|
||||
void disconnect(Type *value_or_instance) {
|
||||
delegate_type call{};
|
||||
call.template connect<Candidate>(value_or_instance);
|
||||
disconnect_if([&call](const auto &elem) { return elem == call; });
|
||||
}
|
||||
|
||||
@@ -445,7 +542,15 @@ public:
|
||||
|
||||
/*! @brief Disconnects all the listeners from a signal. */
|
||||
void disconnect() {
|
||||
signal->calls.clear();
|
||||
signal_or_assert().calls.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true if a sink is correctly initialized, false otherwise.
|
||||
* @return True if a sink is correctly initialized, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] explicit operator bool() const noexcept {
|
||||
return signal != nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
3
src/entt/tools.hpp
Normal file
3
src/entt/tools.hpp
Normal file
@@ -0,0 +1,3 @@
|
||||
// IWYU pragma: begin_exports
|
||||
#include "tools/davey.hpp"
|
||||
// IWYU pragma: end_exports
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user