Compare commits
962 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a2d1a8541 | ||
|
|
59061ec8d3 | ||
|
|
c32b9032fb | ||
|
|
48583b6f55 | ||
|
|
68adf8615c | ||
|
|
8c4a2f79ab | ||
|
|
1098b61060 | ||
|
|
2f8bdb2505 | ||
|
|
e50f20ebf4 | ||
|
|
6b20a3d3ea | ||
|
|
aecdb84606 | ||
|
|
031f340d62 | ||
|
|
f9a509a82b | ||
|
|
3f23bfed61 | ||
|
|
771603f8f6 | ||
|
|
331a8257be | ||
|
|
c97e3d4f96 | ||
|
|
5388c9b432 | ||
|
|
95f84d1488 | ||
|
|
e904300c1f | ||
|
|
8df81238e7 | ||
|
|
8c6bbda4db | ||
|
|
e1bbbee858 | ||
|
|
88c77f87fb | ||
|
|
c5e98d3287 | ||
|
|
b967dbcb90 | ||
|
|
b7cd6fadf4 | ||
|
|
26000fa019 | ||
|
|
b8977df86f | ||
|
|
2cd0ef323d | ||
|
|
30b685411b | ||
|
|
a15751a33b | ||
|
|
8837c7cc1f | ||
|
|
d9ad574a69 | ||
|
|
d87b7a928d | ||
|
|
a3f449408a | ||
|
|
53cb4864ab | ||
|
|
aec84b6598 | ||
|
|
5b7402468f | ||
|
|
688a1905d8 | ||
|
|
f4e4dac376 | ||
|
|
628261b5bf | ||
|
|
2bb7b07191 | ||
|
|
b331e88b8a | ||
|
|
9788a3e6c3 | ||
|
|
341fd03dc8 | ||
|
|
c40c2d41de | ||
|
|
8ca5a6c305 | ||
|
|
6738e3e77d | ||
|
|
36896978b1 | ||
|
|
6db5d78e87 | ||
|
|
414365c770 | ||
|
|
8cfd635f1d | ||
|
|
1de5b066fd | ||
|
|
02f8f00340 | ||
|
|
44ee192b41 | ||
|
|
00c2ddb300 | ||
|
|
a9e575ee8b | ||
|
|
76f988e975 | ||
|
|
a75f864c4f | ||
|
|
d8a98fe5f0 | ||
|
|
a348ff7dd1 | ||
|
|
1620b63a2f | ||
|
|
a70177a725 | ||
|
|
87ac4c0121 | ||
|
|
14a4788417 | ||
|
|
5f18f90571 | ||
|
|
cc79817af3 | ||
|
|
f39c68793e | ||
|
|
28adaa5a8b | ||
|
|
cee7c799ce | ||
|
|
47ff28d92d | ||
|
|
f50750bafd | ||
|
|
fc10b39dc4 | ||
|
|
035e17ff1d | ||
|
|
41f1b4e97b | ||
|
|
1f474e785a | ||
|
|
bf0e78fef9 | ||
|
|
83ebbea352 | ||
|
|
79e1a9a3d9 | ||
|
|
c9d7feccfa | ||
|
|
4f0b7d31dd | ||
|
|
9eef230340 | ||
|
|
69aeeeee30 | ||
|
|
734dfa2c6f | ||
|
|
61b5c193e8 | ||
|
|
84308361ae | ||
|
|
db22dd8e69 | ||
|
|
805cbf9c79 | ||
|
|
50e41076c5 | ||
|
|
1a95c9d03e | ||
|
|
0b01e14f01 | ||
|
|
445b11e88c | ||
|
|
c22224692d | ||
|
|
0207ee7624 | ||
|
|
84cab81c40 | ||
|
|
4859f41c24 | ||
|
|
1de4b20d1f | ||
|
|
2ffd38a355 | ||
|
|
ea660aa466 | ||
|
|
f9fd8432fd | ||
|
|
f28917aec7 | ||
|
|
96480cdd54 | ||
|
|
55b91798c3 | ||
|
|
4b9be6ddb7 | ||
|
|
d6f4bc9909 | ||
|
|
76eb4f4ded | ||
|
|
ff33c2fc02 | ||
|
|
401a8cbb6a | ||
|
|
a329659e2f | ||
|
|
8efd951561 | ||
|
|
911e0d2900 | ||
|
|
d4f2651e5f | ||
|
|
a74a0e4f4e | ||
|
|
f51352467d | ||
|
|
9fafb83a41 | ||
|
|
e1e1d37506 | ||
|
|
f8eb94c7ad | ||
|
|
4f6246cd11 | ||
|
|
dc6a03e08c | ||
|
|
0bb22f273a | ||
|
|
bb6304ecac | ||
|
|
a46905b2d3 | ||
|
|
a5ce90b268 | ||
|
|
a92be68bf7 | ||
|
|
13223125d7 | ||
|
|
9819234238 | ||
|
|
83641a997d | ||
|
|
9907a84b26 | ||
|
|
24eed3da7a | ||
|
|
56f83640a0 | ||
|
|
e8d007a970 | ||
|
|
6e128c47f5 | ||
|
|
9105f742dd | ||
|
|
57b46385a9 | ||
|
|
3f8efac37d | ||
|
|
33f00f400b | ||
|
|
044bfeefba | ||
|
|
21829cc27f | ||
|
|
c742fcf371 | ||
|
|
55f2c0fdc1 | ||
|
|
aa0e16fc93 | ||
|
|
01fa3a06a7 | ||
|
|
5bb5f396ed | ||
|
|
ef552922b3 | ||
|
|
19da9d2358 | ||
|
|
893eb8fabb | ||
|
|
9630977c2d | ||
|
|
dce9abb2f0 | ||
|
|
d62204df4e | ||
|
|
5f1a14cd43 | ||
|
|
80f12a7ac2 | ||
|
|
84f8b5d5bb | ||
|
|
24751f6158 | ||
|
|
fd3e275d86 | ||
|
|
1175bdca62 | ||
|
|
8899c9df7e | ||
|
|
3d10b764eb | ||
|
|
33578dd6da | ||
|
|
2ab5e4deb3 | ||
|
|
a4a87995af | ||
|
|
b5fbc68baa | ||
|
|
42a1905690 | ||
|
|
6d03f4d6f2 | ||
|
|
e4ba5c31d2 | ||
|
|
355010df3b | ||
|
|
dc7ab63b6f | ||
|
|
5ecd5739d0 | ||
|
|
8fd4cf7c45 | ||
|
|
6f7dc0558b | ||
|
|
eabe7be7da | ||
|
|
7ad5eb8c17 | ||
|
|
b485d72ec7 | ||
|
|
63d6db37d8 | ||
|
|
3b4864aeae | ||
|
|
d40e5f3dd5 | ||
|
|
5bb706ceea | ||
|
|
0966cc9063 | ||
|
|
5e1c44195a | ||
|
|
2147e4bbb2 | ||
|
|
9f36bec353 | ||
|
|
c3826c78aa | ||
|
|
d0a23fdf49 | ||
|
|
a39fd8548d | ||
|
|
9c9816110e | ||
|
|
156bc470ce | ||
|
|
50db6f2193 | ||
|
|
628bfbd4ca | ||
|
|
ea4470b4a7 | ||
|
|
90c975904f | ||
|
|
27f7e43b05 | ||
|
|
c909b40b5a | ||
|
|
07e1ac5350 | ||
|
|
c3758edb83 | ||
|
|
328286d201 | ||
|
|
8d149ecfc3 | ||
|
|
c6479e06ae | ||
|
|
eee348f6b1 | ||
|
|
c7e8a998ff | ||
|
|
a4ad1ac3aa | ||
|
|
942df26402 | ||
|
|
0fc5551998 | ||
|
|
da615cc8e0 | ||
|
|
9c6780c0a3 | ||
|
|
0b5cb5ac84 | ||
|
|
5da2c3c3c2 | ||
|
|
94735d04b3 | ||
|
|
116bb91123 | ||
|
|
010e5d4d3a | ||
|
|
0cf60202ff | ||
|
|
becfd01fd4 | ||
|
|
8163a54b71 | ||
|
|
07a372e994 | ||
|
|
818e8755c2 | ||
|
|
4a4e2a38d1 | ||
|
|
236747dd6b | ||
|
|
1d288e9c42 | ||
|
|
9369c94df0 | ||
|
|
e33e1b5e2b | ||
|
|
80ee5e43c2 | ||
|
|
3d89f04e95 | ||
|
|
1923028add | ||
|
|
97befad59a | ||
|
|
cdb1f23cbb | ||
|
|
c97a5f1b50 | ||
|
|
a040353632 | ||
|
|
55615c118f | ||
|
|
93f7d516f2 | ||
|
|
2c9aa2f544 | ||
|
|
89767dc398 | ||
|
|
2310e5143a | ||
|
|
587a012ae1 | ||
|
|
bbd5676bad | ||
|
|
eb6e29167d | ||
|
|
ebd077f71c | ||
|
|
11e6b65108 | ||
|
|
1beac44dda | ||
|
|
7acd6e7f53 | ||
|
|
8b0ef2b942 | ||
|
|
13284b7013 | ||
|
|
689005d452 | ||
|
|
a8118c9389 | ||
|
|
f4c6aa3f16 | ||
|
|
aa52c301fe | ||
|
|
197ba29276 | ||
|
|
af6dd28c66 | ||
|
|
c91fa066af | ||
|
|
872f1c8dd9 | ||
|
|
bb16607a7d | ||
|
|
c94014d057 | ||
|
|
ca0202934c | ||
|
|
9824b44d72 | ||
|
|
5744119e6b | ||
|
|
4ed58d2230 | ||
|
|
4a681e4d00 | ||
|
|
85488bf757 | ||
|
|
42a3d21638 | ||
|
|
7b0b5ad779 | ||
|
|
4e10e60c18 | ||
|
|
8b5fd1ff2e | ||
|
|
f4ad2bd11b | ||
|
|
a5a03de483 | ||
|
|
ab56b89982 | ||
|
|
ba63284d7a | ||
|
|
a8f988893e | ||
|
|
6695bdcb52 | ||
|
|
baf582fce5 | ||
|
|
e76d58c76c | ||
|
|
24808b11bb | ||
|
|
86226ed886 | ||
|
|
04827bd8dc | ||
|
|
8e6c72fdc1 | ||
|
|
b5e7e8c6f6 | ||
|
|
566e38b1d1 | ||
|
|
fb94471b8d | ||
|
|
852aa2969d | ||
|
|
0f64c2341e | ||
|
|
a33212438a | ||
|
|
7431d93691 | ||
|
|
d16fa5c605 | ||
|
|
aee98b3c0a | ||
|
|
2de5b6dd87 | ||
|
|
7e9b27f9c1 | ||
|
|
f104c5de99 | ||
|
|
0475a2404b | ||
|
|
94250af280 | ||
|
|
0a00584cc0 | ||
|
|
ff09c4bcbb | ||
|
|
dfc2c85be0 | ||
|
|
5080289a73 | ||
|
|
34d4b50d0e | ||
|
|
5a2c092cde | ||
|
|
56ef214bf3 | ||
|
|
d331f750f0 | ||
|
|
784235c923 | ||
|
|
dc070b04e2 | ||
|
|
1ddc887ac4 | ||
|
|
fea24b1a22 | ||
|
|
c4501d09b2 | ||
|
|
7f4f779732 | ||
|
|
88db7a8f09 | ||
|
|
36df10fd2c | ||
|
|
e5ef550bcf | ||
|
|
d67f25c23a | ||
|
|
cf099b4511 | ||
|
|
8b7f5e5668 | ||
|
|
c2f87a70c5 | ||
|
|
1bfe022a57 | ||
|
|
0a996f5682 | ||
|
|
9e76dbce1d | ||
|
|
86850ca65b | ||
|
|
ad320cfa2e | ||
|
|
873617fbdf | ||
|
|
312c2b1cdf | ||
|
|
32a83ecf50 | ||
|
|
181f33ae7c | ||
|
|
b275af5848 | ||
|
|
023adf0b38 | ||
|
|
b4d3ad7279 | ||
|
|
2c7cc005cd | ||
|
|
7cfbe89ecf | ||
|
|
f433b7fb41 | ||
|
|
cad70bcd60 | ||
|
|
0753dd1aaa | ||
|
|
d56a0a5fbd | ||
|
|
4a5f021496 | ||
|
|
60019b45c2 | ||
|
|
ce543cf3fb | ||
|
|
81b30c41a6 | ||
|
|
185dfaa08a | ||
|
|
b2cdc6aa57 | ||
|
|
d233a40c3d | ||
|
|
d1073bb181 | ||
|
|
fce847820e | ||
|
|
a53b944afc | ||
|
|
98a35f0f0a | ||
|
|
367f80dca4 | ||
|
|
65f7944b49 | ||
|
|
473a53d796 | ||
|
|
9e7985363e | ||
|
|
fe76478dbc | ||
|
|
59d5e59aa4 | ||
|
|
6f967b3b72 | ||
|
|
44b1ae2c3a | ||
|
|
836847b2a7 | ||
|
|
5f7edc4447 | ||
|
|
e98ccf71b4 | ||
|
|
b48666d3d1 | ||
|
|
54881e58b5 | ||
|
|
013d70a572 | ||
|
|
636e023f5f | ||
|
|
7d8461bae4 | ||
|
|
060590b14f | ||
|
|
b7090b14b7 | ||
|
|
a220803b09 | ||
|
|
8bb83ae64d | ||
|
|
bf6c2aa1ee | ||
|
|
ecb5c9df59 | ||
|
|
cd9b564ce9 | ||
|
|
f420308ff0 | ||
|
|
b5d850c741 | ||
|
|
c78c0c2bf1 | ||
|
|
1bf23c5989 | ||
|
|
24256188d0 | ||
|
|
cb2bf04522 | ||
|
|
b3967dcd39 | ||
|
|
6949f9b945 | ||
|
|
b1b014ebdd | ||
|
|
758c116c58 | ||
|
|
53b85ecb39 | ||
|
|
c15b1f778a | ||
|
|
d361e73aed | ||
|
|
5ab4c1c5d9 | ||
|
|
b4e437f1b1 | ||
|
|
96250f0abb | ||
|
|
3340cf1b3e | ||
|
|
6bf7e2596a | ||
|
|
4d3efa7fb9 | ||
|
|
834cefc35f | ||
|
|
ed62adb744 | ||
|
|
cc6e578f63 | ||
|
|
f4ff91143f | ||
|
|
d9e5dbb587 | ||
|
|
3927ddeb58 | ||
|
|
b7c3cb267c | ||
|
|
9ec6f050c4 | ||
|
|
6c3202e429 | ||
|
|
133f6a0a28 | ||
|
|
c2c5342ea4 | ||
|
|
97ed546e7b | ||
|
|
5130420dd2 | ||
|
|
442821b061 | ||
|
|
88889c5a8a | ||
|
|
3acf37100e | ||
|
|
4fed6228a3 | ||
|
|
9e2bc6c856 | ||
|
|
f49a405dce | ||
|
|
57a567629f | ||
|
|
342db5c24f | ||
|
|
41fe71ebf4 | ||
|
|
9e6e09fe2b | ||
|
|
8b3a2f7ce9 | ||
|
|
5f5907c228 | ||
|
|
d67fd1e72c | ||
|
|
12ae4cd4c1 | ||
|
|
04460d336e | ||
|
|
e262b60225 | ||
|
|
aca7872ba7 | ||
|
|
c9e48c0a14 | ||
|
|
f931687ff0 | ||
|
|
f7cdf202e9 | ||
|
|
154b91e835 | ||
|
|
109434235f | ||
|
|
081c8f799e | ||
|
|
786329c1d0 | ||
|
|
fb47314d6f | ||
|
|
3cec7c1065 | ||
|
|
514e6e710c | ||
|
|
e197a04412 | ||
|
|
87d69c4e77 | ||
|
|
75307337ea | ||
|
|
7ce821f92e | ||
|
|
da8a8650cb | ||
|
|
29bc422a6a | ||
|
|
3299fa1e40 | ||
|
|
f8336ba5a1 | ||
|
|
c28981e30f | ||
|
|
339b78f47c | ||
|
|
8062bcfb3c | ||
|
|
47d71fa6cf | ||
|
|
43e9083456 | ||
|
|
059fa5a7a5 | ||
|
|
d20a5607c0 | ||
|
|
58acc7edd1 | ||
|
|
898dc2a122 | ||
|
|
bf7fe3e42d | ||
|
|
89e3698599 | ||
|
|
cc1a71ec9d | ||
|
|
b3e07785fc | ||
|
|
f068b1f005 | ||
|
|
f5790697ff | ||
|
|
00b32cdfe5 | ||
|
|
ac8369d393 | ||
|
|
cb8f695584 | ||
|
|
2ab27a9b59 | ||
|
|
eae659301b | ||
|
|
86cdb6f55d | ||
|
|
627219f8a2 | ||
|
|
e56d0a6fc9 | ||
|
|
126f3caba0 | ||
|
|
69864955af | ||
|
|
216bd0a629 | ||
|
|
05f758f9ba | ||
|
|
6b84227626 | ||
|
|
404b6bd99c | ||
|
|
c51c6da61b | ||
|
|
0405126c25 | ||
|
|
274d9ea89d | ||
|
|
659bcbc98c | ||
|
|
3291862371 | ||
|
|
22683f843a | ||
|
|
9d1b9933c8 | ||
|
|
e786a3b93d | ||
|
|
f2e825b848 | ||
|
|
13a51c84a6 | ||
|
|
e35f4e751f | ||
|
|
2b07fae839 | ||
|
|
c4552892aa | ||
|
|
51391a9381 | ||
|
|
0349f4f615 | ||
|
|
d951b9e817 | ||
|
|
2dd38bbff2 | ||
|
|
06929572be | ||
|
|
c7f412bd7e | ||
|
|
483cc2b913 | ||
|
|
8138094f0a | ||
|
|
06a8b05cf8 | ||
|
|
d3328accae | ||
|
|
a5e5fded0b | ||
|
|
a16b9cc092 | ||
|
|
539cf5f011 | ||
|
|
abdf06625b | ||
|
|
3c99189ff4 | ||
|
|
cd1fd890ea | ||
|
|
d03c0d0b1a | ||
|
|
7db890ba15 | ||
|
|
8807499548 | ||
|
|
bea578ac87 | ||
|
|
c474fe5b51 | ||
|
|
864ae25a2b | ||
|
|
e864037968 | ||
|
|
76d4f00e9f | ||
|
|
4cb70d3c2d | ||
|
|
4c3fca4673 | ||
|
|
7718b1bb9a | ||
|
|
32ffd625ff | ||
|
|
176b9dafba | ||
|
|
82cd118adf | ||
|
|
88828d2eb5 | ||
|
|
d9a3990f03 | ||
|
|
699dff81b7 | ||
|
|
7bfc92d20e | ||
|
|
c5f6691c72 | ||
|
|
8433060092 | ||
|
|
09da09500a | ||
|
|
6ca5b9c1af | ||
|
|
5f6f9b7b23 | ||
|
|
f0191b9afe | ||
|
|
c885044ce4 | ||
|
|
ee64ca4afb | ||
|
|
9788610124 | ||
|
|
01f851199b | ||
|
|
d278a0f864 | ||
|
|
f9bf4c341d | ||
|
|
2777bade73 | ||
|
|
55637a7284 | ||
|
|
0bd37df6f3 | ||
|
|
aad65bf436 | ||
|
|
9db1d085f9 | ||
|
|
dccc621f07 | ||
|
|
a014656f04 | ||
|
|
1ffbd845dc | ||
|
|
d0f8974e08 | ||
|
|
505bb4c8cb | ||
|
|
5ecf9eb893 | ||
|
|
ab532af047 | ||
|
|
16b05ad8a0 | ||
|
|
54ef3c8850 | ||
|
|
4516210518 | ||
|
|
387528bdcc | ||
|
|
84b48c1a30 | ||
|
|
6615792703 | ||
|
|
991d16084d | ||
|
|
d7c9609ee6 | ||
|
|
af27c43436 | ||
|
|
89e3fcfda0 | ||
|
|
52de32291f | ||
|
|
a530b078d7 | ||
|
|
a68bb70d43 | ||
|
|
f9a243b92a | ||
|
|
4c4bbed0a2 | ||
|
|
9ec4d69b1e | ||
|
|
68f3f650eb | ||
|
|
2cb44f2590 | ||
|
|
f7fd153ee0 | ||
|
|
e5804ac0a2 | ||
|
|
11ade1cbc0 | ||
|
|
46ed48ec48 | ||
|
|
b3b4facaed | ||
|
|
fbe92fc708 | ||
|
|
3b6a987814 | ||
|
|
10749d445a | ||
|
|
83495c967e | ||
|
|
32ae3eef08 | ||
|
|
efc68bdf39 | ||
|
|
1e90bee7ad | ||
|
|
77606c2d20 | ||
|
|
ca303e24ff | ||
|
|
288fcdaf53 | ||
|
|
8f7480ebd1 | ||
|
|
a15511d5ff | ||
|
|
16ed46967f | ||
|
|
4d460ce763 | ||
|
|
4dd427aa4d | ||
|
|
4302842b79 | ||
|
|
5724644653 | ||
|
|
a82a5958b3 | ||
|
|
59935230dc | ||
|
|
e9d30cb10e | ||
|
|
db35537966 | ||
|
|
62e990ed8a | ||
|
|
1f0efb14a0 | ||
|
|
5ac56d6fec | ||
|
|
68a6aef155 | ||
|
|
b8af4c6d71 | ||
|
|
c9ecfa049f | ||
|
|
0c0ce5dfc5 | ||
|
|
138347320e | ||
|
|
4f58be5762 | ||
|
|
ffd3f950f3 | ||
|
|
9ac19020d5 | ||
|
|
feeb10a5f8 | ||
|
|
950509a950 | ||
|
|
0b23b8f342 | ||
|
|
1d1761a13b | ||
|
|
d42d324adb | ||
|
|
42cef49a7f | ||
|
|
b57758c61e | ||
|
|
e4ef61ca93 | ||
|
|
640f02bd16 | ||
|
|
2a86b8fe08 | ||
|
|
9edeb731f5 | ||
|
|
8efe02f28f | ||
|
|
581f2b0a0c | ||
|
|
00e34a2dc7 | ||
|
|
2590b6c3ba | ||
|
|
fb6403f801 | ||
|
|
e2da374e24 | ||
|
|
ceb4259ae3 | ||
|
|
d6d72e6b03 | ||
|
|
35ebf5c413 | ||
|
|
39e7595b3b | ||
|
|
7de152428e | ||
|
|
e2648e418f | ||
|
|
f7e533d061 | ||
|
|
2f8b4835ca | ||
|
|
2a0eb2bcd4 | ||
|
|
4692e72671 | ||
|
|
30d16b850c | ||
|
|
81b878d038 | ||
|
|
cf3d751097 | ||
|
|
1e9ad0fb9c | ||
|
|
d0786338a9 | ||
|
|
7a605742ee | ||
|
|
739bc802b5 | ||
|
|
97a50c23a9 | ||
|
|
b6a71dbdbe | ||
|
|
3a97180b63 | ||
|
|
d92ae29132 | ||
|
|
1e26f3fd81 | ||
|
|
640a06bc68 | ||
|
|
41aab920b0 | ||
|
|
a3cea32f93 | ||
|
|
aadbe3501e | ||
|
|
3ba8268bfd | ||
|
|
45b4241f62 | ||
|
|
0a48f25beb | ||
|
|
7ca3f84891 | ||
|
|
3729eab7d5 | ||
|
|
09ef06db71 | ||
|
|
9f211bacad | ||
|
|
bf854b0b5f | ||
|
|
3823d60932 | ||
|
|
48353cd4dc | ||
|
|
caaf332af3 | ||
|
|
89c87c6d10 | ||
|
|
80c05856c2 | ||
|
|
d990d4e928 | ||
|
|
965a7ba4f0 | ||
|
|
17c9da4329 | ||
|
|
7abe76e4f7 | ||
|
|
6e9be219ba | ||
|
|
a360d14dad | ||
|
|
43d6b0865f | ||
|
|
e44b1e9024 | ||
|
|
c0e68b96f7 | ||
|
|
12e886340c | ||
|
|
9f7668071d | ||
|
|
736b0fe92b | ||
|
|
a89d1a853a | ||
|
|
452e9f8b51 | ||
|
|
c6ec062a4d | ||
|
|
7e413590c6 | ||
|
|
4a3f83a7b8 | ||
|
|
dd4a291272 | ||
|
|
32bd18bf08 | ||
|
|
7d92b42d59 | ||
|
|
4a542c3ad0 | ||
|
|
950a48b5aa | ||
|
|
bcdc6f9ad8 | ||
|
|
6b06fa9990 | ||
|
|
8dbfa40c76 | ||
|
|
bb423bb503 | ||
|
|
3d07cf8781 | ||
|
|
f180927380 | ||
|
|
a1a1e9fd28 | ||
|
|
0d6056fee4 | ||
|
|
c7a9bbf853 | ||
|
|
206110caaf | ||
|
|
f223638350 | ||
|
|
a863536982 | ||
|
|
430822aaa2 | ||
|
|
b69390f283 | ||
|
|
57d3c53014 | ||
|
|
d6588f9b59 | ||
|
|
19f510743e | ||
|
|
02a8803f98 | ||
|
|
a78c2d0592 | ||
|
|
60e5ae22d5 | ||
|
|
aa178dd2cf | ||
|
|
0b6e325109 | ||
|
|
453d32d3fd | ||
|
|
9891e70a20 | ||
|
|
6ca01d2faf | ||
|
|
4c98e3aa1e | ||
|
|
2c700bb812 | ||
|
|
03f94a8834 | ||
|
|
fb87ae392f | ||
|
|
48c122fb28 | ||
|
|
77d646f05d | ||
|
|
35d048304f | ||
|
|
0d0880d81f | ||
|
|
08e9e059b5 | ||
|
|
4602de245c | ||
|
|
0b9d7b3538 | ||
|
|
38c7cddac0 | ||
|
|
d4588208e3 | ||
|
|
666e988995 | ||
|
|
f8e452c7c8 | ||
|
|
f9a9034001 | ||
|
|
d2b6eed671 | ||
|
|
d734c92ee4 | ||
|
|
69f12a1110 | ||
|
|
717696e7a3 | ||
|
|
affbc668df | ||
|
|
6ede10fa47 | ||
|
|
1ba01ca3e2 | ||
|
|
8739e9185a | ||
|
|
40208e6b37 | ||
|
|
32078f033f | ||
|
|
54f27382ca | ||
|
|
5908d01017 | ||
|
|
2f8998bd92 | ||
|
|
48c953d23e | ||
|
|
df87206c94 | ||
|
|
abaa5c468e | ||
|
|
f4431368e3 | ||
|
|
6bc8f4cfa4 | ||
|
|
70c4b1f4c4 | ||
|
|
1be0175a18 | ||
|
|
3796f055d6 | ||
|
|
0128620803 | ||
|
|
a8a86ba090 | ||
|
|
430d1dda86 | ||
|
|
f4e7e9738f | ||
|
|
3dfc65a357 | ||
|
|
fe83215f52 | ||
|
|
4df0f7ebf1 | ||
|
|
3d40651112 | ||
|
|
b96c8e2eff | ||
|
|
ad081f41db | ||
|
|
38b6fb2133 | ||
|
|
b373bb13d1 | ||
|
|
53895c1b9e | ||
|
|
29af24dac1 | ||
|
|
bb57d94185 | ||
|
|
c8a5812cea | ||
|
|
02836f1f43 | ||
|
|
0cf7262d2d | ||
|
|
ec92b7672e | ||
|
|
294792060c | ||
|
|
4bedef459e | ||
|
|
65da029ff9 | ||
|
|
ef563b05e1 | ||
|
|
7952266492 | ||
|
|
9b81532ced | ||
|
|
4a17395b6a | ||
|
|
56edea562a | ||
|
|
bc5dfe3489 | ||
|
|
e0e3786089 | ||
|
|
1c30113338 | ||
|
|
4a8efa2703 | ||
|
|
4fa9767edd | ||
|
|
a3b305b708 | ||
|
|
809fdad185 | ||
|
|
bf7db4c585 | ||
|
|
17d06d96ad | ||
|
|
b8a0031d9c | ||
|
|
283753a3ff | ||
|
|
2c753ff085 | ||
|
|
12a1e7fffc | ||
|
|
098343d275 | ||
|
|
fc8beac28f | ||
|
|
a4c37a4a93 | ||
|
|
4e66f6bf68 | ||
|
|
f9d509251e | ||
|
|
0f2ee043ae | ||
|
|
af56c554af | ||
|
|
fb31c923db | ||
|
|
f4a4ab339a | ||
|
|
02ec48ddc5 | ||
|
|
369bebd38b | ||
|
|
9f29ac8f65 | ||
|
|
4e89acb2ef | ||
|
|
231ff77283 | ||
|
|
a26e35248b | ||
|
|
bcac7a6ea2 | ||
|
|
0e567f8f3b | ||
|
|
f03a6c6a92 | ||
|
|
5f9e4344ba | ||
|
|
41a3c190ec | ||
|
|
5c53209e5d | ||
|
|
d70664c0ad | ||
|
|
55c257c905 | ||
|
|
f0ef2e7270 | ||
|
|
1064857764 | ||
|
|
dc9f0781fc | ||
|
|
5adf7172d1 | ||
|
|
1f78a8e768 | ||
|
|
7870cbfa56 | ||
|
|
69d15470b4 | ||
|
|
fefcbb214a | ||
|
|
94f53958a0 | ||
|
|
d7ed4dcedb | ||
|
|
1178b8e0cf | ||
|
|
f18234abbd | ||
|
|
cc0bd48233 | ||
|
|
921160a483 | ||
|
|
95d8370da4 | ||
|
|
2fba813e23 | ||
|
|
6b707d6af1 | ||
|
|
f481bc307f | ||
|
|
ebc6f64af0 | ||
|
|
ef61d4c3d0 | ||
|
|
cb669d43a2 | ||
|
|
7dbd00e7e7 | ||
|
|
698866df76 | ||
|
|
6922f15095 | ||
|
|
cd82501ed2 | ||
|
|
84fb9af076 | ||
|
|
062837dd31 | ||
|
|
97d46ce64d | ||
|
|
29ec97f464 | ||
|
|
dc424f7d4c | ||
|
|
1893a8c78d | ||
|
|
56c881e087 | ||
|
|
0dcf6c5690 | ||
|
|
e7d29479e4 | ||
|
|
ee55085f3a | ||
|
|
c802b2d687 | ||
|
|
dca2fefaba | ||
|
|
d8ef21b08b | ||
|
|
2f852806aa | ||
|
|
945b8881b6 | ||
|
|
0d71afacd8 | ||
|
|
0f9ee0ac03 | ||
|
|
c3fedcc151 | ||
|
|
2f1c99ab81 | ||
|
|
83576bf94e | ||
|
|
42c8df773c | ||
|
|
536c536563 | ||
|
|
2212f91c97 | ||
|
|
f18c8f14bf | ||
|
|
4e5b6de873 | ||
|
|
84e4b8c3a0 | ||
|
|
e33d8b28ca | ||
|
|
72f718458b | ||
|
|
84ce6163a7 | ||
|
|
a28ff99b08 | ||
|
|
44b3cb1294 | ||
|
|
79dfb454e2 | ||
|
|
7b24bc0005 | ||
|
|
463e8e0098 | ||
|
|
a272676738 | ||
|
|
3a51f71716 | ||
|
|
a922216ab4 | ||
|
|
ba116ba949 | ||
|
|
8b31b165b5 | ||
|
|
91a095dba4 | ||
|
|
371f547d59 | ||
|
|
bec1f60859 | ||
|
|
9db2daf3a7 | ||
|
|
f2a8c0ffe6 | ||
|
|
70eceea864 | ||
|
|
15aa6c7727 | ||
|
|
948fb8337d | ||
|
|
ac3a3d2fbe | ||
|
|
20dd52410e | ||
|
|
48011dcbf8 | ||
|
|
f4f7bdbf69 | ||
|
|
eb531fd7ba | ||
|
|
6cab881c51 | ||
|
|
209768cdbe | ||
|
|
5ecc254a91 | ||
|
|
d013c69bf1 | ||
|
|
1a469a96ce | ||
|
|
4f1e0805db | ||
|
|
4cc5745346 | ||
|
|
b7622a373b | ||
|
|
a0a2ad4404 | ||
|
|
1962027c80 | ||
|
|
8c1c899cb8 | ||
|
|
3b74db258e | ||
|
|
696f78c8a0 | ||
|
|
c31e1a939e | ||
|
|
dd95c35309 | ||
|
|
2c9d37efc3 | ||
|
|
1777db0a09 | ||
|
|
6f7839f8c4 | ||
|
|
ee78d39860 | ||
|
|
8a4a312250 | ||
|
|
ed21813170 | ||
|
|
9ec8adf5c9 | ||
|
|
48c3f58462 | ||
|
|
d0528c7a4d | ||
|
|
4e9b4371e2 | ||
|
|
e3fef531e6 | ||
|
|
5ca2a95f36 | ||
|
|
df1968255b | ||
|
|
6227ff5fcf | ||
|
|
3192f20877 | ||
|
|
bcdc1ebad9 | ||
|
|
a43ffa8f0d | ||
|
|
3a89dadb30 | ||
|
|
4d7b830b20 | ||
|
|
b92d2c98ae | ||
|
|
5d3f155164 | ||
|
|
fa56839bcf | ||
|
|
a6a4bbfc8e | ||
|
|
f6aa7dc725 | ||
|
|
7ea79932ed | ||
|
|
df6da5c208 | ||
|
|
a69e0eb661 | ||
|
|
83aabf7cfc | ||
|
|
61f4ad8837 | ||
|
|
872e0c6dde | ||
|
|
ad743ff327 | ||
|
|
9d07ff18b2 | ||
|
|
1150b980fa | ||
|
|
3ad137f597 | ||
|
|
5a4531baba | ||
|
|
53f8600ae5 | ||
|
|
11d609314c | ||
|
|
97b2b6c5b8 | ||
|
|
c1b7229238 | ||
|
|
c1a211d422 | ||
|
|
00ac590e2f | ||
|
|
31a91c764b | ||
|
|
553accd5f0 | ||
|
|
1ab1676d0b | ||
|
|
14ac11e9ca | ||
|
|
d45ef425a2 | ||
|
|
fdbb6829fa | ||
|
|
38ede6d0c4 | ||
|
|
4b4c038dc7 | ||
|
|
7a71084a8e | ||
|
|
8a57b94a18 | ||
|
|
79d72bd568 | ||
|
|
ece46d4aa3 | ||
|
|
f95b9f0ac9 | ||
|
|
9e9bed06af | ||
|
|
bba8b6bdd8 | ||
|
|
b96631c486 | ||
|
|
469a3cd568 | ||
|
|
d0e2edc13f | ||
|
|
1eb4b36643 | ||
|
|
325d3a9552 | ||
|
|
71bd869fc8 | ||
|
|
7d0cf207fd | ||
|
|
2f625b0a6f | ||
|
|
01957a73fd | ||
|
|
93f20984a6 | ||
|
|
00a58db0a7 | ||
|
|
e3cb863f94 | ||
|
|
e5381cee86 | ||
|
|
406ba3cb7d | ||
|
|
89622127db | ||
|
|
5f73b2e4f2 | ||
|
|
baab46e77b | ||
|
|
6889d9d87b | ||
|
|
dd3feb87cd | ||
|
|
2375c0489b | ||
|
|
353b3d19ee | ||
|
|
68688b2263 | ||
|
|
324716f82e | ||
|
|
51a063206b | ||
|
|
5e0caa6110 | ||
|
|
235ed63e3b | ||
|
|
1dbeedcf12 | ||
|
|
d695bc1b29 | ||
|
|
f08e1ea21c | ||
|
|
4510ba32b5 |
13
.bazelrc
13
.bazelrc
@@ -1,17 +1,16 @@
|
||||
common --enable_bzlmod
|
||||
build --enable_platform_specific_config
|
||||
build --incompatible_use_platforms_repo_for_constraints
|
||||
build --incompatible_enable_cc_toolchain_resolution
|
||||
build --enable_runfiles
|
||||
build --incompatible_strict_action_env
|
||||
|
||||
# required for googletest
|
||||
# required for googletest
|
||||
build:linux --cxxopt=-std=c++17
|
||||
build:macos --cxxopt=-std=c++17
|
||||
|
||||
common:ci --announce_rc
|
||||
common:ci --verbose_failures
|
||||
common:ci --announce_rc
|
||||
common:ci --verbose_failures
|
||||
common:ci --keep_going
|
||||
test:ci --test_output=errors
|
||||
|
||||
try-import %workspace%/user.bazelrc
|
||||
test:ci --test_output=errors
|
||||
|
||||
try-import %workspace%/user.bazelrc
|
||||
|
||||
@@ -2,10 +2,11 @@ BasedOnStyle: llvm
|
||||
---
|
||||
AccessModifierOffset: -4
|
||||
AlignEscapedNewlines: DontAlign
|
||||
AllowShortBlocksOnASingleLine: Empty
|
||||
AllowShortBlocksOnASingleLine: Always
|
||||
AllowShortEnumsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortIfStatementsOnASingleLine: WithoutElse
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BreakBeforeBinaryOperators: NonAssignment
|
||||
@@ -23,6 +24,7 @@ IncludeCategories:
|
||||
Priority: 4
|
||||
- Regex: '.*'
|
||||
Priority: 5
|
||||
IncludeIsMainRegex: "^$"
|
||||
IndentPPDirectives: AfterHash
|
||||
IndentWidth: 4
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
|
||||
30
.clang-tidy
30
.clang-tidy
@@ -2,25 +2,47 @@ Checks: >
|
||||
bugprone-*,
|
||||
concurrency-*,
|
||||
cppcoreguidelines-*,
|
||||
-cppcoreguidelines-owning-memory,
|
||||
-cppcoreguidelines-pro-bounds-constant-array-index,
|
||||
-cppcoreguidelines-pro-type-const-cast,
|
||||
-cppcoreguidelines-pro-type-member-init,
|
||||
-cppcoreguidelines-pro-type-reinterpret-cast,
|
||||
-cppcoreguidelines-pro-type-union-access,
|
||||
misc-*,
|
||||
-misc-include-cleaner,
|
||||
-misc-no-recursion,
|
||||
modernize-*,
|
||||
-modernize-use-trailing-return-type,
|
||||
performance-*,
|
||||
portability-*,
|
||||
readibility-*
|
||||
readability-*,
|
||||
-readability-function-cognitive-complexity,
|
||||
-readability-named-parameter,
|
||||
-readability-uppercase-literal-suffix,
|
||||
CheckOptions:
|
||||
- key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor
|
||||
- key: cppcoreguidelines-avoid-magic-numbers.IgnoreAllFloatingPointValues
|
||||
value: true
|
||||
- key: cppcoreguidelines-avoid-magic-numbers.IgnorePowersOf2IntegerValues
|
||||
value: true
|
||||
- key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions
|
||||
value: true
|
||||
- key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctionsWhenCopyIsDeleted
|
||||
value: true
|
||||
- key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor
|
||||
value: true
|
||||
- key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic
|
||||
value: true
|
||||
- key: misc-non-private-member-variables-in-classes.IgnorePublicMemberVariables
|
||||
value: true
|
||||
- key: cppcoreguidelines-avoid-magic-numbers.IgnorePowersOf2IntegerValues
|
||||
- key: modernize-avoid-c-arrays.AllowStringArrays
|
||||
value: true
|
||||
- key: cppcoreguidelines-avoid-magic-numbers.IgnoreAllFloatingPointValues
|
||||
- key: readability-function-cognitive-complexity.IgnoreMacros
|
||||
value: true
|
||||
- key: readability-identifier-length.MinimumParameterNameLength
|
||||
value: 2
|
||||
- key: readability-identifier-length.MinimumVariableNameLength
|
||||
value: 2
|
||||
- key: readability-magic-numbers.IgnoreAllFloatingPointValues
|
||||
value: true
|
||||
- key: readability-magic-numbers.IgnorePowersOf2IntegerValues
|
||||
value: true
|
||||
|
||||
3
.github/workflows/bazel-release-archive.yml
vendored
3
.github/workflows/bazel-release-archive.yml
vendored
@@ -13,7 +13,10 @@ jobs:
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/setup-go@v5
|
||||
- run: go install github.com/bazelbuild/buildtools/buildozer@latest
|
||||
- uses: actions/checkout@v4
|
||||
- run: ./scripts/sync_bzlmod_version.sh
|
||||
- run: git archive $GITHUB_REF -o "entt-${GITHUB_REF:10}.tar.gz"
|
||||
- run: gh release upload ${GITHUB_REF:10} "entt-${GITHUB_REF:10}.tar.gz"
|
||||
env:
|
||||
|
||||
3
.github/workflows/bazel.yml
vendored
3
.github/workflows/bazel.yml
vendored
@@ -3,6 +3,7 @@ name: bazel
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -10,8 +11,10 @@ jobs:
|
||||
- ubuntu-latest
|
||||
- windows-latest
|
||||
- macos-latest
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
continue-on-error: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: bazelisk test --config=ci ...
|
||||
|
||||
61
.github/workflows/build.yml
vendored
61
.github/workflows/build.yml
vendored
@@ -9,48 +9,45 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, ubuntu-20.04]
|
||||
os: [ubuntu-latest, ubuntu-24.04]
|
||||
compiler:
|
||||
- { pkg: g++, exe: 'g++', version: 7 }
|
||||
- { pkg: g++, exe: 'g++', version: 8 }
|
||||
- { pkg: g++, exe: 'g++', version: 9 }
|
||||
- { pkg: g++, exe: 'g++', version: 10 }
|
||||
- { pkg: g++, exe: 'g++', version: 11 }
|
||||
- { pkg: g++, exe: 'g++', version: 12 }
|
||||
- { pkg: clang, exe: 'clang++', version: 8 }
|
||||
- { pkg: clang, exe: 'clang++', version: 9 }
|
||||
- { pkg: clang, exe: 'clang++', version: 10 }
|
||||
- { pkg: clang, exe: 'clang++', version: 11 }
|
||||
- { pkg: clang, exe: 'clang++', version: 12 }
|
||||
- { pkg: 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: 7 }
|
||||
- os: ubuntu-latest
|
||||
compiler: { pkg: g++, exe: 'g++', version: 8 }
|
||||
- os: ubuntu-latest
|
||||
compiler: { pkg: g++, exe: 'g++', version: 9 }
|
||||
- os: ubuntu-latest
|
||||
compiler: { pkg: clang, exe: 'clang++', version: 8 }
|
||||
- os: ubuntu-latest
|
||||
compiler: { pkg: clang, exe: 'clang++', version: 9 }
|
||||
- os: ubuntu-latest
|
||||
compiler: { pkg: clang, exe: 'clang++', version: 10 }
|
||||
- os: ubuntu-latest
|
||||
compiler: { pkg: clang, exe: 'clang++', version: 11 }
|
||||
- os: ubuntu-20.04
|
||||
compiler: { pkg: g++, exe: 'g++', version: 10 }
|
||||
- os: ubuntu-20.04
|
||||
compiler: { pkg: g++, exe: 'g++', version: 11 }
|
||||
- os: ubuntu-20.04
|
||||
compiler: { pkg: g++, exe: 'g++', version: 12 }
|
||||
- os: ubuntu-20.04
|
||||
compiler: { pkg: clang, exe: 'clang++', version: 12 }
|
||||
- os: ubuntu-20.04
|
||||
- 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-20.04
|
||||
- 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 }}
|
||||
|
||||
@@ -78,10 +75,8 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
toolset: [default, v141, v142, clang-cl]
|
||||
toolset: [default, v142, clang-cl]
|
||||
include:
|
||||
- toolset: v141
|
||||
toolset_option: -T"v141"
|
||||
- toolset: v142
|
||||
toolset_option: -T"v142"
|
||||
- toolset: clang-cl
|
||||
|
||||
2
.github/workflows/sanitizer.yml
vendored
2
.github/workflows/sanitizer.yml
vendored
@@ -17,8 +17,6 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# temporary workaround for https://github.com/actions/runner-images/issues/8659
|
||||
- uses: mjp41/workaround8649@c8550b715ccdc17f89c8d5c28d7a48eeff9c94a8
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
env:
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
name: analyzer
|
||||
name: tools
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- wip
|
||||
- tools
|
||||
|
||||
jobs:
|
||||
|
||||
iwyu:
|
||||
timeout-minutes: 30
|
||||
timeout-minutes: 60
|
||||
|
||||
env:
|
||||
IWYU: "0.20"
|
||||
LLVM: "16"
|
||||
IWYU: "0.22"
|
||||
LLVM: "18"
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
@@ -59,3 +58,24 @@ jobs:
|
||||
-DCMAKE_CXX_INCLUDE_WHAT_YOU_USE="include-what-you-use;-Xiwyu;--mapping_file=${GITHUB_WORKSPACE}/entt.imp;-Xiwyu;--no_fwd_decls;-Xiwyu;--verbose=1" \
|
||||
..
|
||||
make -j4
|
||||
|
||||
clang-tidy:
|
||||
timeout-minutes: 60
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
env:
|
||||
CXX: clang++
|
||||
run: |
|
||||
cmake -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=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
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -13,3 +13,4 @@ cpp.hint
|
||||
/bazel-*
|
||||
/test/bazel-*
|
||||
/user.bazelrc
|
||||
*.bazel.lock
|
||||
|
||||
@@ -25,7 +25,7 @@ endif()
|
||||
|
||||
message(VERBOSE "*")
|
||||
message(VERBOSE "* ${PROJECT_NAME} v${PROJECT_VERSION} (${CMAKE_BUILD_TYPE})")
|
||||
message(VERBOSE "* Copyright (c) 2017-2023 Michele Caini <michele.caini@gmail.com>")
|
||||
message(VERBOSE "* Copyright (c) 2017-2024 Michele Caini <michele.caini@gmail.com>")
|
||||
message(VERBOSE "*")
|
||||
|
||||
# CMake stuff
|
||||
@@ -118,10 +118,12 @@ if(ENTT_INCLUDE_HEADERS)
|
||||
$<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>
|
||||
@@ -131,6 +133,7 @@ if(ENTT_INCLUDE_HEADERS)
|
||||
$<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>
|
||||
@@ -144,6 +147,7 @@ if(ENTT_INCLUDE_HEADERS)
|
||||
$<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>
|
||||
@@ -169,7 +173,6 @@ if(ENTT_INCLUDE_HEADERS)
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/template.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/type_traits.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/utility.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/platform/android-ndk-r17.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/poly/fwd.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/poly/poly.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/process/fwd.hpp>
|
||||
@@ -200,7 +203,6 @@ if(ENTT_HAS_NATVIS)
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/graph.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/locator.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/meta.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/platform.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/poly.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/process.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/resource.natvis>
|
||||
@@ -214,7 +216,7 @@ if(ENTT_HAS_SANITIZER)
|
||||
endif()
|
||||
|
||||
if(ENTT_CLANG_TIDY_EXECUTABLE)
|
||||
set(CMAKE_CXX_CLANG_TIDY "${ENTT_CLANG_TIDY_EXECUTABLE};--config-file=${EnTT_SOURCE_DIR}/.clang-tidy;--header-filter=${EnTT_SOURCE_DIR}/src/entt/.*;--extra-arg=/EHsc")
|
||||
set(CMAKE_CXX_CLANG_TIDY "${ENTT_CLANG_TIDY_EXECUTABLE};--config-file=${EnTT_SOURCE_DIR}/.clang-tidy;--header-filter=${EnTT_SOURCE_DIR}/src/entt/.*")
|
||||
endif()
|
||||
|
||||
if(ENTT_HAS_LIBCPP)
|
||||
@@ -282,7 +284,13 @@ install(
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake
|
||||
)
|
||||
|
||||
install(DIRECTORY src/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
install(
|
||||
DIRECTORY src/
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
FILES_MATCHING
|
||||
PATTERN "*.h"
|
||||
PATTERN "*.hpp"
|
||||
)
|
||||
|
||||
export(PACKAGE EnTT)
|
||||
|
||||
@@ -318,7 +326,7 @@ endif()
|
||||
option(ENTT_BUILD_DOCS "Enable building with documentation." OFF)
|
||||
|
||||
if(ENTT_BUILD_DOCS)
|
||||
find_package(Doxygen 1.8)
|
||||
find_package(Doxygen 1.10)
|
||||
|
||||
if(DOXYGEN_FOUND)
|
||||
add_subdirectory(docs)
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2023 Michele Caini, author of EnTT
|
||||
Copyright (c) 2017-2024 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
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
module(
|
||||
name = "entt",
|
||||
version = "3.12.2",
|
||||
compatibility_level = 3012,
|
||||
)
|
||||
module(name = "entt")
|
||||
|
||||
bazel_dep(name = "rules_cc", version = "0.0.8")
|
||||
bazel_dep(name = "bazel_skylib", version = "1.4.2")
|
||||
|
||||
34
README.md
34
README.md
@@ -1,8 +1,5 @@
|
||||

|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
[](https://github.com/skypjack/entt/actions)
|
||||
[](https://codecov.io/gh/skypjack/entt)
|
||||
[](https://godbolt.org/z/zxW73f)
|
||||
@@ -24,7 +21,7 @@ in [**Minecraft**](https://minecraft.net/en-us/attribution/) by Mojang, the
|
||||
[**ArcGIS Runtime SDKs**](https://developers.arcgis.com/arcgis-runtime/) by Esri
|
||||
and the amazing [**Ragdoll**](https://ragdolldynamics.com/).<br/>
|
||||
If you don't see your project in the list, please open an issue, submit a PR or
|
||||
add the [#entt](https://github.com/topics/entt) tag to your _topics_! :+1:
|
||||
add the [\#entt](https://github.com/topics/entt) tag to your _topics_! :+1:
|
||||
|
||||
---
|
||||
|
||||
@@ -64,9 +61,6 @@ Many thanks to [these people](https://skypjack.github.io/sponsorship/) and
|
||||
* [EnTT in Action](#entt-in-action)
|
||||
* [Contributors](#contributors)
|
||||
* [License](#license)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
@@ -242,9 +236,9 @@ To use `EnTT` from a `CMake` project, just link an existing target to the
|
||||
The library offers everything you need for locating (as in `find_package`),
|
||||
embedding (as in `add_subdirectory`), fetching (as in `FetchContent`) or using
|
||||
it in many of the ways that you can think of and that involve `CMake`.<br/>
|
||||
Covering all possible cases would require a treaty and not a simple README file,
|
||||
but I'm confident that anyone reading this section also knows what it's about
|
||||
and can use `EnTT` from a `CMake` project without problems.
|
||||
Covering all possible cases would require a treatise and not a simple README
|
||||
file, but I'm confident that anyone reading this section also knows what it's
|
||||
about and can use `EnTT` from a `CMake` project without problems.
|
||||
|
||||
## Natvis support
|
||||
|
||||
@@ -354,16 +348,10 @@ To navigate it with your favorite browser:
|
||||
$ cd build
|
||||
$ your_favorite_browser docs/html/index.html
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
The same version is also available [online](https://skypjack.github.io/entt/)
|
||||
for the latest release, that is the last stable tag.<br/>
|
||||
Moreover, there exists a [wiki](https://github.com/skypjack/entt/wiki) dedicated
|
||||
to the project where users can find all related documentation pages.
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Tests
|
||||
|
||||
@@ -380,9 +368,6 @@ To build the most basic set of tests:
|
||||
|
||||
Note that benchmarks are not part of this set.
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# EnTT in Action
|
||||
|
||||
`EnTT` is widely used in private and commercial applications. I cannot even
|
||||
@@ -400,7 +385,7 @@ open an issue or a PR and I'll be glad to add them to the list.
|
||||
|
||||
# Contributors
|
||||
|
||||
Requests for features, PRs, suggestions ad feedback are highly appreciated.
|
||||
Requests for features, PRs, suggestions and feedback are highly appreciated.
|
||||
|
||||
If you find you can help and want to contribute to the project with your
|
||||
experience or you do want to get part of the project for some other reason, feel
|
||||
@@ -410,18 +395,15 @@ I can't promise that each and every contribution will be accepted, but I can
|
||||
assure that I'll do my best to take them all as soon as possible.
|
||||
|
||||
If you decide to participate, please see the guidelines for
|
||||
[contributing](CONTRIBUTING.md) before to create issues or pull
|
||||
requests.<br/>
|
||||
[contributing](https://github.com/skypjack/entt/blob/master/CONTRIBUTING.md)
|
||||
before to create issues or pull requests.<br/>
|
||||
Take also a look at the
|
||||
[contributors list](https://github.com/skypjack/entt/blob/master/AUTHORS) to
|
||||
know who has participated so far.
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# License
|
||||
|
||||
Code and documentation Copyright (c) 2017-2023 Michele Caini.<br/>
|
||||
Code and documentation Copyright (c) 2017-2024 Michele Caini.<br/>
|
||||
Colorful logo Copyright (c) 2018-2021 Richard Caseres.
|
||||
|
||||
Code released under
|
||||
|
||||
33
TODO
33
TODO
@@ -10,10 +10,6 @@ DOC:
|
||||
* bump entities, reserved bits on identifiers
|
||||
|
||||
TODO:
|
||||
* resource cache: avoid using shared ptr with loader and the others
|
||||
* further optimize exclusion lists in multi type views (no existence check)
|
||||
* further improve meta resolve function by id (bimap)
|
||||
* get rid of observers, storage based views made them pointless - document alternatives
|
||||
* deprecate non-owning groups in favor of owning views and view packs, introduce lazy owning views
|
||||
* bring nested groups back in place (see bd34e7f)
|
||||
* work stealing job system (see #100) + mt scheduler based on const awareness for types
|
||||
@@ -21,11 +17,28 @@ TODO:
|
||||
* view: update natvis as needed after the last rework, merge pools/filter in the same array, drop check (?) and turn view into a position
|
||||
* view: type-only view_iterator (dyn get/excl sizes), type-only basic_common_view (dyn get/excl sizes with pointer to array from derived)
|
||||
* combine version-mask-vs-version-bits tricks with reserved bits to allow things like enabling/disabling
|
||||
* basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) uses moved-from packed in assert (same for storage)
|
||||
* maybe drop begin(v)/end(v) from sparse set and only use scoped begin/end at call sites
|
||||
* view unchecked_refresh loop is never executed if Get is 1u
|
||||
* review all // NOLINT + linter workflow + review clang-tidy file
|
||||
* self contained entity traits to avoid explicit specializations (ie enum constants)
|
||||
* review assure conflicts check, hash doesn't fit the purpose maybe
|
||||
* allow to zero-sized versions (with non-regression tests)
|
||||
* auto type info data from types if present
|
||||
* 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
|
||||
* 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
|
||||
|
||||
@@ -15,7 +15,7 @@ void update(entt::registry ®istry) {
|
||||
auto view = registry.view<position, velocity>();
|
||||
|
||||
for(auto entity: view) {
|
||||
// gets only the components that are going to be used ...
|
||||
// gets only the elements that are going to be used ...
|
||||
|
||||
auto &vel = view.get<velocity>(entity);
|
||||
|
||||
@@ -28,7 +28,7 @@ void update(entt::registry ®istry) {
|
||||
|
||||
void update(std::uint64_t dt, entt::registry ®istry) {
|
||||
registry.view<position, velocity>().each([dt](auto &pos, auto &vel) {
|
||||
// gets all the components of the view at once ...
|
||||
// gets all the elements of the view at once ...
|
||||
|
||||
pos.x += vel.dx * dt;
|
||||
pos.y += vel.dy * dt;
|
||||
|
||||
130
docs/doxy.in
130
docs/doxy.in
@@ -1,4 +1,4 @@
|
||||
# Doxyfile 1.9.7
|
||||
# Doxyfile 1.10.0
|
||||
|
||||
# This file describes the settings to be used by the documentation system
|
||||
# doxygen (www.doxygen.org) for a project.
|
||||
@@ -63,6 +63,12 @@ PROJECT_BRIEF =
|
||||
|
||||
PROJECT_LOGO =
|
||||
|
||||
# With the PROJECT_ICON tag one can specify an icon that is included in the tabs
|
||||
# when the HTML document is shown. Doxygen will copy the logo to the output
|
||||
# directory.
|
||||
|
||||
PROJECT_ICON =
|
||||
|
||||
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
|
||||
# into which the generated documentation will be written. If a relative path is
|
||||
# entered, it will be relative to the location where doxygen was started. If
|
||||
@@ -356,13 +362,13 @@ TOC_INCLUDE_HEADINGS = 5
|
||||
# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to
|
||||
# generate identifiers for the Markdown headings. Note: Every identifier is
|
||||
# unique.
|
||||
# Possible values are: DOXYGEN Use a fixed 'autotoc_md' string followed by a
|
||||
# sequence number starting at 0. and GITHUB Use the lower case version of title
|
||||
# with any whitespace replaced by '-' and punctations characters removed..
|
||||
# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a
|
||||
# sequence number starting at 0 and GITHUB use the lower case version of title
|
||||
# with any whitespace replaced by '-' and punctuation characters removed.
|
||||
# The default value is: DOXYGEN.
|
||||
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
|
||||
|
||||
MARKDOWN_ID_STYLE = DOXYGEN
|
||||
MARKDOWN_ID_STYLE = GITHUB
|
||||
|
||||
# When enabled doxygen tries to link words that correspond to documented
|
||||
# classes, or namespaces to their corresponding documentation. Such a link can
|
||||
@@ -968,12 +974,12 @@ INPUT_FILE_ENCODING =
|
||||
# Note the list of default checked file patterns might differ from the list of
|
||||
# default file extension mappings.
|
||||
#
|
||||
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
|
||||
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
|
||||
# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,
|
||||
# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C
|
||||
# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
|
||||
# *.vhdl, *.ucf, *.qsf and *.ice.
|
||||
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm,
|
||||
# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl,
|
||||
# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d,
|
||||
# *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to
|
||||
# be provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
|
||||
# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
|
||||
|
||||
FILE_PATTERNS = *.h \
|
||||
*.hpp \
|
||||
@@ -1129,7 +1135,8 @@ FORTRAN_COMMENT_AFTER = 72
|
||||
SOURCE_BROWSER = YES
|
||||
|
||||
# Setting the INLINE_SOURCES tag to YES will include the body of functions,
|
||||
# classes and enums directly into the documentation.
|
||||
# multi-line macros, enums or list initialized variables directly into the
|
||||
# documentation.
|
||||
# The default value is: NO.
|
||||
|
||||
INLINE_SOURCES = NO
|
||||
@@ -1417,6 +1424,33 @@ HTML_DYNAMIC_MENUS = YES
|
||||
|
||||
HTML_DYNAMIC_SECTIONS = NO
|
||||
|
||||
# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be
|
||||
# dynamically folded and expanded in the generated HTML source code.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_CODE_FOLDING = YES
|
||||
|
||||
# If the HTML_COPY_CLIPBOARD tag is set to YES then doxygen will show an icon in
|
||||
# the top right corner of code and text fragments that allows the user to copy
|
||||
# its content to the clipboard. Note this only works if supported by the browser
|
||||
# and the web page is served via a secure context (see:
|
||||
# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file:
|
||||
# protocol.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_COPY_CLIPBOARD = YES
|
||||
|
||||
# Doxygen stores a couple of settings persistently in the browser (via e.g.
|
||||
# cookies). By default these settings apply to all HTML pages generated by
|
||||
# doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store
|
||||
# the settings under a project specific key, such that the user preferences will
|
||||
# be stored separately.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_PROJECT_COOKIE =
|
||||
|
||||
# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
|
||||
# shown in the various tree structured indices initially; the user can expand
|
||||
# and collapse entries dynamically later on. Doxygen will expand the tree to
|
||||
@@ -2045,7 +2079,7 @@ PDF_HYPERLINKS = YES
|
||||
|
||||
USE_PDFLATEX = YES
|
||||
|
||||
# The LATEX_BATCHMODE tag ignals the behavior of LaTeX in case of an error.
|
||||
# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error.
|
||||
# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch
|
||||
# mode nothing is printed on the terminal, errors are scrolled as if <return> is
|
||||
# hit at every error; missing files that TeX tries to input or request from
|
||||
@@ -2247,6 +2281,32 @@ DOCBOOK_OUTPUT = docbook
|
||||
|
||||
GENERATE_AUTOGEN_DEF = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to Sqlite3 output
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# If the GENERATE_SQLITE3 tag is set to YES doxygen will generate a Sqlite3
|
||||
# database with symbols found by doxygen stored in tables.
|
||||
# The default value is: NO.
|
||||
|
||||
GENERATE_SQLITE3 = NO
|
||||
|
||||
# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be
|
||||
# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put
|
||||
# in front of it.
|
||||
# The default directory is: sqlite3.
|
||||
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
|
||||
|
||||
SQLITE3_OUTPUT = sqlite3
|
||||
|
||||
# The SQLITE3_RECREATE_DB tag is set to YES, the existing doxygen_sqlite3.db
|
||||
# database file will be recreated with each doxygen run. If set to NO, doxygen
|
||||
# will warn if a database file is already found and not modify it.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
|
||||
|
||||
SQLITE3_RECREATE_DB = YES
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the Perl module output
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -2389,15 +2449,15 @@ TAGFILES =
|
||||
|
||||
GENERATE_TAGFILE =
|
||||
|
||||
# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
|
||||
# the class index. If set to NO, only the inherited external classes will be
|
||||
# listed.
|
||||
# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces
|
||||
# will be listed in the class and namespace index. If set to NO, only the
|
||||
# inherited external classes will be listed.
|
||||
# The default value is: NO.
|
||||
|
||||
ALLEXTERNALS = NO
|
||||
|
||||
# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
|
||||
# in the modules index. If set to NO, only the current project's groups will be
|
||||
# in the topic index. If set to NO, only the current project's groups will be
|
||||
# listed.
|
||||
# The default value is: YES.
|
||||
|
||||
@@ -2482,7 +2542,11 @@ DOT_FONTPATH =
|
||||
# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the
|
||||
# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used.
|
||||
# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance
|
||||
# relations will be shown as texts / links.
|
||||
# relations will be shown as texts / links. Explicit enabling an inheritance
|
||||
# graph or choosing a different representation for an inheritance graph of a
|
||||
# specific class, can be accomplished by means of the command \inheritancegraph.
|
||||
# Disabling an inheritance graph can be accomplished by means of the command
|
||||
# \hideinheritancegraph.
|
||||
# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN.
|
||||
# The default value is: YES.
|
||||
|
||||
@@ -2491,15 +2555,21 @@ CLASS_GRAPH = YES
|
||||
# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
|
||||
# graph for each documented class showing the direct and indirect implementation
|
||||
# dependencies (inheritance, containment, and class references variables) of the
|
||||
# class with other documented classes.
|
||||
# class with other documented classes. Explicit enabling a collaboration graph,
|
||||
# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the
|
||||
# command \collaborationgraph. Disabling a collaboration graph can be
|
||||
# accomplished by means of the command \hidecollaborationgraph.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
COLLABORATION_GRAPH = YES
|
||||
|
||||
# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
|
||||
# groups, showing the direct groups dependencies. See also the chapter Grouping
|
||||
# in the manual.
|
||||
# groups, showing the direct groups dependencies. Explicit enabling a group
|
||||
# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means
|
||||
# of the command \groupgraph. Disabling a directory graph can be accomplished by
|
||||
# means of the command \hidegroupgraph. See also the chapter Grouping in the
|
||||
# manual.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
@@ -2541,8 +2611,8 @@ DOT_UML_DETAILS = NO
|
||||
|
||||
# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
|
||||
# to display on a single line. If the actual line length exceeds this threshold
|
||||
# significantly it will wrapped across multiple lines. Some heuristics are apply
|
||||
# to avoid ugly line breaks.
|
||||
# significantly it will be wrapped across multiple lines. Some heuristics are
|
||||
# applied to avoid ugly line breaks.
|
||||
# Minimum value: 0, maximum value: 1000, default value: 17.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
@@ -2559,7 +2629,9 @@ TEMPLATE_RELATIONS = NO
|
||||
# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
|
||||
# YES then doxygen will generate a graph for each documented file showing the
|
||||
# direct and indirect include dependencies of the file with other documented
|
||||
# files.
|
||||
# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO,
|
||||
# can be accomplished by means of the command \includegraph. Disabling an
|
||||
# include graph can be accomplished by means of the command \hideincludegraph.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
@@ -2568,7 +2640,10 @@ INCLUDE_GRAPH = YES
|
||||
# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
|
||||
# set to YES then doxygen will generate a graph for each documented file showing
|
||||
# the direct and indirect include dependencies of the file with other documented
|
||||
# files.
|
||||
# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set
|
||||
# to NO, can be accomplished by means of the command \includedbygraph. Disabling
|
||||
# an included by graph can be accomplished by means of the command
|
||||
# \hideincludedbygraph.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
@@ -2608,7 +2683,10 @@ GRAPHICAL_HIERARCHY = YES
|
||||
# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
|
||||
# dependencies a directory has on other directories in a graphical way. The
|
||||
# dependency relations are determined by the #include relations between the
|
||||
# files in the directories.
|
||||
# files in the directories. Explicit enabling a directory graph, when
|
||||
# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command
|
||||
# \directorygraph. Disabling a directory graph can be accomplished by means of
|
||||
# the command \hidedirectorygraph.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
# Crash Course: configuration
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
@@ -17,9 +14,6 @@
|
||||
* [ENTT_DISABLE_ASSERT](#entt_disable_assert)
|
||||
* [ENTT_NO_ETO](#entt_no_eto)
|
||||
* [ENTT_STANDARD_CPP](#entt_standard_cpp)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
|
||||
@@ -1,32 +1,29 @@
|
||||
# Crash Course: containers
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Containers](#containers)
|
||||
* [Dense map](#dense-map)
|
||||
* [Dense set](#dense-set)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
* [Adaptors](#adaptors)
|
||||
* [Table](#table)
|
||||
|
||||
# Introduction
|
||||
|
||||
The standard C++ library offers a wide range of containers and it's really
|
||||
difficult to do better (although it's very easy to do worse, as many examples
|
||||
available online demonstrate).<br/>
|
||||
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
|
||||
the opposite, given the widespread use that is made of standard containers.<br/>
|
||||
However, the library also tries to fill a gap in features and functionalities by
|
||||
making available some containers initially developed for internal use.
|
||||
making available some containers and adaptors initially developed for internal
|
||||
use.
|
||||
|
||||
This section of the library is likely to grow larger over time. However, for the
|
||||
moment it's quite small and mainly aimed at satisfying some internal needs.<br/>
|
||||
For all containers made available, full test coverage and stability over time is
|
||||
guaranteed as usual.
|
||||
For all containers and adaptors made available, full test coverage and stability
|
||||
over time is guaranteed as usual.
|
||||
|
||||
# Containers
|
||||
|
||||
@@ -65,3 +62,23 @@ The interface is in all respects similar to its counterpart in the standard
|
||||
library, that is, the `std::unordered_set` class.<br/>
|
||||
However, this type of set also supports reverse iteration and therefore offers
|
||||
all the functions necessary for the purpose (such as `rbegin` and `rend`).
|
||||
|
||||
# Adaptors
|
||||
|
||||
## Table
|
||||
|
||||
The `basic_table` class is a container adaptor which manages multiple sequential
|
||||
containers together, treating them as different columns of the same table.<br/>
|
||||
The `table` alias allows users to provide only the types to handle, using
|
||||
`std::vector` as the default sequential container.
|
||||
|
||||
Only a small set of functions is provided, although very close to what the API
|
||||
of the `std::vector` class offers.<br/>
|
||||
The internal implementation is purposely supported by a tuple of containers
|
||||
rather than a container of tuples. The purpose is to allow efficient access to
|
||||
single columns and not just access to the entire data set of the table.
|
||||
|
||||
When a row is accessed, all data are 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.
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
# Crash Course: core functionalities
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Any as in any type](#any-as-in-any-type)
|
||||
* [Small buffer optimization](#small-buffer-optimization)
|
||||
* [Alignment requirement](#alignment-requirement)
|
||||
* [Bit](#bit)
|
||||
* [Compressed pair](#compressed-pair)
|
||||
* [Enum as bitmask](#enum-as-bitmask)
|
||||
* [Hashed strings](#hashed-strings)
|
||||
@@ -19,7 +17,6 @@
|
||||
* [Iota iterator](#iota-iterator)
|
||||
* [Iterable adaptor](#iterable-adaptor)
|
||||
* [Memory](#memory)
|
||||
* [Power of two and fast modulus](#power-of-two-and-fast-modulus)
|
||||
* [Allocator aware unique pointers](#allocator-aware-unique-pointers)
|
||||
* [Monostate](#monostate)
|
||||
* [Type support](#type-support)
|
||||
@@ -39,9 +36,6 @@
|
||||
* [Compile-time generator](#compile-time-generator)
|
||||
* [Runtime generator](#runtime-generator)
|
||||
* [Utilities](#utilities)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
@@ -202,6 +196,22 @@ The `basic_any` class template inspects the alignment requirements in each case,
|
||||
even when not provided and may decide not to use the small buffer optimization
|
||||
in order to meet them.
|
||||
|
||||
# Bit
|
||||
|
||||
Finding out the population count of an unsigned integral value (`popcount`),
|
||||
whether a number is a power of two or not (`has_single_bit`) as well as the next
|
||||
power of two given a random value (`next_power_of_two`) can be useful.<br/>
|
||||
For example, it helps to allocate memory in pages having a size suitable for the
|
||||
fast modulus:
|
||||
|
||||
```cpp
|
||||
const std::size_t result = entt::fast_mod(value, modulus);
|
||||
```
|
||||
|
||||
Where `modulus` is necessarily a power of two. Perhaps not everyone knows that
|
||||
this type of operation is far superior in terms of performance to the basic
|
||||
modulus and for this reason preferred in many areas.
|
||||
|
||||
# Compressed pair
|
||||
|
||||
Primarily designed for internal use and far from being feature complete, the
|
||||
@@ -442,22 +452,6 @@ acronyms like _POCCA_, _POCMA_ or _POCS_.<br/>
|
||||
I won't describe them here in detail. Instead, I recommend reading the inline
|
||||
documentation to those interested in the subject.
|
||||
|
||||
## Power of two and fast modulus
|
||||
|
||||
Finding out if a number is a power of two (`is_power_of_two`) or what the next
|
||||
power of two is given a random value (`next_power_of_two`) is very useful at
|
||||
times.<br/>
|
||||
For example, it helps to allocate memory in pages having a size suitable for the
|
||||
fast modulus:
|
||||
|
||||
```cpp
|
||||
const std::size_t result = entt::fast_mod(value, modulus);
|
||||
```
|
||||
|
||||
Where `modulus` is necessarily a power of two. Perhaps not everyone knows that
|
||||
this type of operation is far superior in terms of performance to the basic
|
||||
modulus and for this reason preferred in many areas.
|
||||
|
||||
## Allocator aware unique pointers
|
||||
|
||||
A nasty thing in C++ (at least up to C++20) is the fact that shared pointers
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
# Crash Course: entity-component system
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
@@ -16,7 +13,7 @@
|
||||
* [Observe changes](#observe-changes)
|
||||
* [Entity lifecycle](#entity-lifecycle)
|
||||
* [Listeners disconnection](#listeners-disconnection)
|
||||
* [They call me Reactive System](#they-call-me-reactive-system)
|
||||
* [They call me reactive storage](#they-call-me-reactive-storage)
|
||||
* [Sorting: is it possible?](#sorting-is-it-possible)
|
||||
* [Helpers](#helpers)
|
||||
* [Null entity](#null-entity)
|
||||
@@ -65,9 +62,6 @@
|
||||
* [Iterators](#iterators)
|
||||
* [Const registry](#const-registry)
|
||||
* [Beyond this document](#beyond-this-document)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
@@ -382,19 +376,17 @@ In all cases, the function type of a listener is equivalent to the following:
|
||||
void(entt::registry &, entt::entity);
|
||||
```
|
||||
|
||||
In all cases, listeners are provided with the registry that triggered the
|
||||
notification and the involved entity.
|
||||
All listeners are provided with the registry that triggered the notification and
|
||||
the involved entity. Note also that:
|
||||
|
||||
Note also that:
|
||||
|
||||
* Listeners for the construction signals are invoked **after** components have
|
||||
been assigned to entities.
|
||||
* Listeners for construction signals are invoked **after** components have been
|
||||
created.
|
||||
|
||||
* Listeners designed to observe changes are invoked **after** components have
|
||||
been updated.
|
||||
|
||||
* Listeners for the destruction signals are invoked **before** components have
|
||||
been removed from entities.
|
||||
* Listeners for destruction signals are invoked **before** components have been
|
||||
destroyed.
|
||||
|
||||
There are also some limitations on what a listener can and cannot do:
|
||||
|
||||
@@ -441,7 +433,11 @@ registry.patch<entt::entity>(entity);
|
||||
```
|
||||
|
||||
Destroying an entity and then updating the version of an identifier **does not**
|
||||
give rise to these types of signals under any circumstances instead.
|
||||
give rise to these types of signals under any circumstances instead.<br/>
|
||||
Finally, note that listeners that _observe_ the destruction of an entity are
|
||||
invoked **after** all components have been removed, not **before**. This is
|
||||
because the entity would be invalidated before deleting its elements otherwise,
|
||||
making it difficult for the user to write component listeners.
|
||||
|
||||
### Listeners disconnection
|
||||
|
||||
@@ -459,11 +455,11 @@ As a result, a listener that wants to access components, entities, or pools can
|
||||
safely do so against a still valid registry, while checking for the existence of
|
||||
the various elements as appropriate.
|
||||
|
||||
### They call me Reactive System
|
||||
## They call me reactive storage
|
||||
|
||||
Signals are the basic tools to construct reactive systems, even if they aren't
|
||||
enough on their own. `EnTT` tries to take another step in that direction with
|
||||
the `observer` class template.<br/>
|
||||
its _reactive mixin_.<br/>
|
||||
In order to explain what reactive systems are, this is a slightly revised quote
|
||||
from the documentation of the library that first introduced this tool,
|
||||
[Entitas](https://github.com/sschmid/Entitas-CSharp):
|
||||
@@ -479,100 +475,168 @@ On these words, however, the similarities with the proposal of `Entitas` also
|
||||
end. The rules of the language and the design of the library obviously impose
|
||||
and allow different things.
|
||||
|
||||
An `observer` is initialized with an instance of a registry and a set of _rules_
|
||||
that describes what are the entities to intercept. As an example:
|
||||
A reactive mixin can be used on a standalone storage with any value type
|
||||
(perhaps using an alias to simplify its use):
|
||||
|
||||
```cpp
|
||||
entt::observer observer{registry, entt::collector.update<sprite>()};
|
||||
using reactive_storage = entt::reactive_mixin<entt::storage<void>>;
|
||||
|
||||
entt::registry registry{};
|
||||
reactive_storage storage{};
|
||||
|
||||
storage.bind(registry);
|
||||
```
|
||||
|
||||
The class is default constructible and is reconfigured at any time by means of
|
||||
the `connect` member function. Moreover, an observer is disconnected from the
|
||||
underlying registry through the `disconnect` member function.<br/>
|
||||
The `observer` offers also what is needed to query its _internal state_ and to
|
||||
know if it's empty or how many entities it contains. Moreover, it can return a
|
||||
raw pointer to the list of entities it contains.
|
||||
|
||||
However, the most important features of this class are that:
|
||||
|
||||
* It's iterable and therefore users can easily walk through the list of entities
|
||||
by means of a range-for loop or the `each` member function.
|
||||
|
||||
* It's clearable and therefore users can consume the entities and literally
|
||||
reset the observer after each iteration.
|
||||
|
||||
These aspects make the observer an incredibly powerful tool to know at any time
|
||||
what are the entities that matched the given rules since the last time one
|
||||
asked:
|
||||
In this case, it must be provided with a reference registry for subsequent
|
||||
operations.<br/>
|
||||
Alternatively, when using the value type provided by `EnTT`, it's also possible
|
||||
to create a reactive storage directly inside a registry:
|
||||
|
||||
```cpp
|
||||
for(const auto entity: observer) {
|
||||
// ...
|
||||
entt::registry registry{};
|
||||
auto &storage = registry.storage<entt::reactive>("observer"_hs);
|
||||
```
|
||||
|
||||
In the latter case there is the advantage that, in the event of destruction of
|
||||
an entity, this storage is also automatically cleaned up.<br/>
|
||||
Also note that, unlike all other storage, these classes don't support signals by
|
||||
default (although they can be enabled if necessary).
|
||||
|
||||
Once it has been created and associated with a registry, the reactive mixin
|
||||
needs to be informed about what it should _observe_.<br/>
|
||||
Here the choice boils down to three main events affecting all elements (entities
|
||||
or components), namely creation, update or destruction:
|
||||
|
||||
```cpp
|
||||
storage
|
||||
// observe position component construction
|
||||
.on_construct<position>()
|
||||
// observe velocity component update
|
||||
.on_update<velocity>()
|
||||
// observe renderable component destruction
|
||||
.on_destroy<renderable>();
|
||||
```
|
||||
|
||||
It goes without saying that it's possible to observe multiple events of the same
|
||||
type or of different types with the same storage.<br/>
|
||||
For example, to know which entities have been assigned or updated a component of
|
||||
a certain type:
|
||||
|
||||
```cpp
|
||||
storage
|
||||
.on_construct<my_type>()
|
||||
.on_update<my_type>();
|
||||
```
|
||||
|
||||
Note that all configurations are in _or_ and never in _and_. Therefore, to track
|
||||
entities that have been assigned two different components, there are a couple of
|
||||
options:
|
||||
|
||||
* Create two reactive storage, then combine them in a view:
|
||||
|
||||
```cpp
|
||||
first_storage.on_construct<position>();
|
||||
second_storage.on_construct<velocity>();
|
||||
|
||||
for(auto entity: entt::basic_view{first_storage, second_storage}) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
* Use a reactive storage with a non-`void` value type and a custom tracking
|
||||
function for the purpose:
|
||||
|
||||
```cpp
|
||||
using my_reactive_storage = entt::reactive_mixin<entt::storage<bool>>;
|
||||
|
||||
void callback(my_reactive_storage &storage, const entt::registry &, const entt::entity entity) {
|
||||
storage.contains(entity) ? (storage.get(entity) = true) : storage.emplace(entity, false);
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
my_reactive_storage storage{};
|
||||
storage
|
||||
.on_construct<position, &callback>()
|
||||
.on_construct<velocity, &callback>();
|
||||
|
||||
// ...
|
||||
|
||||
for(auto [entity, both_were_added]: storage.each()) {
|
||||
if(both_were_added) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
As highlighted in the last example, the reactive mixin tracks the entities that
|
||||
match the given conditions and saves them aside. However, this behavior can be
|
||||
changed.<br/>
|
||||
For example, it's possible to _capture_ all and only the entities for which a
|
||||
certain component has been updated but only if a specific value is within a
|
||||
given range:
|
||||
|
||||
```cpp
|
||||
void callback(reactive_storage &storage, const entt::registry ®istry, const entt::entity entity) {
|
||||
storage.remove(entity);
|
||||
|
||||
if(const auto x = registry.get<position>(entity).x; x >= min_x && x <= max_x) {
|
||||
storage.emplace(entity);
|
||||
}
|
||||
}
|
||||
|
||||
observer.clear();
|
||||
// ...
|
||||
|
||||
storage.on_update<position, &callback>();
|
||||
```
|
||||
|
||||
The snippet above is equivalent to the following:
|
||||
This makes reactive storage extremely flexible and usable in a large number of
|
||||
cases.<br/>
|
||||
Finally, once the entities of interest have been collected, it's possible to
|
||||
_visit_ the storage like any other:
|
||||
|
||||
```cpp
|
||||
observer.each([](const auto entity) {
|
||||
for(auto entity: storage) {
|
||||
// ...
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
At least as long as the `observer` isn't const. This means that the non-const
|
||||
overload of `each` does also reset the underlying data structure before to
|
||||
return to the caller, while the const overload does not for obvious reasons.
|
||||
|
||||
A `collector` is a utility aimed to generate a list of `matcher`s (the actual
|
||||
rules) to use with an `observer`.<br/>
|
||||
There are two types of `matcher`s:
|
||||
|
||||
* Observing matcher: an observer returns at least the entities for which one or
|
||||
more of the given components have been updated and not yet destroyed.
|
||||
|
||||
```cpp
|
||||
entt::collector.update<sprite>();
|
||||
```
|
||||
|
||||
Where _updated_ means that all listeners attached to `on_update` are invoked.
|
||||
In order for this to happen, specific functions such as `patch` must be used.
|
||||
Refer to the specific documentation for more details.
|
||||
|
||||
* Grouping matcher: an observer returns at least the entities that would have
|
||||
entered the given group if it existed and that would have not yet left it.
|
||||
|
||||
```cpp
|
||||
entt::collector.group<position, velocity>(entt::exclude<destroyed>);
|
||||
```
|
||||
|
||||
A grouping matcher supports also exclusion lists as well as single components.
|
||||
|
||||
Roughly speaking, an observing matcher intercepts the entities for which the
|
||||
given components are updated while a grouping matcher tracks the entities that
|
||||
have assigned the given components since the last time one asked.<br/>
|
||||
If an entity already has all the components except one and the missing type is
|
||||
assigned to it, the entity is intercepted by a grouping matcher.
|
||||
|
||||
In addition, matchers support filtering by means of a `where` clause:
|
||||
Wrapping it in a view and combining it with other views is another option:
|
||||
|
||||
```cpp
|
||||
entt::collector.update<sprite>().where<position>(entt::exclude<velocity>);
|
||||
for(auto [entity, pos]: (entt:.basic_view{storage} | registry.view<position>(entt::exclude<velocity>)).each()) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
This clause introduces a way to intercept entities if and only if they are
|
||||
already part of a hypothetical group. If they are not, they aren't returned by
|
||||
the observer, no matter if they matched the given rule.<br/>
|
||||
In the example above, whenever the component `sprite` of an entity is updated,
|
||||
the observer checks the entity itself to verify that it has at least `position`
|
||||
and has not `velocity`. If one of the two conditions isn't satisfied, the entity
|
||||
is discarded, no matter what.
|
||||
In order to simplify this last use case, the reactive mixin also provides a
|
||||
specific function that returns a view of the storage already filtered according
|
||||
to the provided requirements:
|
||||
|
||||
A `where` clause accepts a theoretically unlimited number of types as well as
|
||||
multiple elements in the exclusion list. Moreover, every matcher can have its
|
||||
own clause and multiple clauses for the same matcher are combined in a single
|
||||
one.
|
||||
```cpp
|
||||
for(auto [entity, pos]: storage.view<position>(entt::exclude<velocity>).each()) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
The registry used in this case is the one associated with the storage and also
|
||||
available via the `registry` function.
|
||||
|
||||
It should be noted that a reactive storage never deletes its entities (and
|
||||
elements, if any). To process and then discard entities at regular intervals,
|
||||
refer to the `clear` function available by default for each storage type.<br/>
|
||||
Similarly, the reactive mixin doesn't disconnect itself from observed storages
|
||||
upon destruction. Therefore, users have to do this themselves:
|
||||
|
||||
```cpp
|
||||
entt::registry = storage.registry();
|
||||
|
||||
registry.on_construct<position>().disconnect(&storage);
|
||||
registry.on_construct<velocity>().disconnect(&storage);
|
||||
```
|
||||
|
||||
Destroying a reactive storage without disconnecting it from observed pools will
|
||||
result in undefined behavior.
|
||||
|
||||
## Sorting: is it possible?
|
||||
|
||||
@@ -692,14 +756,15 @@ to create tombstones.
|
||||
|
||||
### To entity
|
||||
|
||||
This function accepts a registry and an instance of a component and returns the
|
||||
entity associated with the latter:
|
||||
This function accepts a storage and an instance of a component of the storage
|
||||
type, then it returns the entity associated with the latter:
|
||||
|
||||
```cpp
|
||||
const auto entity = entt::to_entity(registry, position);
|
||||
const auto entity = entt::to_entity(registry.storage<position>(), instance);
|
||||
```
|
||||
|
||||
A null entity is returned in case the component doesn't belong to the registry.
|
||||
Where `instance` is a component of type `position`. A null entity is returned in
|
||||
case the instance doesn't belong to the registry.
|
||||
|
||||
### Dependencies
|
||||
|
||||
@@ -919,7 +984,7 @@ accessible by both type and _name_ for convenience. The _name_ isn't really a
|
||||
name though. In fact, it's a numeric id of type `id_type` used as a key for the
|
||||
variable. Any value is accepted, even runtime ones.<br/>
|
||||
The context is returned via the `ctx` functions and offers a minimal set of
|
||||
feature including the following:
|
||||
features including the following:
|
||||
|
||||
```cpp
|
||||
// creates a new context variable by type and returns it
|
||||
@@ -947,9 +1012,9 @@ registry.ctx().erase<my_type>();
|
||||
registry.ctx().erase<my_type>("my_variable"_hs);
|
||||
```
|
||||
|
||||
Context variable must be both default constructible and movable. If the supplied
|
||||
type doesn't match that of the variable when using a _name_, the operation
|
||||
fails.<br/>
|
||||
A context variable must be both default constructible and movable. If the
|
||||
supplied type doesn't match that of the variable when using a _name_, the
|
||||
operation fails.<br/>
|
||||
For all users who want to use the context but don't want to create elements, the
|
||||
`contains` and `find` functions are also available:
|
||||
|
||||
@@ -1320,16 +1385,6 @@ fact, entities are subject to different rules with respect to components
|
||||
marked as _ready for reuse_. To iterate all the entities it's necessary to
|
||||
iterate the underlying sparse set instead.
|
||||
|
||||
Moreover, the entity storage offers a couple of additional utilities such as:
|
||||
|
||||
* The `in_use` function which is used to know how many entities are still
|
||||
_in use_. When combined with `size`, it also makes it possible to know how
|
||||
many entities are available for recycling.
|
||||
|
||||
* The `pack` function which is used to make a given set of entities contiguous.
|
||||
This is particularly useful to pass valid lists of entities via iterators
|
||||
(with access usually optimized within the library).
|
||||
|
||||
This kind of storage is designed to be used where any other storage is fine and
|
||||
can therefore be combined with views, groups and so on.
|
||||
|
||||
@@ -1486,11 +1541,11 @@ the mixins. The latter can then make use of any information, which is set via
|
||||
`bind`:
|
||||
|
||||
```cpp
|
||||
base.bind(entt::forward_as_any(registry));
|
||||
base.bind(registry);
|
||||
```
|
||||
|
||||
The `bind` function accepts an `entt::any` object, that is a _typed type-erased_
|
||||
value.<br/>
|
||||
The `bind` function accepts any element by reference or by value and forwards it
|
||||
to derived classes.<br/>
|
||||
This is how a registry _passes_ itself to all pools that support signals and
|
||||
also why a storage keeps sending events without requiring the registry to be
|
||||
passed to it every time.
|
||||
@@ -1510,7 +1565,7 @@ pointer and behaves differently depending on the case:
|
||||
* When the pointer is null, the function tries to default-construct an instance
|
||||
of the object to bind to the entity and returns true on success.
|
||||
|
||||
* When the pointer is non-null, the function tries to copy-construct an instance
|
||||
* When the pointer is not null, the function tries to copy-construct an instance
|
||||
of the object to bind to the entity and returns true on success.
|
||||
|
||||
This means that, starting from a reference to the base, it's possible to bind
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
# Frequently Asked Questions
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
@@ -10,14 +7,10 @@
|
||||
* [Why is my debug build on Windows so slow?](#why-is-my-debug-build-on-windows-so-slow)
|
||||
* [How can I represent hierarchies with my components?](#how-can-i-represent-hierarchies-with-my-components)
|
||||
* [Custom entity identifiers: yay or nay?](#custom-entity-identifiers-yay-or-nay)
|
||||
* [Warning C4307: integral constant overflow](#warning-C4307-integral-constant-overflow)
|
||||
* [Warning C4003: the min, the max and the macro](#warning-C4003-the-min-the-max-and-the-macro)
|
||||
* [Warning C4003: the min, the max and the macro](#warning-c4003-the-min-the-max-and-the-macro)
|
||||
* [The standard and the non-copyable types](#the-standard-and-the-non-copyable-types)
|
||||
* [Which functions trigger which signals](#which-functions-trigger-which-signals)
|
||||
* [Duplicate storage for the same component](#duplicate-storage-for-the-same-component)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
@@ -105,32 +98,6 @@ enum class entity: std::uint32_t {};
|
||||
|
||||
There is no limit to the number of identifiers that can be defined.
|
||||
|
||||
## Warning C4307: integral constant overflow
|
||||
|
||||
According to [this](https://github.com/skypjack/entt/issues/121) issue, using a
|
||||
hashed string under VS (toolset v141) could generate a warning.<br/>
|
||||
First of all, I want to reassure you: it's expected and harmless. However, it
|
||||
can be annoying.
|
||||
|
||||
To suppress it and if you don't want to suppress all the other warnings as well,
|
||||
here is a workaround in the form of a macro:
|
||||
|
||||
```cpp
|
||||
#if defined(_MSC_VER)
|
||||
#define HS(str) __pragma(warning(suppress:4307)) entt::hashed_string{str}
|
||||
#else
|
||||
#define HS(str) entt::hashed_string{str}
|
||||
#endif
|
||||
```
|
||||
|
||||
With an example of use included:
|
||||
|
||||
```cpp
|
||||
constexpr auto identifier = HS("my/resource/identifier");
|
||||
```
|
||||
|
||||
Thanks to [huwpascoe](https://github.com/huwpascoe) for the courtesy.
|
||||
|
||||
## Warning C4003: the min, the max and the macro
|
||||
|
||||
On Windows, a header file defines two macros `min` and `max` which may result in
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
# Crash Course: graph
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
@@ -14,9 +11,6 @@
|
||||
* [Fake resources and order of execution](#fake-resources-and-order-of-execution)
|
||||
* [Sync points](#sync-points)
|
||||
* [Execution graph](#execution-graph)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
# Push EnTT across boundaries
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Working across boundaries](#working-across-boundaries)
|
||||
* [Smooth until proven otherwise](#smooth-until-proven-otherwise)
|
||||
* [Meta context](#meta-context)
|
||||
* [Memory management](#memory-management)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Working across boundaries
|
||||
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
# EnTT in Action
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
@@ -11,9 +8,6 @@
|
||||
* [Engines and the like](#engines-and-the-like)
|
||||
* [Articles, videos and blog posts](#articles-videos-and-blog-posts)
|
||||
* [Any Other Business](#any-other-business)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
@@ -58,7 +52,7 @@ I hope the following lists can grow much more in the future.
|
||||
* Apparently [D&D Dark Alliance](https://darkalliance.wizards.com) by
|
||||
[Wizards of the Coast](https://company.wizards.com): your party, their
|
||||
funeral.
|
||||
* [TiltedOnline](https://github.com/tiltedphoques/TiltedOnline) by
|
||||
* [TiltedEvolution](https://github.com/tiltedphoques/TiltedEvolution) by
|
||||
[Tilted Phoques](https://github.com/tiltedphoques): Skyrim and Fallout 4 mod
|
||||
to play online.
|
||||
* [Antkeeper](https://github.com/antkeeper/antkeeper-source): an ant colony
|
||||
@@ -137,9 +131,20 @@ I hope the following lists can grow much more in the future.
|
||||
Interstellar Observation Network (a space shooter game).
|
||||
* [EnTT Boids](https://github.com/DanielEliasib/entt_boids): a simple boids
|
||||
implementation using `EnTT` and `Raylib`.
|
||||
* [PalmRide: After Flight](https://store.steampowered.com/app/2812540/PalmRide_After_Flight/):
|
||||
an on-rails shooter with retro outrun aesthetics.
|
||||
* [Exhibition of Speed](https://store.steampowered.com/app/2947450/Exhibition_of_Speed/):
|
||||
build your own car and go racing.
|
||||
* [Lichgate](https://buas.itch.io/lichgate): top-down action rogue-like where
|
||||
users unlock abilities to fight horde of enemies in an endless world.
|
||||
* [Letalka](https://github.com/dviglo2d-learn/mini_games/tree/main/letalka):
|
||||
small demo game with ships and bullets flying everywhere on the screen.
|
||||
|
||||
## Engines and the like:
|
||||
|
||||
* [Hazel Engine](https://github.com/TheCherno/Hazel): a work in progress
|
||||
engine created by [The Cherno](https://github.com/TheCherno/Hazel) during
|
||||
one of his most famous video series.
|
||||
* [Aether Engine](https://hadean.com/spatial-simulation/)
|
||||
[v1.1+](https://docs.hadean.com/v1.1/Licenses/) by
|
||||
[Hadean](https://hadean.com/): a library designed for spatially partitioning
|
||||
@@ -221,6 +226,14 @@ I hope the following lists can grow much more in the future.
|
||||
libary that combines its built-in reflection system with `ImGui`.
|
||||
* [Era Game Engine](https://github.com/EldarMuradov/EraGameEngine): a modern
|
||||
ECS-based game engine.
|
||||
* [Core SDK of Trollworks engine](https://github.com/trollworks/sdk-core): 2D
|
||||
game engine based on procrastination.
|
||||
* [Rocky](https://github.com/pelicanmapping/rocky): 3D geospatial application
|
||||
engine.
|
||||
* [Donner](https://github.com/jwmcglynn/donner): a modern C++20 SVG2 rendering
|
||||
API with CSS3.
|
||||
* [Coral Engine](https://github.com/GuusKemperman/CoralEngine): open-source
|
||||
student engine with the tools to make games in C++ and Visual scripting.
|
||||
|
||||
## Articles, videos and blog posts:
|
||||
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
# Crash Course: service locator
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Service locator](#service-locator)
|
||||
* [Opaque handles](#opaque-handles)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
|
||||
109
docs/md/meta.md
109
docs/md/meta.md
@@ -1,8 +1,5 @@
|
||||
# Crash Course: runtime reflection system
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
@@ -18,12 +15,11 @@
|
||||
* [From void to any](#from-void-to-any)
|
||||
* [Policies: the more, the less](#policies-the-more-the-less)
|
||||
* [Named constants and enums](#named-constants-and-enums)
|
||||
* [Properties and meta objects](#properties-and-meta-objects)
|
||||
* [User defined data](#user-defined-data)
|
||||
* [Traits](#traits)
|
||||
* [Custom data](#custom-data)
|
||||
* [Unregister types](#unregister-types)
|
||||
* [Meta context](#meta-context)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
@@ -841,48 +837,91 @@ auto max = entt::resolve<int>().data("max_int"_hs).get({}).cast<int>();
|
||||
All this happens behind the scenes without any allocation because of the small
|
||||
object optimization performed by the `meta_any` class.
|
||||
|
||||
## Properties and meta objects
|
||||
## User defined data
|
||||
|
||||
Sometimes (for example, when it comes to creating an editor) it might be useful
|
||||
to attach properties to the meta objects created. Fortunately, this is possible
|
||||
for most of them:
|
||||
to attach _traits_ or arbitrary _custom data_ to the meta objects created.
|
||||
|
||||
The main difference between them is that:
|
||||
|
||||
* Traits are simple user-defined flags with much higher access performance. The
|
||||
library reserves up to 16 bits for traits, that is 16 flags for a bitmask or
|
||||
2^16 values otherwise.
|
||||
|
||||
* Custom data are stored in a generic quick access area reserved for the user
|
||||
and which the library will never use under any circumstances.
|
||||
|
||||
In all cases, this support is currently available only for meta types, meta data
|
||||
and meta functions.
|
||||
|
||||
### Traits
|
||||
|
||||
User-defined traits are set via a meta factory:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>().type("reflected_type"_hs).prop("tooltip"_hs, "message");
|
||||
entt::meta<my_type>().traits(my_traits::required | my_traits::hidden);
|
||||
```
|
||||
|
||||
Properties are always in the key/value form. The key is a numeric identifier,
|
||||
mostly similar to the identifier used to register meta objects. There are no
|
||||
restrictions on the type of the value instead, as long as it's movable.<br/>
|
||||
Key only properties are also supported out of the box:
|
||||
In the example above, `EnTT` bitmask enum support is used but any integral value
|
||||
is fine, as long as it 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:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>().type("reflected_type"_hs).prop(my_enum::key_only);
|
||||
entt::meta<my_type>()
|
||||
.data<&my_type::data_member, entt::as_ref_t>("member"_hs)
|
||||
.traits(my_traits::internal);
|
||||
```
|
||||
|
||||
To attach multiple properties to a meta object, just invoke `prop` more than
|
||||
once.<br/>
|
||||
It's also possible to call `prop` at different times, as long as the factory is
|
||||
reset to the meta object of interest.
|
||||
|
||||
The meta objects for which properties are supported are currently meta types,
|
||||
meta data and meta functions.<br/>
|
||||
These types also offer a couple of member functions named `prop` to iterate all
|
||||
properties at once or to search a specific property by key:
|
||||
Once created, all meta objects offer a member function named `traits` to get the
|
||||
currently set value:
|
||||
|
||||
```cpp
|
||||
// iterate all properties of a meta type
|
||||
for(auto &&[id, prop]: entt::resolve<my_type>().prop()) {
|
||||
// ...
|
||||
}
|
||||
|
||||
// search for a given property by name
|
||||
auto prop = entt::resolve<my_type>().prop("tooltip"_hs);
|
||||
auto value = entt::resolve<my_type>().traits<my_traits>();
|
||||
```
|
||||
|
||||
Meta properties are objects having a fairly poor interface, all in all. They
|
||||
only provide the `value` member function to retrieve the contained value in the
|
||||
form of a `meta_any` object.
|
||||
Note that the type is erased upon registration and must therefore be repeated
|
||||
when traits are _extracted_, so as to allow the library to _reconstruct_ them
|
||||
correctly.
|
||||
|
||||
### Custom data
|
||||
|
||||
Custom arbitrary data are set via a meta factory:
|
||||
|
||||
```cpp
|
||||
entt::meta<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
|
||||
calls to the `custom` function overwrite previous values. However, this value
|
||||
can be read from meta objects and used to update existing data with a factory,
|
||||
effectively updating them as needed.<br/>
|
||||
Likewise, users can also set custom data on meta objects later if needed, as
|
||||
long as the factory is reset to the meta object of interest:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>()
|
||||
.func<&my_type::member_function>("member"_hs)
|
||||
.custom<function_data>("tooltip");
|
||||
```
|
||||
|
||||
Once created, all meta objects offer a member function named `custom` to get the
|
||||
currently set value as a reference or as a pointer to an element:
|
||||
|
||||
```cpp
|
||||
const type_data &value = entt::resolve<my_type>().custom();
|
||||
```
|
||||
|
||||
Note that the returned object performs an extra check in debug before converting
|
||||
to the requested type, so as to avoid subtle bugs.<br/>
|
||||
Only in the case of conversion to a pointer is this check safe and such that a
|
||||
null pointer is returned to inform the user of the failed attempt.
|
||||
|
||||
## Unregister types
|
||||
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
# Crash Course: poly
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
@@ -14,9 +11,6 @@
|
||||
* [Inheritance](#inheritance)
|
||||
* [Static polymorphism in the wild](#static-polymorphism-in-the-wild)
|
||||
* [Storage size and alignment requirement](#storage-size-and-alignment-requirement)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
@@ -236,8 +230,8 @@ For a deduced concept, inheritance is achieved in a few steps:
|
||||
```cpp
|
||||
struct DrawableAndErasable: entt::type_list<> {
|
||||
template<typename Base>
|
||||
struct type: typename Drawable::template type<Base> {
|
||||
static constexpr auto base = std::tuple_size_v<typename entt::poly_vtable<Drawable>::type>;
|
||||
struct type: typename Drawable::type<Base> {
|
||||
static constexpr auto base = Drawable::impl<Drawable::type<entt::poly_inspector>>::size;
|
||||
void erase() { entt::poly_call<base + 0>(*this); }
|
||||
};
|
||||
|
||||
@@ -273,8 +267,7 @@ a `decltype` as it follows:
|
||||
```cpp
|
||||
struct DrawableAndErasable: entt::type_list_cat_t<
|
||||
decltype(as_type_list(std::declval<Drawable>())),
|
||||
entt::type_list<void()>
|
||||
> {
|
||||
entt::type_list<void()>> {
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
# Crash Course: cooperative scheduler
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [The process](#the-process)
|
||||
* [Adaptor](#adaptor)
|
||||
* [The scheduler](#the-scheduler)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
# Similar projects
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Similar projects](#similar-projects)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
@@ -55,8 +49,12 @@ 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.
|
||||
* [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.
|
||||
|
||||
@@ -79,7 +77,6 @@ details.
|
||||
entity registry for ECS designs inspired by `EnTT`.
|
||||
|
||||
* Rust:
|
||||
* [Legion](https://github.com/TomGillen/legion): a chunk based archetype ECS.
|
||||
* [Shipyard](https://github.com/leudz/shipyard): it borrows some ideas from
|
||||
`EnTT` and offers a sparse sets based ECS with grouping functionalities.
|
||||
* [Sparsey](https://github.com/LechintanTudor/sparsey): sparse set based ECS
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
# Crash Course: resource management
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [The resource, the loader and the cache](#the-resource-the-loader-and-the-cache)
|
||||
* [Resource handle](#resource-handle)
|
||||
* [Loaders](#loader)
|
||||
* [The cache class](#the-cache)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
* [Loaders](#loaders)
|
||||
* [The cache class](#the-cache-class)
|
||||
|
||||
# Introduction
|
||||
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
# Crash Course: events, signals and everything in between
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
@@ -14,9 +11,6 @@
|
||||
* [Event dispatcher](#event-dispatcher)
|
||||
* [Named queues](#named-queues)
|
||||
* [Event emitter](#event-emitter)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
# EnTT and Unreal Engine
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Enable Cpp17](#enable-cpp17)
|
||||
* [EnTT as a third party module](#entt-as-a-third-party-module)
|
||||
* [Include EnTT](#include-entt)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
## Enable Cpp17
|
||||
|
||||
|
||||
13
entt.imp
13
entt.imp
@@ -5,7 +5,9 @@
|
||||
# forward files
|
||||
{ "include": [ "@[\"<].*/container/fwd\\.hpp[\">]", "private", "<entt/container/dense_map.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/container/fwd\\.hpp[\">]", "private", "<entt/container/dense_set.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/container/fwd\\.hpp[\">]", "private", "<entt/container/table.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/any.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/compressed_pair.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/family.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/hashed_string.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/ident.hpp>", "public" ] },
|
||||
@@ -17,8 +19,10 @@
|
||||
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/group.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/handle.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/helper.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/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" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/runtime_view.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/snapshot.hpp>", "public" ] },
|
||||
@@ -38,5 +42,12 @@
|
||||
{ "include": [ "@[\"<].*/signal/fwd\\.hpp[\">]", "private", "<entt/signal/delegate.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/signal/fwd\\.hpp[\">]", "private", "<entt/signal/dispatcher.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/signal/fwd\\.hpp[\">]", "private", "<entt/signal/emitter.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/signal/fwd\\.hpp[\">]", "private", "<entt/signal/sigh.hpp>", "public" ] }
|
||||
{ "include": [ "@[\"<].*/signal/fwd\\.hpp[\">]", "private", "<entt/signal/sigh.hpp>", "public" ] },
|
||||
# symbols
|
||||
{ symbol: [ "std::allocator", private, "<entt/container/fwd.hpp>", public ] },
|
||||
{ symbol: [ "std::allocator", private, "<entt/entity/fwd.hpp>", public ] },
|
||||
{ symbol: [ "std::allocator", private, "<entt/graph/fwd.hpp>", public ] },
|
||||
{ symbol: [ "std::allocator", private, "<entt/process/fwd.hpp>", public ] },
|
||||
{ symbol: [ "std::allocator", private, "<entt/resource/fwd.hpp>", public ] },
|
||||
{ symbol: [ "std::allocator", private, "<entt/signal/fwd.hpp>", public ] }
|
||||
]
|
||||
|
||||
@@ -30,4 +30,10 @@
|
||||
</IndexListItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_table<*>">
|
||||
<DisplayString>{ payload }</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem>payload</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
</AutoVisualizer>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<Intrinsic Name="first" Optional="true" Expression="*(first_base::base_type*)this"/>
|
||||
<Intrinsic Name="second" Optional="true" Expression="((second_base*)this)->value"/>
|
||||
<Intrinsic Name="second" Optional="true" Expression="*(second_base::base_type*)this"/>
|
||||
<DisplayString >({ first() }, { second() })</DisplayString>
|
||||
<DisplayString>({ first() }, { second() })</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[first]">first()</Item>
|
||||
<Item Name="[second]">second()</Item>
|
||||
|
||||
@@ -7,40 +7,55 @@
|
||||
<Synthetic Name="[pools]">
|
||||
<DisplayString>{ pools.size() }</DisplayString>
|
||||
<Expand>
|
||||
<IndexListItems ExcludeView="simple">
|
||||
<Size>pools.size()</Size>
|
||||
<ValueNode>*pools.packed.first_base::value[$i].element.second</ValueNode>
|
||||
</IndexListItems>
|
||||
<IndexListItems IncludeView="simple">
|
||||
<Size>pools.size()</Size>
|
||||
<ValueNode>*pools.packed.first_base::value[$i].element.second,view(simple)</ValueNode>
|
||||
</IndexListItems>
|
||||
<CustomListItems>
|
||||
<Variable Name="pos" InitialValue="0" />
|
||||
<Variable Name="last" InitialValue="pools.size()"/>
|
||||
<Loop>
|
||||
<Break Condition="pos == last"/>
|
||||
<Item Name="[{ pools.packed.first_base::value[pos].element.first }]">
|
||||
*pools.packed.first_base::value[pos].element.second,view(simple)
|
||||
</Item>
|
||||
<Exec>++pos</Exec>
|
||||
</Loop>
|
||||
</CustomListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
<Item Name="[groups]" ExcludeView="simple">groups.size()</Item>
|
||||
<Item Name="[groups]">groups.size()</Item>
|
||||
<Synthetic Name="[vars]">
|
||||
<DisplayString>{ vars.ctx.size() }</DisplayString>
|
||||
<Expand>
|
||||
<IndexListItems>
|
||||
<Size>vars.ctx.size()</Size>
|
||||
<ValueNode>vars.ctx.packed.first_base::value[$i].element.second</ValueNode>
|
||||
</IndexListItems>
|
||||
<CustomListItems>
|
||||
<Variable Name="pos" InitialValue="0" />
|
||||
<Variable Name="last" InitialValue="vars.ctx.size()"/>
|
||||
<Loop>
|
||||
<Break Condition="pos == last"/>
|
||||
<Item Name="[{ vars.ctx.packed.first_base::value[pos].element.first }]">
|
||||
vars.ctx.packed.first_base::value[pos].element.second
|
||||
</Item>
|
||||
<Exec>++pos</Exec>
|
||||
</Loop>
|
||||
</CustomListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_sparse_set<*>">
|
||||
<Intrinsic Name="cap" Expression="(traits_type::version_mask << traits_type::length)"/>
|
||||
<Intrinsic Name="is_valid_position" Expression="sparse[page] && ((*((traits_type::entity_type *)&sparse[page][offset]) & traits_type::entity_mask) != traits_type::entity_mask)">
|
||||
<Parameter Name="page" Type="traits_type::entity_type"/>
|
||||
<Parameter Name="offset" Type="traits_type::entity_type"/>
|
||||
</Intrinsic>
|
||||
<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>
|
||||
<Expand>
|
||||
<Item Name="[capacity]" ExcludeView="simple">packed.capacity()</Item>
|
||||
<Item Name="[policy]">mode,en</Item>
|
||||
<Item Name="[free_list]">head</Item>
|
||||
<Item Name="[policy]" ExcludeView="simple">mode,en</Item>
|
||||
<Item Name="[free_list]" ExcludeView="simple">head</Item>
|
||||
<Synthetic Name="[sparse]">
|
||||
<DisplayString>{ sparse.size() * traits_type::page_size }</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem IncludeView="simple">sparse,view(simple)</ExpandedItem>
|
||||
<CustomListItems ExcludeView="simple">
|
||||
<CustomListItems>
|
||||
<Variable Name="pos" InitialValue="0"/>
|
||||
<Variable Name="page" InitialValue="0"/>
|
||||
<Variable Name="offset" InitialValue="0"/>
|
||||
@@ -49,7 +64,7 @@
|
||||
<Break Condition="pos == last"/>
|
||||
<Exec>page = pos / traits_type::page_size</Exec>
|
||||
<Exec>offset = pos & (traits_type::page_size - 1)</Exec>
|
||||
<If Condition="sparse[page] && (*((traits_type::entity_type *)&sparse[page][offset]) < cap())">
|
||||
<If Condition="is_valid_position(page, offset)">
|
||||
<Item Name="[{ pos }]">*((traits_type::entity_type *)&sparse[page][offset]) & traits_type::entity_mask</Item>
|
||||
</If>
|
||||
<Exec>++pos</Exec>
|
||||
@@ -60,13 +75,12 @@
|
||||
<Synthetic Name="[packed]">
|
||||
<DisplayString>{ packed.size() }</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem IncludeView="simple">packed,view(simple)</ExpandedItem>
|
||||
<CustomListItems ExcludeView="simple">
|
||||
<CustomListItems>
|
||||
<Variable Name="pos" InitialValue="0"/>
|
||||
<Variable Name="last" InitialValue="packed.size()"/>
|
||||
<Loop>
|
||||
<Break Condition="pos == last"/>
|
||||
<If Condition="*((traits_type::entity_type *)&packed[pos]) < cap()">
|
||||
<If Condition="is_valid_entity(packed[pos])">
|
||||
<Item Name="[{ pos }]">packed[pos]</Item>
|
||||
</If>
|
||||
<Exec>++pos</Exec>
|
||||
@@ -77,20 +91,22 @@
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_storage<*>">
|
||||
<Intrinsic Name="cap" Expression="(base_type::traits_type::version_mask << base_type::traits_type::length)"/>
|
||||
<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>
|
||||
<Expand>
|
||||
<Item Name="[capacity]" Optional="true" ExcludeView="simple">payload.capacity() * traits_type::page_size</Item>
|
||||
<Item Name="[page size]" Optional="true" ExcludeView="simple">traits_type::page_size</Item>
|
||||
<Item Name="[placeholder]" Optional="true" ExcludeView="simple">placeholder</Item>
|
||||
<Item Name="[base]" ExcludeView="simple">(base_type*)this,nand</Item>
|
||||
<Item Name="[base]" IncludeView="simple">(base_type*)this,view(simple)nand</Item>
|
||||
<!-- having SFINAE-like techniques in natvis is priceless :) -->
|
||||
<CustomListItems Condition="payload.size() != 0" Optional="true">
|
||||
<Variable Name="pos" InitialValue="0" />
|
||||
<Variable Name="last" InitialValue="base_type::packed.size()"/>
|
||||
<Loop>
|
||||
<Break Condition="pos == last"/>
|
||||
<If Condition="*((base_type::traits_type::entity_type *)&base_type::packed[pos]) < cap()">
|
||||
<If Condition="is_valid_entity(base_type::packed[pos])">
|
||||
<Item Name="[{ pos }:{ base_type::packed[pos] }]">payload[pos / traits_type::page_size][pos & (traits_type::page_size - 1)]</Item>
|
||||
</If>
|
||||
<Exec>++pos</Exec>
|
||||
@@ -98,15 +114,28 @@
|
||||
</CustomListItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_view<*>">
|
||||
<DisplayString Condition="leading != nullptr">{{ size_hint={ leading->packed.size() } }}</DisplayString>
|
||||
<Type Name="entt::basic_common_view<*,*,*>">
|
||||
<DisplayString Condition="index != $T2">{{ size_hint={ pools[index]->packed.size() } }}</DisplayString>
|
||||
<DisplayString>{{ size_hint=0 }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[pools]" Optional="true">pools,na</Item>
|
||||
<Item Name="[filter]" Optional="true">filter,na</Item>
|
||||
<Item Name="[pools]">pools,na</Item>
|
||||
<Item Name="[filter]">filter,na</Item>
|
||||
<Item Name="[handle]" Condition="index != $T2">pools[index],na</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_storage_view<*>">
|
||||
<DisplayString Condition="leading != nullptr">{{ size={ leading->packed.size() } }}</DisplayString>
|
||||
<DisplayString>{{ size=0 }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[handle]" Condition="leading != nullptr">leading,na</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_view<*>">
|
||||
<DisplayString>{ *(base_type*)this }</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem>*(base_type*)this</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_runtime_view<*>">
|
||||
<DisplayString Condition="pools.size() != 0u">{{ size_hint={ pools[0]->packed.size() } }}</DisplayString>
|
||||
<DisplayString>{{ size_hint=0 }}</DisplayString>
|
||||
@@ -115,6 +144,34 @@
|
||||
<Item Name="[filter]">filter,na</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_handle<*>">
|
||||
<Intrinsic Name="pool_at" Expression="owner->pools.packed.first_base::value[index].element.second._Ptr">
|
||||
<Parameter Name="index" Type="unsigned int"/>
|
||||
</Intrinsic>
|
||||
<DisplayString>{{ entity={ entt } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[entity]">entt</Item>
|
||||
<Item Name="[registry]" Condition="owner != nullptr">owner,na</Item>
|
||||
<Synthetic Name="[components]" Condition="owner != nullptr">
|
||||
<Expand>
|
||||
<CustomListItems>
|
||||
<Variable Name="entity_mask" InitialValue="traits_type::entity_mask"/>
|
||||
<Variable Name="page" InitialValue="((*((traits_type::entity_type *)&entt)) & entity_mask) / traits_type::page_size"/>
|
||||
<Variable Name="offset" InitialValue="(*((traits_type::entity_type *)&entt)) & (traits_type::page_size - 1u)"/>
|
||||
<Variable Name="last" InitialValue="owner->pools.packed.first_base::value.size()"/>
|
||||
<Variable Name="pos" InitialValue="0u"/>
|
||||
<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>
|
||||
</If>
|
||||
<Exec>++pos</Exec>
|
||||
</Loop>
|
||||
</CustomListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::null_t">
|
||||
<DisplayString><null></DisplayString>
|
||||
</Type>
|
||||
|
||||
@@ -1,46 +1,98 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
<Type Name="entt::internal::meta_base_node">
|
||||
<DisplayString Condition="resolve != nullptr">{{ type={ type } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_conv_node">
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_ctor_node">
|
||||
<DisplayString>{{ arity={ arity } }}</DisplayString>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_data_node">
|
||||
<Intrinsic Name="has_property" Expression="!!(traits & property)">
|
||||
<Parameter Name="property" Type="int"/>
|
||||
</Intrinsic>
|
||||
<DisplayString>{{ arity={ arity } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[arity]">arity</Item>
|
||||
<Item Name="[is_const]">has_property(entt::internal::meta_traits::is_const)</Item>
|
||||
<Item Name="[is_static]">has_property(entt::internal::meta_traits::is_static)</Item>
|
||||
<Item Name="[prop]">prop</Item>
|
||||
<Item Name="[type]">type</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_func_node" >
|
||||
<Intrinsic Name="has_property" Expression="!!(traits & property)">
|
||||
<Type Name="entt::internal::meta_conv_node">
|
||||
<DisplayString Condition="conv != nullptr">{{ type={ type } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[type]">type</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_ctor_node">
|
||||
<DisplayString Condition="invoke != nullptr">{{ id={ id } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[id]">id</Item>
|
||||
<Item Name="[arity]">arity</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_custom_node">
|
||||
<DisplayString Condition="value != nullptr">{{ type={ type } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[type]">type</Item>
|
||||
<Item Name="[value]">value</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_data_node">
|
||||
<Intrinsic Name="has_trait" Expression="!!(traits & property)">
|
||||
<Parameter Name="property" Type="int"/>
|
||||
</Intrinsic>
|
||||
<DisplayString>{{ arity={ arity } }}</DisplayString>
|
||||
<DisplayString Condition="get != nullptr">{{ id={ id } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[is_const]">has_property(entt::internal::meta_traits::is_const)</Item>
|
||||
<Item Name="[is_static]">has_property(entt::internal::meta_traits::is_static)</Item>
|
||||
<Item Name="[id]">id</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>
|
||||
</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="invoke != nullptr">{{ id={ id } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[id]">id</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</Item>
|
||||
<Item Name="[prop]">prop,view(simple)</Item>
|
||||
<Item Name="[custom]">custom</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_prop_node">
|
||||
<DisplayString>{ value }</DisplayString>
|
||||
<DisplayString Condition="value != nullptr">{{ key={ id } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[key]">id</Item>
|
||||
<Item Name="[value]">value</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_template_node">
|
||||
<DisplayString>{{ arity={ arity } }}</DisplayString>
|
||||
<DisplayString Condition="arity != 0u">{{ arity={ arity } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[arity]">arity</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_type_descriptor">
|
||||
<DisplayString/>
|
||||
<Expand>
|
||||
<Item Name="[ctor]">ctor,view(simple)</Item>
|
||||
<Item Name="[base]">base,view(simple)</Item>
|
||||
<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_property" Expression="!!(traits & property)">
|
||||
<Intrinsic Name="has_trait" Expression="!!(traits & property)">
|
||||
<Parameter Name="property" Type="int"/>
|
||||
</Intrinsic>
|
||||
<DisplayString Condition="info != nullptr">{{ type={ info->alias,na } }}</DisplayString>
|
||||
@@ -48,20 +100,22 @@
|
||||
<Expand>
|
||||
<Item Name="[id]">id</Item>
|
||||
<Item Name="[sizeof]">size_of</Item>
|
||||
<Item Name="[is_arithmetic]">has_property(entt::internal::meta_traits::is_arithmetic)</Item>
|
||||
<Item Name="[is_integral]">has_property(entt::internal::meta_traits::is_integral)</Item>
|
||||
<Item Name="[is_signed]">has_property(entt::internal::meta_traits::is_signed)</Item>
|
||||
<Item Name="[is_array]">has_property(entt::internal::meta_traits::is_array)</Item>
|
||||
<Item Name="[is_enum]">has_property(entt::internal::meta_traits::is_enum)</Item>
|
||||
<Item Name="[is_class]">has_property(entt::internal::meta_traits::is_class)</Item>
|
||||
<Item Name="[is_meta_pointer_like]">has_property(entt::internal::meta_traits::is_meta_pointer_like)</Item>
|
||||
<Item Name="[is_meta_sequence_container]">has_property(entt::internal::meta_traits::is_meta_sequence_container)</Item>
|
||||
<Item Name="[is_meta_associative_container]">has_property(entt::internal::meta_traits::is_meta_associative_container)</Item>
|
||||
<Item Name="[is_arithmetic]">has_trait(entt::internal::meta_traits::is_arithmetic)</Item>
|
||||
<Item Name="[is_integral]">has_trait(entt::internal::meta_traits::is_integral)</Item>
|
||||
<Item Name="[is_signed]">has_trait(entt::internal::meta_traits::is_signed)</Item>
|
||||
<Item Name="[is_array]">has_trait(entt::internal::meta_traits::is_array)</Item>
|
||||
<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="[default_constructor]">default_constructor != nullptr</Item>
|
||||
<Item Name="[conversion_helper]">conversion_helper != nullptr</Item>
|
||||
<Item Name="[from_void]">from_void != nullptr</Item>
|
||||
<Item Name="[template_info]">templ</Item>
|
||||
<Item Name="[details]" Condition="details != nullptr">*details</Item>
|
||||
<Item Name="[custom]">custom</Item>
|
||||
<Item Name="[details]" Condition="!(details == nullptr)">*details</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_any">
|
||||
@@ -74,17 +128,26 @@
|
||||
</Type>
|
||||
<Type Name="entt::meta_handle">
|
||||
<DisplayString>{ any }</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem>any</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_associative_container">
|
||||
<DisplayString>{ storage }</DisplayString>
|
||||
<DisplayString Condition="data != nullptr">{{ const={ const_only } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
|
||||
<Item Name="[const]">const_only</Item>
|
||||
<Item Name="[data]">data</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_sequence_container">
|
||||
<DisplayString>{ storage }</DisplayString>
|
||||
<DisplayString Condition="data != nullptr">{{ const={ const_only } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
|
||||
<Item Name="[const]">const_only</Item>
|
||||
<Item Name="[data]">data</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_data">
|
||||
@@ -104,10 +167,10 @@
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_prop">
|
||||
<DisplayString Condition="node != nullptr">{ *node }</DisplayString>
|
||||
<DisplayString Condition="node.type != nullptr">{ node }</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
|
||||
<ExpandedItem Condition="node.type != nullptr">node</ExpandedItem>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
@@ -118,4 +181,21 @@
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_ctx">
|
||||
<Intrinsic Name="element_at" Expression="value.packed.first_base::value[pos].element">
|
||||
<Parameter Name="pos" Type="int"/>
|
||||
</Intrinsic>
|
||||
<DisplayString>{ value }</DisplayString>
|
||||
<Expand>
|
||||
<CustomListItems>
|
||||
<Variable Name="pos" InitialValue="0"/>
|
||||
<Variable Name="last" InitialValue="value.size()"/>
|
||||
<Loop>
|
||||
<Break Condition="pos == last"/>
|
||||
<Item Name="[{ element_at(pos).first }]">element_at(pos).second</Item>
|
||||
<Exec>++pos</Exec>
|
||||
</Loop>
|
||||
</CustomListItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
</AutoVisualizer>
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
</AutoVisualizer>
|
||||
@@ -7,9 +7,20 @@
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::resource_cache<*>">
|
||||
<DisplayString>{ pool.first_base::value }</DisplayString>
|
||||
<Intrinsic Name="size" Expression="pool.first_base::value.size()"/>
|
||||
<DisplayString>{{ size={ size() } }}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem>pool.first_base::value</ExpandedItem>
|
||||
<CustomListItems>
|
||||
<Variable Name="pos" InitialValue="0" />
|
||||
<Variable Name="last" InitialValue="size()"/>
|
||||
<Loop>
|
||||
<Break Condition="pos == last"/>
|
||||
<Item Name="[{ pool.first_base::value.packed.first_base::value[pos].element.first }]">
|
||||
*pool.first_base::value.packed.first_base::value[pos].element.second
|
||||
</Item>
|
||||
<Exec>++pos</Exec>
|
||||
</Loop>
|
||||
</CustomListItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
</AutoVisualizer>
|
||||
|
||||
@@ -11,15 +11,10 @@
|
||||
<Intrinsic Name="size" Expression="pools.first_base::value.size()"/>
|
||||
<DisplayString>{{ size={ size() } }}</DisplayString>
|
||||
<Expand>
|
||||
<Synthetic Name="[pools]">
|
||||
<DisplayString>{ size() }</DisplayString>
|
||||
<Expand>
|
||||
<IndexListItems>
|
||||
<Size>size()</Size>
|
||||
<ValueNode>*pools.first_base::value.packed.first_base::value[$i].element.second</ValueNode>
|
||||
</IndexListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
<IndexListItems>
|
||||
<Size>size()</Size>
|
||||
<ValueNode>*pools.first_base::value.packed.first_base::value[$i].element.second</ValueNode>
|
||||
</IndexListItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::internal::dispatcher_handler<*>">
|
||||
@@ -29,7 +24,7 @@
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::emitter<*>">
|
||||
<DisplayString>{{ size={ handlers.first_base::value.packed.first_base::value.size() } }}</DisplayString>
|
||||
<DisplayString>{{ size={ handlers.first_base::value.size() } }}</DisplayString>
|
||||
</Type>
|
||||
<Type Name="entt::connection">
|
||||
<DisplayString>{{ bound={ signal != nullptr } }}</DisplayString>
|
||||
|
||||
28
scripts/sync_bzlmod_version.sh
Normal file
28
scripts/sync_bzlmod_version.sh
Normal file
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
|
||||
VERSION_HEADER=$(realpath "$SCRIPT_DIR/../src/entt/config/version.h" --relative-to=$(pwd))
|
||||
BAZEL_MODULE=$(realpath "$SCRIPT_DIR/../MODULE.bazel" --relative-to=$(pwd))
|
||||
|
||||
if [[ -z "${VERSION_HEADER}" ]]; then
|
||||
echo "Cannot find version header"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Getting version from $VERSION_HEADER ..."
|
||||
|
||||
ENTT_MAJOR_VERSION=$(sed -nr 's/#define ENTT_VERSION_MAJOR ([0-9]+)/\1/p' $VERSION_HEADER)
|
||||
ENTT_MINOR_VERSION=$(sed -nr 's/#define ENTT_VERSION_MINOR ([0-9]+)/\1/p' $VERSION_HEADER)
|
||||
ENTT_PATCH_VERSION=$(sed -nr 's/#define ENTT_VERSION_PATCH ([0-9]+)/\1/p' $VERSION_HEADER)
|
||||
|
||||
VERSION="$ENTT_MAJOR_VERSION.$ENTT_MINOR_VERSION.$ENTT_PATCH_VERSION"
|
||||
|
||||
echo "Found $VERSION"
|
||||
|
||||
buildozer "set version $VERSION" //MODULE.bazel:%module
|
||||
|
||||
# a commit is needed for 'git archive'
|
||||
git add $BAZEL_MODULE
|
||||
git commit -m "chore: update MODULE.bazel version to $VERSION"
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -127,7 +127,7 @@
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -169,7 +169,7 @@
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -296,7 +296,7 @@
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -5248,7 +5248,7 @@ struct radix_sort {
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -10297,7 +10297,7 @@ private:
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -10419,7 +10419,7 @@ private:
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -18188,27 +18188,28 @@ class group_handler<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> fin
|
||||
using base_type = std::common_type_t<typename Owned::base_type..., typename Get::base_type..., typename Exclude::base_type...>;
|
||||
using entity_type = typename base_type::entity_type;
|
||||
|
||||
void swap_elements(const std::size_t pos, const entity_type entt) {
|
||||
std::apply([pos, entt](auto *...cpool) { (cpool->swap_elements(cpool->data()[pos], entt), ...); }, pools);
|
||||
template<std::size_t... Index>
|
||||
void swap_elements(const std::size_t pos, const entity_type entt, std::index_sequence<Index...>) {
|
||||
(std::get<Index>(pools)->swap_elements(std::get<Index>(pools)->data()[pos], entt), ...);
|
||||
}
|
||||
|
||||
void push_on_construct(const entity_type entt) {
|
||||
if(std::apply([entt, len = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < len) && (other->contains(entt) && ...); }, pools)
|
||||
&& std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) {
|
||||
swap_elements(len++, entt);
|
||||
swap_elements(len++, entt, std::index_sequence_for<Owned...>{});
|
||||
}
|
||||
}
|
||||
|
||||
void push_on_destroy(const entity_type entt) {
|
||||
if(std::apply([entt, len = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < len) && (other->contains(entt) && ...); }, pools)
|
||||
&& std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) {
|
||||
swap_elements(len++, entt);
|
||||
swap_elements(len++, entt, std::index_sequence_for<Owned...>{});
|
||||
}
|
||||
}
|
||||
|
||||
void remove_if(const entity_type entt) {
|
||||
if(std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < len)) {
|
||||
swap_elements(--len, entt);
|
||||
swap_elements(--len, entt, std::index_sequence_for<Owned...>{});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18242,13 +18243,11 @@ public:
|
||||
return len;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
Type pools_as() const noexcept {
|
||||
auto pools_as_tuple() const noexcept {
|
||||
return pools;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
Type filter_as() const noexcept {
|
||||
auto filter_as_tuple() const noexcept {
|
||||
return filter;
|
||||
}
|
||||
|
||||
@@ -18310,13 +18309,11 @@ public:
|
||||
return elem;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
Type pools_as() const noexcept {
|
||||
auto pools_as_tuple() const noexcept {
|
||||
return pools;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
Type filter_as() const noexcept {
|
||||
auto filter_as_tuple() const noexcept {
|
||||
return filter;
|
||||
}
|
||||
|
||||
@@ -18370,12 +18367,12 @@ class basic_group<owned_t<>, get_t<Get...>, exclude_t<Exclude...>> {
|
||||
|
||||
auto pools() const noexcept {
|
||||
using return_type = std::tuple<Get *...>;
|
||||
return descriptor ? descriptor->template pools_as<return_type>() : return_type{};
|
||||
return descriptor ? descriptor->pools_as_tuple() : return_type{};
|
||||
}
|
||||
|
||||
auto filter() const noexcept {
|
||||
using return_type = std::tuple<Exclude *...>;
|
||||
return descriptor ? descriptor->template filter_as<return_type>() : return_type{};
|
||||
return descriptor ? descriptor->filter_as_tuple() : return_type{};
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -18791,12 +18788,12 @@ class basic_group<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> {
|
||||
|
||||
auto pools() const noexcept {
|
||||
using return_type = std::tuple<Owned *..., Get *...>;
|
||||
return descriptor ? descriptor->template pools_as<return_type>() : return_type{};
|
||||
return descriptor ? descriptor->pools_as_tuple() : return_type{};
|
||||
}
|
||||
|
||||
auto filter() const noexcept {
|
||||
using return_type = std::tuple<Exclude *...>;
|
||||
return descriptor ? descriptor->template filter_as<return_type>() : return_type{};
|
||||
return descriptor ? descriptor->filter_as_tuple() : return_type{};
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -19569,7 +19566,7 @@ template<typename... Args, typename... Other>
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -19688,7 +19685,7 @@ template<typename... Args, typename... Other>
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -21214,27 +21211,28 @@ class group_handler<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> fin
|
||||
using base_type = std::common_type_t<typename Owned::base_type..., typename Get::base_type..., typename Exclude::base_type...>;
|
||||
using entity_type = typename base_type::entity_type;
|
||||
|
||||
void swap_elements(const std::size_t pos, const entity_type entt) {
|
||||
std::apply([pos, entt](auto *...cpool) { (cpool->swap_elements(cpool->data()[pos], entt), ...); }, pools);
|
||||
template<std::size_t... Index>
|
||||
void swap_elements(const std::size_t pos, const entity_type entt, std::index_sequence<Index...>) {
|
||||
(std::get<Index>(pools)->swap_elements(std::get<Index>(pools)->data()[pos], entt), ...);
|
||||
}
|
||||
|
||||
void push_on_construct(const entity_type entt) {
|
||||
if(std::apply([entt, len = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < len) && (other->contains(entt) && ...); }, pools)
|
||||
&& std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) {
|
||||
swap_elements(len++, entt);
|
||||
swap_elements(len++, entt, std::index_sequence_for<Owned...>{});
|
||||
}
|
||||
}
|
||||
|
||||
void push_on_destroy(const entity_type entt) {
|
||||
if(std::apply([entt, len = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < len) && (other->contains(entt) && ...); }, pools)
|
||||
&& std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) {
|
||||
swap_elements(len++, entt);
|
||||
swap_elements(len++, entt, std::index_sequence_for<Owned...>{});
|
||||
}
|
||||
}
|
||||
|
||||
void remove_if(const entity_type entt) {
|
||||
if(std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < len)) {
|
||||
swap_elements(--len, entt);
|
||||
swap_elements(--len, entt, std::index_sequence_for<Owned...>{});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21268,13 +21266,11 @@ public:
|
||||
return len;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
Type pools_as() const noexcept {
|
||||
auto pools_as_tuple() const noexcept {
|
||||
return pools;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
Type filter_as() const noexcept {
|
||||
auto filter_as_tuple() const noexcept {
|
||||
return filter;
|
||||
}
|
||||
|
||||
@@ -21336,13 +21332,11 @@ public:
|
||||
return elem;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
Type pools_as() const noexcept {
|
||||
auto pools_as_tuple() const noexcept {
|
||||
return pools;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
Type filter_as() const noexcept {
|
||||
auto filter_as_tuple() const noexcept {
|
||||
return filter;
|
||||
}
|
||||
|
||||
@@ -21396,12 +21390,12 @@ class basic_group<owned_t<>, get_t<Get...>, exclude_t<Exclude...>> {
|
||||
|
||||
auto pools() const noexcept {
|
||||
using return_type = std::tuple<Get *...>;
|
||||
return descriptor ? descriptor->template pools_as<return_type>() : return_type{};
|
||||
return descriptor ? descriptor->pools_as_tuple() : return_type{};
|
||||
}
|
||||
|
||||
auto filter() const noexcept {
|
||||
using return_type = std::tuple<Exclude *...>;
|
||||
return descriptor ? descriptor->template filter_as<return_type>() : return_type{};
|
||||
return descriptor ? descriptor->filter_as_tuple() : return_type{};
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -21817,12 +21811,12 @@ class basic_group<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> {
|
||||
|
||||
auto pools() const noexcept {
|
||||
using return_type = std::tuple<Owned *..., Get *...>;
|
||||
return descriptor ? descriptor->template pools_as<return_type>() : return_type{};
|
||||
return descriptor ? descriptor->pools_as_tuple() : return_type{};
|
||||
}
|
||||
|
||||
auto filter() const noexcept {
|
||||
using return_type = std::tuple<Exclude *...>;
|
||||
return descriptor ? descriptor->template filter_as<return_type>() : return_type{};
|
||||
return descriptor ? descriptor->filter_as_tuple() : return_type{};
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -24509,11 +24503,12 @@ public:
|
||||
*/
|
||||
template<typename It, typename... Args>
|
||||
void insert(It first, It last, Args &&...args) {
|
||||
auto from = underlying_type::size();
|
||||
underlying_type::insert(first, last, std::forward<Args>(args)...);
|
||||
|
||||
if(auto ® = owner_or_assert(); !construction.empty()) {
|
||||
for(; first != last; ++first) {
|
||||
construction.publish(reg, *first);
|
||||
for(const auto to = underlying_type::size(); from != to; ++from) {
|
||||
construction.publish(reg, underlying_type::operator[](from));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25126,7 +25121,7 @@ private:
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -25446,7 +25441,7 @@ private:
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -25952,7 +25947,7 @@ private:
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -26079,7 +26074,7 @@ private:
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -33480,7 +33475,7 @@ private:
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -33607,7 +33602,7 @@ private:
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -37658,11 +37653,12 @@ public:
|
||||
*/
|
||||
template<typename It, typename... Args>
|
||||
void insert(It first, It last, Args &&...args) {
|
||||
auto from = underlying_type::size();
|
||||
underlying_type::insert(first, last, std::forward<Args>(args)...);
|
||||
|
||||
if(auto ® = owner_or_assert(); !construction.empty()) {
|
||||
for(; first != last; ++first) {
|
||||
construction.publish(reg, *first);
|
||||
for(const auto to = underlying_type::size(); from != to; ++from) {
|
||||
construction.publish(reg, underlying_type::operator[](from));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39272,6 +39268,17 @@ public:
|
||||
for(auto first = storage->data(), last = first + storage->size(); first != last; ++first) {
|
||||
archive(*first);
|
||||
}
|
||||
} else if constexpr(component_traits<Type>::in_place_delete) {
|
||||
const typename registry_type::common_type &base = *storage;
|
||||
|
||||
for(auto it = base.rbegin(), last = base.rend(); it != last; ++it) {
|
||||
if(const auto entt = *it; entt == tombstone) {
|
||||
archive(static_cast<entity_type>(null));
|
||||
} else {
|
||||
archive(entt);
|
||||
std::apply([&archive](auto &&...args) { (archive(std::forward<decltype(args)>(args)), ...); }, storage->get_as_tuple(entt));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(auto elem: storage->reach()) {
|
||||
std::apply([&archive](auto &&...args) { (archive(std::forward<decltype(args)>(args)), ...); }, elem);
|
||||
@@ -39350,7 +39357,7 @@ public:
|
||||
basic_snapshot_loader(registry_type &source) noexcept
|
||||
: reg{&source} {
|
||||
// restoring a snapshot as a whole requires a clean registry
|
||||
ENTT_ASSERT(reg->template storage<entity_type>().empty() && (reg->storage().begin() == reg->storage().end()), "Registry must be empty");
|
||||
ENTT_ASSERT(reg->template storage<entity_type>().free_list() == 0u, "Registry must be empty");
|
||||
}
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
@@ -42964,7 +42971,7 @@ basic_view(std::tuple<Get &...>, std::tuple<Exclude &...> = {}) -> basic_view<ge
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -43284,7 +43291,7 @@ private:
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -43851,7 +43858,7 @@ void dot(std::ostream &out, const Graph &graph) {
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -43978,7 +43985,7 @@ void dot(std::ostream &out, const Graph &graph) {
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -50681,7 +50688,7 @@ private:
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -50998,7 +51005,7 @@ struct adl_meta_pointer_like {
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -51125,7 +51132,7 @@ struct adl_meta_pointer_like {
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -55838,7 +55845,7 @@ private:
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -56122,7 +56129,7 @@ class meta_ctx: private internal::meta_context {
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -59830,7 +59837,7 @@ struct std::tuple_element<Index, entt::value_list<Value...>>: entt::value_list_e
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -67805,7 +67812,7 @@ using invoke_result_t = typename std::invoke_result<Func, Args...>::type;
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -71981,7 +71988,7 @@ struct process_adaptor: process<process_adaptor<Func, Delta>, Delta>, private Fu
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -72108,7 +72115,7 @@ struct process_adaptor: process<process_adaptor<Func, Delta>, Delta>, private Fu
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -74120,7 +74127,7 @@ private:
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -74247,7 +74254,7 @@ private:
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -78034,7 +78041,7 @@ struct uses_allocator<entt::internal::dense_map_node<Key, Value>, Allocator>
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -80659,7 +80666,7 @@ template<typename Lhs, typename Rhs>
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -80778,7 +80785,7 @@ template<typename Lhs, typename Rhs>
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -82225,7 +82232,7 @@ delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate<Ret
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
@@ -82352,7 +82359,7 @@ delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate<Ret
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#include "version.h"
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
|
||||
|
||||
#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
|
||||
# define ENTT_CONSTEXPR
|
||||
# define ENTT_THROW throw
|
||||
@@ -42,7 +44,7 @@
|
||||
# define ENTT_ASSERT(condition, msg) (void(0))
|
||||
#elif !defined ENTT_ASSERT
|
||||
# include <cassert>
|
||||
# define ENTT_ASSERT(condition, msg) assert(condition)
|
||||
# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg)))
|
||||
#endif
|
||||
|
||||
#ifdef ENTT_DISABLE_ASSERT
|
||||
@@ -60,6 +62,12 @@
|
||||
# define ENTT_ETO_TYPE(Type) Type
|
||||
#endif
|
||||
|
||||
#ifdef ENTT_NO_MIXIN
|
||||
# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__
|
||||
#else
|
||||
# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__>
|
||||
#endif
|
||||
|
||||
#ifdef ENTT_STANDARD_CPP
|
||||
# define ENTT_NONSTD false
|
||||
#else
|
||||
@@ -82,4 +90,6 @@
|
||||
# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
|
||||
#endif
|
||||
|
||||
// NOLINTEND(cppcoreguidelines-macro-usage)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
#ifndef ENTT_CONFIG_MACRO_H
|
||||
#define ENTT_CONFIG_MACRO_H
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
|
||||
|
||||
#define ENTT_STR(arg) #arg
|
||||
#define ENTT_XSTR(arg) ENTT_STR(arg)
|
||||
|
||||
// NOLINTEND(cppcoreguidelines-macro-usage)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,12 +3,16 @@
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_MINOR 14
|
||||
#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)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../config/config.h"
|
||||
#include "../core/bit.hpp"
|
||||
#include "../core/compressed_pair.hpp"
|
||||
#include "../core/iterator.hpp"
|
||||
#include "../core/memory.hpp"
|
||||
@@ -122,7 +123,7 @@ public:
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr reference operator*() const noexcept {
|
||||
return {it->element.first, it->element.second};
|
||||
return operator[](0);
|
||||
}
|
||||
|
||||
template<typename Lhs, typename Rhs>
|
||||
@@ -267,6 +268,7 @@ class dense_map {
|
||||
|
||||
template<typename Other>
|
||||
[[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
|
||||
return fast_mod(static_cast<size_type>(sparse.second()(key)), bucket_count());
|
||||
}
|
||||
|
||||
@@ -325,7 +327,7 @@ class dense_map {
|
||||
|
||||
void move_and_pop(const std::size_t pos) {
|
||||
if(const auto last = size() - 1u; pos != last) {
|
||||
size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first);
|
||||
size_type *curr = &sparse.first()[key_to_bucket(packed.first().back().element.first)];
|
||||
packed.first()[pos] = std::move(packed.first().back());
|
||||
for(; *curr != last; curr = &packed.first()[*curr].next) {}
|
||||
*curr = pos;
|
||||
@@ -341,6 +343,8 @@ class dense_map {
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Key type of the container. */
|
||||
using key_type = Key;
|
||||
/*! @brief Mapped type of the container. */
|
||||
@@ -353,8 +357,6 @@ public:
|
||||
using hasher = Hash;
|
||||
/*! @brief Type of function to use to compare the keys for equality. */
|
||||
using key_equal = KeyEqual;
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Input iterator type. */
|
||||
using iterator = internal::dense_map_iterator<typename packed_container_type::iterator>;
|
||||
/*! @brief Constant input iterator type. */
|
||||
@@ -404,8 +406,7 @@ public:
|
||||
*/
|
||||
explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{})
|
||||
: sparse{allocator, hash},
|
||||
packed{allocator, equal},
|
||||
threshold{default_threshold} {
|
||||
packed{allocator, equal} {
|
||||
rehash(cnt);
|
||||
}
|
||||
|
||||
@@ -423,7 +424,7 @@ public:
|
||||
threshold{other.threshold} {}
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v<compressed_pair<sparse_container_type, hasher>> &&std::is_nothrow_move_constructible_v<compressed_pair<packed_container_type, key_equal>>) = default;
|
||||
dense_map(dense_map &&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
@@ -435,6 +436,9 @@ public:
|
||||
packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))},
|
||||
threshold{other.threshold} {}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~dense_map() = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator.
|
||||
* @return This container.
|
||||
@@ -445,7 +449,7 @@ public:
|
||||
* @brief Default move assignment operator.
|
||||
* @return This container.
|
||||
*/
|
||||
dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v<compressed_pair<sparse_container_type, hasher>> &&std::is_nothrow_move_assignable_v<compressed_pair<packed_container_type, key_equal>>) = default;
|
||||
dense_map &operator=(dense_map &&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Returns the associated allocator.
|
||||
@@ -677,7 +681,7 @@ public:
|
||||
* @return Number of elements removed (either 0 or 1).
|
||||
*/
|
||||
size_type erase(const key_type &key) {
|
||||
for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].next) {
|
||||
for(size_type *curr = &sparse.first()[key_to_bucket(key)]; *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].next) {
|
||||
if(packed.second()(packed.first()[*curr].element.first, key)) {
|
||||
const auto index = *curr;
|
||||
*curr = packed.first()[*curr].next;
|
||||
@@ -693,7 +697,7 @@ public:
|
||||
* @brief Exchanges the contents with those of a given container.
|
||||
* @param other Container to exchange the content with.
|
||||
*/
|
||||
void swap(dense_map &other) {
|
||||
void swap(dense_map &other) noexcept {
|
||||
using std::swap;
|
||||
swap(sparse, other.sparse);
|
||||
swap(packed, other.packed);
|
||||
@@ -1022,7 +1026,7 @@ public:
|
||||
private:
|
||||
compressed_pair<sparse_container_type, hasher> sparse;
|
||||
compressed_pair<packed_container_type, key_equal> packed;
|
||||
float threshold;
|
||||
float threshold{default_threshold};
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../config/config.h"
|
||||
#include "../core/bit.hpp"
|
||||
#include "../core/compressed_pair.hpp"
|
||||
#include "../core/memory.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
@@ -85,11 +85,11 @@ public:
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr pointer operator->() const noexcept {
|
||||
return std::addressof(it->second);
|
||||
return std::addressof(operator[](0));
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr reference operator*() const noexcept {
|
||||
return *operator->();
|
||||
return operator[](0);
|
||||
}
|
||||
|
||||
template<typename Lhs, typename Rhs>
|
||||
@@ -271,7 +271,7 @@ class dense_set {
|
||||
|
||||
void move_and_pop(const std::size_t pos) {
|
||||
if(const auto last = size() - 1u; pos != last) {
|
||||
size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second);
|
||||
size_type *curr = &sparse.first()[value_to_bucket(packed.first().back().second)];
|
||||
packed.first()[pos] = std::move(packed.first().back());
|
||||
for(; *curr != last; curr = &packed.first()[*curr].first) {}
|
||||
*curr = pos;
|
||||
@@ -287,6 +287,8 @@ class dense_set {
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Key type of the container. */
|
||||
using key_type = Type;
|
||||
/*! @brief Value type of the container. */
|
||||
@@ -297,8 +299,6 @@ public:
|
||||
using hasher = Hash;
|
||||
/*! @brief Type of function to use to compare the elements for equality. */
|
||||
using key_equal = KeyEqual;
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator = internal::dense_set_iterator<typename packed_container_type::iterator>;
|
||||
/*! @brief Constant random access iterator type. */
|
||||
@@ -352,8 +352,7 @@ public:
|
||||
*/
|
||||
explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{})
|
||||
: sparse{allocator, hash},
|
||||
packed{allocator, equal},
|
||||
threshold{default_threshold} {
|
||||
packed{allocator, equal} {
|
||||
rehash(cnt);
|
||||
}
|
||||
|
||||
@@ -371,7 +370,7 @@ public:
|
||||
threshold{other.threshold} {}
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
dense_set(dense_set &&) noexcept(std::is_nothrow_move_constructible_v<compressed_pair<sparse_container_type, hasher>> &&std::is_nothrow_move_constructible_v<compressed_pair<packed_container_type, key_equal>>) = default;
|
||||
dense_set(dense_set &&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
@@ -383,6 +382,9 @@ public:
|
||||
packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))},
|
||||
threshold{other.threshold} {}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~dense_set() = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator.
|
||||
* @return This container.
|
||||
@@ -393,7 +395,7 @@ public:
|
||||
* @brief Default move assignment operator.
|
||||
* @return This container.
|
||||
*/
|
||||
dense_set &operator=(dense_set &&) noexcept(std::is_nothrow_move_assignable_v<compressed_pair<sparse_container_type, hasher>> &&std::is_nothrow_move_assignable_v<compressed_pair<packed_container_type, key_equal>>) = default;
|
||||
dense_set &operator=(dense_set &&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Returns the associated allocator.
|
||||
@@ -609,7 +611,7 @@ public:
|
||||
* @return Number of elements removed (either 0 or 1).
|
||||
*/
|
||||
size_type erase(const value_type &value) {
|
||||
for(size_type *curr = sparse.first().data() + value_to_bucket(value); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].first) {
|
||||
for(size_type *curr = &sparse.first()[value_to_bucket(value)]; *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].first) {
|
||||
if(packed.second()(packed.first()[*curr].second, value)) {
|
||||
const auto index = *curr;
|
||||
*curr = packed.first()[*curr].first;
|
||||
@@ -625,7 +627,7 @@ public:
|
||||
* @brief Exchanges the contents with those of a given container.
|
||||
* @param other Container to exchange the content with.
|
||||
*/
|
||||
void swap(dense_set &other) {
|
||||
void swap(dense_set &other) noexcept {
|
||||
using std::swap;
|
||||
swap(sparse, other.sparse);
|
||||
swap(packed, other.packed);
|
||||
@@ -917,7 +919,7 @@ public:
|
||||
private:
|
||||
compressed_pair<sparse_container_type, hasher> sparse;
|
||||
compressed_pair<packed_container_type, key_equal> packed;
|
||||
float threshold;
|
||||
float threshold{default_threshold};
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace entt {
|
||||
|
||||
@@ -11,17 +12,27 @@ template<
|
||||
typename Key,
|
||||
typename Type,
|
||||
typename = std::hash<Key>,
|
||||
typename = std::equal_to<Key>,
|
||||
typename = std::equal_to<>,
|
||||
typename = std::allocator<std::pair<const Key, Type>>>
|
||||
class dense_map;
|
||||
|
||||
template<
|
||||
typename Type,
|
||||
typename = std::hash<Type>,
|
||||
typename = std::equal_to<Type>,
|
||||
typename = std::equal_to<>,
|
||||
typename = std::allocator<Type>>
|
||||
class dense_set;
|
||||
|
||||
template<typename...>
|
||||
class basic_table;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Type Element types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
using table = basic_table<std::vector<Type>...>;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
|
||||
460
src/entt/container/table.hpp
Normal file
460
src/entt/container/table.hpp
Normal file
@@ -0,0 +1,460 @@
|
||||
#ifndef ENTT_CONTAINER_TABLE_HPP
|
||||
#define ENTT_CONTAINER_TABLE_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/iterator.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename... It>
|
||||
class table_iterator {
|
||||
template<typename...>
|
||||
friend class table_iterator;
|
||||
|
||||
public:
|
||||
using value_type = decltype(std::forward_as_tuple(*std::declval<It>()...));
|
||||
using pointer = input_iterator_pointer<value_type>;
|
||||
using reference = value_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using iterator_concept = std::random_access_iterator_tag;
|
||||
|
||||
constexpr table_iterator() noexcept
|
||||
: it{} {}
|
||||
|
||||
constexpr table_iterator(It... from) noexcept
|
||||
: it{from...} {}
|
||||
|
||||
template<typename... Other, typename = std::enable_if_t<(std::is_constructible_v<It, Other> && ...)>>
|
||||
constexpr table_iterator(const table_iterator<Other...> &other) noexcept
|
||||
: table_iterator{std::get<Other>(other.it)...} {}
|
||||
|
||||
constexpr table_iterator &operator++() noexcept {
|
||||
return (++std::get<It>(it), ...), *this;
|
||||
}
|
||||
|
||||
constexpr table_iterator operator++(int) noexcept {
|
||||
table_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
constexpr table_iterator &operator--() noexcept {
|
||||
return (--std::get<It>(it), ...), *this;
|
||||
}
|
||||
|
||||
constexpr table_iterator operator--(int) noexcept {
|
||||
table_iterator orig = *this;
|
||||
return operator--(), orig;
|
||||
}
|
||||
|
||||
constexpr table_iterator &operator+=(const difference_type value) noexcept {
|
||||
return ((std::get<It>(it) += value), ...), *this;
|
||||
}
|
||||
|
||||
constexpr table_iterator operator+(const difference_type value) const noexcept {
|
||||
table_iterator copy = *this;
|
||||
return (copy += value);
|
||||
}
|
||||
|
||||
constexpr table_iterator &operator-=(const difference_type value) noexcept {
|
||||
return (*this += -value);
|
||||
}
|
||||
|
||||
constexpr table_iterator operator-(const difference_type value) const noexcept {
|
||||
return (*this + -value);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
|
||||
return std::forward_as_tuple(std::get<It>(it)[value]...);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr pointer operator->() const noexcept {
|
||||
return {operator[](0)};
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr reference operator*() const noexcept {
|
||||
return operator[](0);
|
||||
}
|
||||
|
||||
template<typename... Lhs, typename... Rhs>
|
||||
friend constexpr std::ptrdiff_t operator-(const table_iterator<Lhs...> &, const table_iterator<Rhs...> &) noexcept;
|
||||
|
||||
template<typename... Lhs, typename... Rhs>
|
||||
friend constexpr bool operator==(const table_iterator<Lhs...> &, const table_iterator<Rhs...> &) noexcept;
|
||||
|
||||
template<typename... Lhs, typename... Rhs>
|
||||
friend constexpr bool operator<(const table_iterator<Lhs...> &, const table_iterator<Rhs...> &) noexcept;
|
||||
|
||||
private:
|
||||
std::tuple<It...> it;
|
||||
};
|
||||
|
||||
template<typename... Lhs, typename... Rhs>
|
||||
[[nodiscard]] constexpr std::ptrdiff_t operator-(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
|
||||
return std::get<0>(lhs.it) - std::get<0>(rhs.it);
|
||||
}
|
||||
|
||||
template<typename... Lhs, typename... Rhs>
|
||||
[[nodiscard]] constexpr bool operator==(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
|
||||
return std::get<0>(lhs.it) == std::get<0>(rhs.it);
|
||||
}
|
||||
|
||||
template<typename... Lhs, typename... Rhs>
|
||||
[[nodiscard]] constexpr bool operator!=(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template<typename... Lhs, typename... Rhs>
|
||||
[[nodiscard]] constexpr bool operator<(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
|
||||
return std::get<0>(lhs.it) < std::get<0>(rhs.it);
|
||||
}
|
||||
|
||||
template<typename... Lhs, typename... Rhs>
|
||||
[[nodiscard]] constexpr bool operator>(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
|
||||
return rhs < lhs;
|
||||
}
|
||||
|
||||
template<typename... Lhs, typename... Rhs>
|
||||
[[nodiscard]] constexpr bool operator<=(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
|
||||
return !(lhs > rhs);
|
||||
}
|
||||
|
||||
template<typename... Lhs, typename... Rhs>
|
||||
[[nodiscard]] constexpr bool operator>=(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Basic table implementation.
|
||||
*
|
||||
* Internal data structures arrange elements to maximize performance. There are
|
||||
* no guarantees that objects are returned in the insertion order when iterate
|
||||
* a table. Do not make assumption on the order in any case.
|
||||
*
|
||||
* @tparam Container Sequence container row types.
|
||||
*/
|
||||
template<typename... Container>
|
||||
class basic_table {
|
||||
using container_type = std::tuple<Container...>;
|
||||
|
||||
public:
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Input iterator type. */
|
||||
using iterator = internal::table_iterator<typename Container::iterator...>;
|
||||
/*! @brief Constant input iterator type. */
|
||||
using const_iterator = internal::table_iterator<typename Container::const_iterator...>;
|
||||
/*! @brief Reverse iterator type. */
|
||||
using reverse_iterator = internal::table_iterator<typename Container::reverse_iterator...>;
|
||||
/*! @brief Constant reverse iterator type. */
|
||||
using const_reverse_iterator = internal::table_iterator<typename Container::const_reverse_iterator...>;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
basic_table()
|
||||
: payload{} {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy constructs the underlying containers.
|
||||
* @param container The containers to copy from.
|
||||
*/
|
||||
explicit basic_table(const Container &...container) noexcept
|
||||
: payload{container...} {
|
||||
ENTT_ASSERT((((std::get<Container>(payload).size() * sizeof...(Container)) == (std::get<Container>(payload).size() + ...)) && ...), "Unexpected container size");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move constructs the underlying containers.
|
||||
* @param container The containers to move from.
|
||||
*/
|
||||
explicit basic_table(Container &&...container) noexcept
|
||||
: payload{std::move(container)...} {
|
||||
ENTT_ASSERT((((std::get<Container>(payload).size() * sizeof...(Container)) == (std::get<Container>(payload).size() + ...)) && ...), "Unexpected container size");
|
||||
}
|
||||
|
||||
/*! @brief Default copy constructor, deleted on purpose. */
|
||||
basic_table(const basic_table &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
basic_table(basic_table &&other) noexcept
|
||||
: payload{std::move(other.payload)} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs the underlying containers using a given allocator.
|
||||
* @tparam Allocator Type of allocator.
|
||||
* @param allocator A valid allocator.
|
||||
*/
|
||||
template<typename Allocator>
|
||||
explicit basic_table(const Allocator &allocator)
|
||||
: payload{Container{allocator}...} {}
|
||||
|
||||
/**
|
||||
* @brief Copy constructs the underlying containers using a given allocator.
|
||||
* @tparam Allocator Type of allocator.
|
||||
* @param container The containers to copy from.
|
||||
* @param allocator A valid allocator.
|
||||
*/
|
||||
template<class Allocator>
|
||||
basic_table(const Container &...container, const Allocator &allocator) noexcept
|
||||
: payload{Container{container, allocator}...} {
|
||||
ENTT_ASSERT((((std::get<Container>(payload).size() * sizeof...(Container)) == (std::get<Container>(payload).size() + ...)) && ...), "Unexpected container size");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move constructs the underlying containers using a given allocator.
|
||||
* @tparam Allocator Type of allocator.
|
||||
* @param container The containers to move from.
|
||||
* @param allocator A valid allocator.
|
||||
*/
|
||||
template<class Allocator>
|
||||
basic_table(Container &&...container, const Allocator &allocator) noexcept
|
||||
: payload{Container{std::move(container), allocator}...} {
|
||||
ENTT_ASSERT((((std::get<Container>(payload).size() * sizeof...(Container)) == (std::get<Container>(payload).size() + ...)) && ...), "Unexpected container size");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
* @tparam Allocator Type of allocator.
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
template<class Allocator>
|
||||
basic_table(basic_table &&other, const Allocator &allocator)
|
||||
: payload{Container{std::move(std::get<Container>(other.payload)), allocator}...} {}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~basic_table() = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator, deleted on purpose.
|
||||
* @return This container.
|
||||
*/
|
||||
basic_table &operator=(const basic_table &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
* @param other The instance to move from.
|
||||
* @return This container.
|
||||
*/
|
||||
basic_table &operator=(basic_table &&other) noexcept {
|
||||
swap(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exchanges the contents with those of a given table.
|
||||
* @param other Table to exchange the content with.
|
||||
*/
|
||||
void swap(basic_table &other) noexcept {
|
||||
using std::swap;
|
||||
swap(payload, other.payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Increases the capacity of a table.
|
||||
*
|
||||
* If the new capacity is greater than the current capacity, new storage is
|
||||
* allocated, otherwise the method does nothing.
|
||||
*
|
||||
* @param cap Desired capacity.
|
||||
*/
|
||||
void reserve(const size_type cap) {
|
||||
(std::get<Container>(payload).reserve(cap), ...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of rows that a table has currently allocated
|
||||
* space for.
|
||||
* @return Capacity of the table.
|
||||
*/
|
||||
[[nodiscard]] size_type capacity() const noexcept {
|
||||
return std::get<0>(payload).capacity();
|
||||
}
|
||||
|
||||
/*! @brief Requests the removal of unused capacity. */
|
||||
void shrink_to_fit() {
|
||||
(std::get<Container>(payload).shrink_to_fit(), ...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of rows in a table.
|
||||
* @return Number of rows.
|
||||
*/
|
||||
[[nodiscard]] size_type size() const noexcept {
|
||||
return std::get<0>(payload).size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether a table is empty.
|
||||
* @return True if the table is empty, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool empty() const noexcept {
|
||||
return std::get<0>(payload).empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning.
|
||||
*
|
||||
* If the table is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @return An iterator to the first row of the table.
|
||||
*/
|
||||
[[nodiscard]] const_iterator cbegin() const noexcept {
|
||||
return {std::get<Container>(payload).cbegin()...};
|
||||
}
|
||||
|
||||
/*! @copydoc cbegin */
|
||||
[[nodiscard]] const_iterator begin() const noexcept {
|
||||
return cbegin();
|
||||
}
|
||||
|
||||
/*! @copydoc begin */
|
||||
[[nodiscard]] iterator begin() noexcept {
|
||||
return {std::get<Container>(payload).begin()...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the end.
|
||||
* @return An iterator to the element following the last row of the table.
|
||||
*/
|
||||
[[nodiscard]] const_iterator cend() const noexcept {
|
||||
return {std::get<Container>(payload).cend()...};
|
||||
}
|
||||
|
||||
/*! @copydoc cend */
|
||||
[[nodiscard]] const_iterator end() const noexcept {
|
||||
return cend();
|
||||
}
|
||||
|
||||
/*! @copydoc end */
|
||||
[[nodiscard]] iterator end() noexcept {
|
||||
return {std::get<Container>(payload).end()...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reverse iterator to the beginning.
|
||||
*
|
||||
* If the table is empty, the returned iterator will be equal to `rend()`.
|
||||
*
|
||||
* @return An iterator to the first row of the reversed table.
|
||||
*/
|
||||
[[nodiscard]] const_reverse_iterator crbegin() const noexcept {
|
||||
return {std::get<Container>(payload).crbegin()...};
|
||||
}
|
||||
|
||||
/*! @copydoc crbegin */
|
||||
[[nodiscard]] const_reverse_iterator rbegin() const noexcept {
|
||||
return crbegin();
|
||||
}
|
||||
|
||||
/*! @copydoc rbegin */
|
||||
[[nodiscard]] reverse_iterator rbegin() noexcept {
|
||||
return {std::get<Container>(payload).rbegin()...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reverse iterator to the end.
|
||||
* @return An iterator to the element following the last row of the reversed
|
||||
* table.
|
||||
*/
|
||||
[[nodiscard]] const_reverse_iterator crend() const noexcept {
|
||||
return {std::get<Container>(payload).crend()...};
|
||||
}
|
||||
|
||||
/*! @copydoc crend */
|
||||
[[nodiscard]] const_reverse_iterator rend() const noexcept {
|
||||
return crend();
|
||||
}
|
||||
|
||||
/*! @copydoc rend */
|
||||
[[nodiscard]] reverse_iterator rend() noexcept {
|
||||
return {std::get<Container>(payload).rend()...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Appends a row to the end of a table.
|
||||
* @tparam Args Types of arguments to use to construct the row data.
|
||||
* @param args Parameters to use to construct the row data.
|
||||
* @return A reference to the newly created row data.
|
||||
*/
|
||||
template<typename... Args>
|
||||
std::tuple<typename Container::value_type &...> emplace(Args &&...args) {
|
||||
if constexpr(sizeof...(Args) == 0u) {
|
||||
return std::forward_as_tuple(std::get<Container>(payload).emplace_back()...);
|
||||
} else {
|
||||
return std::forward_as_tuple(std::get<Container>(payload).emplace_back(std::forward<Args>(args))...);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes a row from a table.
|
||||
* @param pos An iterator to the row to remove.
|
||||
* @return An iterator following the removed row.
|
||||
*/
|
||||
iterator erase(const_iterator pos) {
|
||||
const auto diff = pos - begin();
|
||||
return {std::get<Container>(payload).erase(std::get<Container>(payload).begin() + diff)...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes a row from a table.
|
||||
* @param pos Index of the row to remove.
|
||||
*/
|
||||
void erase(const size_type pos) {
|
||||
ENTT_ASSERT(pos < size(), "Index out of bounds");
|
||||
erase(begin() + static_cast<typename const_iterator::difference_type>(pos));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the row data at specified location.
|
||||
* @param pos The row for which to return the data.
|
||||
* @return The row data at specified location.
|
||||
*/
|
||||
[[nodiscard]] std::tuple<const typename Container::value_type &...> operator[](const size_type pos) const {
|
||||
ENTT_ASSERT(pos < size(), "Index out of bounds");
|
||||
return std::forward_as_tuple(std::get<Container>(payload)[pos]...);
|
||||
}
|
||||
|
||||
/*! @copydoc operator[] */
|
||||
[[nodiscard]] std::tuple<typename Container::value_type &...> operator[](const size_type pos) {
|
||||
ENTT_ASSERT(pos < size(), "Index out of bounds");
|
||||
return std::forward_as_tuple(std::get<Container>(payload)[pos]...);
|
||||
}
|
||||
|
||||
/*! @brief Clears a table. */
|
||||
void clear() {
|
||||
(std::get<Container>(payload).clear(), ...);
|
||||
}
|
||||
|
||||
private:
|
||||
container_type payload;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace std {
|
||||
|
||||
template<typename... Container, typename Allocator>
|
||||
struct uses_allocator<entt::basic_table<Container...>, Allocator>
|
||||
: std::bool_constant<(std::uses_allocator_v<Container, Allocator> && ...)> {};
|
||||
|
||||
} // namespace std
|
||||
/*! @endcond */
|
||||
|
||||
#endif
|
||||
@@ -58,9 +58,11 @@ struct insertion_sort {
|
||||
auto value = std::move(*it);
|
||||
auto pre = it;
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
for(; pre > first && compare(value, *(pre - 1)); --pre) {
|
||||
*pre = std::move(*(pre - 1));
|
||||
}
|
||||
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
|
||||
*pre = std::move(value);
|
||||
}
|
||||
@@ -104,13 +106,16 @@ struct radix_sort {
|
||||
constexpr auto mask = (1 << Bit) - 1;
|
||||
constexpr auto buckets = 1 << Bit;
|
||||
|
||||
std::size_t index[buckets]{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
|
||||
std::size_t count[buckets]{};
|
||||
|
||||
for(auto it = from; it != to; ++it) {
|
||||
++count[(getter(*it) >> start) & mask];
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
|
||||
std::size_t index[buckets]{};
|
||||
|
||||
for(std::size_t pos{}, end = buckets - 1u; pos < end; ++pos) {
|
||||
index[pos + 1u] = index[pos] + count[pos];
|
||||
}
|
||||
|
||||
@@ -50,66 +50,67 @@ class basic_any {
|
||||
using vtable_type = const void *(const operation, const basic_any &, const void *);
|
||||
|
||||
struct storage_type {
|
||||
alignas(Align) std::byte data[Len + !Len];
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
|
||||
alignas(Align) std::byte data[Len + static_cast<std::size_t>(Len == 0u)];
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v<Type>;
|
||||
static constexpr bool in_situ = (Len != 0u) && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v<Type>;
|
||||
|
||||
template<typename Type>
|
||||
static const void *basic_vtable(const operation op, const basic_any &value, const void *other) {
|
||||
static_assert(!std::is_void_v<Type> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
|
||||
const Type *element = nullptr;
|
||||
const Type *elem = nullptr;
|
||||
|
||||
if constexpr(in_situ<Type>) {
|
||||
element = (value.mode == any_policy::owner) ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance);
|
||||
elem = (value.mode == any_policy::owner) ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance);
|
||||
} else {
|
||||
element = static_cast<const Type *>(value.instance);
|
||||
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>(*element);
|
||||
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 *>(element))};
|
||||
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:
|
||||
if constexpr(std::is_move_assignable_v<Type>) {
|
||||
*const_cast<Type *>(element) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
|
||||
*const_cast<Type *>(elem) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
|
||||
return other;
|
||||
}
|
||||
[[fallthrough]];
|
||||
case operation::assign:
|
||||
if constexpr(std::is_copy_assignable_v<Type>) {
|
||||
*const_cast<Type *>(element) = *static_cast<const Type *>(other);
|
||||
*const_cast<Type *>(elem) = *static_cast<const Type *>(other);
|
||||
return other;
|
||||
}
|
||||
break;
|
||||
case operation::destroy:
|
||||
if constexpr(in_situ<Type>) {
|
||||
element->~Type();
|
||||
elem->~Type();
|
||||
} else if constexpr(std::is_array_v<Type>) {
|
||||
delete[] element;
|
||||
delete[] elem;
|
||||
} else {
|
||||
delete element;
|
||||
delete elem;
|
||||
}
|
||||
break;
|
||||
case operation::compare:
|
||||
if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) {
|
||||
return *element == *static_cast<const Type *>(other) ? other : nullptr;
|
||||
return *elem == *static_cast<const Type *>(other) ? other : nullptr;
|
||||
} else {
|
||||
return (element == other) ? other : nullptr;
|
||||
return (elem == other) ? other : nullptr;
|
||||
}
|
||||
case operation::get:
|
||||
return element;
|
||||
return elem;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
@@ -117,26 +118,31 @@ class basic_any {
|
||||
|
||||
template<typename Type, typename... Args>
|
||||
void initialize([[maybe_unused]] Args &&...args) {
|
||||
info = &type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
|
||||
using plain_type = std::remove_cv_t<std::remove_reference_t<Type>>;
|
||||
info = &type_id<plain_type>();
|
||||
|
||||
if constexpr(!std::is_void_v<Type>) {
|
||||
vtable = basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>;
|
||||
vtable = basic_vtable<plain_type>;
|
||||
|
||||
if constexpr(std::is_lvalue_reference_v<Type>) {
|
||||
static_assert((std::is_lvalue_reference_v<Args> && ...) && (sizeof...(Args) == 1u), "Invalid arguments");
|
||||
mode = std::is_const_v<std::remove_reference_t<Type>> ? any_policy::cref : any_policy::ref;
|
||||
instance = (std::addressof(args), ...);
|
||||
} else if constexpr(in_situ<std::remove_cv_t<std::remove_reference_t<Type>>>) {
|
||||
if constexpr(std::is_aggregate_v<std::remove_cv_t<std::remove_reference_t<Type>>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>)) {
|
||||
new(&storage) std::remove_cv_t<std::remove_reference_t<Type>>{std::forward<Args>(args)...};
|
||||
} else 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 {
|
||||
new(&storage) std::remove_cv_t<std::remove_reference_t<Type>>(std::forward<Args>(args)...);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
|
||||
::new(&storage) plain_type(std::forward<Args>(args)...);
|
||||
}
|
||||
} else {
|
||||
if constexpr(std::is_aggregate_v<std::remove_cv_t<std::remove_reference_t<Type>>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>)) {
|
||||
instance = new std::remove_cv_t<std::remove_reference_t<Type>>{std::forward<Args>(args)...};
|
||||
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 std::remove_cv_t<std::remove_reference_t<Type>>(std::forward<Args>(args)...);
|
||||
instance = new plain_type(std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -220,10 +226,12 @@ public:
|
||||
* @return This any object.
|
||||
*/
|
||||
basic_any &operator=(const basic_any &other) {
|
||||
reset();
|
||||
if(this != &other) {
|
||||
reset();
|
||||
|
||||
if(other.vtable) {
|
||||
other.vtable(operation::copy, other, this);
|
||||
if(other.vtable) {
|
||||
other.vtable(operation::copy, other, this);
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
@@ -235,6 +243,8 @@ public:
|
||||
* @return This any object.
|
||||
*/
|
||||
basic_any &operator=(basic_any &&other) noexcept {
|
||||
ENTT_ASSERT(this != &other, "Self move assignment");
|
||||
|
||||
reset();
|
||||
|
||||
if(other.vtable) {
|
||||
@@ -253,9 +263,8 @@ public:
|
||||
* @param value An instance of an object to use to initialize the wrapper.
|
||||
* @return This any object.
|
||||
*/
|
||||
template<typename Type>
|
||||
std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>, basic_any &>
|
||||
operator=(Type &&value) {
|
||||
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
|
||||
basic_any &operator=(Type &&value) {
|
||||
emplace<std::decay_t<Type>>(std::forward<Type>(value));
|
||||
return *this;
|
||||
}
|
||||
@@ -332,9 +341,9 @@ public:
|
||||
if(vtable && mode != any_policy::cref && *info == *other.info) {
|
||||
if(auto *val = other.data(); val) {
|
||||
return (vtable(operation::transfer, *this, val) != nullptr);
|
||||
} else {
|
||||
return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr);
|
||||
}
|
||||
|
||||
return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -396,14 +405,6 @@ public:
|
||||
return basic_any{*this, any_policy::cref};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true if a wrapper owns its object, false otherwise.
|
||||
* @return True if the wrapper owns its object, false otherwise.
|
||||
*/
|
||||
[[deprecated("use policy() and any_policy instead")]] [[nodiscard]] bool owner() const noexcept {
|
||||
return (mode == any_policy::owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the current mode of an any object.
|
||||
* @return The current mode of the any object.
|
||||
@@ -431,7 +432,7 @@ private:
|
||||
* @return The element converted to the requested type.
|
||||
*/
|
||||
template<typename Type, std::size_t Len, std::size_t Align>
|
||||
[[nodiscard]] Type any_cast(const basic_any<Len, Align> &data) noexcept {
|
||||
[[nodiscard]] std::remove_const_t<Type> any_cast(const basic_any<Len, Align> &data) noexcept {
|
||||
const auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
|
||||
ENTT_ASSERT(instance, "Invalid instance");
|
||||
return static_cast<Type>(*instance);
|
||||
@@ -439,7 +440,7 @@ template<typename Type, std::size_t Len, std::size_t Align>
|
||||
|
||||
/*! @copydoc any_cast */
|
||||
template<typename Type, std::size_t Len, std::size_t Align>
|
||||
[[nodiscard]] Type any_cast(basic_any<Len, Align> &data) noexcept {
|
||||
[[nodiscard]] std::remove_const_t<Type> any_cast(basic_any<Len, Align> &data) noexcept {
|
||||
// forces const on non-reference types to make them work also with wrappers for const references
|
||||
auto *const instance = any_cast<std::remove_reference_t<const Type>>(&data);
|
||||
ENTT_ASSERT(instance, "Invalid instance");
|
||||
@@ -448,13 +449,13 @@ template<typename Type, std::size_t Len, std::size_t Align>
|
||||
|
||||
/*! @copydoc any_cast */
|
||||
template<typename Type, std::size_t Len, std::size_t Align>
|
||||
[[nodiscard]] Type any_cast(basic_any<Len, Align> &&data) noexcept {
|
||||
[[nodiscard]] std::remove_const_t<Type> any_cast(basic_any<Len, Align> &&data) noexcept {
|
||||
if constexpr(std::is_copy_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
|
||||
if(auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); instance) {
|
||||
return static_cast<Type>(std::move(*instance));
|
||||
} else {
|
||||
return any_cast<Type>(data);
|
||||
}
|
||||
|
||||
return any_cast<Type>(data);
|
||||
} else {
|
||||
auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
|
||||
ENTT_ASSERT(instance, "Invalid instance");
|
||||
|
||||
69
src/entt/core/bit.hpp
Normal file
69
src/entt/core/bit.hpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#ifndef ENTT_CORE_BIT_HPP
|
||||
#define ENTT_CORE_BIT_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @brief Returns the number of set bits in a value (waiting for C++20 and
|
||||
* `std::popcount`).
|
||||
* @tparam Type Unsigned integer type.
|
||||
* @param value A value of unsigned integer type.
|
||||
* @return The number of set bits in the value.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, int> popcount(const Type value) noexcept {
|
||||
return value ? (int(value & 1) + popcount(static_cast<Type>(value >> 1))) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether a value is a power of two or not (waiting for C++20 and
|
||||
* `std::has_single_bit`).
|
||||
* @tparam Type Unsigned integer type.
|
||||
* @param value A value of unsigned integer type.
|
||||
* @return True if the value is a power of two, false otherwise.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, bool> has_single_bit(const Type value) noexcept {
|
||||
return value && ((value & (value - 1)) == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Computes the smallest power of two greater than or equal to a value
|
||||
* (waiting for C++20 and `std::bit_ceil`).
|
||||
* @tparam Type Unsigned integer type.
|
||||
* @param value A value of unsigned integer type.
|
||||
* @return The smallest power of two greater than or equal to the given value.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, Type> next_power_of_two(const Type value) noexcept {
|
||||
ENTT_ASSERT_CONSTEXPR(value < (Type{1u} << (std::numeric_limits<Type>::digits - 1)), "Numeric limits exceeded");
|
||||
Type curr = value - (value != 0u);
|
||||
|
||||
for(int next = 1; next < std::numeric_limits<Type>::digits; next = next * 2) {
|
||||
curr |= (curr >> next);
|
||||
}
|
||||
|
||||
return ++curr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Fast module utility function (powers of two only).
|
||||
* @tparam Type Unsigned integer type.
|
||||
* @param value A value of unsigned integer type.
|
||||
* @param mod _Modulus_, it must be a power of two.
|
||||
* @return The common remainder.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, Type> fast_mod(const Type value, const std::size_t mod) noexcept {
|
||||
ENTT_ASSERT_CONSTEXPR(has_single_bit(mod), "Value must be a power of two");
|
||||
return value & (mod - 1u);
|
||||
}
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "fwd.hpp"
|
||||
#include "type_traits.hpp"
|
||||
|
||||
namespace entt {
|
||||
@@ -17,9 +18,9 @@ struct compressed_pair_element {
|
||||
using reference = Type &;
|
||||
using const_reference = const Type &;
|
||||
|
||||
template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>>
|
||||
constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<Type>)
|
||||
: value{} {}
|
||||
template<typename Dummy = Type, typename = std::enable_if_t<std::is_default_constructible_v<Dummy>>>
|
||||
// NOLINTNEXTLINE(modernize-use-equals-default)
|
||||
constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<Type>) {}
|
||||
|
||||
template<typename Arg, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Arg>>, compressed_pair_element>>>
|
||||
constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v<Type, Arg>)
|
||||
@@ -38,7 +39,7 @@ struct compressed_pair_element {
|
||||
}
|
||||
|
||||
private:
|
||||
Type value;
|
||||
Type value{};
|
||||
};
|
||||
|
||||
template<typename Type, std::size_t Tag>
|
||||
@@ -47,7 +48,7 @@ struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Ty
|
||||
using const_reference = const Type &;
|
||||
using base_type = Type;
|
||||
|
||||
template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>>
|
||||
template<typename Dummy = Type, typename = std::enable_if_t<std::is_default_constructible_v<Dummy>>>
|
||||
constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<base_type>)
|
||||
: base_type{} {}
|
||||
|
||||
@@ -102,7 +103,7 @@ public:
|
||||
* @tparam Dummy Dummy template parameter used for internal purposes.
|
||||
*/
|
||||
template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>>
|
||||
constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v<first_base> &&std::is_nothrow_default_constructible_v<second_base>)
|
||||
constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v<first_base> && std::is_nothrow_default_constructible_v<second_base>)
|
||||
: first_base{},
|
||||
second_base{} {}
|
||||
|
||||
@@ -110,13 +111,13 @@ public:
|
||||
* @brief Copy constructor.
|
||||
* @param other The instance to copy from.
|
||||
*/
|
||||
constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v<first_base> &&std::is_nothrow_copy_constructible_v<second_base>) = default;
|
||||
constexpr compressed_pair(const compressed_pair &other) = default;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v<first_base> &&std::is_nothrow_move_constructible_v<second_base>) = default;
|
||||
constexpr compressed_pair(compressed_pair &&other) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Constructs a pair from its values.
|
||||
@@ -126,7 +127,7 @@ public:
|
||||
* @param other Value to use to initialize the second element.
|
||||
*/
|
||||
template<typename Arg, typename Other>
|
||||
constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v<first_base, Arg> &&std::is_nothrow_constructible_v<second_base, Other>)
|
||||
constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v<first_base, Arg> && std::is_nothrow_constructible_v<second_base, Other>)
|
||||
: first_base{std::forward<Arg>(arg)},
|
||||
second_base{std::forward<Other>(other)} {}
|
||||
|
||||
@@ -138,23 +139,26 @@ public:
|
||||
* @param other Arguments to use to initialize the second element.
|
||||
*/
|
||||
template<typename... Args, typename... Other>
|
||||
constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other) noexcept(std::is_nothrow_constructible_v<first_base, Args...> &&std::is_nothrow_constructible_v<second_base, Other...>)
|
||||
constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other) noexcept(std::is_nothrow_constructible_v<first_base, Args...> && std::is_nothrow_constructible_v<second_base, Other...>)
|
||||
: first_base{std::move(args), std::index_sequence_for<Args...>{}},
|
||||
second_base{std::move(other), std::index_sequence_for<Other...>{}} {}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~compressed_pair() = default;
|
||||
|
||||
/**
|
||||
* @brief Copy assignment operator.
|
||||
* @param other The instance to copy from.
|
||||
* @return This compressed pair object.
|
||||
*/
|
||||
constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v<first_base> &&std::is_nothrow_copy_assignable_v<second_base>) = default;
|
||||
constexpr compressed_pair &operator=(const compressed_pair &other) = default;
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
* @param other The instance to move from.
|
||||
* @return This compressed pair object.
|
||||
*/
|
||||
constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v<first_base> &&std::is_nothrow_move_assignable_v<second_base>) = default;
|
||||
constexpr compressed_pair &operator=(compressed_pair &&other) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Returns the first element that a pair stores.
|
||||
@@ -186,7 +190,7 @@ public:
|
||||
* @brief Swaps two compressed pair objects.
|
||||
* @param other The compressed pair to swap with.
|
||||
*/
|
||||
constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v<first_type> &&std::is_nothrow_swappable_v<second_type>) {
|
||||
constexpr void swap(compressed_pair &other) noexcept {
|
||||
using std::swap;
|
||||
swap(first(), other.first());
|
||||
swap(second(), other.second());
|
||||
@@ -199,7 +203,7 @@ public:
|
||||
* reference to the second element if `Index` is 1.
|
||||
*/
|
||||
template<std::size_t Index>
|
||||
constexpr decltype(auto) get() noexcept {
|
||||
[[nodiscard]] constexpr decltype(auto) get() noexcept {
|
||||
if constexpr(Index == 0u) {
|
||||
return first();
|
||||
} else {
|
||||
@@ -210,7 +214,7 @@ public:
|
||||
|
||||
/*! @copydoc get */
|
||||
template<std::size_t Index>
|
||||
constexpr decltype(auto) get() const noexcept {
|
||||
[[nodiscard]] constexpr decltype(auto) get() const noexcept {
|
||||
if constexpr(Index == 0u) {
|
||||
return first();
|
||||
} else {
|
||||
@@ -242,8 +246,6 @@ inline constexpr void swap(compressed_pair<First, Second> &lhs, compressed_pair<
|
||||
|
||||
} // namespace entt
|
||||
|
||||
// disable structured binding support for clang 6, it messes when specializing tuple_size
|
||||
#if !defined __clang_major__ || __clang_major__ > 6
|
||||
namespace std {
|
||||
|
||||
/**
|
||||
@@ -266,6 +268,5 @@ struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<I
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace entt {
|
||||
*/
|
||||
template<typename...>
|
||||
class family {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
inline static ENTT_MAYBE_ATOMIC(id_type) identifier{};
|
||||
|
||||
public:
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
|
||||
template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(double[2])>
|
||||
class basic_any;
|
||||
|
||||
@@ -15,6 +16,21 @@ using id_type = ENTT_ID_TYPE;
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using any = basic_any<>;
|
||||
|
||||
template<typename, typename>
|
||||
class compressed_pair;
|
||||
|
||||
template<typename>
|
||||
class basic_hashed_string;
|
||||
|
||||
/*! @brief Aliases for common character types. */
|
||||
using hashed_string = basic_hashed_string<char>;
|
||||
|
||||
/*! @brief Aliases for common character types. */
|
||||
using hashed_wstring = basic_hashed_string<wchar_t>;
|
||||
|
||||
// NOLINTNEXTLINE(bugprone-forward-declaration-namespace)
|
||||
struct type_info;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
@@ -10,21 +11,19 @@ namespace entt {
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename>
|
||||
struct fnv1a_traits;
|
||||
template<typename = id_type>
|
||||
struct fnv_1a_params;
|
||||
|
||||
template<>
|
||||
struct fnv1a_traits<std::uint32_t> {
|
||||
using type = std::uint32_t;
|
||||
static constexpr std::uint32_t offset = 2166136261;
|
||||
static constexpr std::uint32_t prime = 16777619;
|
||||
struct fnv_1a_params<std::uint32_t> {
|
||||
static constexpr auto offset = 2166136261;
|
||||
static constexpr auto prime = 16777619;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct fnv1a_traits<std::uint64_t> {
|
||||
using type = std::uint64_t;
|
||||
static constexpr std::uint64_t offset = 14695981039346656037ull;
|
||||
static constexpr std::uint64_t prime = 1099511628211ull;
|
||||
struct fnv_1a_params<std::uint64_t> {
|
||||
static constexpr auto offset = 14695981039346656037ull;
|
||||
static constexpr auto prime = 1099511628211ull;
|
||||
};
|
||||
|
||||
template<typename Char>
|
||||
@@ -59,7 +58,7 @@ struct basic_hashed_string {
|
||||
template<typename Char>
|
||||
class basic_hashed_string: internal::basic_hashed_string<Char> {
|
||||
using base_type = internal::basic_hashed_string<Char>;
|
||||
using traits_type = internal::fnv1a_traits<id_type>;
|
||||
using params = internal::fnv_1a_params<>;
|
||||
|
||||
struct const_wrapper {
|
||||
// non-explicit constructor on purpose
|
||||
@@ -70,22 +69,11 @@ class basic_hashed_string: internal::basic_hashed_string<Char> {
|
||||
};
|
||||
|
||||
// Fowler–Noll–Vo hash function v. 1a - the good
|
||||
[[nodiscard]] static constexpr auto helper(const Char *str) noexcept {
|
||||
base_type base{str, 0u, traits_type::offset};
|
||||
[[nodiscard]] static constexpr auto helper(const std::basic_string_view<Char> view) noexcept {
|
||||
base_type base{view.data(), view.size(), params::offset};
|
||||
|
||||
for(; str[base.length]; ++base.length) {
|
||||
base.hash = (base.hash ^ static_cast<traits_type::type>(str[base.length])) * traits_type::prime;
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
// Fowler–Noll–Vo hash function v. 1a - the good
|
||||
[[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept {
|
||||
base_type base{str, len, traits_type::offset};
|
||||
|
||||
for(size_type pos{}; pos < len; ++pos) {
|
||||
base.hash = (base.hash ^ static_cast<traits_type::type>(str[pos])) * traits_type::prime;
|
||||
for(auto &&curr: view) {
|
||||
base.hash = (base.hash ^ static_cast<id_type>(curr)) * params::prime;
|
||||
}
|
||||
|
||||
return base;
|
||||
@@ -116,6 +104,7 @@ public:
|
||||
* @return The numeric representation of the string.
|
||||
*/
|
||||
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 {
|
||||
return basic_hashed_string{str};
|
||||
}
|
||||
@@ -131,7 +120,7 @@ public:
|
||||
|
||||
/*! @brief Constructs an empty hashed string. */
|
||||
constexpr basic_hashed_string() noexcept
|
||||
: base_type{} {}
|
||||
: basic_hashed_string{nullptr, 0u} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a hashed string from a string view.
|
||||
@@ -139,7 +128,7 @@ 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)} {}
|
||||
: base_type{helper({str, len})} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a hashed string from an array of const characters.
|
||||
@@ -147,8 +136,9 @@ public:
|
||||
* @param str Human-readable identifier.
|
||||
*/
|
||||
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(str)} {}
|
||||
: base_type{helper({static_cast<const value_type *>(str)})} {}
|
||||
|
||||
/**
|
||||
* @brief Explicit constructor on purpose to avoid constructing a hashed
|
||||
@@ -160,14 +150,14 @@ 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{helper({wrapper.repr})} {}
|
||||
|
||||
/**
|
||||
* @brief Returns the size a hashed string.
|
||||
* @return The size of the hashed string.
|
||||
*/
|
||||
[[nodiscard]] constexpr size_type size() const noexcept {
|
||||
return base_type::length; // NOLINT
|
||||
return base_type::length;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,7 +197,7 @@ public:
|
||||
* @param len Length of the string to hash.
|
||||
*/
|
||||
template<typename Char>
|
||||
basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string<Char>;
|
||||
basic_hashed_string(const Char *str, std::size_t len) -> basic_hashed_string<Char>;
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
@@ -216,6 +206,7 @@ basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_stri
|
||||
* @param str Human-readable identifier.
|
||||
*/
|
||||
template<typename Char, std::size_t N>
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
|
||||
basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string<Char>;
|
||||
|
||||
/**
|
||||
@@ -293,12 +284,6 @@ template<typename Char>
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
/*! @brief Aliases for common character types. */
|
||||
using hashed_string = basic_hashed_string<char>;
|
||||
|
||||
/*! @brief Aliases for common character types. */
|
||||
using hashed_wstring = basic_hashed_string<wchar_t>;
|
||||
|
||||
inline namespace literals {
|
||||
|
||||
/**
|
||||
|
||||
@@ -147,7 +147,7 @@ struct iterable_adaptor final {
|
||||
using sentinel = Sentinel;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v<iterator> &&std::is_nothrow_default_constructible_v<sentinel>)
|
||||
constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v<iterator> && std::is_nothrow_default_constructible_v<sentinel>)
|
||||
: first{},
|
||||
last{} {}
|
||||
|
||||
@@ -156,7 +156,7 @@ struct iterable_adaptor final {
|
||||
* @param from Begin iterator.
|
||||
* @param to End iterator.
|
||||
*/
|
||||
constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v<iterator> &&std::is_nothrow_move_constructible_v<sentinel>)
|
||||
constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v<iterator> && std::is_nothrow_move_constructible_v<sentinel>)
|
||||
: first{std::move(from)},
|
||||
last{std::move(to)} {}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#define ENTT_CORE_MEMORY_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
@@ -11,44 +10,6 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @brief Checks whether a value is a power of two or not (waiting for C++20 and
|
||||
* `std::has_single_bit`).
|
||||
* @param value A value that may or may not be a power of two.
|
||||
* @return True if the value is a power of two, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept {
|
||||
return value && ((value & (value - 1)) == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Computes the smallest power of two greater than or equal to a value
|
||||
* (waiting for C++20 and `std::bit_ceil`).
|
||||
* @param value The value to use.
|
||||
* @return The smallest power of two greater than or equal to the given value.
|
||||
*/
|
||||
[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept {
|
||||
ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded");
|
||||
std::size_t curr = value - (value != 0u);
|
||||
|
||||
for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) {
|
||||
curr |= curr >> next;
|
||||
}
|
||||
|
||||
return ++curr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Fast module utility function (powers of two only).
|
||||
* @param value A value for which to calculate the modulus.
|
||||
* @param mod _Modulus_, it must be a power of two.
|
||||
* @return The common remainder.
|
||||
*/
|
||||
[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept {
|
||||
ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two");
|
||||
return value & (mod - 1u);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20).
|
||||
* @tparam Type Pointer type.
|
||||
@@ -275,7 +236,7 @@ constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...ar
|
||||
*/
|
||||
template<typename Type, typename Allocator, typename... Args>
|
||||
constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) {
|
||||
return std::apply([value](auto &&...curr) { return new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
|
||||
return std::apply([value](auto &&...curr) { return ::new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -23,10 +23,12 @@ struct monostate {
|
||||
* @brief Assigns a value of a specific type to a given key.
|
||||
* @tparam Type Type of the value to assign.
|
||||
* @param val User data to assign to the given key.
|
||||
* @return This monostate object.
|
||||
*/
|
||||
template<typename Type>
|
||||
void operator=(Type val) const noexcept {
|
||||
monostate &operator=(Type val) noexcept {
|
||||
value<Type> = val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -41,6 +43,7 @@ struct monostate {
|
||||
|
||||
private:
|
||||
template<typename Type>
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
inline static ENTT_MAYBE_ATOMIC(Type) value{};
|
||||
};
|
||||
|
||||
@@ -49,7 +52,8 @@ private:
|
||||
* @tparam Value Value used to differentiate between different variables.
|
||||
*/
|
||||
template<id_type Value>
|
||||
inline monostate<Value> monostate_v = {};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
inline monostate<Value> monostate_v{};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
|
||||
20
src/entt/core/ranges.hpp
Normal file
20
src/entt/core/ranges.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef ENTT_CORE_RANGES_HPP
|
||||
#define ENTT_CORE_RANGES_HPP
|
||||
|
||||
#if __has_include(<version>)
|
||||
# include <version>
|
||||
#
|
||||
# if defined(__cpp_lib_ranges)
|
||||
# include <ranges>
|
||||
# include "iterator.hpp"
|
||||
|
||||
template<class... Args>
|
||||
inline constexpr bool std::ranges::enable_borrowed_range<entt::iterable_adaptor<Args...>>{true};
|
||||
|
||||
template<class... Args>
|
||||
inline constexpr bool std::ranges::enable_view<entt::iterable_adaptor<Args...>>{true};
|
||||
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -24,7 +24,7 @@ struct ENTT_API type_index final {
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr auto stripped_type_name() noexcept {
|
||||
#if defined ENTT_PRETTY_FUNCTION
|
||||
std::string_view pretty_function{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);
|
||||
return value;
|
||||
@@ -136,10 +136,12 @@ struct type_info final {
|
||||
* @tparam Type Type for which to construct a type info object.
|
||||
*/
|
||||
template<typename Type>
|
||||
// NOLINTBEGIN(modernize-use-transparent-functors)
|
||||
constexpr type_info(std::in_place_type_t<Type>) noexcept
|
||||
: seq{type_index<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
|
||||
identifier{type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
|
||||
alias{type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()} {}
|
||||
// NOLINTEND(modernize-use-transparent-functors)
|
||||
|
||||
/**
|
||||
* @brief Type index.
|
||||
@@ -197,7 +199,7 @@ private:
|
||||
* @param rhs A valid type info object.
|
||||
* @return True if the first element is less than the second, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept {
|
||||
[[nodiscard]] inline constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept {
|
||||
return lhs.index() < rhs.index();
|
||||
}
|
||||
|
||||
@@ -208,7 +210,7 @@ private:
|
||||
* @return True if the first element is less than or equal to the second, false
|
||||
* otherwise.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept {
|
||||
[[nodiscard]] inline constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept {
|
||||
return !(rhs < lhs);
|
||||
}
|
||||
|
||||
@@ -219,7 +221,7 @@ private:
|
||||
* @return True if the first element is greater than the second, false
|
||||
* otherwise.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept {
|
||||
[[nodiscard]] inline constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept {
|
||||
return rhs < lhs;
|
||||
}
|
||||
|
||||
@@ -230,7 +232,7 @@ private:
|
||||
* @return True if the first element is greater than or equal to the second,
|
||||
* false otherwise.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept {
|
||||
[[nodiscard]] inline constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept {
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
|
||||
@@ -574,7 +574,7 @@ inline constexpr bool value_list_contains_v = value_list_contains<List, Value>::
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename...>
|
||||
class value_list_diff;
|
||||
struct value_list_diff;
|
||||
|
||||
/**
|
||||
* @brief Computes the difference between two value lists.
|
||||
@@ -582,12 +582,9 @@ class value_list_diff;
|
||||
* @tparam Other Values provided by the second value list.
|
||||
*/
|
||||
template<auto... Value, auto... Other>
|
||||
class value_list_diff<value_list<Value...>, value_list<Other...>> {
|
||||
using v141_toolset_workaround = value_list<Other...>;
|
||||
|
||||
public:
|
||||
struct value_list_diff<value_list<Value...>, value_list<Other...>> {
|
||||
/*! @brief A value list that is the difference between the two value lists. */
|
||||
using type = value_list_cat_t<std::conditional_t<value_list_contains_v<v141_toolset_workaround, Value>, value_list<>, value_list<Value>>...>;
|
||||
using type = value_list_cat_t<std::conditional_t<value_list_contains_v<value_list<Other...>, Value>, value_list<>, value_list<Value>>...>;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -772,14 +769,11 @@ template<typename Type>
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr bool dispatch_is_equality_comparable() {
|
||||
// NOLINTBEGIN(modernize-use-transparent-functors)
|
||||
if constexpr(std::is_array_v<Type>) {
|
||||
return false;
|
||||
} else if constexpr(is_iterator_v<Type>) {
|
||||
return maybe_equality_comparable<Type>(0);
|
||||
} else if constexpr(has_value_type<Type>::value) {
|
||||
if constexpr(std::is_same_v<typename Type::value_type, Type>) {
|
||||
return maybe_equality_comparable<Type>(0);
|
||||
} else if constexpr(dispatch_is_equality_comparable<typename Type::value_type>()) {
|
||||
} 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;
|
||||
@@ -793,6 +787,7 @@ template<typename Type>
|
||||
} else {
|
||||
return maybe_equality_comparable<Type>(0);
|
||||
}
|
||||
// NOLINTEND(modernize-use-transparent-functors)
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
@@ -873,9 +868,9 @@ template<typename Member>
|
||||
using member_class_t = typename member_class<Member>::type;
|
||||
|
||||
/**
|
||||
* @brief Extracts the n-th argument of a given function or member function.
|
||||
* @brief Extracts the n-th argument of a _callable_ type.
|
||||
* @tparam Index The index of the argument to extract.
|
||||
* @tparam Candidate A valid function, member function or data member type.
|
||||
* @tparam Candidate A valid _callable_ type.
|
||||
*/
|
||||
template<std::size_t Index, typename Candidate>
|
||||
class nth_argument {
|
||||
@@ -891,8 +886,11 @@ class nth_argument {
|
||||
template<typename Type, typename Class>
|
||||
static constexpr type_list<Type> pick_up(Type Class ::*);
|
||||
|
||||
template<typename Type>
|
||||
static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&);
|
||||
|
||||
public:
|
||||
/*! @brief N-th argument of the given function or member function. */
|
||||
/*! @brief N-th argument of the _callable_ type. */
|
||||
using type = type_list_element_t<Index, decltype(pick_up(std::declval<Candidate>()))>;
|
||||
};
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
#include "../core/bit.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
@@ -12,12 +13,6 @@ namespace entt {
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
// waiting for C++20 and std::popcount
|
||||
template<typename Type>
|
||||
constexpr int popcount(Type value) noexcept {
|
||||
return value ? (int(value & 1) + popcount(value >> 1)) : 0;
|
||||
}
|
||||
|
||||
template<typename, typename = void>
|
||||
struct entt_traits;
|
||||
|
||||
@@ -64,10 +59,10 @@ struct entt_traits<std::uint64_t> {
|
||||
*/
|
||||
template<typename Traits>
|
||||
class basic_entt_traits {
|
||||
static constexpr auto length = internal::popcount(Traits::entity_mask);
|
||||
static constexpr auto length = popcount(Traits::entity_mask);
|
||||
|
||||
static_assert(Traits::entity_mask && ((typename Traits::entity_type{1} << length) == (Traits::entity_mask + 1)), "Invalid entity mask");
|
||||
static_assert((typename Traits::entity_type{1} << internal::popcount(Traits::version_mask)) == (Traits::version_mask + 1), "Invalid version mask");
|
||||
static_assert(Traits::entity_mask && ((Traits::entity_mask & (Traits::entity_mask + 1)) == 0), "Invalid entity mask");
|
||||
static_assert((Traits::version_mask & (Traits::version_mask + 1)) == 0, "Invalid version mask");
|
||||
|
||||
public:
|
||||
/*! @brief Value type. */
|
||||
@@ -106,7 +101,11 @@ public:
|
||||
* @return The integral representation of the version part.
|
||||
*/
|
||||
[[nodiscard]] static constexpr version_type to_version(const value_type value) noexcept {
|
||||
return (static_cast<version_type>(to_integral(value) >> length) & version_mask);
|
||||
if constexpr(Traits::version_mask == 0u) {
|
||||
return version_type{};
|
||||
} else {
|
||||
return (static_cast<version_type>(to_integral(value) >> length) & version_mask);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,7 +129,11 @@ public:
|
||||
* @return A properly constructed identifier.
|
||||
*/
|
||||
[[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) noexcept {
|
||||
return value_type{(entity & entity_mask) | (static_cast<entity_type>(version & version_mask) << length)};
|
||||
if constexpr(Traits::version_mask == 0u) {
|
||||
return value_type{entity & entity_mask};
|
||||
} else {
|
||||
return value_type{(entity & entity_mask) | (static_cast<entity_type>(version & version_mask) << length)};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,7 +147,11 @@ public:
|
||||
* @return A properly constructed identifier.
|
||||
*/
|
||||
[[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept {
|
||||
return value_type{(lhs & entity_mask) | (rhs & (version_mask << length))};
|
||||
if constexpr(Traits::version_mask == 0u) {
|
||||
return value_type{lhs & entity_mask};
|
||||
} else {
|
||||
return value_type{(lhs & entity_mask) | (rhs & (version_mask << length))};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -252,25 +259,25 @@ struct null_t {
|
||||
/**
|
||||
* @brief Compares a null object and an identifier of any type.
|
||||
* @tparam Entity Type of identifier.
|
||||
* @param entity Identifier with which to compare.
|
||||
* @param other A null object yet to be converted.
|
||||
* @param lhs Identifier with which to compare.
|
||||
* @param rhs A null object yet to be converted.
|
||||
* @return False if the two elements differ, true otherwise.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr bool operator==(const Entity entity, const null_t other) noexcept {
|
||||
return other.operator==(entity);
|
||||
[[nodiscard]] constexpr bool operator==(const Entity lhs, const null_t rhs) noexcept {
|
||||
return rhs.operator==(lhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares a null object and an identifier of any type.
|
||||
* @tparam Entity Type of identifier.
|
||||
* @param entity Identifier with which to compare.
|
||||
* @param other A null object yet to be converted.
|
||||
* @param lhs Identifier with which to compare.
|
||||
* @param rhs A null object yet to be converted.
|
||||
* @return True if the two elements differ, false otherwise.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr bool operator!=(const Entity entity, const null_t other) noexcept {
|
||||
return !(other == entity);
|
||||
[[nodiscard]] constexpr bool operator!=(const Entity lhs, const null_t rhs) noexcept {
|
||||
return !(rhs == lhs);
|
||||
}
|
||||
|
||||
/*! @brief Tombstone object for all identifiers. */
|
||||
@@ -314,7 +321,12 @@ struct tombstone_t {
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept {
|
||||
using traits_type = entt_traits<Entity>;
|
||||
return traits_type::to_version(entity) == traits_type::to_version(*this);
|
||||
|
||||
if constexpr(traits_type::version_mask == 0u) {
|
||||
return false;
|
||||
} else {
|
||||
return (traits_type::to_version(entity) == traits_type::to_version(*this));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -332,25 +344,25 @@ struct tombstone_t {
|
||||
/**
|
||||
* @brief Compares a tombstone object and an identifier of any type.
|
||||
* @tparam Entity Type of identifier.
|
||||
* @param entity Identifier with which to compare.
|
||||
* @param other A tombstone object yet to be converted.
|
||||
* @param lhs Identifier with which to compare.
|
||||
* @param rhs A tombstone object yet to be converted.
|
||||
* @return False if the two elements differ, true otherwise.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr bool operator==(const Entity entity, const tombstone_t other) noexcept {
|
||||
return other.operator==(entity);
|
||||
[[nodiscard]] constexpr bool operator==(const Entity lhs, const tombstone_t rhs) noexcept {
|
||||
return rhs.operator==(lhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares a tombstone object and an identifier of any type.
|
||||
* @tparam Entity Type of identifier.
|
||||
* @param entity Identifier with which to compare.
|
||||
* @param other A tombstone object yet to be converted.
|
||||
* @param lhs Identifier with which to compare.
|
||||
* @param rhs A tombstone object yet to be converted.
|
||||
* @return True if the two elements differ, false otherwise.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr bool operator!=(const Entity entity, const tombstone_t other) noexcept {
|
||||
return !(other == entity);
|
||||
[[nodiscard]] constexpr bool operator!=(const Entity lhs, const tombstone_t rhs) noexcept {
|
||||
return !(rhs == lhs);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
#include "../core/fwd.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
|
||||
@@ -31,6 +32,9 @@ class basic_storage;
|
||||
template<typename, typename>
|
||||
class basic_sigh_mixin;
|
||||
|
||||
template<typename, typename>
|
||||
class basic_reactive_mixin;
|
||||
|
||||
template<typename Entity = entity, typename = std::allocator<Entity>>
|
||||
class basic_registry;
|
||||
|
||||
@@ -43,14 +47,14 @@ class basic_runtime_view;
|
||||
template<typename, typename, typename>
|
||||
class basic_group;
|
||||
|
||||
template<typename, typename Mask = std::uint32_t, typename = std::allocator<Mask>>
|
||||
template<typename, typename = std::allocator<void>>
|
||||
class basic_observer;
|
||||
|
||||
template<typename>
|
||||
class basic_organizer;
|
||||
|
||||
template<typename, typename...>
|
||||
struct basic_handle;
|
||||
class basic_handle;
|
||||
|
||||
template<typename>
|
||||
class basic_snapshot;
|
||||
@@ -66,7 +70,7 @@ using sparse_set = basic_sparse_set<>;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Type Type of objects assigned to the entities.
|
||||
* @tparam Type Element type.
|
||||
*/
|
||||
template<typename Type>
|
||||
using storage = basic_storage<Type>;
|
||||
@@ -78,6 +82,13 @@ using storage = basic_storage<Type>;
|
||||
template<typename Type>
|
||||
using sigh_mixin = basic_sigh_mixin<Type, basic_registry<typename Type::entity_type, typename Type::base_type::allocator_type>>;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Type Underlying storage type.
|
||||
*/
|
||||
template<typename Type>
|
||||
using reactive_mixin = basic_reactive_mixin<Type, basic_registry<typename Type::entity_type, typename Type::base_type::allocator_type>>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using registry = basic_registry<>;
|
||||
|
||||
@@ -129,7 +140,7 @@ using const_runtime_view = basic_runtime_view<const sparse_set>;
|
||||
template<typename... Type>
|
||||
struct exclude_t final: type_list<Type...> {
|
||||
/*! @brief Default constructor. */
|
||||
explicit constexpr exclude_t() {}
|
||||
explicit constexpr exclude_t() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -140,34 +151,34 @@ template<typename... Type>
|
||||
inline constexpr exclude_t<Type...> exclude{};
|
||||
|
||||
/**
|
||||
* @brief Alias for lists of observed components.
|
||||
* @brief Alias for lists of observed elements.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
struct get_t final: type_list<Type...> {
|
||||
/*! @brief Default constructor. */
|
||||
explicit constexpr get_t() {}
|
||||
explicit constexpr get_t() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Variable template for lists of observed components.
|
||||
* @brief Variable template for lists of observed elements.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
inline constexpr get_t<Type...> get{};
|
||||
|
||||
/**
|
||||
* @brief Alias for lists of owned components.
|
||||
* @brief Alias for lists of owned elements.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
struct owned_t final: type_list<Type...> {
|
||||
/*! @brief Default constructor. */
|
||||
explicit constexpr owned_t() {}
|
||||
explicit constexpr owned_t() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Variable template for lists of owned components.
|
||||
* @brief Variable template for lists of owned elements.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
@@ -215,7 +226,21 @@ struct type_list_transform<owned_t<Type...>, Op> {
|
||||
template<typename Type, typename Entity = entity, typename Allocator = std::allocator<Type>, typename = void>
|
||||
struct storage_type {
|
||||
/*! @brief Type-to-storage conversion result. */
|
||||
using type = sigh_mixin<basic_storage<Type, Entity, Allocator>>;
|
||||
using type = ENTT_STORAGE(sigh_mixin, basic_storage<Type, Entity, Allocator>);
|
||||
};
|
||||
|
||||
/*! @brief Empty value type for reactive storage types. */
|
||||
struct reactive final {};
|
||||
|
||||
/**
|
||||
* @ brief Partial specialization for reactive storage types.
|
||||
* @tparam Entity A valid entity type.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Entity, typename Allocator>
|
||||
struct storage_type<reactive, Entity, Allocator> {
|
||||
/*! @brief Type-to-storage conversion result. */
|
||||
using type = ENTT_STORAGE(reactive_mixin, basic_storage<reactive, Entity, Allocator>);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
#ifndef ENTT_ENTITY_GROUP_HPP
|
||||
#define ENTT_ENTITY_GROUP_HPP
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/algorithm.hpp"
|
||||
#include "../core/fwd.hpp"
|
||||
#include "../core/iterator.hpp"
|
||||
#include "../core/type_info.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "sparse_set.hpp"
|
||||
#include "storage.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
@@ -25,8 +27,8 @@ class extended_group_iterator;
|
||||
template<typename It, typename... Owned, typename... Get>
|
||||
class extended_group_iterator<It, owned_t<Owned...>, get_t<Get...>> {
|
||||
template<typename Type>
|
||||
auto index_to_element([[maybe_unused]] Type &cpool) const {
|
||||
if constexpr(Type::traits_type::page_size == 0u) {
|
||||
[[nodiscard]] auto index_to_element([[maybe_unused]] Type &cpool) const {
|
||||
if constexpr(std::is_void_v<typename Type::value_type>) {
|
||||
return std::make_tuple();
|
||||
} else {
|
||||
return std::forward_as_tuple(cpool.rbegin()[it.index()]);
|
||||
@@ -35,10 +37,10 @@ class extended_group_iterator<It, owned_t<Owned...>, get_t<Get...>> {
|
||||
|
||||
public:
|
||||
using iterator_type = It;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::declval<Owned>().get_as_tuple({})..., std::declval<Get>().get_as_tuple({})...));
|
||||
using pointer = input_iterator_pointer<value_type>;
|
||||
using reference = value_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using iterator_concept = std::forward_iterator_tag;
|
||||
|
||||
@@ -46,9 +48,9 @@ public:
|
||||
: it{},
|
||||
pools{} {}
|
||||
|
||||
extended_group_iterator(iterator_type from, const std::tuple<Owned *..., Get *...> &cpools)
|
||||
extended_group_iterator(iterator_type from, std::tuple<Owned *..., Get *...> cpools)
|
||||
: it{from},
|
||||
pools{cpools} {}
|
||||
pools{std::move(cpools)} {}
|
||||
|
||||
extended_group_iterator &operator++() noexcept {
|
||||
return ++it, *this;
|
||||
@@ -92,100 +94,93 @@ template<typename... Lhs, typename... Rhs>
|
||||
struct group_descriptor {
|
||||
using size_type = std::size_t;
|
||||
virtual ~group_descriptor() = default;
|
||||
virtual size_type owned(const id_type *, const size_type) const noexcept {
|
||||
return 0u;
|
||||
[[nodiscard]] virtual bool owned(const id_type) const noexcept {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename, typename, typename>
|
||||
class group_handler;
|
||||
|
||||
template<typename... Owned, typename... Get, typename... Exclude>
|
||||
class group_handler<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> final: public group_descriptor {
|
||||
// nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here
|
||||
static_assert(!std::disjunction_v<std::bool_constant<Owned::traits_type::in_place_delete>...>, "Groups do not support in-place delete");
|
||||
static_assert(!std::disjunction_v<std::is_const<Owned>..., std::is_const<Get>..., std::is_const<Exclude>...>, "Const storage type not allowed");
|
||||
|
||||
using base_type = std::common_type_t<typename Owned::base_type..., typename Get::base_type..., typename Exclude::base_type...>;
|
||||
using entity_type = typename base_type::entity_type;
|
||||
template<typename Type, std::size_t Owned, std::size_t Get, std::size_t Exclude>
|
||||
class group_handler final: public group_descriptor {
|
||||
using entity_type = typename Type::entity_type;
|
||||
|
||||
void swap_elements(const std::size_t pos, const entity_type entt) {
|
||||
std::apply([pos, entt](auto *...cpool) { (cpool->swap_elements(cpool->data()[pos], entt), ...); }, pools);
|
||||
for(size_type next{}; next < Owned; ++next) {
|
||||
pools[next]->swap_elements((*pools[next])[pos], entt);
|
||||
}
|
||||
}
|
||||
|
||||
void push_on_construct(const entity_type entt) {
|
||||
if(std::apply([entt, len = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < len) && (other->contains(entt) && ...); }, pools)
|
||||
if(std::apply([entt, pos = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < pos) && (other->contains(entt) && ...); }, pools)
|
||||
&& std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) {
|
||||
swap_elements(len++, entt);
|
||||
}
|
||||
}
|
||||
|
||||
void push_on_destroy(const entity_type entt) {
|
||||
if(std::apply([entt, len = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < len) && (other->contains(entt) && ...); }, pools)
|
||||
if(std::apply([entt, pos = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < pos) && (other->contains(entt) && ...); }, pools)
|
||||
&& std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) {
|
||||
swap_elements(len++, entt);
|
||||
}
|
||||
}
|
||||
|
||||
void remove_if(const entity_type entt) {
|
||||
if(std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < len)) {
|
||||
if(pools[0u]->contains(entt) && (pools[0u]->index(entt) < len)) {
|
||||
swap_elements(--len, entt);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
using size_type = typename base_type::size_type;
|
||||
|
||||
group_handler(Owned &...opool, Get &...gpool, Exclude &...epool)
|
||||
: pools{&opool..., &gpool...},
|
||||
filter{&epool...},
|
||||
len{} {
|
||||
std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::push_on_construct>(*this), cpool->on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, pools);
|
||||
std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::remove_if>(*this), cpool->on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, filter);
|
||||
|
||||
void common_setup() {
|
||||
// we cannot iterate backwards because we want to leave behind valid entities in case of owned types
|
||||
for(auto *first = std::get<0>(pools)->data(), *last = first + std::get<0>(pools)->size(); first != last; ++first) {
|
||||
for(auto first = pools[0u]->rbegin(), last = first + pools[0u]->size(); first != last; ++first) {
|
||||
push_on_construct(*first);
|
||||
}
|
||||
}
|
||||
|
||||
size_type owned(const id_type *elem, const size_type length) const noexcept final {
|
||||
size_type cnt = 0u;
|
||||
public:
|
||||
using common_type = Type;
|
||||
using size_type = typename Type::size_type;
|
||||
|
||||
for(auto pos = 0u; pos < length; ++pos) {
|
||||
cnt += ((elem[pos] == entt::type_hash<typename Owned::value_type>::value()) || ...);
|
||||
template<typename... OGType, typename... EType>
|
||||
group_handler(std::tuple<OGType &...> ogpool, std::tuple<EType &...> epool)
|
||||
: pools{std::apply([](auto &&...cpool) { return std::array<common_type *, (Owned + Get)>{&cpool...}; }, ogpool)},
|
||||
filter{std::apply([](auto &&...cpool) { return std::array<common_type *, Exclude>{&cpool...}; }, epool)} {
|
||||
std::apply([this](auto &...cpool) { ((cpool.on_construct().template connect<&group_handler::push_on_construct>(*this), cpool.on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, ogpool);
|
||||
std::apply([this](auto &...cpool) { ((cpool.on_construct().template connect<&group_handler::remove_if>(*this), cpool.on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, epool);
|
||||
common_setup();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool owned(const id_type hash) const noexcept override {
|
||||
for(size_type pos{}; pos < Owned; ++pos) {
|
||||
if(pools[pos]->type().hash() == hash) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return cnt;
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] size_type length() const noexcept {
|
||||
return len;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
Type pools_as() const noexcept {
|
||||
return pools;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
Type filter_as() const noexcept {
|
||||
return filter;
|
||||
template<std::size_t Index>
|
||||
[[nodiscard]] common_type *storage() const noexcept {
|
||||
if constexpr(Index < (Owned + Get)) {
|
||||
return pools[Index];
|
||||
} else {
|
||||
return filter[Index - (Owned + Get)];
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::tuple<Owned *..., Get *...> pools;
|
||||
std::tuple<Exclude *...> filter;
|
||||
std::size_t len;
|
||||
std::array<common_type *, (Owned + Get)> pools;
|
||||
std::array<common_type *, Exclude> filter;
|
||||
std::size_t len{};
|
||||
};
|
||||
|
||||
template<typename... Get, typename... Exclude>
|
||||
class group_handler<owned_t<>, get_t<Get...>, exclude_t<Exclude...>> final: public group_descriptor {
|
||||
// nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here
|
||||
static_assert(!std::disjunction_v<std::is_const<Get>..., std::is_const<Exclude>...>, "Const storage type not allowed");
|
||||
|
||||
using base_type = std::common_type_t<typename Get::base_type..., typename Exclude::base_type...>;
|
||||
using entity_type = typename base_type::entity_type;
|
||||
template<typename Type, std::size_t Get, std::size_t Exclude>
|
||||
class group_handler<Type, 0u, Get, Exclude> final: public group_descriptor {
|
||||
using entity_type = typename Type::entity_type;
|
||||
|
||||
void push_on_construct(const entity_type entt) {
|
||||
if(!elem.contains(entt)
|
||||
@@ -207,44 +202,46 @@ class group_handler<owned_t<>, get_t<Get...>, exclude_t<Exclude...>> final: publ
|
||||
elem.remove(entt);
|
||||
}
|
||||
|
||||
public:
|
||||
using common_type = base_type;
|
||||
|
||||
template<typename Alloc>
|
||||
group_handler(const Alloc &alloc, Get &...gpool, Exclude &...epool)
|
||||
: pools{&gpool...},
|
||||
filter{&epool...},
|
||||
elem{alloc} {
|
||||
std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::push_on_construct>(*this), cpool->on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, pools);
|
||||
std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::remove_if>(*this), cpool->on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, filter);
|
||||
|
||||
for(const auto entity: static_cast<base_type &>(*std::get<0>(pools))) {
|
||||
void common_setup() {
|
||||
for(const auto entity: *pools[0u]) {
|
||||
push_on_construct(entity);
|
||||
}
|
||||
}
|
||||
|
||||
common_type &handle() noexcept {
|
||||
public:
|
||||
using common_type = Type;
|
||||
|
||||
template<typename Allocator, typename... GType, typename... EType>
|
||||
group_handler(const Allocator &allocator, std::tuple<GType &...> gpool, std::tuple<EType &...> epool)
|
||||
: pools{std::apply([](auto &&...cpool) { return std::array<common_type *, Get>{&cpool...}; }, gpool)},
|
||||
filter{std::apply([](auto &&...cpool) { return std::array<common_type *, Exclude>{&cpool...}; }, epool)},
|
||||
elem{allocator} {
|
||||
std::apply([this](auto &...cpool) { ((cpool.on_construct().template connect<&group_handler::push_on_construct>(*this), cpool.on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, gpool);
|
||||
std::apply([this](auto &...cpool) { ((cpool.on_construct().template connect<&group_handler::remove_if>(*this), cpool.on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, epool);
|
||||
common_setup();
|
||||
}
|
||||
|
||||
[[nodiscard]] common_type &handle() noexcept {
|
||||
return elem;
|
||||
}
|
||||
|
||||
const common_type &handle() const noexcept {
|
||||
[[nodiscard]] const common_type &handle() const noexcept {
|
||||
return elem;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
Type pools_as() const noexcept {
|
||||
return pools;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
Type filter_as() const noexcept {
|
||||
return filter;
|
||||
template<std::size_t Index>
|
||||
[[nodiscard]] common_type *storage() const noexcept {
|
||||
if constexpr(Index < Get) {
|
||||
return pools[Index];
|
||||
} else {
|
||||
return filter[Index - Get];
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::tuple<Get *...> pools;
|
||||
std::tuple<Exclude *...> filter;
|
||||
base_type elem;
|
||||
std::array<common_type *, Get> pools;
|
||||
std::array<common_type *, Exclude> filter;
|
||||
common_type elem;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
@@ -271,7 +268,7 @@ class basic_group;
|
||||
* Iterators aren't invalidated if:
|
||||
*
|
||||
* * New elements are added to the storage.
|
||||
* * The entity currently pointed is modified (for example, components are added
|
||||
* * The entity currently pointed is modified (for example, elements are added
|
||||
* or removed from it).
|
||||
* * The entity currently pointed is destroyed.
|
||||
*
|
||||
@@ -287,16 +284,12 @@ class basic_group<owned_t<>, get_t<Get...>, exclude_t<Exclude...>> {
|
||||
using underlying_type = typename base_type::entity_type;
|
||||
|
||||
template<typename Type>
|
||||
static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Get::value_type..., typename Exclude::value_type...>>;
|
||||
static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Get::element_type..., typename Exclude::element_type...>>;
|
||||
|
||||
auto pools() const noexcept {
|
||||
template<std::size_t... Index>
|
||||
[[nodiscard]] auto pools_for(std::index_sequence<Index...>) const noexcept {
|
||||
using return_type = std::tuple<Get *...>;
|
||||
return descriptor ? descriptor->template pools_as<return_type>() : return_type{};
|
||||
}
|
||||
|
||||
auto filter() const noexcept {
|
||||
using return_type = std::tuple<Exclude *...>;
|
||||
return descriptor ? descriptor->template filter_as<return_type>() : return_type{};
|
||||
return descriptor ? return_type{static_cast<Get *>(descriptor->template storage<Index>())...} : return_type{};
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -308,12 +301,20 @@ public:
|
||||
using common_type = base_type;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator = typename common_type::iterator;
|
||||
/*! @brief Reversed iterator type. */
|
||||
/*! @brief Reverse iterator type. */
|
||||
using reverse_iterator = typename common_type::reverse_iterator;
|
||||
/*! @brief Iterable group type. */
|
||||
using iterable = iterable_adaptor<internal::extended_group_iterator<iterator, owned_t<>, get_t<Get...>>>;
|
||||
/*! @brief Group handler type. */
|
||||
using handler = internal::group_handler<owned_t<>, get_t<std::remove_const_t<Get>...>, exclude_t<std::remove_const_t<Exclude>...>>;
|
||||
using handler = internal::group_handler<common_type, 0u, sizeof...(Get), sizeof...(Exclude)>;
|
||||
|
||||
/**
|
||||
* @brief Group opaque identifier.
|
||||
* @return Group opaque identifier.
|
||||
*/
|
||||
static id_type group_id() noexcept {
|
||||
return type_hash<basic_group<owned_t<>, get_t<std::remove_const_t<Get>...>, exclude_t<std::remove_const_t<Exclude>...>>>::value();
|
||||
}
|
||||
|
||||
/*! @brief Default constructor to use to create empty, invalid groups. */
|
||||
basic_group() noexcept
|
||||
@@ -335,9 +336,9 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the storage for a given component type, if any.
|
||||
* @tparam Type Type of component of which to return the storage.
|
||||
* @return The storage for the given component type.
|
||||
* @brief Returns the storage for a given element type, if any.
|
||||
* @tparam Type Type of element of which to return the storage.
|
||||
* @return The storage for the given element type.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] auto *storage() const noexcept {
|
||||
@@ -351,13 +352,8 @@ public:
|
||||
*/
|
||||
template<std::size_t Index>
|
||||
[[nodiscard]] auto *storage() const noexcept {
|
||||
constexpr auto offset = sizeof...(Get);
|
||||
|
||||
if constexpr(Index < offset) {
|
||||
return std::get<Index>(pools());
|
||||
} else {
|
||||
return std::get<Index - offset>(filter());
|
||||
}
|
||||
using type = type_list_element_t<Index, type_list<Get..., Exclude...>>;
|
||||
return *this ? static_cast<type *>(descriptor->template storage<Index>()) : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -490,11 +486,11 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the components assigned to the given entity.
|
||||
* @tparam Type Type of the component to get.
|
||||
* @tparam Other Other types of components to get.
|
||||
* @brief Returns the elements assigned to the given entity.
|
||||
* @tparam Type Type of the element to get.
|
||||
* @tparam Other Other types of elements to get.
|
||||
* @param entt A valid identifier.
|
||||
* @return The components assigned to the entity.
|
||||
* @return The elements assigned to the entity.
|
||||
*/
|
||||
template<typename Type, typename... Other>
|
||||
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
|
||||
@@ -502,14 +498,14 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the components assigned to the given entity.
|
||||
* @tparam Index Indexes of the components to get.
|
||||
* @brief Returns the elements assigned to the given entity.
|
||||
* @tparam Index Indexes of the elements to get.
|
||||
* @param entt A valid identifier.
|
||||
* @return The components assigned to the entity.
|
||||
* @return The elements assigned to the entity.
|
||||
*/
|
||||
template<std::size_t... Index>
|
||||
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
|
||||
const auto cpools = pools();
|
||||
const auto cpools = pools_for(std::index_sequence_for<Get...>{});
|
||||
|
||||
if constexpr(sizeof...(Index) == 0) {
|
||||
return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools);
|
||||
@@ -521,12 +517,12 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates entities and components and applies the given function
|
||||
* @brief Iterates entities and elements and applies the given function
|
||||
* object to them.
|
||||
*
|
||||
* The function object is invoked for each entity. It is provided with the
|
||||
* entity itself and a set of references to non-empty components. The
|
||||
* _constness_ of the components is as requested.<br/>
|
||||
* entity itself and a set of references to non-empty elements. The
|
||||
* _constness_ of the elements is as requested.<br/>
|
||||
* The signature of the function must be equivalent to one of the following
|
||||
* forms:
|
||||
*
|
||||
@@ -557,8 +553,8 @@ public:
|
||||
* @brief Returns an iterable object to use to _visit_ a group.
|
||||
*
|
||||
* The iterable object returns tuples that contain the current entity and a
|
||||
* set of references to its non-empty components. The _constness_ of the
|
||||
* components is as requested.
|
||||
* set of references to its non-empty elements. The _constness_ of the
|
||||
* elements is as requested.
|
||||
*
|
||||
* @note
|
||||
* Empty types aren't explicitly instantiated and therefore they are never
|
||||
@@ -567,7 +563,7 @@ public:
|
||||
* @return An iterable object to use to _visit_ the group.
|
||||
*/
|
||||
[[nodiscard]] iterable each() const noexcept {
|
||||
const auto cpools = pools();
|
||||
const auto cpools = pools_for(std::index_sequence_for<Get...>{});
|
||||
return iterable{{begin(), cpools}, {end(), cpools}};
|
||||
}
|
||||
|
||||
@@ -595,8 +591,8 @@ public:
|
||||
* * An iterator past the last element of the range to sort.
|
||||
* * A comparison function to use to compare the elements.
|
||||
*
|
||||
* @tparam Type Optional type of component to compare.
|
||||
* @tparam Other Other optional types of components to compare.
|
||||
* @tparam Type Optional type of element to compare.
|
||||
* @tparam Other Other optional types of elements to compare.
|
||||
* @tparam Compare Type of comparison function object.
|
||||
* @tparam Sort Type of sort function object.
|
||||
* @tparam Args Types of arguments to forward to the sort function object.
|
||||
@@ -614,7 +610,7 @@ public:
|
||||
*
|
||||
* @sa sort
|
||||
*
|
||||
* @tparam Index Optional indexes of components to compare.
|
||||
* @tparam Index Optional indexes of elements to compare.
|
||||
* @tparam Compare Type of comparison function object.
|
||||
* @tparam Sort Type of sort function object.
|
||||
* @tparam Args Types of arguments to forward to the sort function object.
|
||||
@@ -629,7 +625,7 @@ public:
|
||||
static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function");
|
||||
descriptor->handle().sort(std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
} else {
|
||||
auto comp = [&compare, cpools = pools()](const entity_type lhs, const entity_type rhs) {
|
||||
auto comp = [&compare, cpools = pools_for(std::index_sequence_for<Get...>{})](const entity_type lhs, const entity_type rhs) {
|
||||
if constexpr(sizeof...(Index) == 1) {
|
||||
return compare((std::get<Index>(cpools)->get(lhs), ...), (std::get<Index>(cpools)->get(rhs), ...));
|
||||
} else {
|
||||
@@ -659,14 +655,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sort entities according to their order in a range.
|
||||
* @param other The storage to use to impose the order.
|
||||
*/
|
||||
[[deprecated("use iterator based sort_as instead")]] void sort_as(const common_type &other) const {
|
||||
sort_as(other.begin(), other.end());
|
||||
}
|
||||
|
||||
private:
|
||||
handler *descriptor;
|
||||
};
|
||||
@@ -679,7 +667,7 @@ private:
|
||||
*
|
||||
* * It's guaranteed that the entity list is tightly packed in memory for fast
|
||||
* iterations.
|
||||
* * It's guaranteed that all components in the owned storage are tightly packed
|
||||
* * It's guaranteed that all elements in the owned storage are tightly packed
|
||||
* in memory for even faster iterations and to allow direct access.
|
||||
* * They stay true to the order of the owned storage and all instances have the
|
||||
* same order in memory.
|
||||
@@ -691,7 +679,7 @@ private:
|
||||
* Iterators aren't invalidated if:
|
||||
*
|
||||
* * New elements are added to the storage.
|
||||
* * The entity currently pointed is modified (for example, components are added
|
||||
* * The entity currently pointed is modified (for example, elements are added
|
||||
* or removed from it).
|
||||
* * The entity currently pointed is destroyed.
|
||||
*
|
||||
@@ -704,20 +692,18 @@ private:
|
||||
*/
|
||||
template<typename... Owned, typename... Get, typename... Exclude>
|
||||
class basic_group<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> {
|
||||
static_assert(((Owned::storage_policy != deletion_policy::in_place) && ...), "Groups do not support in-place delete");
|
||||
|
||||
using base_type = std::common_type_t<typename Owned::base_type..., typename Get::base_type..., typename Exclude::base_type...>;
|
||||
using underlying_type = typename base_type::entity_type;
|
||||
|
||||
template<typename Type>
|
||||
static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Owned::value_type..., typename Get::value_type..., typename Exclude::value_type...>>;
|
||||
static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Owned::element_type..., typename Get::element_type..., typename Exclude::element_type...>>;
|
||||
|
||||
auto pools() const noexcept {
|
||||
template<std::size_t... Index, std::size_t... Other>
|
||||
[[nodiscard]] auto pools_for(std::index_sequence<Index...>, std::index_sequence<Other...>) const noexcept {
|
||||
using return_type = std::tuple<Owned *..., Get *...>;
|
||||
return descriptor ? descriptor->template pools_as<return_type>() : return_type{};
|
||||
}
|
||||
|
||||
auto filter() const noexcept {
|
||||
using return_type = std::tuple<Exclude *...>;
|
||||
return descriptor ? descriptor->template filter_as<return_type>() : return_type{};
|
||||
return descriptor ? return_type{static_cast<Owned *>(descriptor->template storage<Index>())..., static_cast<Get *>(descriptor->template storage<sizeof...(Owned) + Other>())...} : return_type{};
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -729,12 +715,20 @@ public:
|
||||
using common_type = base_type;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator = typename common_type::iterator;
|
||||
/*! @brief Reversed iterator type. */
|
||||
/*! @brief Reverse iterator type. */
|
||||
using reverse_iterator = typename common_type::reverse_iterator;
|
||||
/*! @brief Iterable group type. */
|
||||
using iterable = iterable_adaptor<internal::extended_group_iterator<iterator, owned_t<Owned...>, get_t<Get...>>>;
|
||||
/*! @brief Group handler type. */
|
||||
using handler = internal::group_handler<owned_t<std::remove_const_t<Owned>...>, get_t<std::remove_const_t<Get>...>, exclude_t<std::remove_const_t<Exclude>...>>;
|
||||
using handler = internal::group_handler<common_type, sizeof...(Owned), sizeof...(Get), sizeof...(Exclude)>;
|
||||
|
||||
/**
|
||||
* @brief Group opaque identifier.
|
||||
* @return Group opaque identifier.
|
||||
*/
|
||||
static id_type group_id() noexcept {
|
||||
return type_hash<basic_group<owned_t<std::remove_const_t<Owned>...>, get_t<std::remove_const_t<Get>...>, exclude_t<std::remove_const_t<Exclude>...>>>::value();
|
||||
}
|
||||
|
||||
/*! @brief Default constructor to use to create empty, invalid groups. */
|
||||
basic_group() noexcept
|
||||
@@ -756,9 +750,9 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the storage for a given component type, if any.
|
||||
* @tparam Type Type of component of which to return the storage.
|
||||
* @return The storage for the given component type.
|
||||
* @brief Returns the storage for a given element type, if any.
|
||||
* @tparam Type Type of element of which to return the storage.
|
||||
* @return The storage for the given element type.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] auto *storage() const noexcept {
|
||||
@@ -772,13 +766,8 @@ public:
|
||||
*/
|
||||
template<std::size_t Index>
|
||||
[[nodiscard]] auto *storage() const noexcept {
|
||||
constexpr auto offset = sizeof...(Owned) + sizeof...(Get);
|
||||
|
||||
if constexpr(Index < offset) {
|
||||
return std::get<Index>(pools());
|
||||
} else {
|
||||
return std::get<Index - offset>(filter());
|
||||
}
|
||||
using type = type_list_element_t<Index, type_list<Owned..., Get..., Exclude...>>;
|
||||
return *this ? static_cast<type *>(descriptor->template storage<Index>()) : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -896,11 +885,11 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the components assigned to the given entity.
|
||||
* @tparam Type Type of the component to get.
|
||||
* @tparam Other Other types of components to get.
|
||||
* @brief Returns the elements assigned to the given entity.
|
||||
* @tparam Type Type of the element to get.
|
||||
* @tparam Other Other types of elements to get.
|
||||
* @param entt A valid identifier.
|
||||
* @return The components assigned to the entity.
|
||||
* @return The elements assigned to the entity.
|
||||
*/
|
||||
template<typename Type, typename... Other>
|
||||
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
|
||||
@@ -908,14 +897,14 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the components assigned to the given entity.
|
||||
* @tparam Index Indexes of the components to get.
|
||||
* @brief Returns the elements assigned to the given entity.
|
||||
* @tparam Index Indexes of the elements to get.
|
||||
* @param entt A valid identifier.
|
||||
* @return The components assigned to the entity.
|
||||
* @return The elements assigned to the entity.
|
||||
*/
|
||||
template<std::size_t... Index>
|
||||
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
|
||||
const auto cpools = pools();
|
||||
const auto cpools = pools_for(std::index_sequence_for<Owned...>{}, std::index_sequence_for<Get...>{});
|
||||
|
||||
if constexpr(sizeof...(Index) == 0) {
|
||||
return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools);
|
||||
@@ -927,12 +916,12 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates entities and components and applies the given function
|
||||
* @brief Iterates entities and elements and applies the given function
|
||||
* object to them.
|
||||
*
|
||||
* The function object is invoked for each entity. It is provided with the
|
||||
* entity itself and a set of references to non-empty components. The
|
||||
* _constness_ of the components is as requested.<br/>
|
||||
* entity itself and a set of references to non-empty elements. The
|
||||
* _constness_ of the elements is as requested.<br/>
|
||||
* The signature of the function must be equivalent to one of the following
|
||||
* forms:
|
||||
*
|
||||
@@ -963,8 +952,8 @@ public:
|
||||
* @brief Returns an iterable object to use to _visit_ a group.
|
||||
*
|
||||
* The iterable object returns tuples that contain the current entity and a
|
||||
* set of references to its non-empty components. The _constness_ of the
|
||||
* components is as requested.
|
||||
* set of references to its non-empty elements. The _constness_ of the
|
||||
* elements is as requested.
|
||||
*
|
||||
* @note
|
||||
* Empty types aren't explicitly instantiated and therefore they are never
|
||||
@@ -973,8 +962,8 @@ public:
|
||||
* @return An iterable object to use to _visit_ the group.
|
||||
*/
|
||||
[[nodiscard]] iterable each() const noexcept {
|
||||
const auto cpools = pools();
|
||||
return {{begin(), cpools}, {end(), cpools}};
|
||||
const auto cpools = pools_for(std::index_sequence_for<Owned...>{}, std::index_sequence_for<Get...>{});
|
||||
return iterable{{begin(), cpools}, {end(), cpools}};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1002,8 +991,8 @@ public:
|
||||
* * An iterator past the last element of the range to sort.
|
||||
* * A comparison function to use to compare the elements.
|
||||
*
|
||||
* @tparam Type Optional type of component to compare.
|
||||
* @tparam Other Other optional types of components to compare.
|
||||
* @tparam Type Optional type of element to compare.
|
||||
* @tparam Other Other optional types of elements to compare.
|
||||
* @tparam Compare Type of comparison function object.
|
||||
* @tparam Sort Type of sort function object.
|
||||
* @tparam Args Types of arguments to forward to the sort function object.
|
||||
@@ -1021,7 +1010,7 @@ public:
|
||||
*
|
||||
* @sa sort
|
||||
*
|
||||
* @tparam Index Optional indexes of components to compare.
|
||||
* @tparam Index Optional indexes of elements to compare.
|
||||
* @tparam Compare Type of comparison function object.
|
||||
* @tparam Sort Type of sort function object.
|
||||
* @tparam Args Types of arguments to forward to the sort function object.
|
||||
@@ -1031,7 +1020,7 @@ public:
|
||||
*/
|
||||
template<std::size_t... Index, typename Compare, typename Sort = std_sort, typename... Args>
|
||||
void sort(Compare compare, Sort algo = Sort{}, Args &&...args) const {
|
||||
const auto cpools = pools();
|
||||
const auto cpools = pools_for(std::index_sequence_for<Owned...>{}, std::index_sequence_for<Get...>{});
|
||||
|
||||
if constexpr(sizeof...(Index) == 0) {
|
||||
static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function");
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/iterator.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "entity.hpp"
|
||||
@@ -94,19 +95,29 @@ template<typename ILhs, typename IRhs>
|
||||
* @tparam Scope Types to which to restrict the scope of a handle.
|
||||
*/
|
||||
template<typename Registry, typename... Scope>
|
||||
struct basic_handle {
|
||||
class basic_handle {
|
||||
using traits_type = entt_traits<typename Registry::entity_type>;
|
||||
|
||||
[[nodiscard]] auto &owner_or_assert() const noexcept {
|
||||
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
|
||||
return static_cast<Registry &>(*owner);
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Type of registry accepted by the handle. */
|
||||
using registry_type = Registry;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename registry_type::entity_type;
|
||||
using entity_type = typename traits_type::value_type;
|
||||
/*! @brief Underlying version type. */
|
||||
using version_type = typename registry_type::version_type;
|
||||
using version_type = typename traits_type::version_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename registry_type::size_type;
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Iterable handle type. */
|
||||
using iterable = iterable_adaptor<internal::handle_storage_iterator<typename decltype(std::declval<registry_type>().storage())::iterator>>;
|
||||
|
||||
/*! @brief Constructs an invalid handle. */
|
||||
basic_handle() noexcept
|
||||
: reg{},
|
||||
: owner{},
|
||||
entt{null} {}
|
||||
|
||||
/**
|
||||
@@ -115,7 +126,7 @@ struct basic_handle {
|
||||
* @param value A valid identifier.
|
||||
*/
|
||||
basic_handle(registry_type &ref, entity_type value) noexcept
|
||||
: reg{&ref},
|
||||
: owner{&ref},
|
||||
entt{value} {}
|
||||
|
||||
/**
|
||||
@@ -128,49 +139,23 @@ struct basic_handle {
|
||||
*
|
||||
* @return An iterable object to use to _visit_ the handle.
|
||||
*/
|
||||
[[nodiscard]] auto storage() const noexcept {
|
||||
auto iterable = reg->storage();
|
||||
using iterator_type = internal::handle_storage_iterator<typename decltype(iterable)::iterator>;
|
||||
return iterable_adaptor{iterator_type{entt, iterable.begin(), iterable.end()}, iterator_type{entt, iterable.end(), iterable.end()}};
|
||||
[[nodiscard]] iterable storage() const noexcept {
|
||||
auto underlying = owner_or_assert().storage();
|
||||
return iterable{{entt, underlying.begin(), underlying.end()}, {entt, underlying.end(), underlying.end()}};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructs a const handle from a non-const one.
|
||||
* @tparam Other A valid entity type.
|
||||
* @tparam Args Scope of the handle to construct.
|
||||
* @return A const handle referring to the same registry and the same
|
||||
* entity.
|
||||
*/
|
||||
template<typename Other, typename... Args>
|
||||
operator basic_handle<Other, Args...>() const noexcept {
|
||||
static_assert(std::is_same_v<Other, Registry> || std::is_same_v<std::remove_const_t<Other>, Registry>, "Invalid conversion between different handles");
|
||||
static_assert((sizeof...(Scope) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Scope)) && ... && (type_list_contains_v<type_list<Scope...>, Args>))), "Invalid conversion between different handles");
|
||||
|
||||
return reg ? basic_handle<Other, Args...>{*reg, entt} : basic_handle<Other, Args...>{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Converts a handle to its underlying entity.
|
||||
* @return The contained identifier.
|
||||
*/
|
||||
[[nodiscard]] operator entity_type() const noexcept {
|
||||
return entity();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a handle refers to non-null registry pointer and entity.
|
||||
* @return True if the handle refers to non-null registry and entity, false otherwise.
|
||||
*/
|
||||
/*! @copydoc valid */
|
||||
[[nodiscard]] explicit operator bool() const noexcept {
|
||||
return reg && reg->valid(entt);
|
||||
return owner && owner->valid(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a handle refers to a valid entity or not.
|
||||
* @return True if the handle refers to a valid entity, false otherwise.
|
||||
* @brief Checks if a handle refers to a valid registry and entity.
|
||||
* @return True if the handle refers to a valid registry and entity, false
|
||||
* otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool valid() const {
|
||||
return reg->valid(entt);
|
||||
return static_cast<bool>(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,7 +163,7 @@ struct basic_handle {
|
||||
* @return A pointer to the underlying registry, if any.
|
||||
*/
|
||||
[[nodiscard]] registry_type *registry() const noexcept {
|
||||
return reg;
|
||||
return owner;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,9 +174,14 @@ struct basic_handle {
|
||||
return entt;
|
||||
}
|
||||
|
||||
/*! @copydoc entity */
|
||||
[[nodiscard]] operator entity_type() const noexcept {
|
||||
return entity();
|
||||
}
|
||||
|
||||
/*! @brief Destroys the entity associated with a handle. */
|
||||
void destroy() {
|
||||
reg->destroy(std::exchange(entt, null));
|
||||
owner_or_assert().destroy(std::exchange(entt, null));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -199,148 +189,164 @@ struct basic_handle {
|
||||
* @param version A desired version upon destruction.
|
||||
*/
|
||||
void destroy(const version_type version) {
|
||||
reg->destroy(std::exchange(entt, null), version);
|
||||
owner_or_assert().destroy(std::exchange(entt, null), version);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns the given component to a handle.
|
||||
* @tparam Component Type of component to create.
|
||||
* @tparam Args Types of arguments to use to construct the component.
|
||||
* @param args Parameters to use to initialize the component.
|
||||
* @return A reference to the newly created component.
|
||||
* @brief Assigns the given element to a handle.
|
||||
* @tparam Type Type of element to create.
|
||||
* @tparam Args Types of arguments to use to construct the element.
|
||||
* @param args Parameters to use to initialize the element.
|
||||
* @return A reference to the newly created element.
|
||||
*/
|
||||
template<typename Component, typename... Args>
|
||||
template<typename Type, typename... Args>
|
||||
// NOLINTNEXTLINE(modernize-use-nodiscard)
|
||||
decltype(auto) emplace(Args &&...args) const {
|
||||
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
|
||||
return reg->template emplace<Component>(entt, std::forward<Args>(args)...);
|
||||
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type");
|
||||
return owner_or_assert().template emplace<Type>(entt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns or replaces the given component for a handle.
|
||||
* @tparam Component Type of component to assign or replace.
|
||||
* @tparam Args Types of arguments to use to construct the component.
|
||||
* @param args Parameters to use to initialize the component.
|
||||
* @return A reference to the newly created component.
|
||||
* @brief Assigns or replaces the given element for a handle.
|
||||
* @tparam Type Type of element to assign or replace.
|
||||
* @tparam Args Types of arguments to use to construct the element.
|
||||
* @param args Parameters to use to initialize the element.
|
||||
* @return A reference to the newly created element.
|
||||
*/
|
||||
template<typename Component, typename... Args>
|
||||
template<typename Type, typename... Args>
|
||||
decltype(auto) emplace_or_replace(Args &&...args) const {
|
||||
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
|
||||
return reg->template emplace_or_replace<Component>(entt, std::forward<Args>(args)...);
|
||||
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type");
|
||||
return owner_or_assert().template emplace_or_replace<Type>(entt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Patches the given component for a handle.
|
||||
* @tparam Component Type of component to patch.
|
||||
* @brief Patches the given element for a handle.
|
||||
* @tparam Type Type of element to patch.
|
||||
* @tparam Func Types of the function objects to invoke.
|
||||
* @param func Valid function objects.
|
||||
* @return A reference to the patched component.
|
||||
* @return A reference to the patched element.
|
||||
*/
|
||||
template<typename Component, typename... Func>
|
||||
template<typename Type, typename... Func>
|
||||
decltype(auto) patch(Func &&...func) const {
|
||||
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
|
||||
return reg->template patch<Component>(entt, std::forward<Func>(func)...);
|
||||
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type");
|
||||
return owner_or_assert().template patch<Type>(entt, std::forward<Func>(func)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Replaces the given component for a handle.
|
||||
* @tparam Component Type of component to replace.
|
||||
* @tparam Args Types of arguments to use to construct the component.
|
||||
* @param args Parameters to use to initialize the component.
|
||||
* @return A reference to the component being replaced.
|
||||
* @brief Replaces the given element for a handle.
|
||||
* @tparam Type Type of element to replace.
|
||||
* @tparam Args Types of arguments to use to construct the element.
|
||||
* @param args Parameters to use to initialize the element.
|
||||
* @return A reference to the element being replaced.
|
||||
*/
|
||||
template<typename Component, typename... Args>
|
||||
template<typename Type, typename... Args>
|
||||
decltype(auto) replace(Args &&...args) const {
|
||||
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
|
||||
return reg->template replace<Component>(entt, std::forward<Args>(args)...);
|
||||
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type");
|
||||
return owner_or_assert().template replace<Type>(entt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes the given components from a handle.
|
||||
* @tparam Component Types of components to remove.
|
||||
* @return The number of components actually removed.
|
||||
* @brief Removes the given elements from a handle.
|
||||
* @tparam Type Types of elements to remove.
|
||||
* @return The number of elements actually removed.
|
||||
*/
|
||||
template<typename... Component>
|
||||
template<typename... Type>
|
||||
// NOLINTNEXTLINE(modernize-use-nodiscard)
|
||||
size_type remove() const {
|
||||
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Component> && ...), "Invalid type");
|
||||
return reg->template remove<Component...>(entt);
|
||||
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Type> && ...), "Invalid type");
|
||||
return owner_or_assert().template remove<Type...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erases the given components from a handle.
|
||||
* @tparam Component Types of components to erase.
|
||||
* @brief Erases the given elements from a handle.
|
||||
* @tparam Type Types of elements to erase.
|
||||
*/
|
||||
template<typename... Component>
|
||||
template<typename... Type>
|
||||
void erase() const {
|
||||
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Component> && ...), "Invalid type");
|
||||
reg->template erase<Component...>(entt);
|
||||
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Type> && ...), "Invalid type");
|
||||
owner_or_assert().template erase<Type...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a handle has all the given components.
|
||||
* @tparam Component Components for which to perform the check.
|
||||
* @return True if the handle has all the components, false otherwise.
|
||||
* @brief Checks if a handle has all the given elements.
|
||||
* @tparam Type Elements for which to perform the check.
|
||||
* @return True if the handle has all the elements, false otherwise.
|
||||
*/
|
||||
template<typename... Component>
|
||||
template<typename... Type>
|
||||
[[nodiscard]] decltype(auto) all_of() const {
|
||||
return reg->template all_of<Component...>(entt);
|
||||
return owner_or_assert().template all_of<Type...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a handle has at least one of the given components.
|
||||
* @tparam Component Components for which to perform the check.
|
||||
* @return True if the handle has at least one of the given components,
|
||||
* @brief Checks if a handle has at least one of the given elements.
|
||||
* @tparam Type Elements for which to perform the check.
|
||||
* @return True if the handle has at least one of the given elements,
|
||||
* false otherwise.
|
||||
*/
|
||||
template<typename... Component>
|
||||
template<typename... Type>
|
||||
[[nodiscard]] decltype(auto) any_of() const {
|
||||
return reg->template any_of<Component...>(entt);
|
||||
return owner_or_assert().template any_of<Type...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns references to the given components for a handle.
|
||||
* @tparam Component Types of components to get.
|
||||
* @return References to the components owned by the handle.
|
||||
* @brief Returns references to the given elements for a handle.
|
||||
* @tparam Type Types of elements to get.
|
||||
* @return References to the elements owned by the handle.
|
||||
*/
|
||||
template<typename... Component>
|
||||
template<typename... Type>
|
||||
[[nodiscard]] decltype(auto) get() const {
|
||||
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Component> && ...), "Invalid type");
|
||||
return reg->template get<Component...>(entt);
|
||||
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Type> && ...), "Invalid type");
|
||||
return owner_or_assert().template get<Type...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the given component for a handle.
|
||||
* @tparam Component Type of component to get.
|
||||
* @tparam Args Types of arguments to use to construct the component.
|
||||
* @param args Parameters to use to initialize the component.
|
||||
* @return Reference to the component owned by the handle.
|
||||
* @brief Returns a reference to the given element for a handle.
|
||||
* @tparam Type Type of element to get.
|
||||
* @tparam Args Types of arguments to use to construct the element.
|
||||
* @param args Parameters to use to initialize the element.
|
||||
* @return Reference to the element owned by the handle.
|
||||
*/
|
||||
template<typename Component, typename... Args>
|
||||
template<typename Type, typename... Args>
|
||||
[[nodiscard]] decltype(auto) get_or_emplace(Args &&...args) const {
|
||||
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
|
||||
return reg->template get_or_emplace<Component>(entt, std::forward<Args>(args)...);
|
||||
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type");
|
||||
return owner_or_assert().template get_or_emplace<Type>(entt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns pointers to the given components for a handle.
|
||||
* @tparam Component Types of components to get.
|
||||
* @return Pointers to the components owned by the handle.
|
||||
* @brief Returns pointers to the given elements for a handle.
|
||||
* @tparam Type Types of elements to get.
|
||||
* @return Pointers to the elements owned by the handle.
|
||||
*/
|
||||
template<typename... Component>
|
||||
template<typename... Type>
|
||||
[[nodiscard]] auto try_get() const {
|
||||
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Component> && ...), "Invalid type");
|
||||
return reg->template try_get<Component...>(entt);
|
||||
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Type> && ...), "Invalid type");
|
||||
return owner_or_assert().template try_get<Type...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a handle has components assigned.
|
||||
* @return True if the handle has no components assigned, false otherwise.
|
||||
* @brief Checks if a handle has elements assigned.
|
||||
* @return True if the handle has no elements assigned, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool orphan() const {
|
||||
return reg->orphan(entt);
|
||||
return owner_or_assert().orphan(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a const handle from a non-const one.
|
||||
* @tparam Other A valid entity type.
|
||||
* @tparam Args Scope of the handle to construct.
|
||||
* @return A const handle referring to the same registry and the same
|
||||
* entity.
|
||||
*/
|
||||
template<typename Other, typename... Args>
|
||||
operator basic_handle<Other, Args...>() const noexcept {
|
||||
static_assert(std::is_same_v<Other, Registry> || std::is_same_v<std::remove_const_t<Other>, Registry>, "Invalid conversion between different handles");
|
||||
static_assert((sizeof...(Scope) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Scope)) && ... && (type_list_contains_v<type_list<Scope...>, Args>))), "Invalid conversion between different handles");
|
||||
return owner ? basic_handle<Other, Args...>{*owner, entt} : basic_handle<Other, Args...>{};
|
||||
}
|
||||
|
||||
private:
|
||||
registry_type *reg;
|
||||
registry_type *owner;
|
||||
entity_type entt;
|
||||
};
|
||||
|
||||
@@ -372,6 +378,54 @@ template<typename... Args, typename... Other>
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares a handle with the null object.
|
||||
* @tparam Args Scope of the handle.
|
||||
* @param lhs A valid handle.
|
||||
* @param rhs A null object yet to be converted.
|
||||
* @return False if the two elements differ, true otherwise.
|
||||
*/
|
||||
template<typename... Args>
|
||||
[[nodiscard]] constexpr bool operator==(const basic_handle<Args...> &lhs, const null_t rhs) noexcept {
|
||||
return (lhs.entity() == rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares a handle with the null object.
|
||||
* @tparam Args Scope of the handle.
|
||||
* @param lhs A null object yet to be converted.
|
||||
* @param rhs A valid handle.
|
||||
* @return False if the two elements differ, true otherwise.
|
||||
*/
|
||||
template<typename... Args>
|
||||
[[nodiscard]] constexpr bool operator==(const null_t lhs, const basic_handle<Args...> &rhs) noexcept {
|
||||
return (rhs == lhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares a handle with the null object.
|
||||
* @tparam Args Scope of the handle.
|
||||
* @param lhs A valid handle.
|
||||
* @param rhs A null object yet to be converted.
|
||||
* @return True if the two elements differ, false otherwise.
|
||||
*/
|
||||
template<typename... Args>
|
||||
[[nodiscard]] constexpr bool operator!=(const basic_handle<Args...> &lhs, const null_t rhs) noexcept {
|
||||
return (lhs.entity() != rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares a handle with the null object.
|
||||
* @tparam Args Scope of the handle.
|
||||
* @param lhs A null object yet to be converted.
|
||||
* @param rhs A valid handle.
|
||||
* @return True if the two elements differ, false otherwise.
|
||||
*/
|
||||
template<typename... Args>
|
||||
[[nodiscard]] constexpr bool operator!=(const null_t lhs, const basic_handle<Args...> &rhs) noexcept {
|
||||
return (rhs != lhs);
|
||||
}
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <utility>
|
||||
#include "../core/fwd.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "../signal/delegate.hpp"
|
||||
#include "component.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "group.hpp"
|
||||
#include "storage.hpp"
|
||||
@@ -21,8 +21,8 @@ namespace entt {
|
||||
template<typename Registry>
|
||||
class as_view {
|
||||
template<typename... Get, typename... Exclude>
|
||||
auto dispatch(get_t<Get...>, exclude_t<Exclude...>) const {
|
||||
return reg.template view<constness_as_t<typename Get::value_type, Get>...>(exclude_t<constness_as_t<typename Exclude::value_type, Exclude>...>{});
|
||||
[[nodiscard]] auto dispatch(get_t<Get...>, exclude_t<Exclude...>) const {
|
||||
return reg.template view<constness_as_t<typename Get::element_type, Get>...>(exclude_t<constness_as_t<typename Exclude::element_type, Exclude>...>{});
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -60,11 +60,11 @@ private:
|
||||
template<typename Registry>
|
||||
class as_group {
|
||||
template<typename... Owned, typename... Get, typename... Exclude>
|
||||
auto dispatch(owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>) const {
|
||||
[[nodiscard]] auto dispatch(owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>) const {
|
||||
if constexpr(std::is_const_v<registry_type>) {
|
||||
return reg.template group_if_exists<typename Owned::value_type...>(get_t<typename Get::value_type...>{}, exclude_t<typename Exclude::value_type...>{});
|
||||
return reg.template group_if_exists<typename Owned::element_type...>(get_t<typename Get::element_type...>{}, exclude_t<typename Exclude::element_type...>{});
|
||||
} else {
|
||||
return reg.template group<constness_as_t<typename Owned::value_type, Owned>...>(get_t<constness_as_t<typename Get::value_type, Get>...>{}, exclude_t<constness_as_t<typename Exclude::value_type, Exclude>...>{});
|
||||
return reg.template group<constness_as_t<typename Owned::element_type, Owned>...>(get_t<constness_as_t<typename Get::element_type, Get>...>{}, exclude_t<constness_as_t<typename Exclude::element_type, Exclude>...>{});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,39 +99,38 @@ private:
|
||||
|
||||
/**
|
||||
* @brief Helper to create a listener that directly invokes a member function.
|
||||
* @tparam Member Member function to invoke on a component of the given type.
|
||||
* @tparam Member Member function to invoke on an element of the given type.
|
||||
* @tparam Registry Basic registry type.
|
||||
* @param reg A registry that contains the given entity and its components.
|
||||
* @param entt Entity from which to get the component.
|
||||
* @param reg A registry that contains the given entity and its elements.
|
||||
* @param entt Entity from which to get the element.
|
||||
*/
|
||||
template<auto Member, typename Registry = std::decay_t<nth_argument_t<0u, decltype(Member)>>>
|
||||
void invoke(Registry ®, const typename Registry::entity_type entt) {
|
||||
static_assert(std::is_member_function_pointer_v<decltype(Member)>, "Invalid pointer to non-static member function");
|
||||
delegate<void(Registry &, const typename Registry::entity_type)> func;
|
||||
func.template connect<Member>(reg.template get<member_class_t<decltype(Member)>>(entt));
|
||||
func(reg, entt);
|
||||
(reg.template get<member_class_t<decltype(Member)>>(entt).*Member)(reg, entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the entity associated with a given component.
|
||||
* @brief Returns the entity associated with a given element.
|
||||
*
|
||||
* @warning
|
||||
* Currently, this function only works correctly with the default storage as it
|
||||
* makes assumptions about how the components are laid out.
|
||||
* makes assumptions about how the elements are laid out.
|
||||
*
|
||||
* @tparam Args Storage type template parameters.
|
||||
* @param storage A storage that contains the given component.
|
||||
* @param instance A valid component instance.
|
||||
* @return The entity associated with the given component.
|
||||
* @param storage A storage that contains the given element.
|
||||
* @param instance A valid element instance.
|
||||
* @return The entity associated with the given element.
|
||||
*/
|
||||
template<typename... Args>
|
||||
auto to_entity(const basic_storage<Args...> &storage, const typename basic_storage<Args...>::value_type &instance) -> typename basic_storage<Args...>::entity_type {
|
||||
constexpr auto page_size = basic_storage<Args...>::traits_type::page_size;
|
||||
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>;
|
||||
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);
|
||||
|
||||
for(auto it = base.rbegin(), last = base.rend(); it < last; it += page_size) {
|
||||
if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < static_cast<decltype(dist)>(page_size)) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -139,23 +138,6 @@ auto to_entity(const basic_storage<Args...> &storage, const typename basic_stora
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @copybrief to_entity
|
||||
* @tparam Args Registry type template parameters.
|
||||
* @tparam Component Type of component.
|
||||
* @param reg A registry that contains the given entity and its components.
|
||||
* @param instance A valid component instance.
|
||||
* @return The entity associated with the given component.
|
||||
*/
|
||||
template<typename... Args, typename Component>
|
||||
[[deprecated("use storage based to_entity instead")]] typename basic_registry<Args...>::entity_type to_entity(const basic_registry<Args...> ®, const Component &instance) {
|
||||
if(const auto *storage = reg.template storage<Component>(); storage) {
|
||||
return to_entity(*storage, instance);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename...>
|
||||
struct sigh_helper;
|
||||
|
||||
@@ -5,12 +5,54 @@
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/any.hpp"
|
||||
#include "../core/type_info.hpp"
|
||||
#include "../signal/sigh.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename, typename, typename = void>
|
||||
struct has_on_construct final: std::false_type {};
|
||||
|
||||
template<typename Type, typename Registry>
|
||||
struct has_on_construct<Type, Registry, std::void_t<decltype(Type::on_construct(std::declval<Registry &>(), std::declval<Registry>().create()))>>
|
||||
: std::true_type {};
|
||||
|
||||
template<typename, typename, typename = void>
|
||||
struct has_on_update final: std::false_type {};
|
||||
|
||||
template<typename Type, typename Registry>
|
||||
struct has_on_update<Type, Registry, std::void_t<decltype(Type::on_update(std::declval<Registry &>(), std::declval<Registry>().create()))>>
|
||||
: std::true_type {};
|
||||
|
||||
template<typename, typename, typename = void>
|
||||
struct has_on_destroy final: std::false_type {};
|
||||
|
||||
template<typename Type, typename Registry>
|
||||
struct has_on_destroy<Type, Registry, std::void_t<decltype(Type::on_destroy(std::declval<Registry &>(), std::declval<Registry>().create()))>>
|
||||
: std::true_type {};
|
||||
|
||||
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 */
|
||||
|
||||
/**
|
||||
* @brief Mixin type used to add signal support to storage types.
|
||||
*
|
||||
@@ -30,17 +72,18 @@ class basic_sigh_mixin final: public Type {
|
||||
using underlying_type = Type;
|
||||
using owner_type = Registry;
|
||||
|
||||
using basic_registry_type = basic_registry<typename underlying_type::entity_type, typename underlying_type::base_type::allocator_type>;
|
||||
using basic_registry_type = basic_registry<typename owner_type::entity_type, typename owner_type::allocator_type>;
|
||||
using sigh_type = sigh<void(owner_type &, const typename underlying_type::entity_type), typename underlying_type::allocator_type>;
|
||||
using underlying_iterator = typename underlying_type::base_type::basic_iterator;
|
||||
|
||||
static_assert(std::is_base_of_v<basic_registry_type, owner_type>, "Invalid registry type");
|
||||
|
||||
owner_type &owner_or_assert() const noexcept {
|
||||
[[nodiscard]] auto &owner_or_assert() const noexcept {
|
||||
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
|
||||
return static_cast<owner_type &>(*owner);
|
||||
}
|
||||
|
||||
private:
|
||||
void pop(underlying_iterator first, underlying_iterator last) final {
|
||||
if(auto ® = owner_or_assert(); destruction.empty()) {
|
||||
underlying_type::pop(first, last);
|
||||
@@ -56,16 +99,18 @@ class basic_sigh_mixin final: public Type {
|
||||
|
||||
void pop_all() final {
|
||||
if(auto ® = owner_or_assert(); !destruction.empty()) {
|
||||
for(auto it = underlying_type::base_type::begin(0), last = underlying_type::base_type::end(0); it != last; ++it) {
|
||||
if constexpr(std::is_same_v<typename underlying_type::value_type, typename underlying_type::entity_type>) {
|
||||
destruction.publish(reg, *it);
|
||||
} else {
|
||||
if constexpr(underlying_type::traits_type::in_place_delete) {
|
||||
if(const auto entt = *it; entt != tombstone) {
|
||||
if constexpr(std::is_same_v<typename underlying_type::element_type, entity_type>) {
|
||||
for(typename underlying_type::size_type pos{}, last = underlying_type::free_list(); pos < last; ++pos) {
|
||||
destruction.publish(reg, underlying_type::base_type::operator[](pos));
|
||||
}
|
||||
} else {
|
||||
for(auto entt: static_cast<typename underlying_type::base_type &>(*this)) {
|
||||
if constexpr(underlying_type::storage_policy == deletion_policy::in_place) {
|
||||
if(entt != tombstone) {
|
||||
destruction.publish(reg, entt);
|
||||
}
|
||||
} else {
|
||||
destruction.publish(reg, *it);
|
||||
destruction.publish(reg, entt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,6 +129,11 @@ class basic_sigh_mixin final: public Type {
|
||||
return it;
|
||||
}
|
||||
|
||||
void bind_any(any value) noexcept final {
|
||||
owner = internal::any_to_owner<registry_type>(value);
|
||||
underlying_type::bind_any(std::move(value));
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = typename underlying_type::allocator_type;
|
||||
@@ -105,7 +155,22 @@ public:
|
||||
owner{},
|
||||
construction{allocator},
|
||||
destruction{allocator},
|
||||
update{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>();
|
||||
}
|
||||
|
||||
if constexpr(internal::has_on_update<typename underlying_type::element_type, Registry>::value) {
|
||||
entt::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>();
|
||||
}
|
||||
}
|
||||
|
||||
/*! @brief Default copy constructor, deleted on purpose. */
|
||||
basic_sigh_mixin(const basic_sigh_mixin &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
@@ -123,24 +188,29 @@ public:
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
basic_sigh_mixin(basic_sigh_mixin &&other, const allocator_type &allocator) noexcept
|
||||
basic_sigh_mixin(basic_sigh_mixin &&other, const allocator_type &allocator)
|
||||
: underlying_type{std::move(other), allocator},
|
||||
owner{other.owner},
|
||||
construction{std::move(other.construction), allocator},
|
||||
destruction{std::move(other.destruction), allocator},
|
||||
update{std::move(other.update), allocator} {}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~basic_sigh_mixin() override = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator, deleted on purpose.
|
||||
* @return This mixin.
|
||||
*/
|
||||
basic_sigh_mixin &operator=(const basic_sigh_mixin &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
* @param other The instance to move from.
|
||||
* @return This storage.
|
||||
* @return This mixin.
|
||||
*/
|
||||
basic_sigh_mixin &operator=(basic_sigh_mixin &&other) noexcept {
|
||||
underlying_type::operator=(std::move(other));
|
||||
owner = other.owner;
|
||||
construction = std::move(other.construction);
|
||||
destruction = std::move(other.destruction);
|
||||
update = std::move(other.update);
|
||||
swap(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -148,13 +218,13 @@ public:
|
||||
* @brief Exchanges the contents with those of a given storage.
|
||||
* @param other Storage to exchange the content with.
|
||||
*/
|
||||
void swap(basic_sigh_mixin &other) {
|
||||
void swap(basic_sigh_mixin &other) noexcept {
|
||||
using std::swap;
|
||||
underlying_type::swap(other);
|
||||
swap(owner, other.owner);
|
||||
swap(construction, other.construction);
|
||||
swap(destruction, other.destruction);
|
||||
swap(update, other.update);
|
||||
underlying_type::swap(other);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -202,6 +272,27 @@ public:
|
||||
return sink{destruction};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a mixin refers to a valid registry.
|
||||
* @return True if the mixin refers to a valid registry, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] explicit operator bool() const noexcept {
|
||||
return (owner != nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a pointer to the underlying registry, if any.
|
||||
* @return A pointer to the underlying registry, if any.
|
||||
*/
|
||||
[[nodiscard]] const registry_type ®istry() const noexcept {
|
||||
return owner_or_assert();
|
||||
}
|
||||
|
||||
/*! @copydoc registry */
|
||||
[[nodiscard]] registry_type ®istry() noexcept {
|
||||
return owner_or_assert();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Emplace elements into a storage.
|
||||
*
|
||||
@@ -209,7 +300,7 @@ public:
|
||||
* (for example, components vs entities).<br/>
|
||||
* Refer to the specific documentation for more details.
|
||||
*
|
||||
* @return A return value as returned by the underlying storage.
|
||||
* @return Whatever the underlying storage returns.
|
||||
*/
|
||||
auto emplace() {
|
||||
const auto entt = underlying_type::emplace();
|
||||
@@ -227,11 +318,12 @@ public:
|
||||
* @tparam Args Types of arguments to forward to the underlying storage.
|
||||
* @param hint A valid identifier.
|
||||
* @param args Parameters to forward to the underlying storage.
|
||||
* @return A return value as returned by the underlying storage.
|
||||
* @return Whatever the underlying storage returns.
|
||||
*/
|
||||
template<typename... Args>
|
||||
decltype(auto) emplace(const entity_type hint, Args &&...args) {
|
||||
if constexpr(std::is_same_v<typename underlying_type::value_type, typename underlying_type::entity_type>) {
|
||||
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;
|
||||
@@ -271,25 +363,16 @@ public:
|
||||
*/
|
||||
template<typename It, typename... Args>
|
||||
void insert(It first, It last, Args &&...args) {
|
||||
auto from = underlying_type::size();
|
||||
underlying_type::insert(first, last, std::forward<Args>(args)...);
|
||||
|
||||
if(auto ® = owner_or_assert(); !construction.empty()) {
|
||||
for(; first != last; ++first) {
|
||||
construction.publish(reg, *first);
|
||||
for(const auto to = underlying_type::size(); from != to; ++from) {
|
||||
construction.publish(reg, underlying_type::operator[](from));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Forwards variables to derived classes, if any.
|
||||
* @param value A variable wrapped in an opaque container.
|
||||
*/
|
||||
void bind(any value) noexcept final {
|
||||
auto *reg = any_cast<basic_registry_type>(&value);
|
||||
owner = reg ? reg : owner;
|
||||
underlying_type::bind(std::move(value));
|
||||
}
|
||||
|
||||
private:
|
||||
basic_registry_type *owner;
|
||||
sigh_type construction;
|
||||
@@ -297,6 +380,194 @@ private:
|
||||
sigh_type update;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Mixin type used to add _reactive_ support to storage types.
|
||||
* @tparam Type Underlying storage type.
|
||||
* @tparam Registry Basic registry type.
|
||||
*/
|
||||
template<typename Type, typename Registry>
|
||||
class basic_reactive_mixin final: public Type {
|
||||
using underlying_type = Type;
|
||||
using owner_type = Registry;
|
||||
|
||||
using basic_registry_type = basic_registry<typename owner_type::entity_type, typename owner_type::allocator_type>;
|
||||
|
||||
static_assert(std::is_base_of_v<basic_registry_type, owner_type>, "Invalid registry type");
|
||||
|
||||
[[nodiscard]] auto &owner_or_assert() const noexcept {
|
||||
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
|
||||
return static_cast<owner_type &>(*owner);
|
||||
}
|
||||
|
||||
void emplace_element(const Registry &, typename underlying_type::entity_type entity) {
|
||||
if(!underlying_type::contains(entity)) {
|
||||
underlying_type::emplace(entity);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void bind_any(any value) noexcept final {
|
||||
owner = internal::any_to_owner<registry_type>(value);
|
||||
underlying_type::bind_any(std::move(value));
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = typename underlying_type::allocator_type;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename underlying_type::entity_type;
|
||||
/*! @brief Expected registry type. */
|
||||
using registry_type = owner_type;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
basic_reactive_mixin()
|
||||
: basic_reactive_mixin{allocator_type{}} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an empty storage with a given allocator.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit basic_reactive_mixin(const allocator_type &allocator)
|
||||
: underlying_type{allocator},
|
||||
owner{} {
|
||||
}
|
||||
|
||||
/*! @brief Default copy constructor, deleted on purpose. */
|
||||
basic_reactive_mixin(const basic_reactive_mixin &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
basic_reactive_mixin(basic_reactive_mixin &&other) noexcept
|
||||
: underlying_type{std::move(other)},
|
||||
owner{other.owner} {}
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
basic_reactive_mixin(basic_reactive_mixin &&other, const allocator_type &allocator)
|
||||
: underlying_type{std::move(other), allocator},
|
||||
owner{other.owner} {}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~basic_reactive_mixin() override = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator, deleted on purpose.
|
||||
* @return This mixin.
|
||||
*/
|
||||
basic_reactive_mixin &operator=(const basic_reactive_mixin &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
* @param other The instance to move from.
|
||||
* @return This mixin.
|
||||
*/
|
||||
basic_reactive_mixin &operator=(basic_reactive_mixin &&other) noexcept {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Makes storage _react_ to creation of objects of the given type.
|
||||
* @tparam Clazz Type of element to _react_ to.
|
||||
* @tparam Candidate Function to use to _react_ to the event.
|
||||
* @param id Optional name used to map the storage within the registry.
|
||||
* @return This mixin.
|
||||
*/
|
||||
template<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
|
||||
basic_reactive_mixin &on_construct(const id_type id = type_hash<Clazz>::value()) {
|
||||
owner_or_assert().template storage<Clazz>(id).on_construct().template connect<Candidate>(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Makes storage _react_ to update of objects of the given type.
|
||||
* @tparam Clazz Type of element to _react_ to.
|
||||
* @tparam Candidate Function to use to _react_ to the event.
|
||||
* @param id Optional name used to map the storage within the registry.
|
||||
* @return This mixin.
|
||||
*/
|
||||
template<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
|
||||
basic_reactive_mixin &on_update(const id_type id = type_hash<Clazz>::value()) {
|
||||
owner_or_assert().template storage<Clazz>(id).on_update().template connect<Candidate>(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Makes storage _react_ to destruction of objects of the given type.
|
||||
* @tparam Clazz Type of element to _react_ to.
|
||||
* @tparam Candidate Function to use to _react_ to the event.
|
||||
* @param id Optional name used to map the storage within the registry.
|
||||
* @return This mixin.
|
||||
*/
|
||||
template<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
|
||||
basic_reactive_mixin &on_destroy(const id_type id = type_hash<Clazz>::value()) {
|
||||
owner_or_assert().template storage<Clazz>(id).on_destroy().template connect<Candidate>(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a mixin refers to a valid registry.
|
||||
* @return True if the mixin refers to a valid registry, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] explicit operator bool() const noexcept {
|
||||
return (owner != nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a pointer to the underlying registry, if any.
|
||||
* @return A pointer to the underlying registry, if any.
|
||||
*/
|
||||
[[nodiscard]] const registry_type ®istry() const noexcept {
|
||||
return owner_or_assert();
|
||||
}
|
||||
|
||||
/*! @copydoc registry */
|
||||
[[nodiscard]] registry_type ®istry() noexcept {
|
||||
return owner_or_assert();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a view that is filtered by the underlying storage.
|
||||
* @tparam Get Types of elements used to construct the view.
|
||||
* @tparam Exclude Types of elements used to filter the view.
|
||||
* @return A newly created view.
|
||||
*/
|
||||
template<typename... Get, typename... Exclude>
|
||||
[[nodiscard]] basic_view<get_t<const basic_reactive_mixin, typename basic_registry_type::template storage_for_type<const Get>...>, exclude_t<typename basic_registry_type::template storage_for_type<const Exclude>...>>
|
||||
view(exclude_t<Exclude...> = exclude_t{}) const {
|
||||
const owner_type &parent = owner_or_assert();
|
||||
basic_view<get_t<const basic_reactive_mixin, typename basic_registry_type::template storage_for_type<const Get>...>, exclude_t<typename basic_registry_type::template storage_for_type<const Exclude>...>> elem{};
|
||||
[&elem](const auto *...curr) { ((curr ? elem.storage(*curr) : void()), ...); }(parent.template storage<std::remove_const_t<Exclude>>()..., parent.template storage<std::remove_const_t<Get>>()..., this);
|
||||
return elem;
|
||||
}
|
||||
|
||||
/*! @copydoc view */
|
||||
template<typename... Get, typename... Exclude>
|
||||
[[nodiscard]] basic_view<get_t<const basic_reactive_mixin, typename basic_registry_type::template storage_for_type<Get>...>, exclude_t<typename basic_registry_type::template storage_for_type<Exclude>...>>
|
||||
view(exclude_t<Exclude...> = exclude_t{}) {
|
||||
owner_type &parent = owner_or_assert();
|
||||
return {*this, parent.template storage<std::remove_const_t<Get>>()..., parent.template storage<std::remove_const_t<Exclude>>()...};
|
||||
}
|
||||
|
||||
private:
|
||||
basic_registry_type *owner;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "../signal/delegate.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "storage.hpp"
|
||||
|
||||
@@ -38,8 +37,8 @@ template<>
|
||||
struct basic_collector<> {
|
||||
/**
|
||||
* @brief Adds a grouping matcher to the collector.
|
||||
* @tparam AllOf Types of components tracked by the matcher.
|
||||
* @tparam NoneOf Types of components used to filter out entities.
|
||||
* @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>
|
||||
@@ -49,7 +48,7 @@ struct basic_collector<> {
|
||||
|
||||
/**
|
||||
* @brief Adds an observing matcher to the collector.
|
||||
* @tparam AnyOf Type of component for which changes should be detected.
|
||||
* @tparam AnyOf Type of element for which changes should be detected.
|
||||
* @return The updated collector.
|
||||
*/
|
||||
template<typename AnyOf>
|
||||
@@ -67,14 +66,14 @@ struct basic_collector<> {
|
||||
* @tparam Other Other matchers.
|
||||
*/
|
||||
template<typename... Reject, typename... Require, typename... Rule, typename... Other>
|
||||
struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule...>, Other...> {
|
||||
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 components tracked by the matcher.
|
||||
* @tparam NoneOf Types of components used to filter out entities.
|
||||
* @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>
|
||||
@@ -84,7 +83,7 @@ struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule
|
||||
|
||||
/**
|
||||
* @brief Adds an observing matcher to the collector.
|
||||
* @tparam AnyOf Type of component for which changes should be detected.
|
||||
* @tparam AnyOf Type of element for which changes should be detected.
|
||||
* @return The updated collector.
|
||||
*/
|
||||
template<typename AnyOf>
|
||||
@@ -94,8 +93,8 @@ struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule
|
||||
|
||||
/**
|
||||
* @brief Updates the filter of the last added matcher.
|
||||
* @tparam AllOf Types of components required by the matcher.
|
||||
* @tparam NoneOf Types of components used to filter out entities.
|
||||
* @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>
|
||||
@@ -114,13 +113,13 @@ inline constexpr basic_collector<> collector{};
|
||||
* An observer returns all the entities and only the entities that fit the
|
||||
* requirements of at least one matcher. Moreover, it's guaranteed that the
|
||||
* entity list is tightly packed in memory for fast iterations.<br/>
|
||||
* In general, observers don't stay true to the order of any set of components.
|
||||
* 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 components have been updated and not yet
|
||||
* 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
|
||||
@@ -140,13 +139,13 @@ inline constexpr basic_collector<> collector{};
|
||||
*
|
||||
* Iterators aren't invalidated if:
|
||||
*
|
||||
* * New instances of the given components are created and assigned to entities.
|
||||
* * 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 components is removed from the entity to which the iterator points).
|
||||
* 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 components in any
|
||||
* way invalidates all the iterators.
|
||||
* 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
|
||||
@@ -155,12 +154,19 @@ inline constexpr basic_collector<> collector{};
|
||||
* pointers.
|
||||
*
|
||||
* @tparam Registry Basic registry type.
|
||||
* @tparam Mask Mask type.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Registry, typename Mask, typename Allocator>
|
||||
class basic_observer: private basic_storage<Mask, typename Registry::entity_type, Allocator> {
|
||||
using base_type = basic_storage<Mask, typename Registry::entity_type, Allocator>;
|
||||
template<typename 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;
|
||||
@@ -168,108 +174,93 @@ class basic_observer: private basic_storage<Mask, typename Registry::entity_type
|
||||
template<typename... Reject, typename... Require, typename AnyOf>
|
||||
struct matcher_handler<matcher<type_list<Reject...>, type_list<Require...>, AnyOf>> {
|
||||
template<std::size_t Index>
|
||||
static void maybe_valid_if(basic_observer &obs, Registry ®, const typename Registry::entity_type entt) {
|
||||
if(reg.template all_of<Require...>(entt) && !reg.template any_of<Reject...>(entt)) {
|
||||
if(!obs.contains(entt)) {
|
||||
obs.emplace(entt);
|
||||
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);
|
||||
}
|
||||
|
||||
obs.get(entt) |= (1 << Index);
|
||||
storage.get(entt) |= (1 << Index);
|
||||
}
|
||||
}
|
||||
|
||||
template<std::size_t Index>
|
||||
static void discard_if(basic_observer &obs, Registry &, const typename Registry::entity_type entt) {
|
||||
if(obs.contains(entt) && !(obs.get(entt) &= (~(1 << Index)))) {
|
||||
obs.erase(entt);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
template<std::size_t Index>
|
||||
static void connect(basic_observer &obs, Registry ®) {
|
||||
(reg.template on_destroy<Require>().template connect<&discard_if<Index>>(obs), ...);
|
||||
(reg.template on_construct<Reject>().template connect<&discard_if<Index>>(obs), ...);
|
||||
reg.template on_update<AnyOf>().template connect<&maybe_valid_if<Index>>(obs);
|
||||
reg.template on_destroy<AnyOf>().template connect<&discard_if<Index>>(obs);
|
||||
}
|
||||
|
||||
static void disconnect(basic_observer &obs, Registry ®) {
|
||||
(reg.template on_destroy<Require>().disconnect(&obs), ...);
|
||||
(reg.template on_construct<Reject>().disconnect(&obs), ...);
|
||||
reg.template on_update<AnyOf>().disconnect(&obs);
|
||||
reg.template on_destroy<AnyOf>().disconnect(&obs);
|
||||
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(basic_observer &obs, Registry ®, const typename Registry::entity_type entt) {
|
||||
auto condition = [®, entt]() {
|
||||
if constexpr(sizeof...(Ignore) == 0) {
|
||||
return reg.template all_of<AllOf..., Require...>(entt) && !reg.template any_of<NoneOf..., Reject...>(entt);
|
||||
} else {
|
||||
return reg.template all_of<AllOf..., Require...>(entt) && ((std::is_same_v<Ignore..., NoneOf> || !reg.template any_of<NoneOf>(entt)) && ...) && !reg.template any_of<Reject...>(entt);
|
||||
}
|
||||
};
|
||||
static void maybe_valid_if(storage_type &storage, Registry &parent, const typename Registry::entity_type entt) {
|
||||
bool guard{};
|
||||
|
||||
if(condition()) {
|
||||
if(!obs.contains(entt)) {
|
||||
obs.emplace(entt);
|
||||
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);
|
||||
}
|
||||
|
||||
obs.get(entt) |= (1 << Index);
|
||||
storage.get(entt) |= (1 << Index);
|
||||
}
|
||||
}
|
||||
|
||||
template<std::size_t Index>
|
||||
static void discard_if(basic_observer &obs, Registry &, const typename Registry::entity_type entt) {
|
||||
if(obs.contains(entt) && !(obs.get(entt) &= (~(1 << Index)))) {
|
||||
obs.erase(entt);
|
||||
}
|
||||
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), ...);
|
||||
}
|
||||
|
||||
template<std::size_t Index>
|
||||
static void connect(basic_observer &obs, Registry ®) {
|
||||
(reg.template on_destroy<Require>().template connect<&discard_if<Index>>(obs), ...);
|
||||
(reg.template on_construct<Reject>().template connect<&discard_if<Index>>(obs), ...);
|
||||
(reg.template on_construct<AllOf>().template connect<&maybe_valid_if<Index>>(obs), ...);
|
||||
(reg.template on_destroy<NoneOf>().template connect<&maybe_valid_if<Index, NoneOf>>(obs), ...);
|
||||
(reg.template on_destroy<AllOf>().template connect<&discard_if<Index>>(obs), ...);
|
||||
(reg.template on_construct<NoneOf>().template connect<&discard_if<Index>>(obs), ...);
|
||||
}
|
||||
|
||||
static void disconnect(basic_observer &obs, Registry ®) {
|
||||
(reg.template on_destroy<Require>().disconnect(&obs), ...);
|
||||
(reg.template on_construct<Reject>().disconnect(&obs), ...);
|
||||
(reg.template on_construct<AllOf>().disconnect(&obs), ...);
|
||||
(reg.template on_destroy<NoneOf>().disconnect(&obs), ...);
|
||||
(reg.template on_destroy<AllOf>().disconnect(&obs), ...);
|
||||
(reg.template on_construct<NoneOf>().disconnect(&obs), ...);
|
||||
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 ®, basic_observer &obs) {
|
||||
(matcher_handler<Matcher>::disconnect(obs, reg), ...);
|
||||
static void disconnect(Registry &parent, storage_type &storage) {
|
||||
(matcher_handler<Matcher>::disconnect(storage, parent), ...);
|
||||
}
|
||||
|
||||
template<typename... Matcher, std::size_t... Index>
|
||||
void connect(Registry ®, std::index_sequence<Index...>) {
|
||||
static_assert(sizeof...(Matcher) < std::numeric_limits<typename base_type::value_type>::digits, "Too many matchers");
|
||||
(matcher_handler<Matcher>::template connect<Index>(*this, reg), ...);
|
||||
release.template connect<&basic_observer::disconnect<Matcher...>>(reg);
|
||||
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 Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator = typename registry_type::common_type::iterator;
|
||||
|
||||
@@ -282,8 +273,9 @@ public:
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit basic_observer(const allocator_type &allocator)
|
||||
: base_type{allocator},
|
||||
release{} {}
|
||||
: release{},
|
||||
parent{},
|
||||
storage{allocator} {}
|
||||
|
||||
/*! @brief Default copy constructor, deleted on purpose. */
|
||||
basic_observer(const basic_observer &) = delete;
|
||||
@@ -299,10 +291,15 @@ public:
|
||||
*/
|
||||
template<typename... Matcher>
|
||||
basic_observer(registry_type ®, basic_collector<Matcher...>, const allocator_type &allocator = allocator_type{})
|
||||
: basic_observer{allocator} {
|
||||
connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{});
|
||||
: 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.
|
||||
@@ -323,15 +320,18 @@ public:
|
||||
template<typename... Matcher>
|
||||
void connect(registry_type ®, basic_collector<Matcher...>) {
|
||||
disconnect();
|
||||
connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{});
|
||||
base_type::clear();
|
||||
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(*this);
|
||||
release.reset();
|
||||
release(*parent, storage);
|
||||
release = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,7 +340,7 @@ public:
|
||||
* @return Number of elements.
|
||||
*/
|
||||
[[nodiscard]] size_type size() const noexcept {
|
||||
return base_type::size();
|
||||
return storage.size();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -348,7 +348,7 @@ public:
|
||||
* @return True if the observer is empty, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool empty() const noexcept {
|
||||
return base_type::empty();
|
||||
return storage.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -364,7 +364,7 @@ public:
|
||||
* @return A pointer to the array of entities.
|
||||
*/
|
||||
[[nodiscard]] const entity_type *data() const noexcept {
|
||||
return base_type::data();
|
||||
return storage.data();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -375,7 +375,7 @@ public:
|
||||
* @return An iterator to the first entity of the observer.
|
||||
*/
|
||||
[[nodiscard]] iterator begin() const noexcept {
|
||||
return base_type::base_type::begin();
|
||||
return storage.storage_type::base_type::begin();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -384,12 +384,12 @@ public:
|
||||
* observer.
|
||||
*/
|
||||
[[nodiscard]] iterator end() const noexcept {
|
||||
return base_type::base_type::end();
|
||||
return storage.storage_type::base_type::end();
|
||||
}
|
||||
|
||||
/*! @brief Clears the underlying container. */
|
||||
void clear() noexcept {
|
||||
base_type::clear();
|
||||
storage.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -428,7 +428,9 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
delegate<void(basic_observer &)> release;
|
||||
void (*release)(registry_type &, storage_type &);
|
||||
registry_type *parent;
|
||||
storage_type storage;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -52,8 +52,8 @@ struct unpack_type<const basic_registry<Args...>, type_list<Override...>>
|
||||
|
||||
template<typename... Get, typename... Exclude, typename... Override>
|
||||
struct unpack_type<basic_view<get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>> {
|
||||
using ro = type_list_cat_t<type_list<typename Exclude::value_type...>, typename unpack_type<constness_as_t<typename Get::value_type, Get>, type_list<Override...>>::ro...>;
|
||||
using rw = type_list_cat_t<typename unpack_type<constness_as_t<typename Get::value_type, Get>, type_list<Override...>>::rw...>;
|
||||
using ro = type_list_cat_t<type_list<typename Exclude::element_type...>, typename unpack_type<constness_as_t<typename Get::element_type, Get>, type_list<Override...>>::ro...>;
|
||||
using rw = type_list_cat_t<typename unpack_type<constness_as_t<typename Get::element_type, Get>, type_list<Override...>>::rw...>;
|
||||
};
|
||||
|
||||
template<typename... Get, typename... Exclude, typename... Override>
|
||||
@@ -108,7 +108,7 @@ class basic_organizer final {
|
||||
const char *name{};
|
||||
const void *payload{};
|
||||
callback_type *callback{};
|
||||
dependency_type *dependency;
|
||||
dependency_type *dependency{};
|
||||
prepare_type *prepare{};
|
||||
const type_info *info{};
|
||||
};
|
||||
@@ -130,14 +130,16 @@ class basic_organizer final {
|
||||
}
|
||||
|
||||
template<typename... Type>
|
||||
static std::size_t fill_dependencies(type_list<Type...>, [[maybe_unused]] const type_info **buffer, [[maybe_unused]] const std::size_t count) {
|
||||
[[nodiscard]] static std::size_t fill_dependencies(type_list<Type...>, [[maybe_unused]] const type_info **buffer, [[maybe_unused]] const std::size_t count) {
|
||||
if constexpr(sizeof...(Type) == 0u) {
|
||||
return {};
|
||||
} else {
|
||||
const type_info *info[sizeof...(Type)]{&type_id<Type>()...};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
|
||||
const type_info *info[]{&type_id<Type>()...};
|
||||
const auto length = count < sizeof...(Type) ? count : sizeof...(Type);
|
||||
|
||||
for(std::size_t pos{}; pos < length; ++pos) {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
buffer[pos] = info[pos];
|
||||
}
|
||||
|
||||
@@ -167,14 +169,14 @@ public:
|
||||
struct vertex {
|
||||
/**
|
||||
* @brief Constructs a vertex of the task graph.
|
||||
* @param vtype True if the vertex is a top-level one, false otherwise.
|
||||
* @param data The data associated with the vertex.
|
||||
* @param edges The indices of the children in the adjacency list.
|
||||
* @param from List of in-edges of the vertex.
|
||||
* @param to List of out-edges of the vertex.
|
||||
*/
|
||||
vertex(const bool vtype, vertex_data data, std::vector<std::size_t> edges)
|
||||
: is_top_level{vtype},
|
||||
node{std::move(data)},
|
||||
reachable{std::move(edges)} {}
|
||||
vertex(vertex_data data, std::vector<std::size_t> from, std::vector<std::size_t> to)
|
||||
: node{std::move(data)},
|
||||
in{std::move(from)},
|
||||
out{std::move(to)} {}
|
||||
|
||||
/**
|
||||
* @brief Fills a buffer with the type info objects for the writable
|
||||
@@ -183,7 +185,7 @@ public:
|
||||
* @param length The length of the user-supplied buffer.
|
||||
* @return The number of type info objects written to the buffer.
|
||||
*/
|
||||
size_type ro_dependency(const type_info **buffer, const std::size_t length) const noexcept {
|
||||
[[nodiscard]] size_type ro_dependency(const type_info **buffer, const std::size_t length) const noexcept {
|
||||
return node.dependency(false, buffer, length);
|
||||
}
|
||||
|
||||
@@ -194,7 +196,7 @@ public:
|
||||
* @param length The length of the user-supplied buffer.
|
||||
* @return The number of type info objects written to the buffer.
|
||||
*/
|
||||
size_type rw_dependency(const type_info **buffer, const std::size_t length) const noexcept {
|
||||
[[nodiscard]] size_type rw_dependency(const type_info **buffer, const std::size_t length) const noexcept {
|
||||
return node.dependency(true, buffer, length);
|
||||
}
|
||||
|
||||
@@ -202,7 +204,7 @@ public:
|
||||
* @brief Returns the number of read-only resources of a vertex.
|
||||
* @return The number of read-only resources of the vertex.
|
||||
*/
|
||||
size_type ro_count() const noexcept {
|
||||
[[nodiscard]] size_type ro_count() const noexcept {
|
||||
return node.ro_count;
|
||||
}
|
||||
|
||||
@@ -210,7 +212,7 @@ public:
|
||||
* @brief Returns the number of writable resources of a vertex.
|
||||
* @return The number of writable resources of the vertex.
|
||||
*/
|
||||
size_type rw_count() const noexcept {
|
||||
[[nodiscard]] size_type rw_count() const noexcept {
|
||||
return node.rw_count;
|
||||
}
|
||||
|
||||
@@ -218,15 +220,15 @@ public:
|
||||
* @brief Checks if a vertex is also a top-level one.
|
||||
* @return True if the vertex is a top-level one, false otherwise.
|
||||
*/
|
||||
bool top_level() const noexcept {
|
||||
return is_top_level;
|
||||
[[nodiscard]] bool top_level() const noexcept {
|
||||
return in.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a type info object associated with a vertex.
|
||||
* @return A properly initialized type info object.
|
||||
*/
|
||||
const type_info &info() const noexcept {
|
||||
[[nodiscard]] const type_info &info() const noexcept {
|
||||
return *node.info;
|
||||
}
|
||||
|
||||
@@ -234,7 +236,7 @@ public:
|
||||
* @brief Returns a user defined name associated with a vertex, if any.
|
||||
* @return The user defined name associated with the vertex, if any.
|
||||
*/
|
||||
const char *name() const noexcept {
|
||||
[[nodiscard]] const char *name() const noexcept {
|
||||
return node.name;
|
||||
}
|
||||
|
||||
@@ -242,7 +244,7 @@ public:
|
||||
* @brief Returns the function associated with a vertex.
|
||||
* @return The function associated with the vertex.
|
||||
*/
|
||||
function_type *callback() const noexcept {
|
||||
[[nodiscard]] function_type *callback() const noexcept {
|
||||
return node.callback;
|
||||
}
|
||||
|
||||
@@ -250,16 +252,32 @@ public:
|
||||
* @brief Returns the payload associated with a vertex, if any.
|
||||
* @return The payload associated with the vertex, if any.
|
||||
*/
|
||||
const void *data() const noexcept {
|
||||
[[nodiscard]] const void *data() const noexcept {
|
||||
return node.payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the list of in-edges of a vertex.
|
||||
* @return The list of in-edges of a vertex.
|
||||
*/
|
||||
[[nodiscard]] const std::vector<std::size_t> &in_edges() const noexcept {
|
||||
return in;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the list of out-edges of a vertex.
|
||||
* @return The list of out-edges of a vertex.
|
||||
*/
|
||||
[[nodiscard]] const std::vector<std::size_t> &out_edges() const noexcept {
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the list of nodes reachable from a given vertex.
|
||||
* @return The list of nodes reachable from the vertex.
|
||||
*/
|
||||
const std::vector<std::size_t> &children() const noexcept {
|
||||
return reachable;
|
||||
[[deprecated("use ::out_edges")]] [[nodiscard]] const std::vector<std::size_t> &children() const noexcept {
|
||||
return out_edges();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -272,9 +290,9 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
bool is_top_level;
|
||||
vertex_data node;
|
||||
std::vector<std::size_t> reachable;
|
||||
std::vector<std::size_t> in;
|
||||
std::vector<std::size_t> out;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -369,20 +387,24 @@ public:
|
||||
* @brief Generates a task graph for the current content.
|
||||
* @return The adjacency list of the task graph.
|
||||
*/
|
||||
std::vector<vertex> graph() {
|
||||
[[nodiscard]] std::vector<vertex> graph() {
|
||||
std::vector<vertex> adjacency_list{};
|
||||
adjacency_list.reserve(vertices.size());
|
||||
auto adjacency_matrix = builder.graph();
|
||||
|
||||
for(auto curr: adjacency_matrix.vertices()) {
|
||||
const auto iterable = adjacency_matrix.in_edges(curr);
|
||||
std::vector<std::size_t> reachable{};
|
||||
std::vector<std::size_t> in{};
|
||||
std::vector<std::size_t> out{};
|
||||
|
||||
for(auto &&edge: adjacency_matrix.out_edges(curr)) {
|
||||
reachable.push_back(edge.second);
|
||||
for(auto &&edge: adjacency_matrix.in_edges(curr)) {
|
||||
in.push_back(edge.first);
|
||||
}
|
||||
|
||||
adjacency_list.emplace_back(iterable.cbegin() == iterable.cend(), vertices[curr], std::move(reachable));
|
||||
for(auto &&edge: adjacency_matrix.out_edges(curr)) {
|
||||
out.push_back(edge.second);
|
||||
}
|
||||
|
||||
adjacency_list.emplace_back(vertices[curr], std::move(in), std::move(out));
|
||||
}
|
||||
|
||||
return adjacency_list;
|
||||
|
||||
26
src/entt/entity/ranges.hpp
Normal file
26
src/entt/entity/ranges.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef ENTT_ENTITY_RANGES_HPP
|
||||
#define ENTT_ENTITY_RANGES_HPP
|
||||
|
||||
#if __has_include(<version>)
|
||||
# include <version>
|
||||
#
|
||||
# if defined(__cpp_lib_ranges)
|
||||
# include <ranges>
|
||||
# include "fwd.hpp"
|
||||
|
||||
template<class... Args>
|
||||
inline constexpr bool std::ranges::enable_borrowed_range<entt::basic_view<Args...>>{true};
|
||||
|
||||
template<class... Args>
|
||||
inline constexpr bool std::ranges::enable_borrowed_range<entt::basic_group<Args...>>{true};
|
||||
|
||||
template<class... Args>
|
||||
inline constexpr bool std::ranges::enable_view<entt::basic_view<Args...>>{true};
|
||||
|
||||
template<class... Args>
|
||||
inline constexpr bool std::ranges::enable_view<entt::basic_group<Args...>>{true};
|
||||
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -2,6 +2,7 @@
|
||||
#define ENTT_ENTITY_REGISTRY_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
@@ -99,7 +100,7 @@ public:
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr reference operator*() const noexcept {
|
||||
return {it->first, *it->second};
|
||||
return operator[](0);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr pointer operator->() const noexcept {
|
||||
@@ -218,7 +219,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
dense_map<id_type, basic_any<0u>, identity, std::equal_to<id_type>, allocator_type> ctx;
|
||||
dense_map<id_type, basic_any<0u>, identity, std::equal_to<>, allocator_type> ctx;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
@@ -232,39 +233,42 @@ private:
|
||||
template<typename Entity, typename Allocator>
|
||||
class basic_registry {
|
||||
using base_type = basic_sparse_set<Entity, Allocator>;
|
||||
|
||||
using alloc_traits = std::allocator_traits<Allocator>;
|
||||
static_assert(std::is_same_v<typename alloc_traits::value_type, Entity>, "Invalid value type");
|
||||
|
||||
// std::shared_ptr because of its type erased allocator which is useful here
|
||||
using pool_container_type = dense_map<id_type, std::shared_ptr<base_type>, identity, std::equal_to<id_type>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, std::shared_ptr<base_type>>>>;
|
||||
using group_container_type = dense_map<id_type, std::shared_ptr<internal::group_descriptor>, identity, std::equal_to<id_type>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, std::shared_ptr<internal::group_descriptor>>>>;
|
||||
using pool_container_type = dense_map<id_type, std::shared_ptr<base_type>, identity, std::equal_to<>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, std::shared_ptr<base_type>>>>;
|
||||
using group_container_type = dense_map<id_type, std::shared_ptr<internal::group_descriptor>, identity, std::equal_to<>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, std::shared_ptr<internal::group_descriptor>>>>;
|
||||
using traits_type = entt_traits<Entity>;
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] auto &assure([[maybe_unused]] const id_type id = type_hash<Type>::value()) {
|
||||
static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Non-decayed types not allowed");
|
||||
|
||||
if constexpr(std::is_same_v<Type, entity_type>) {
|
||||
ENTT_ASSERT(id == type_hash<Type>::value(), "User entity storage not allowed");
|
||||
return entities;
|
||||
} else {
|
||||
auto &cpool = pools[id];
|
||||
using storage_type = storage_for_type<Type>;
|
||||
|
||||
if(!cpool) {
|
||||
using storage_type = storage_for_type<Type>;
|
||||
using alloc_type = typename storage_type::allocator_type;
|
||||
|
||||
if constexpr(std::is_void_v<Type> && !std::is_constructible_v<alloc_type, allocator_type>) {
|
||||
// std::allocator<void> has no cross constructors (waiting for C++20)
|
||||
cpool = std::allocate_shared<storage_type>(get_allocator(), alloc_type{});
|
||||
} else {
|
||||
cpool = std::allocate_shared<storage_type>(get_allocator(), get_allocator());
|
||||
}
|
||||
|
||||
cpool->bind(forward_as_any(*this));
|
||||
if(auto it = pools.find(id); it != pools.cend()) {
|
||||
ENTT_ASSERT(it->second->type() == type_id<Type>(), "Unexpected type");
|
||||
return static_cast<storage_type &>(*it->second);
|
||||
}
|
||||
|
||||
ENTT_ASSERT(cpool->type() == type_id<Type>(), "Unexpected type");
|
||||
return static_cast<storage_for_type<Type> &>(*cpool);
|
||||
using alloc_type = typename storage_type::allocator_type;
|
||||
typename pool_container_type::mapped_type cpool{};
|
||||
|
||||
if constexpr(std::is_void_v<Type> && !std::is_constructible_v<alloc_type, allocator_type>) {
|
||||
// std::allocator<void> has no cross constructors (waiting for C++20)
|
||||
cpool = std::allocate_shared<storage_type>(get_allocator(), alloc_type{});
|
||||
} else {
|
||||
cpool = std::allocate_shared<storage_type>(get_allocator(), get_allocator());
|
||||
}
|
||||
|
||||
pools.emplace(id, cpool);
|
||||
cpool->bind(*this);
|
||||
|
||||
return static_cast<storage_type &>(*cpool);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,6 +277,7 @@ class basic_registry {
|
||||
static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Non-decayed types not allowed");
|
||||
|
||||
if constexpr(std::is_same_v<Type, entity_type>) {
|
||||
ENTT_ASSERT(id == type_hash<Type>::value(), "User entity storage not allowed");
|
||||
return &entities;
|
||||
} else {
|
||||
if(const auto it = pools.find(id); it != pools.cend()) {
|
||||
@@ -285,16 +290,14 @@ class basic_registry {
|
||||
}
|
||||
|
||||
void rebind() {
|
||||
entities.bind(forward_as_any(*this));
|
||||
entities.bind(*this);
|
||||
|
||||
for(auto &&curr: pools) {
|
||||
curr.second->bind(forward_as_any(*this));
|
||||
curr.second->bind(*this);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Entity traits. */
|
||||
using traits_type = typename base_type::traits_type;
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
@@ -344,6 +347,9 @@ public:
|
||||
rebind();
|
||||
}
|
||||
|
||||
/*! @brief Default copy constructor, deleted on purpose. */
|
||||
basic_registry(const basic_registry &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
@@ -356,19 +362,22 @@ public:
|
||||
rebind();
|
||||
}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~basic_registry() = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator, deleted on purpose.
|
||||
* @return This mixin.
|
||||
*/
|
||||
basic_registry &operator=(const basic_registry &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
* @param other The instance to move from.
|
||||
* @return This registry.
|
||||
*/
|
||||
basic_registry &operator=(basic_registry &&other) noexcept {
|
||||
vars = std::move(other.vars);
|
||||
pools = std::move(other.pools);
|
||||
groups = std::move(other.groups);
|
||||
entities = std::move(other.entities);
|
||||
|
||||
rebind();
|
||||
|
||||
swap(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -376,7 +385,7 @@ public:
|
||||
* @brief Exchanges the contents with those of a given registry.
|
||||
* @param other Registry to exchange the content with.
|
||||
*/
|
||||
void swap(basic_registry &other) {
|
||||
void swap(basic_registry &other) noexcept {
|
||||
using std::swap;
|
||||
|
||||
swap(vars, other.vars);
|
||||
@@ -405,12 +414,12 @@ public:
|
||||
* @return An iterable object to use to _visit_ the registry.
|
||||
*/
|
||||
[[nodiscard]] iterable storage() noexcept {
|
||||
return iterable_adaptor{internal::registry_storage_iterator{pools.begin()}, internal::registry_storage_iterator{pools.end()}};
|
||||
return iterable{pools.begin(), pools.end()};
|
||||
}
|
||||
|
||||
/*! @copydoc storage */
|
||||
[[nodiscard]] const_iterable storage() const noexcept {
|
||||
return iterable_adaptor{internal::registry_storage_iterator{pools.cbegin()}, internal::registry_storage_iterator{pools.cend()}};
|
||||
return const_iterable{pools.cbegin(), pools.cend()};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -433,10 +442,10 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the storage for a given component type.
|
||||
* @tparam Type Type of component of which to return the storage.
|
||||
* @brief Returns the storage for a given element type.
|
||||
* @tparam Type Type of element of which to return the storage.
|
||||
* @param id Optional name used to map the storage within the registry.
|
||||
* @return The storage for the given component type.
|
||||
* @return The storage for the given element type.
|
||||
*/
|
||||
template<typename Type>
|
||||
storage_for_type<Type> &storage(const id_type id = type_hash<Type>::value()) {
|
||||
@@ -444,23 +453,33 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the storage for a given component type, if any.
|
||||
* @tparam Type Type of component of which to return the storage.
|
||||
* @brief Returns the storage for a given element type, if any.
|
||||
* @tparam Type Type of element of which to return the storage.
|
||||
* @param id Optional name used to map the storage within the registry.
|
||||
* @return The storage for the given component type.
|
||||
* @return The storage for the given element type.
|
||||
*/
|
||||
template<typename Type>
|
||||
const storage_for_type<Type> *storage(const id_type id = type_hash<Type>::value()) const {
|
||||
[[nodiscard]] const storage_for_type<Type> *storage(const id_type id = type_hash<Type>::value()) const {
|
||||
return assure<Type>(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Discards the storage associated with a given name, if any.
|
||||
* @param id Name used to map the storage within the registry.
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
bool reset(const id_type id) {
|
||||
ENTT_ASSERT(id != type_hash<entity_type>::value(), "Cannot reset entity storage");
|
||||
return !(pools.erase(id) == 0u);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if an identifier refers to a valid entity.
|
||||
* @param entt An identifier, either valid or not.
|
||||
* @return True if the identifier is valid, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool valid(const entity_type entt) const {
|
||||
return entities.contains(entt) && (entities.index(entt) < entities.free_list());
|
||||
return static_cast<size_type>(entities.find(entt).index()) < entities.free_list();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -512,14 +531,14 @@ public:
|
||||
* @brief Destroys an entity and releases its identifier.
|
||||
*
|
||||
* @warning
|
||||
* Adding or removing components to an entity that is being destroyed can
|
||||
* Adding or removing elements to an entity that is being destroyed can
|
||||
* result in undefined behavior.
|
||||
*
|
||||
* @param entt A valid identifier.
|
||||
* @return The version of the recycled entity.
|
||||
*/
|
||||
version_type destroy(const entity_type entt) {
|
||||
for(size_type pos = pools.size(); pos; --pos) {
|
||||
for(size_type pos = pools.size(); pos != 0u; --pos) {
|
||||
pools.begin()[pos - 1u].second->remove(entt);
|
||||
}
|
||||
|
||||
@@ -556,10 +575,8 @@ public:
|
||||
*/
|
||||
template<typename It>
|
||||
void destroy(It first, It last) {
|
||||
entities.sort_as(first, last);
|
||||
|
||||
const auto from = entities.cbegin(0);
|
||||
const auto to = from + std::distance(first, last);
|
||||
const auto to = entities.sort_as(first, last);
|
||||
const auto from = entities.cend() - entities.free_list();
|
||||
|
||||
for(auto &&curr: pools) {
|
||||
curr.second->remove(from, to);
|
||||
@@ -569,81 +586,82 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns the given component to an entity.
|
||||
* @brief Assigns the given element to an entity.
|
||||
*
|
||||
* The component must have a proper constructor or be of aggregate type.
|
||||
* The element must have a proper constructor or be of aggregate type.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to assign a component to an entity that already owns it
|
||||
* results in undefined behavior.
|
||||
* Attempting to assign an element to an entity that already owns it results
|
||||
* in undefined behavior.
|
||||
*
|
||||
* @tparam Type Type of component to create.
|
||||
* @tparam Args Types of arguments to use to construct the component.
|
||||
* @tparam Type Type of element to create.
|
||||
* @tparam Args Types of arguments to use to construct the element.
|
||||
* @param entt A valid identifier.
|
||||
* @param args Parameters to use to initialize the component.
|
||||
* @return A reference to the newly created component.
|
||||
* @param args Parameters to use to initialize the element.
|
||||
* @return A reference to the newly created element.
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
decltype(auto) emplace(const entity_type entt, Args &&...args) {
|
||||
ENTT_ASSERT(valid(entt), "Invalid entity");
|
||||
return assure<Type>().emplace(entt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns each entity in a range the given component.
|
||||
* @brief Assigns each entity in a range the given element.
|
||||
*
|
||||
* @sa emplace
|
||||
*
|
||||
* @tparam Type Type of component to create.
|
||||
* @tparam Type Type of element to create.
|
||||
* @tparam It Type of input iterator.
|
||||
* @param first An iterator to the first element of the range of entities.
|
||||
* @param last An iterator past the last element of the range of entities.
|
||||
* @param value An instance of the component to assign.
|
||||
* @param value An instance of the element to assign.
|
||||
*/
|
||||
template<typename Type, typename It>
|
||||
void insert(It first, It last, const Type &value = {}) {
|
||||
ENTT_ASSERT(std::all_of(first, last, [this](const auto entt) { return valid(entt); }), "Invalid entity");
|
||||
assure<Type>().insert(std::move(first), std::move(last), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns each entity in a range the given components.
|
||||
* @brief Assigns each entity in a range the given elements.
|
||||
*
|
||||
* @sa emplace
|
||||
*
|
||||
* @tparam Type Type of component to create.
|
||||
* @tparam Type Type of element to create.
|
||||
* @tparam EIt Type of input iterator.
|
||||
* @tparam CIt Type of input iterator.
|
||||
* @param first An iterator to the first element of the range of entities.
|
||||
* @param last An iterator past the last element of the range of entities.
|
||||
* @param from An iterator to the first element of the range of components.
|
||||
* @param from An iterator to the first element of the range of elements.
|
||||
*/
|
||||
template<typename Type, typename EIt, typename CIt, typename = std::enable_if_t<std::is_same_v<typename std::iterator_traits<CIt>::value_type, Type>>>
|
||||
void insert(EIt first, EIt last, CIt from) {
|
||||
ENTT_ASSERT(std::all_of(first, last, [this](const auto entt) { return valid(entt); }), "Invalid entity");
|
||||
assure<Type>().insert(first, last, from);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns or replaces the given component for an entity.
|
||||
* @brief Assigns or replaces the given element for an entity.
|
||||
*
|
||||
* @sa emplace
|
||||
* @sa replace
|
||||
*
|
||||
* @tparam Type Type of component to assign or replace.
|
||||
* @tparam Args Types of arguments to use to construct the component.
|
||||
* @tparam Type Type of element to assign or replace.
|
||||
* @tparam Args Types of arguments to use to construct the element.
|
||||
* @param entt A valid identifier.
|
||||
* @param args Parameters to use to initialize the component.
|
||||
* @return A reference to the newly created component.
|
||||
* @param args Parameters to use to initialize the element.
|
||||
* @return A reference to the newly created element.
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
decltype(auto) emplace_or_replace(const entity_type entt, Args &&...args) {
|
||||
if(auto &cpool = assure<Type>(); cpool.contains(entt)) {
|
||||
return cpool.patch(entt, [&args...](auto &...curr) { ((curr = Type{std::forward<Args>(args)...}), ...); });
|
||||
} else {
|
||||
return cpool.emplace(entt, std::forward<Args>(args)...);
|
||||
}
|
||||
auto &cpool = assure<Type>();
|
||||
ENTT_ASSERT(valid(entt), "Invalid entity");
|
||||
return cpool.contains(entt) ? cpool.patch(entt, [&args...](auto &...curr) { ((curr = Type{std::forward<Args>(args)...}), ...); }) : cpool.emplace(entt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Patches the given component for an entity.
|
||||
* @brief Patches the given element for an entity.
|
||||
*
|
||||
* The signature of the function should be equivalent to the following:
|
||||
*
|
||||
@@ -652,14 +670,14 @@ public:
|
||||
* @endcode
|
||||
*
|
||||
* @warning
|
||||
* Attempting to patch a component of an entity that doesn't own it
|
||||
* results in undefined behavior.
|
||||
* Attempting to patch an element of an entity that doesn't own it results
|
||||
* in undefined behavior.
|
||||
*
|
||||
* @tparam Type Type of component to patch.
|
||||
* @tparam Type Type of element to patch.
|
||||
* @tparam Func Types of the function objects to invoke.
|
||||
* @param entt A valid identifier.
|
||||
* @param func Valid function objects.
|
||||
* @return A reference to the patched component.
|
||||
* @return A reference to the patched element.
|
||||
*/
|
||||
template<typename Type, typename... Func>
|
||||
decltype(auto) patch(const entity_type entt, Func &&...func) {
|
||||
@@ -667,19 +685,19 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Replaces the given component for an entity.
|
||||
* @brief Replaces the given element for an entity.
|
||||
*
|
||||
* The component must have a proper constructor or be of aggregate type.
|
||||
* The element must have a proper constructor or be of aggregate type.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to replace a component of an entity that doesn't own it
|
||||
* results in undefined behavior.
|
||||
* Attempting to replace an element of an entity that doesn't own it results
|
||||
* in undefined behavior.
|
||||
*
|
||||
* @tparam Type Type of component to replace.
|
||||
* @tparam Args Types of arguments to use to construct the component.
|
||||
* @tparam Type Type of element to replace.
|
||||
* @tparam Args Types of arguments to use to construct the element.
|
||||
* @param entt A valid identifier.
|
||||
* @param args Parameters to use to initialize the component.
|
||||
* @return A reference to the component being replaced.
|
||||
* @param args Parameters to use to initialize the element.
|
||||
* @return A reference to the element being replaced.
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
decltype(auto) replace(const entity_type entt, Args &&...args) {
|
||||
@@ -687,11 +705,11 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes the given components from an entity.
|
||||
* @tparam Type Type of component to remove.
|
||||
* @tparam Other Other types of components to remove.
|
||||
* @brief Removes the given elements from an entity.
|
||||
* @tparam Type Type of element to remove.
|
||||
* @tparam Other Other types of elements to remove.
|
||||
* @param entt A valid identifier.
|
||||
* @return The number of components actually removed.
|
||||
* @return The number of elements actually removed.
|
||||
*/
|
||||
template<typename Type, typename... Other>
|
||||
size_type remove(const entity_type entt) {
|
||||
@@ -699,33 +717,34 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes the given components from all the entities in a range.
|
||||
* @brief Removes the given elements from all the entities in a range.
|
||||
*
|
||||
* @sa remove
|
||||
*
|
||||
* @tparam Type Type of component to remove.
|
||||
* @tparam Other Other types of components to remove.
|
||||
* @tparam Type Type of element to remove.
|
||||
* @tparam Other Other types of elements to remove.
|
||||
* @tparam It Type of input iterator.
|
||||
* @param first An iterator to the first element of the range of entities.
|
||||
* @param last An iterator past the last element of the range of entities.
|
||||
* @return The number of components actually removed.
|
||||
* @return The number of elements actually removed.
|
||||
*/
|
||||
template<typename Type, typename... Other, typename It>
|
||||
size_type remove(It first, It last) {
|
||||
size_type count{};
|
||||
|
||||
if constexpr(std::is_same_v<It, typename common_type::iterator>) {
|
||||
common_type *cpools[sizeof...(Other) + 1u]{&assure<Type>(), &assure<Other>()...};
|
||||
std::array cpools{static_cast<common_type *>(&assure<Type>()), static_cast<common_type *>(&assure<Other>())...};
|
||||
|
||||
for(size_type pos{}, len = sizeof...(Other) + 1u; pos < len; ++pos) {
|
||||
for(auto from = cpools.begin(), to = cpools.end(); from != to; ++from) {
|
||||
if constexpr(sizeof...(Other) != 0u) {
|
||||
if(cpools[pos]->data() == first.data()) {
|
||||
std::swap(cpools[pos], cpools[sizeof...(Other)]);
|
||||
if((*from)->data() == first.data()) {
|
||||
std::swap((*from), cpools.back());
|
||||
}
|
||||
}
|
||||
|
||||
count += cpools[pos]->remove(first, last);
|
||||
count += (*from)->remove(first, last);
|
||||
}
|
||||
|
||||
} else {
|
||||
for(auto cpools = std::forward_as_tuple(assure<Type>(), assure<Other>()...); first != last; ++first) {
|
||||
count += std::apply([entt = *first](auto &...curr) { return (curr.remove(entt) + ... + 0u); }, cpools);
|
||||
@@ -736,14 +755,14 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erases the given components from an entity.
|
||||
* @brief Erases the given elements from an entity.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to erase a component from an entity that doesn't own it
|
||||
* results in undefined behavior.
|
||||
* Attempting to erase an element from an entity that doesn't own it results
|
||||
* in undefined behavior.
|
||||
*
|
||||
* @tparam Type Types of components to erase.
|
||||
* @tparam Other Other types of components to erase.
|
||||
* @tparam Type Types of elements to erase.
|
||||
* @tparam Other Other types of elements to erase.
|
||||
* @param entt A valid identifier.
|
||||
*/
|
||||
template<typename Type, typename... Other>
|
||||
@@ -752,12 +771,12 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erases the given components from all the entities in a range.
|
||||
* @brief Erases the given elements from all the entities in a range.
|
||||
*
|
||||
* @sa erase
|
||||
*
|
||||
* @tparam Type Types of components to erase.
|
||||
* @tparam Other Other types of components to erase.
|
||||
* @tparam Type Types of elements to erase.
|
||||
* @tparam Other Other types of elements to erase.
|
||||
* @tparam It Type of input iterator.
|
||||
* @param first An iterator to the first element of the range of entities.
|
||||
* @param last An iterator past the last element of the range of entities.
|
||||
@@ -765,16 +784,16 @@ public:
|
||||
template<typename Type, typename... Other, typename It>
|
||||
void erase(It first, It last) {
|
||||
if constexpr(std::is_same_v<It, typename common_type::iterator>) {
|
||||
common_type *cpools[sizeof...(Other) + 1u]{&assure<Type>(), &assure<Other>()...};
|
||||
std::array cpools{static_cast<common_type *>(&assure<Type>()), static_cast<common_type *>(&assure<Other>())...};
|
||||
|
||||
for(size_type pos{}, len = sizeof...(Other) + 1u; pos < len; ++pos) {
|
||||
for(auto from = cpools.begin(), to = cpools.end(); from != to; ++from) {
|
||||
if constexpr(sizeof...(Other) != 0u) {
|
||||
if(cpools[pos]->data() == first.data()) {
|
||||
std::swap(cpools[pos], cpools[sizeof...(Other)]);
|
||||
if((*from)->data() == first.data()) {
|
||||
std::swap(*from, cpools.back());
|
||||
}
|
||||
}
|
||||
|
||||
cpools[pos]->erase(first, last);
|
||||
(*from)->erase(first, last);
|
||||
}
|
||||
} else {
|
||||
for(auto cpools = std::forward_as_tuple(assure<Type>(), assure<Other>()...); first != last; ++first) {
|
||||
@@ -784,12 +803,12 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erases components satisfying specific criteria from an entity.
|
||||
* @brief Erases elements satisfying specific criteria from an entity.
|
||||
*
|
||||
* The function type is equivalent to:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const id_type, typename basic_registry<Entity>::base_type &);
|
||||
* void(const id_type, typename basic_registry<Entity>::common_type &);
|
||||
* @endcode
|
||||
*
|
||||
* Only storage where the entity exists are passed to the function.
|
||||
@@ -809,8 +828,8 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Removes all tombstones from a registry or only the pools for the
|
||||
* given components.
|
||||
* @tparam Type Types of components for which to clear all tombstones.
|
||||
* given elements.
|
||||
* @tparam Type Types of elements for which to clear all tombstones.
|
||||
*/
|
||||
template<typename... Type>
|
||||
void compact() {
|
||||
@@ -852,15 +871,15 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns references to the given components for an entity.
|
||||
* @brief Returns references to the given elements for an entity.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to get a component from an entity that doesn't own it results
|
||||
* Attempting to get an element from an entity that doesn't own it results
|
||||
* in undefined behavior.
|
||||
*
|
||||
* @tparam Type Types of components to get.
|
||||
* @tparam Type Types of elements to get.
|
||||
* @param entt A valid identifier.
|
||||
* @return References to the components owned by the entity.
|
||||
* @return References to the elements owned by the entity.
|
||||
*/
|
||||
template<typename... Type>
|
||||
[[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entt) const {
|
||||
@@ -882,38 +901,36 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the given component for an entity.
|
||||
* @brief Returns a reference to the given element for an entity.
|
||||
*
|
||||
* In case the entity doesn't own the component, the parameters provided are
|
||||
* In case the entity doesn't own the element, the parameters provided are
|
||||
* used to construct it.
|
||||
*
|
||||
* @sa get
|
||||
* @sa emplace
|
||||
*
|
||||
* @tparam Type Type of component to get.
|
||||
* @tparam Args Types of arguments to use to construct the component.
|
||||
* @tparam Type Type of element to get.
|
||||
* @tparam Args Types of arguments to use to construct the element.
|
||||
* @param entt A valid identifier.
|
||||
* @param args Parameters to use to initialize the component.
|
||||
* @return Reference to the component owned by the entity.
|
||||
* @param args Parameters to use to initialize the element.
|
||||
* @return Reference to the element owned by the entity.
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
[[nodiscard]] decltype(auto) get_or_emplace(const entity_type entt, Args &&...args) {
|
||||
if(auto &cpool = assure<Type>(); cpool.contains(entt)) {
|
||||
return cpool.get(entt);
|
||||
} else {
|
||||
return cpool.emplace(entt, std::forward<Args>(args)...);
|
||||
}
|
||||
auto &cpool = assure<Type>();
|
||||
ENTT_ASSERT(valid(entt), "Invalid entity");
|
||||
return cpool.contains(entt) ? cpool.get(entt) : cpool.emplace(entt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns pointers to the given components for an entity.
|
||||
* @brief Returns pointers to the given elements for an entity.
|
||||
*
|
||||
* @note
|
||||
* The registry retains ownership of the pointed-to components.
|
||||
* The registry retains ownership of the pointed-to elements.
|
||||
*
|
||||
* @tparam Type Types of components to get.
|
||||
* @tparam Type Types of elements to get.
|
||||
* @param entt A valid identifier.
|
||||
* @return Pointers to the components owned by the entity.
|
||||
* @return Pointers to the elements owned by the entity.
|
||||
*/
|
||||
template<typename... Type>
|
||||
[[nodiscard]] auto try_get([[maybe_unused]] const entity_type entt) const {
|
||||
@@ -936,8 +953,8 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clears a whole registry or the pools for the given components.
|
||||
* @tparam Type Types of components to remove from their entities.
|
||||
* @brief Clears a whole registry or the pools for the given elements.
|
||||
* @tparam Type Types of elements to remove from their entities.
|
||||
*/
|
||||
template<typename... Type>
|
||||
void clear() {
|
||||
@@ -954,30 +971,30 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if an entity has components assigned.
|
||||
* @brief Checks if an entity has elements assigned.
|
||||
* @param entt A valid identifier.
|
||||
* @return True if the entity has no components assigned, false otherwise.
|
||||
* @return True if the entity has no elements assigned, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool orphan(const entity_type entt) const {
|
||||
return std::none_of(pools.cbegin(), pools.cend(), [entt](auto &&curr) { return curr.second->contains(entt); });
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a sink object for the given component.
|
||||
* @brief Returns a sink object for the given element.
|
||||
*
|
||||
* Use this function to receive notifications whenever a new instance of the
|
||||
* given component is created and assigned to an entity.<br/>
|
||||
* given element is created and assigned to an entity.<br/>
|
||||
* The function type for a listener is equivalent to:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(basic_registry<Entity> &, Entity);
|
||||
* @endcode
|
||||
*
|
||||
* Listeners are invoked **after** assigning the component to the entity.
|
||||
* Listeners are invoked **after** assigning the element to the entity.
|
||||
*
|
||||
* @sa sink
|
||||
*
|
||||
* @tparam Type Type of component of which to get the sink.
|
||||
* @tparam Type Type of element of which to get the sink.
|
||||
* @param id Optional name used to map the storage within the registry.
|
||||
* @return A temporary sink object.
|
||||
*/
|
||||
@@ -987,21 +1004,21 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a sink object for the given component.
|
||||
* @brief Returns a sink object for the given element.
|
||||
*
|
||||
* Use this function to receive notifications whenever an instance of the
|
||||
* given component is explicitly updated.<br/>
|
||||
* given element is explicitly updated.<br/>
|
||||
* The function type for a listener is equivalent to:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(basic_registry<Entity> &, Entity);
|
||||
* @endcode
|
||||
*
|
||||
* Listeners are invoked **after** updating the component.
|
||||
* Listeners are invoked **after** updating the element.
|
||||
*
|
||||
* @sa sink
|
||||
*
|
||||
* @tparam Type Type of component of which to get the sink.
|
||||
* @tparam Type Type of element of which to get the sink.
|
||||
* @param id Optional name used to map the storage within the registry.
|
||||
* @return A temporary sink object.
|
||||
*/
|
||||
@@ -1011,21 +1028,21 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a sink object for the given component.
|
||||
* @brief Returns a sink object for the given element.
|
||||
*
|
||||
* Use this function to receive notifications whenever an instance of the
|
||||
* given component is removed from an entity and thus destroyed.<br/>
|
||||
* given element is removed from an entity and thus destroyed.<br/>
|
||||
* The function type for a listener is equivalent to:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(basic_registry<Entity> &, Entity);
|
||||
* @endcode
|
||||
*
|
||||
* Listeners are invoked **before** removing the component from the entity.
|
||||
* Listeners are invoked **before** removing the element from the entity.
|
||||
*
|
||||
* @sa sink
|
||||
*
|
||||
* @tparam Type Type of component of which to get the sink.
|
||||
* @tparam Type Type of element of which to get the sink.
|
||||
* @param id Optional name used to map the storage within the registry.
|
||||
* @return A temporary sink object.
|
||||
*/
|
||||
@@ -1035,18 +1052,17 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a view for the given components.
|
||||
* @tparam Type Type of component used to construct the view.
|
||||
* @tparam Other Other types of components used to construct the view.
|
||||
* @tparam Exclude Types of components used to filter the view.
|
||||
* @brief Returns a view for the given elements.
|
||||
* @tparam Type Type of element used to construct the view.
|
||||
* @tparam Other Other types of elements used to construct the view.
|
||||
* @tparam Exclude Types of elements used to filter the view.
|
||||
* @return A newly created view.
|
||||
*/
|
||||
template<typename Type, typename... Other, typename... Exclude>
|
||||
[[nodiscard]] basic_view<get_t<storage_for_type<const Type>, storage_for_type<const Other>...>, exclude_t<storage_for_type<const Exclude>...>>
|
||||
view(exclude_t<Exclude...> = exclude_t{}) const {
|
||||
const auto cpools = std::make_tuple(assure<std::remove_const_t<Type>>(), assure<std::remove_const_t<Other>>()..., assure<std::remove_const_t<Exclude>>()...);
|
||||
basic_view<get_t<storage_for_type<const Type>, storage_for_type<const Other>...>, exclude_t<storage_for_type<const Exclude>...>> elem{};
|
||||
std::apply([&elem](const auto *...curr) { ((curr ? elem.storage(*curr) : void()), ...); }, cpools);
|
||||
[&elem](const auto *...curr) { ((curr ? elem.storage(*curr) : void()), ...); }(assure<std::remove_const_t<Exclude>>()..., assure<std::remove_const_t<Other>>()..., assure<std::remove_const_t<Type>>());
|
||||
return elem;
|
||||
}
|
||||
|
||||
@@ -1058,7 +1074,7 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a group for the given components.
|
||||
* @brief Returns a group for the given elements.
|
||||
* @tparam Owned Types of storage _owned_ by the group.
|
||||
* @tparam Get Types of storage _observed_ by the group, if any.
|
||||
* @tparam Exclude Types of storage used to filter the group, if any.
|
||||
@@ -1067,33 +1083,34 @@ public:
|
||||
template<typename... Owned, typename... Get, typename... Exclude>
|
||||
basic_group<owned_t<storage_for_type<Owned>...>, get_t<storage_for_type<Get>...>, exclude_t<storage_for_type<Exclude>...>>
|
||||
group(get_t<Get...> = get_t{}, exclude_t<Exclude...> = exclude_t{}) {
|
||||
using handler_type = typename basic_group<owned_t<storage_for_type<Owned>...>, get_t<storage_for_type<Get>...>, exclude_t<storage_for_type<Exclude>...>>::handler;
|
||||
using group_type = basic_group<owned_t<storage_for_type<Owned>...>, get_t<storage_for_type<Get>...>, exclude_t<storage_for_type<Exclude>...>>;
|
||||
using handler_type = typename group_type::handler;
|
||||
|
||||
if(auto it = groups.find(type_hash<handler_type>::value()); it != groups.cend()) {
|
||||
if(auto it = groups.find(group_type::group_id()); it != groups.cend()) {
|
||||
return {*std::static_pointer_cast<handler_type>(it->second)};
|
||||
}
|
||||
|
||||
std::shared_ptr<handler_type> handler{};
|
||||
|
||||
if constexpr(sizeof...(Owned) == 0u) {
|
||||
handler = std::allocate_shared<handler_type>(get_allocator(), get_allocator(), assure<std::remove_const_t<Get>>()..., assure<std::remove_const_t<Exclude>>()...);
|
||||
handler = std::allocate_shared<handler_type>(get_allocator(), get_allocator(), std::forward_as_tuple(assure<std::remove_const_t<Get>>()...), std::forward_as_tuple(assure<std::remove_const_t<Exclude>>()...));
|
||||
} else {
|
||||
handler = std::allocate_shared<handler_type>(get_allocator(), assure<std::remove_const_t<Owned>>()..., assure<std::remove_const_t<Get>>()..., assure<std::remove_const_t<Exclude>>()...);
|
||||
[[maybe_unused]] const id_type elem[]{type_hash<std::remove_const_t<Owned>>::value()..., type_hash<std::remove_const_t<Get>>::value()..., type_hash<std::remove_const_t<Exclude>>::value()...};
|
||||
ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), [&elem](const auto &data) { return data.second->owned(elem, sizeof...(Owned)) == 0u; }), "Conflicting groups");
|
||||
handler = std::allocate_shared<handler_type>(get_allocator(), std::forward_as_tuple(assure<std::remove_const_t<Owned>>()..., assure<std::remove_const_t<Get>>()...), std::forward_as_tuple(assure<std::remove_const_t<Exclude>>()...));
|
||||
ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), [](const auto &data) { return !(data.second->owned(type_id<Owned>().hash()) || ...); }), "Conflicting groups");
|
||||
}
|
||||
|
||||
groups.emplace(type_hash<handler_type>::value(), handler);
|
||||
groups.emplace(group_type::group_id(), handler);
|
||||
return {*handler};
|
||||
}
|
||||
|
||||
/*! @copydoc group */
|
||||
template<typename... Owned, typename... Get, typename... Exclude>
|
||||
basic_group<owned_t<storage_for_type<const Owned>...>, get_t<storage_for_type<const Get>...>, exclude_t<storage_for_type<const Exclude>...>>
|
||||
[[nodiscard]] basic_group<owned_t<storage_for_type<const Owned>...>, get_t<storage_for_type<const Get>...>, exclude_t<storage_for_type<const Exclude>...>>
|
||||
group_if_exists(get_t<Get...> = get_t{}, exclude_t<Exclude...> = exclude_t{}) const {
|
||||
using handler_type = typename basic_group<owned_t<storage_for_type<const Owned>...>, get_t<storage_for_type<const Get>...>, exclude_t<storage_for_type<const Exclude>...>>::handler;
|
||||
using group_type = basic_group<owned_t<storage_for_type<const Owned>...>, get_t<storage_for_type<const Get>...>, exclude_t<storage_for_type<const Exclude>...>>;
|
||||
using handler_type = typename group_type::handler;
|
||||
|
||||
if(auto it = groups.find(type_hash<handler_type>::value()); it != groups.cend()) {
|
||||
if(auto it = groups.find(group_type::group_id()); it != groups.cend()) {
|
||||
return {*std::static_pointer_cast<handler_type>(it->second)};
|
||||
}
|
||||
|
||||
@@ -1101,20 +1118,18 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether the given components belong to any group.
|
||||
* @tparam Type Type of component in which one is interested.
|
||||
* @tparam Other Other types of components in which one is interested.
|
||||
* @return True if the pools of the given components are _free_, false
|
||||
* @brief Checks whether the given elements belong to any group.
|
||||
* @tparam Type Types of elements in which one is interested.
|
||||
* @return True if the pools of the given elements are _free_, false
|
||||
* otherwise.
|
||||
*/
|
||||
template<typename Type, typename... Other>
|
||||
template<typename... Type>
|
||||
[[nodiscard]] bool owned() const {
|
||||
const id_type elem[]{type_hash<std::remove_const_t<Type>>::value(), type_hash<std::remove_const_t<Other>>::value()...};
|
||||
return std::any_of(groups.cbegin(), groups.cend(), [&elem](auto &&data) { return data.second->owned(elem, 1u + sizeof...(Other)); });
|
||||
return std::any_of(groups.cbegin(), groups.cend(), [](auto &&data) { return (data.second->owned(type_id<Type>().hash()) || ...); });
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sorts the elements of a given component.
|
||||
* @brief Sorts the elements of a given element.
|
||||
*
|
||||
* The comparison function object returns `true` if the first element is
|
||||
* _less_ than the second one, `false` otherwise. Its signature is also
|
||||
@@ -1136,9 +1151,9 @@ public:
|
||||
* passed along with the other parameters to this member function.
|
||||
*
|
||||
* @warning
|
||||
* Pools of components owned by a group cannot be sorted.
|
||||
* Pools of elements owned by a group cannot be sorted.
|
||||
*
|
||||
* @tparam Type Type of components to sort.
|
||||
* @tparam Type Type of elements to sort.
|
||||
* @tparam Compare Type of comparison function object.
|
||||
* @tparam Sort Type of sort function object.
|
||||
* @tparam Args Types of arguments to forward to the sort function object.
|
||||
@@ -1160,17 +1175,17 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sorts two pools of components in the same way.
|
||||
* @brief Sorts two pools of elements in the same way.
|
||||
*
|
||||
* Entities and components in `To` which are part of both storage are sorted
|
||||
* Entities and elements in `To` which are part of both storage are sorted
|
||||
* internally with the order they have in `From`. The others follow in no
|
||||
* particular order.
|
||||
*
|
||||
* @warning
|
||||
* Pools of components owned by a group cannot be sorted.
|
||||
* Pools of elements owned by a group cannot be sorted.
|
||||
*
|
||||
* @tparam To Type of components to sort.
|
||||
* @tparam From Type of components to use to sort.
|
||||
* @tparam To Type of elements to sort.
|
||||
* @tparam From Type of elements to use to sort.
|
||||
*/
|
||||
template<typename To, typename From>
|
||||
void sort() {
|
||||
@@ -1183,12 +1198,12 @@ public:
|
||||
* @brief Returns the context object, that is, a general purpose container.
|
||||
* @return The context object, that is, a general purpose container.
|
||||
*/
|
||||
context &ctx() noexcept {
|
||||
[[nodiscard]] context &ctx() noexcept {
|
||||
return vars;
|
||||
}
|
||||
|
||||
/*! @copydoc ctx */
|
||||
const context &ctx() const noexcept {
|
||||
[[nodiscard]] const context &ctx() const noexcept {
|
||||
return vars;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace internal {
|
||||
template<typename Set>
|
||||
class runtime_view_iterator final {
|
||||
using iterator_type = typename Set::iterator;
|
||||
using iterator_traits = std::iterator_traits<iterator_type>;
|
||||
|
||||
[[nodiscard]] bool valid() const {
|
||||
return (!tombstone_check || *it != tombstone)
|
||||
@@ -25,10 +26,10 @@ class runtime_view_iterator final {
|
||||
}
|
||||
|
||||
public:
|
||||
using difference_type = typename iterator_type::difference_type;
|
||||
using value_type = typename iterator_type::value_type;
|
||||
using pointer = typename iterator_type::pointer;
|
||||
using reference = typename iterator_type::reference;
|
||||
using value_type = typename iterator_traits::value_type;
|
||||
using pointer = typename iterator_traits::pointer;
|
||||
using reference = typename iterator_traits::reference;
|
||||
using difference_type = typename iterator_traits::difference_type;
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
|
||||
constexpr runtime_view_iterator() noexcept
|
||||
@@ -37,6 +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
|
||||
: pools{&cpools},
|
||||
filter{&ignore},
|
||||
@@ -48,7 +50,8 @@ public:
|
||||
}
|
||||
|
||||
runtime_view_iterator &operator++() {
|
||||
while(++it != (*pools)[0]->end() && !valid()) {}
|
||||
++it;
|
||||
for(const auto last = (*pools)[0]->end(); it != last && !valid(); ++it) {}
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -58,7 +61,8 @@ public:
|
||||
}
|
||||
|
||||
runtime_view_iterator &operator--() {
|
||||
while(--it != (*pools)[0]->begin() && !valid()) {}
|
||||
--it;
|
||||
for(const auto first = (*pools)[0]->begin(); it != first && !valid(); --it) {}
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -98,15 +102,15 @@ private:
|
||||
*
|
||||
* Runtime views iterate over those entities that are at least in the given
|
||||
* storage. During initialization, a runtime view looks at the number of
|
||||
* entities available for each component and uses the smallest set in order to
|
||||
* get a performance boost when iterating.
|
||||
* entities available for each element and uses the smallest set in order to get
|
||||
* a performance boost when iterating.
|
||||
*
|
||||
* @b Important
|
||||
*
|
||||
* Iterators aren't invalidated if:
|
||||
*
|
||||
* * New elements are added to the storage.
|
||||
* * The entity currently pointed is modified (for example, components are added
|
||||
* * The entity currently pointed is modified (for example, elements are added
|
||||
* or removed from it).
|
||||
* * The entity currently pointed is destroyed.
|
||||
*
|
||||
@@ -159,7 +163,7 @@ public:
|
||||
filter{other.filter, allocator} {}
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
basic_runtime_view(basic_runtime_view &&) noexcept(std::is_nothrow_move_constructible_v<container_type>) = default;
|
||||
basic_runtime_view(basic_runtime_view &&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
@@ -170,23 +174,26 @@ public:
|
||||
: pools{std::move(other.pools), allocator},
|
||||
filter{std::move(other.filter), allocator} {}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~basic_runtime_view() = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator.
|
||||
* @return This container.
|
||||
* @return This runtime view.
|
||||
*/
|
||||
basic_runtime_view &operator=(const basic_runtime_view &) = default;
|
||||
|
||||
/**
|
||||
* @brief Default move assignment operator.
|
||||
* @return This container.
|
||||
* @return This runtime view.
|
||||
*/
|
||||
basic_runtime_view &operator=(basic_runtime_view &&) noexcept(std::is_nothrow_move_assignable_v<container_type>) = default;
|
||||
basic_runtime_view &operator=(basic_runtime_view &&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Exchanges the contents with those of a given view.
|
||||
* @param other View to exchange the content with.
|
||||
*/
|
||||
void swap(basic_runtime_view &other) {
|
||||
void swap(basic_runtime_view &other) noexcept {
|
||||
using std::swap;
|
||||
swap(pools, other.pools);
|
||||
swap(filter, other.filter);
|
||||
@@ -241,11 +248,11 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity that has the given
|
||||
* components.
|
||||
* elements.
|
||||
*
|
||||
* If the view is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @return An iterator to the first entity that has the given components.
|
||||
* @return An iterator to the first entity that has the given elements.
|
||||
*/
|
||||
[[nodiscard]] iterator begin() const {
|
||||
return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->begin()};
|
||||
@@ -253,14 +260,22 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity that has the
|
||||
* given components.
|
||||
* given elements.
|
||||
* @return An iterator to the entity following the last entity that has the
|
||||
* given components.
|
||||
* given elements.
|
||||
*/
|
||||
[[nodiscard]] iterator end() const {
|
||||
return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->end()};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether a view is initialized or not.
|
||||
* @return True if the view is initialized, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] explicit operator bool() const noexcept {
|
||||
return !(pools.empty() && filter.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a view contains an entity.
|
||||
* @param entt A valid identifier.
|
||||
|
||||
@@ -37,7 +37,7 @@ void orphans(Registry ®istry) {
|
||||
* @brief Utility class to create snapshots from a registry.
|
||||
*
|
||||
* A _snapshot_ can be either a dump of the entire registry or a narrower
|
||||
* selection of components of interest.<br/>
|
||||
* selection of elements of interest.<br/>
|
||||
* This type can be used in both cases if provided with a correctly configured
|
||||
* output archive.
|
||||
*
|
||||
@@ -46,7 +46,7 @@ void orphans(Registry ®istry) {
|
||||
template<typename Registry>
|
||||
class basic_snapshot {
|
||||
static_assert(!std::is_const_v<Registry>, "Non-const registry type required");
|
||||
using traits_type = typename Registry::traits_type;
|
||||
using traits_type = entt_traits<typename Registry::entity_type>;
|
||||
|
||||
public:
|
||||
/*! Basic registry type. */
|
||||
@@ -61,10 +61,25 @@ public:
|
||||
basic_snapshot(const registry_type &source) noexcept
|
||||
: reg{&source} {}
|
||||
|
||||
/*! @brief Default copy constructor, deleted on purpose. */
|
||||
basic_snapshot(const basic_snapshot &) = delete;
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
basic_snapshot(basic_snapshot &&) noexcept = default;
|
||||
|
||||
/*! @brief Default move assignment operator. @return This snapshot. */
|
||||
/*! @brief Default destructor. */
|
||||
~basic_snapshot() = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator, deleted on purpose.
|
||||
* @return This snapshot.
|
||||
*/
|
||||
basic_snapshot &operator=(const basic_snapshot &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Default move assignment operator.
|
||||
* @return This snapshot.
|
||||
*/
|
||||
basic_snapshot &operator=(basic_snapshot &&) noexcept = default;
|
||||
|
||||
/**
|
||||
@@ -78,14 +93,25 @@ public:
|
||||
template<typename Type, typename Archive>
|
||||
const basic_snapshot &get(Archive &archive, const id_type id = type_hash<Type>::value()) const {
|
||||
if(const auto *storage = reg->template storage<Type>(id); storage) {
|
||||
const typename registry_type::common_type &base = *storage;
|
||||
|
||||
archive(static_cast<typename traits_type::entity_type>(storage->size()));
|
||||
|
||||
if constexpr(std::is_same_v<Type, entity_type>) {
|
||||
archive(static_cast<typename traits_type::entity_type>(storage->free_list()));
|
||||
|
||||
for(auto first = storage->data(), last = first + storage->size(); first != last; ++first) {
|
||||
for(auto first = base.rbegin(), last = base.rend(); first != last; ++first) {
|
||||
archive(*first);
|
||||
}
|
||||
} else if constexpr(registry_type::template storage_for_type<Type>::storage_policy == deletion_policy::in_place) {
|
||||
for(auto it = base.rbegin(), last = base.rend(); it != last; ++it) {
|
||||
if(const auto entt = *it; entt == tombstone) {
|
||||
archive(static_cast<entity_type>(null));
|
||||
} else {
|
||||
archive(entt);
|
||||
std::apply([&archive](auto &&...args) { (archive(std::forward<decltype(args)>(args)), ...); }, storage->get_as_tuple(entt));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(auto elem: storage->reach()) {
|
||||
std::apply([&archive](auto &&...args) { (archive(std::forward<decltype(args)>(args)), ...); }, elem);
|
||||
@@ -149,7 +175,7 @@ private:
|
||||
template<typename Registry>
|
||||
class basic_snapshot_loader {
|
||||
static_assert(!std::is_const_v<Registry>, "Non-const registry type required");
|
||||
using traits_type = typename Registry::traits_type;
|
||||
using traits_type = entt_traits<typename Registry::entity_type>;
|
||||
|
||||
public:
|
||||
/*! Basic registry type. */
|
||||
@@ -164,13 +190,28 @@ public:
|
||||
basic_snapshot_loader(registry_type &source) noexcept
|
||||
: reg{&source} {
|
||||
// restoring a snapshot as a whole requires a clean registry
|
||||
ENTT_ASSERT(reg->template storage<entity_type>().empty() && (reg->storage().begin() == reg->storage().end()), "Registry must be empty");
|
||||
ENTT_ASSERT(reg->template storage<entity_type>().free_list() == 0u, "Registry must be empty");
|
||||
}
|
||||
|
||||
/*! @brief Default copy constructor, deleted on purpose. */
|
||||
basic_snapshot_loader(const basic_snapshot_loader &) = delete;
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
basic_snapshot_loader(basic_snapshot_loader &&) noexcept = default;
|
||||
|
||||
/*! @brief Default move assignment operator. @return This loader. */
|
||||
/*! @brief Default destructor. */
|
||||
~basic_snapshot_loader() = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator, deleted on purpose.
|
||||
* @return This loader.
|
||||
*/
|
||||
basic_snapshot_loader &operator=(const basic_snapshot_loader &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Default move assignment operator.
|
||||
* @return This loader.
|
||||
*/
|
||||
basic_snapshot_loader &operator=(basic_snapshot_loader &&) noexcept = default;
|
||||
|
||||
/**
|
||||
@@ -224,10 +265,10 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroys those entities that have no components.
|
||||
* @brief Destroys those entities that have no elements.
|
||||
*
|
||||
* In case all the entities were serialized but only part of the components
|
||||
* was saved, it could happen that some of the entities have no components
|
||||
* In case all the entities were serialized but only part of the elements
|
||||
* was saved, it could happen that some of the entities have no elements
|
||||
* once restored.<br/>
|
||||
* This function helps to identify and destroy those entities.
|
||||
*
|
||||
@@ -261,7 +302,7 @@ private:
|
||||
template<typename Registry>
|
||||
class basic_continuous_loader {
|
||||
static_assert(!std::is_const_v<Registry>, "Non-const registry type required");
|
||||
using traits_type = typename Registry::traits_type;
|
||||
using traits_type = entt_traits<typename Registry::entity_type>;
|
||||
|
||||
void restore(typename Registry::entity_type entt) {
|
||||
if(const auto entity = to_entity(entt); remloc.contains(entity) && remloc[entity].first == entt) {
|
||||
@@ -332,11 +373,26 @@ public:
|
||||
: remloc{source.get_allocator()},
|
||||
reg{&source} {}
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
basic_continuous_loader(basic_continuous_loader &&) = default;
|
||||
/*! @brief Default copy constructor, deleted on purpose. */
|
||||
basic_continuous_loader(const basic_continuous_loader &) = delete;
|
||||
|
||||
/*! @brief Default move assignment operator. @return This loader. */
|
||||
basic_continuous_loader &operator=(basic_continuous_loader &&) = default;
|
||||
/*! @brief Default move constructor. */
|
||||
basic_continuous_loader(basic_continuous_loader &&) noexcept = default;
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~basic_continuous_loader() = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator, deleted on purpose.
|
||||
* @return This loader.
|
||||
*/
|
||||
basic_continuous_loader &operator=(const basic_continuous_loader &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Default move assignment operator.
|
||||
* @return This loader.
|
||||
*/
|
||||
basic_continuous_loader &operator=(basic_continuous_loader &&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Restores all elements of a type with associated identifiers.
|
||||
@@ -406,10 +462,10 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroys those entities that have no components.
|
||||
* @brief Destroys those entities that have no elements.
|
||||
*
|
||||
* In case all the entities were serialized but only part of the components
|
||||
* was saved, it could happen that some of the entities have no components
|
||||
* In case all the entities were serialized but only part of the elements
|
||||
* was saved, it could happen that some of the entities have no elements
|
||||
* once restored.<br/>
|
||||
* This function helps to identify and destroy those entities.
|
||||
*
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "../config/config.h"
|
||||
#include "../core/algorithm.hpp"
|
||||
#include "../core/any.hpp"
|
||||
#include "../core/memory.hpp"
|
||||
#include "../core/bit.hpp"
|
||||
#include "../core/type_info.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
@@ -33,7 +33,7 @@ struct sparse_set_iterator final {
|
||||
offset{} {}
|
||||
|
||||
constexpr sparse_set_iterator(const Container &ref, const difference_type idx) noexcept
|
||||
: packed{std::addressof(ref)},
|
||||
: packed{&ref},
|
||||
offset{idx} {}
|
||||
|
||||
constexpr sparse_set_iterator &operator++() noexcept {
|
||||
@@ -73,15 +73,15 @@ struct sparse_set_iterator final {
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
|
||||
return packed->data()[index() - value];
|
||||
return (*packed)[index() - value];
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr pointer operator->() const noexcept {
|
||||
return packed->data() + index();
|
||||
return std::addressof(operator[](0));
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr reference operator*() const noexcept {
|
||||
return *operator->();
|
||||
return operator[](0);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr pointer data() const noexcept {
|
||||
@@ -136,7 +136,7 @@ template<typename Container>
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Basic sparse set implementation.
|
||||
* @brief Sparse set implementation.
|
||||
*
|
||||
* Sparse set or packed array or whatever is the name users give it.<br/>
|
||||
* Two arrays: an _external_ one and an _internal_ one; a _sparse_ one and a
|
||||
@@ -160,7 +160,13 @@ class basic_sparse_set {
|
||||
static_assert(std::is_same_v<typename alloc_traits::value_type, Entity>, "Invalid value type");
|
||||
using sparse_container_type = std::vector<typename alloc_traits::pointer, typename alloc_traits::template rebind_alloc<typename alloc_traits::pointer>>;
|
||||
using packed_container_type = std::vector<Entity, Allocator>;
|
||||
using underlying_type = typename entt_traits<Entity>::entity_type;
|
||||
using traits_type = entt_traits<Entity>;
|
||||
|
||||
static constexpr auto max_size = static_cast<std::size_t>(traits_type::to_entity(null));
|
||||
|
||||
[[nodiscard]] auto policy_to_head() const noexcept {
|
||||
return static_cast<size_type>(max_size * static_cast<decltype(max_size)>(mode != deletion_policy::swap_only));
|
||||
}
|
||||
|
||||
[[nodiscard]] auto sparse_ptr(const Entity entt) const {
|
||||
const auto pos = static_cast<size_type>(traits_type::to_entity(entt));
|
||||
@@ -208,27 +214,23 @@ class basic_sparse_set {
|
||||
}
|
||||
}
|
||||
|
||||
void swap_at(const std::size_t from, const std::size_t to) {
|
||||
auto &lhs = packed[from];
|
||||
auto &rhs = packed[to];
|
||||
void swap_at(const std::size_t lhs, const std::size_t rhs) {
|
||||
auto &from = packed[lhs];
|
||||
auto &to = packed[rhs];
|
||||
|
||||
sparse_ref(lhs) = traits_type::combine(static_cast<typename traits_type::entity_type>(to), traits_type::to_integral(lhs));
|
||||
sparse_ref(rhs) = traits_type::combine(static_cast<typename traits_type::entity_type>(from), traits_type::to_integral(rhs));
|
||||
sparse_ref(from) = traits_type::combine(static_cast<typename traits_type::entity_type>(rhs), traits_type::to_integral(from));
|
||||
sparse_ref(to) = traits_type::combine(static_cast<typename traits_type::entity_type>(lhs), traits_type::to_integral(to));
|
||||
|
||||
std::swap(lhs, rhs);
|
||||
}
|
||||
|
||||
underlying_type policy_to_head() {
|
||||
return traits_type::entity_mask * (mode != deletion_policy::swap_only);
|
||||
std::swap(from, to);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual const void *get_at(const std::size_t) const {
|
||||
[[nodiscard]] virtual const void *get_at(const std::size_t) const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual void swap_or_move([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) {
|
||||
ENTT_ASSERT((mode != deletion_policy::swap_only) || (((lhs < free_list()) + (rhs < free_list())) != 1u), "Cross swapping is not supported");
|
||||
ENTT_ASSERT((mode != deletion_policy::swap_only) || ((lhs < head) == (rhs < head)), "Cross swapping is not supported");
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -241,9 +243,9 @@ protected:
|
||||
*/
|
||||
void swap_only(const basic_iterator it) {
|
||||
ENTT_ASSERT(mode == deletion_policy::swap_only, "Deletion policy mismatch");
|
||||
const auto pos = static_cast<underlying_type>(index(*it));
|
||||
const auto pos = index(*it);
|
||||
bump(traits_type::next(*it));
|
||||
swap_at(pos, static_cast<size_type>(head -= (pos < head)));
|
||||
swap_at(pos, head -= (pos < head));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -269,11 +271,10 @@ protected:
|
||||
*/
|
||||
void in_place_pop(const basic_iterator it) {
|
||||
ENTT_ASSERT(mode == deletion_policy::in_place, "Deletion policy mismatch");
|
||||
const auto entt = traits_type::to_entity(std::exchange(sparse_ref(*it), null));
|
||||
packed[static_cast<size_type>(entt)] = traits_type::combine(std::exchange(head, entt), tombstone);
|
||||
const auto pos = static_cast<size_type>(traits_type::to_entity(std::exchange(sparse_ref(*it), null)));
|
||||
packed[pos] = traits_type::combine(static_cast<typename traits_type::entity_type>(std::exchange(head, pos)), tombstone);
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Erases entities from a sparse set.
|
||||
* @param first An iterator to the first element of the range of entities.
|
||||
@@ -303,7 +304,7 @@ protected:
|
||||
virtual void pop_all() {
|
||||
switch(mode) {
|
||||
case deletion_policy::in_place:
|
||||
if(head != traits_type::to_entity(null)) {
|
||||
if(head != max_size) {
|
||||
for(auto first = begin(); !(first.index() < 0); ++first) {
|
||||
if(*first != tombstone) {
|
||||
sparse_ref(*first) = null;
|
||||
@@ -331,16 +332,17 @@ protected:
|
||||
* @return Iterator pointing to the emplaced element.
|
||||
*/
|
||||
virtual basic_iterator try_emplace(const Entity entt, const bool force_back, const void * = nullptr) {
|
||||
ENTT_ASSERT(entt != null && entt != tombstone, "Invalid element");
|
||||
auto &elem = assure_at_least(entt);
|
||||
auto pos = size();
|
||||
|
||||
switch(mode) {
|
||||
case deletion_policy::in_place:
|
||||
if(head != traits_type::to_entity(null) && !force_back) {
|
||||
pos = static_cast<size_type>(head);
|
||||
if(head != max_size && !force_back) {
|
||||
pos = head;
|
||||
ENTT_ASSERT(elem == null, "Slot not available");
|
||||
elem = traits_type::combine(head, traits_type::to_integral(entt));
|
||||
head = traits_type::to_entity(std::exchange(packed[pos], entt));
|
||||
elem = traits_type::combine(static_cast<typename traits_type::entity_type>(head), traits_type::to_integral(entt));
|
||||
head = static_cast<size_type>(traits_type::to_entity(std::exchange(packed[pos], entt)));
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
@@ -354,32 +356,31 @@ protected:
|
||||
packed.push_back(entt);
|
||||
elem = traits_type::combine(static_cast<typename traits_type::entity_type>(packed.size() - 1u), traits_type::to_integral(entt));
|
||||
} else {
|
||||
ENTT_ASSERT(!(traits_type::to_entity(elem) < head), "Slot not available");
|
||||
ENTT_ASSERT(!(static_cast<size_type>(traits_type::to_entity(elem)) < head), "Slot not available");
|
||||
bump(entt);
|
||||
}
|
||||
|
||||
if(force_back) {
|
||||
pos = static_cast<size_type>(head++);
|
||||
swap_at(static_cast<size_type>(traits_type::to_entity(elem)), pos);
|
||||
}
|
||||
|
||||
pos = head++;
|
||||
swap_at(static_cast<size_type>(traits_type::to_entity(elem)), pos);
|
||||
break;
|
||||
}
|
||||
|
||||
return --(end() - pos);
|
||||
return --(end() - static_cast<typename iterator::difference_type>(pos));
|
||||
}
|
||||
|
||||
/*! @brief Forwards variables to derived classes, if any. */
|
||||
// NOLINTNEXTLINE(performance-unnecessary-value-param)
|
||||
virtual void bind_any(any) noexcept {}
|
||||
|
||||
public:
|
||||
/*! @brief Entity traits. */
|
||||
using traits_type = entt_traits<Entity>;
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename traits_type::value_type;
|
||||
/*! @brief Underlying version type. */
|
||||
using version_type = typename traits_type::version_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Pointer type to contained entities. */
|
||||
using pointer = typename packed_container_type::const_pointer;
|
||||
/*! @brief Random access iterator type. */
|
||||
@@ -400,7 +401,7 @@ public:
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit basic_sparse_set(const allocator_type &allocator)
|
||||
: basic_sparse_set{type_id<void>(), deletion_policy::swap_and_pop, allocator} {}
|
||||
: basic_sparse_set{deletion_policy::swap_and_pop, allocator} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an empty container with the given policy and allocator.
|
||||
@@ -422,7 +423,12 @@ public:
|
||||
packed{allocator},
|
||||
info{&elem},
|
||||
mode{pol},
|
||||
head{policy_to_head()} {}
|
||||
head{policy_to_head()} {
|
||||
ENTT_ASSERT(traits_type::version_mask || mode != deletion_policy::in_place, "Policy does not support zero-sized versions");
|
||||
}
|
||||
|
||||
/*! @brief Default copy constructor, deleted on purpose. */
|
||||
basic_sparse_set(const basic_sparse_set &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
@@ -440,13 +446,13 @@ public:
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) noexcept
|
||||
basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator)
|
||||
: sparse{std::move(other.sparse), allocator},
|
||||
packed{std::move(other.packed), allocator},
|
||||
info{other.info},
|
||||
mode{other.mode},
|
||||
head{std::exchange(other.head, policy_to_head())} {
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed");
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a sparse set is not allowed");
|
||||
}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
@@ -454,20 +460,20 @@ public:
|
||||
release_sparse_pages();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator, deleted on purpose.
|
||||
* @return This sparse set.
|
||||
*/
|
||||
basic_sparse_set &operator=(const basic_sparse_set &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
* @param other The instance to move from.
|
||||
* @return This sparse set.
|
||||
*/
|
||||
basic_sparse_set &operator=(basic_sparse_set &&other) noexcept {
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed");
|
||||
|
||||
release_sparse_pages();
|
||||
sparse = std::move(other.sparse);
|
||||
packed = std::move(other.packed);
|
||||
info = other.info;
|
||||
mode = other.mode;
|
||||
head = std::exchange(other.head, policy_to_head());
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a sparse set is not allowed");
|
||||
swap(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -475,7 +481,7 @@ public:
|
||||
* @brief Exchanges the contents with those of a given sparse set.
|
||||
* @param other Sparse set to exchange the content with.
|
||||
*/
|
||||
void swap(basic_sparse_set &other) {
|
||||
void swap(basic_sparse_set &other) noexcept {
|
||||
using std::swap;
|
||||
swap(sparse, other.sparse);
|
||||
swap(packed, other.packed);
|
||||
@@ -501,20 +507,20 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the head of the free list, if any.
|
||||
* @return The head of the free list.
|
||||
* @brief Returns data on the free list whose meaning depends on the mode.
|
||||
* @return Free list information that is mode dependent.
|
||||
*/
|
||||
[[nodiscard]] size_type free_list() const noexcept {
|
||||
return static_cast<size_type>(head);
|
||||
return head;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the head of the free list, if possible.
|
||||
* @param len The value to use as the new head of the free list.
|
||||
* @brief Sets data on the free list whose meaning depends on the mode.
|
||||
* @param value Free list information that is mode dependent.
|
||||
*/
|
||||
void free_list(const size_type len) noexcept {
|
||||
ENTT_ASSERT((mode == deletion_policy::swap_only) && !(len > packed.size()), "Invalid value");
|
||||
head = static_cast<underlying_type>(len);
|
||||
void free_list(const size_type value) noexcept {
|
||||
ENTT_ASSERT((mode == deletion_policy::swap_only) && !(value > packed.size()), "Invalid value");
|
||||
head = value;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -584,7 +590,7 @@ public:
|
||||
* @return True if the sparse set is fully packed, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool contiguous() const noexcept {
|
||||
return (mode != deletion_policy::in_place) || (head == traits_type::to_entity(null));
|
||||
return (mode != deletion_policy::in_place) || (head == max_size);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -659,46 +665,6 @@ public:
|
||||
return rend();
|
||||
}
|
||||
|
||||
/*! @copydoc begin Useful only in case of swap-only policy. */
|
||||
[[nodiscard]] iterator begin(int) const noexcept {
|
||||
return (mode == deletion_policy::swap_only) ? (end() - static_cast<typename iterator::difference_type>(head)) : begin();
|
||||
}
|
||||
|
||||
/*! @copydoc cbegin Useful only in case of swap-only policy. */
|
||||
[[nodiscard]] const_iterator cbegin(int) const noexcept {
|
||||
return begin(0);
|
||||
}
|
||||
|
||||
/*! @copydoc end Useful only in case of swap-only policy. */
|
||||
[[nodiscard]] iterator end(int) const noexcept {
|
||||
return end();
|
||||
}
|
||||
|
||||
/*! @copydoc cend Useful only in case of swap-only policy. */
|
||||
[[nodiscard]] const_iterator cend(int) const noexcept {
|
||||
return end(0);
|
||||
}
|
||||
|
||||
/*! @copydoc rbegin Useful only in case of swap-only policy. */
|
||||
[[nodiscard]] reverse_iterator rbegin(int) const noexcept {
|
||||
return std::make_reverse_iterator(end(0));
|
||||
}
|
||||
|
||||
/*! @copydoc rbegin Useful only in case of swap-only policy. */
|
||||
[[nodiscard]] const_reverse_iterator crbegin(int) const noexcept {
|
||||
return rbegin(0);
|
||||
}
|
||||
|
||||
/*! @copydoc rbegin Useful only in case of swap-only policy. */
|
||||
[[nodiscard]] reverse_iterator rend(int) const noexcept {
|
||||
return std::make_reverse_iterator(begin(0));
|
||||
}
|
||||
|
||||
/*! @copydoc rbegin Useful only in case of swap-only policy. */
|
||||
[[nodiscard]] const_reverse_iterator crend(int) const noexcept {
|
||||
return rend(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finds an entity.
|
||||
* @param entt A valid identifier.
|
||||
@@ -715,7 +681,7 @@ public:
|
||||
* @return True if the sparse set contains the entity, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool contains(const entity_type entt) const noexcept {
|
||||
const auto elem = sparse_ptr(entt);
|
||||
const auto *elem = sparse_ptr(entt);
|
||||
constexpr auto cap = traits_type::entity_mask;
|
||||
constexpr auto mask = traits_type::to_integral(null) & ~cap;
|
||||
// testing versions permits to avoid accessing the packed array
|
||||
@@ -729,7 +695,7 @@ public:
|
||||
* version otherwise.
|
||||
*/
|
||||
[[nodiscard]] version_type current(const entity_type entt) const noexcept {
|
||||
const auto elem = sparse_ptr(entt);
|
||||
const auto *elem = sparse_ptr(entt);
|
||||
constexpr auto fallback = traits_type::to_version(tombstone);
|
||||
return elem ? traits_type::to_version(*elem) : fallback;
|
||||
}
|
||||
@@ -750,21 +716,12 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the entity at specified location, with bounds checking.
|
||||
* @param pos The position for which to return the entity.
|
||||
* @return The entity at specified location if any, a null entity otherwise.
|
||||
*/
|
||||
[[deprecated("use .begin()[pos] instead")]] [[nodiscard]] entity_type at(const size_type pos) const noexcept {
|
||||
return pos < packed.size() ? packed[pos] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the entity at specified location, without bounds checking.
|
||||
* @brief Returns the entity at specified location.
|
||||
* @param pos The position for which to return the entity.
|
||||
* @return The entity at specified location.
|
||||
*/
|
||||
[[nodiscard]] entity_type operator[](const size_type pos) const noexcept {
|
||||
ENTT_ASSERT(pos < packed.size(), "Position is out of bounds");
|
||||
ENTT_ASSERT(pos < packed.size(), "Index out of bounds");
|
||||
return packed[pos];
|
||||
}
|
||||
|
||||
@@ -800,7 +757,7 @@ public:
|
||||
* `end()` iterator otherwise.
|
||||
*/
|
||||
iterator push(const entity_type entt, const void *elem = nullptr) {
|
||||
return try_emplace(entt, (mode == deletion_policy::swap_only), elem);
|
||||
return try_emplace(entt, false, elem);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -818,11 +775,13 @@ public:
|
||||
*/
|
||||
template<typename It>
|
||||
iterator push(It first, It last) {
|
||||
for(auto it = first; it != last; ++it) {
|
||||
try_emplace(*it, true);
|
||||
auto curr = end();
|
||||
|
||||
for(; first != last; ++first) {
|
||||
curr = try_emplace(*first, true);
|
||||
}
|
||||
|
||||
return first == last ? end() : find(*first);
|
||||
return curr;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -836,10 +795,10 @@ public:
|
||||
* @return The version of the given identifier.
|
||||
*/
|
||||
version_type bump(const entity_type entt) {
|
||||
auto &entity = sparse_ref(entt);
|
||||
ENTT_ASSERT(entt != tombstone && entity != null, "Cannot set the required version");
|
||||
entity = traits_type::combine(traits_type::to_integral(entity), traits_type::to_integral(entt));
|
||||
packed[static_cast<size_type>(traits_type::to_entity(entity))] = entt;
|
||||
auto &elem = sparse_ref(entt);
|
||||
ENTT_ASSERT(entt != null && elem != tombstone, "Cannot set the required version");
|
||||
elem = traits_type::combine(traits_type::to_integral(elem), traits_type::to_integral(entt));
|
||||
packed[static_cast<size_type>(traits_type::to_entity(elem))] = entt;
|
||||
return traits_type::to_version(entt);
|
||||
}
|
||||
|
||||
@@ -925,17 +884,18 @@ public:
|
||||
void compact() {
|
||||
if(mode == deletion_policy::in_place) {
|
||||
size_type from = packed.size();
|
||||
for(; from && packed[from - 1u] == tombstone; --from) {}
|
||||
underlying_type pos = std::exchange(head, traits_type::entity_mask);
|
||||
size_type pos = std::exchange(head, max_size);
|
||||
|
||||
while(pos != traits_type::to_entity(null)) {
|
||||
if(const auto to = static_cast<size_type>(std::exchange(pos, traits_type::to_entity(packed[pos]))); to < from) {
|
||||
for(; from && packed[from - 1u] == tombstone; --from) {}
|
||||
|
||||
while(pos != max_size) {
|
||||
if(const auto to = std::exchange(pos, static_cast<size_type>(traits_type::to_entity(packed[pos]))); to < from) {
|
||||
--from;
|
||||
swap_or_move(from, to);
|
||||
|
||||
packed[to] = packed[from];
|
||||
const auto entity = static_cast<typename traits_type::entity_type>(to);
|
||||
sparse_ref(packed[to]) = traits_type::combine(entity, traits_type::to_integral(packed[to]));
|
||||
const auto elem = static_cast<typename traits_type::entity_type>(to);
|
||||
sparse_ref(packed[to]) = traits_type::combine(elem, traits_type::to_integral(packed[to]));
|
||||
|
||||
for(; from && packed[from - 1u] == tombstone; --from) {}
|
||||
}
|
||||
@@ -999,7 +959,7 @@ public:
|
||||
*/
|
||||
template<typename Compare, typename Sort = std_sort, typename... Args>
|
||||
void sort_n(const size_type length, Compare compare, Sort algo = Sort{}, Args &&...args) {
|
||||
ENTT_ASSERT((mode != deletion_policy::in_place) || (head == traits_type::to_entity(null)), "Sorting with tombstones not allowed");
|
||||
ENTT_ASSERT((mode != deletion_policy::in_place) || (head == max_size), "Sorting with tombstones not allowed");
|
||||
ENTT_ASSERT(!(length > packed.size()), "Length exceeds the number of elements");
|
||||
|
||||
algo(packed.rend() - length, packed.rend(), std::move(compare), std::forward<Args>(args)...);
|
||||
@@ -1013,8 +973,8 @@ public:
|
||||
const auto entt = packed[curr];
|
||||
|
||||
swap_or_move(next, idx);
|
||||
const auto entity = static_cast<typename traits_type::entity_type>(curr);
|
||||
sparse_ref(entt) = traits_type::combine(entity, traits_type::to_integral(packed[curr]));
|
||||
const auto elem = static_cast<typename traits_type::entity_type>(curr);
|
||||
sparse_ref(entt) = traits_type::combine(elem, traits_type::to_integral(packed[curr]));
|
||||
curr = std::exchange(next, idx);
|
||||
}
|
||||
}
|
||||
@@ -1034,7 +994,8 @@ public:
|
||||
*/
|
||||
template<typename Compare, typename Sort = std_sort, typename... Args>
|
||||
void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
|
||||
sort_n(static_cast<size_type>(end(0) - begin(0)), std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
const size_type len = (mode == deletion_policy::swap_only) ? head : packed.size();
|
||||
sort_n(len, std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1048,12 +1009,15 @@ public:
|
||||
* @tparam It Type of input iterator.
|
||||
* @param first An iterator to the first element of the range of entities.
|
||||
* @param last An iterator past the last element of the range of entities.
|
||||
* @return An iterator past the last of the elements actually shared.
|
||||
*/
|
||||
template<typename It>
|
||||
void sort_as(It first, It last) {
|
||||
ENTT_ASSERT((mode != deletion_policy::in_place) || (head == traits_type::to_entity(null)), "Sorting with tombstones not allowed");
|
||||
iterator sort_as(It first, It last) {
|
||||
ENTT_ASSERT((mode != deletion_policy::in_place) || (head == max_size), "Sorting with tombstones not allowed");
|
||||
const size_type len = (mode == deletion_policy::swap_only) ? head : packed.size();
|
||||
auto it = end() - static_cast<typename iterator::difference_type>(len);
|
||||
|
||||
for(auto it = begin(0); it.index() && first != last; ++first) {
|
||||
for(const auto other = end(); (it != other) && (first != last); ++first) {
|
||||
if(const auto curr = *first; contains(curr)) {
|
||||
if(const auto entt = *it; entt != curr) {
|
||||
// basic no-leak guarantee (with invalid state) if swapping throws
|
||||
@@ -1063,14 +1027,8 @@ public:
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @copybrief sort_as
|
||||
* @param other The sparse sets that imposes the order of the entities.
|
||||
*/
|
||||
[[deprecated("use iterator based sort_as instead")]] void sort_as(const basic_sparse_set &other) {
|
||||
sort_as(other.begin(), other.end());
|
||||
return it;
|
||||
}
|
||||
|
||||
/*! @brief Clears a sparse set. */
|
||||
@@ -1086,19 +1044,41 @@ public:
|
||||
* @brief Returned value type, if any.
|
||||
* @return Returned value type, if any.
|
||||
*/
|
||||
const type_info &type() const noexcept {
|
||||
[[nodiscard]] const type_info &type() const noexcept {
|
||||
return *info;
|
||||
}
|
||||
|
||||
/*! @brief Forwards variables to derived classes, if any. */
|
||||
virtual void bind(any) noexcept {}
|
||||
/**
|
||||
* @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 {
|
||||
bind_any(forward_as_any(std::forward<Type>(value)));
|
||||
}
|
||||
|
||||
private:
|
||||
sparse_container_type sparse;
|
||||
packed_container_type packed;
|
||||
const type_info *info;
|
||||
deletion_policy mode;
|
||||
underlying_type head;
|
||||
size_type head;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../config/config.h"
|
||||
#include "../core/bit.hpp"
|
||||
#include "../core/iterator.hpp"
|
||||
#include "../core/memory.hpp"
|
||||
#include "../core/type_info.hpp"
|
||||
@@ -22,9 +23,9 @@ namespace entt {
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename Container, std::size_t Size>
|
||||
template<typename Container>
|
||||
class storage_iterator final {
|
||||
friend storage_iterator<const Container, Size>;
|
||||
friend storage_iterator<const Container>;
|
||||
|
||||
using container_type = std::remove_const_t<Container>;
|
||||
using alloc_traits = std::allocator_traits<typename container_type::allocator_type>;
|
||||
@@ -48,7 +49,7 @@ public:
|
||||
offset{idx} {}
|
||||
|
||||
template<bool Const = std::is_const_v<Container>, typename = std::enable_if_t<Const>>
|
||||
constexpr storage_iterator(const storage_iterator<std::remove_const_t<Container>, Size> &other) noexcept
|
||||
constexpr storage_iterator(const storage_iterator<std::remove_const_t<Container>> &other) noexcept
|
||||
: storage_iterator{other.payload, other.offset} {}
|
||||
|
||||
constexpr storage_iterator &operator++() noexcept {
|
||||
@@ -89,16 +90,16 @@ public:
|
||||
|
||||
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
|
||||
const auto pos = index() - value;
|
||||
return (*payload)[pos / Size][fast_mod(pos, Size)];
|
||||
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)];
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr pointer operator->() const noexcept {
|
||||
const auto pos = index();
|
||||
return (*payload)[pos / Size] + fast_mod(pos, Size);
|
||||
return std::addressof(operator[](0));
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr reference operator*() const noexcept {
|
||||
return *operator->();
|
||||
return operator[](0);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr difference_type index() const noexcept {
|
||||
@@ -110,38 +111,38 @@ private:
|
||||
difference_type offset;
|
||||
};
|
||||
|
||||
template<typename Lhs, typename Rhs, std::size_t Size>
|
||||
[[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &rhs) noexcept {
|
||||
return rhs.index() - lhs.index();
|
||||
}
|
||||
|
||||
template<typename Lhs, typename Rhs, std::size_t Size>
|
||||
[[nodiscard]] constexpr bool operator==(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator==(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &rhs) noexcept {
|
||||
return lhs.index() == rhs.index();
|
||||
}
|
||||
|
||||
template<typename Lhs, typename Rhs, std::size_t Size>
|
||||
[[nodiscard]] constexpr bool operator!=(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator!=(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &rhs) noexcept {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template<typename Lhs, typename Rhs, std::size_t Size>
|
||||
[[nodiscard]] constexpr bool operator<(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator<(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &rhs) noexcept {
|
||||
return lhs.index() > rhs.index();
|
||||
}
|
||||
|
||||
template<typename Lhs, typename Rhs, std::size_t Size>
|
||||
[[nodiscard]] constexpr bool operator>(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator>(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &rhs) noexcept {
|
||||
return rhs < lhs;
|
||||
}
|
||||
|
||||
template<typename Lhs, typename Rhs, std::size_t Size>
|
||||
[[nodiscard]] constexpr bool operator<=(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator<=(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &rhs) noexcept {
|
||||
return !(lhs > rhs);
|
||||
}
|
||||
|
||||
template<typename Lhs, typename Rhs, std::size_t Size>
|
||||
[[nodiscard]] constexpr bool operator>=(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator>=(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &rhs) noexcept {
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
@@ -152,10 +153,10 @@ class extended_storage_iterator final {
|
||||
|
||||
public:
|
||||
using iterator_type = It;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::forward_as_tuple(*std::declval<Other>()...)));
|
||||
using pointer = input_iterator_pointer<value_type>;
|
||||
using reference = value_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using iterator_concept = std::forward_iterator_tag;
|
||||
|
||||
@@ -211,7 +212,7 @@ template<typename... Lhs, typename... Rhs>
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Basic storage implementation.
|
||||
* @brief Storage implementation.
|
||||
*
|
||||
* Internal data structures arrange elements to maximize performance. There are
|
||||
* no guarantees that objects are returned in the insertion order when iterate
|
||||
@@ -221,7 +222,7 @@ template<typename... Lhs, typename... Rhs>
|
||||
* Empty types aren't explicitly instantiated. Therefore, many of the functions
|
||||
* normally available for non-empty types will not be available for empty ones.
|
||||
*
|
||||
* @tparam Type Type of objects assigned to the entities.
|
||||
* @tparam Type Element type.
|
||||
* @tparam Entity A valid entity type.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
@@ -232,6 +233,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>;
|
||||
|
||||
[[nodiscard]] auto &element_at(const std::size_t pos) const {
|
||||
return payload[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)];
|
||||
@@ -264,8 +266,8 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
|
||||
const auto it = base_type::try_emplace(entt, force_back);
|
||||
|
||||
ENTT_TRY {
|
||||
auto elem = assure_at_least(static_cast<size_type>(it.index()));
|
||||
entt::uninitialized_construct_using_allocator(to_address(elem), get_allocator(), std::forward<Args>(args)...);
|
||||
auto *elem = to_address(assure_at_least(static_cast<size_type>(it.index())));
|
||||
entt::uninitialized_construct_using_allocator(elem, get_allocator(), std::forward<Args>(args)...);
|
||||
}
|
||||
ENTT_CATCH {
|
||||
base_type::pop(it, it + 1u);
|
||||
@@ -297,7 +299,7 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
|
||||
}
|
||||
|
||||
private:
|
||||
const void *get_at(const std::size_t pos) const final {
|
||||
[[nodiscard]] const void *get_at(const std::size_t pos) const final {
|
||||
return std::addressof(element_at(pos));
|
||||
}
|
||||
|
||||
@@ -372,14 +374,14 @@ protected:
|
||||
* @return Iterator pointing to the emplaced element.
|
||||
*/
|
||||
underlying_iterator try_emplace([[maybe_unused]] const Entity entt, [[maybe_unused]] const bool force_back, const void *value) override {
|
||||
if(value) {
|
||||
if constexpr(std::is_copy_constructible_v<value_type>) {
|
||||
return emplace_element(entt, force_back, *static_cast<const value_type *>(value));
|
||||
if(value != nullptr) {
|
||||
if constexpr(std::is_copy_constructible_v<element_type>) {
|
||||
return emplace_element(entt, force_back, *static_cast<const element_type *>(value));
|
||||
} else {
|
||||
return base_type::end();
|
||||
}
|
||||
} else {
|
||||
if constexpr(std::is_default_constructible_v<value_type>) {
|
||||
if constexpr(std::is_default_constructible_v<element_type>) {
|
||||
return emplace_element(entt, force_back);
|
||||
} else {
|
||||
return base_type::end();
|
||||
@@ -388,26 +390,26 @@ protected:
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Base type. */
|
||||
using base_type = underlying_type;
|
||||
/*! @brief Element type. */
|
||||
using element_type = Type;
|
||||
/*! @brief Type of the objects assigned to entities. */
|
||||
using value_type = Type;
|
||||
/*! @brief Component traits. */
|
||||
using traits_type = component_traits<value_type>;
|
||||
using value_type = element_type;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief 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, traits_type::page_size>;
|
||||
using iterator = internal::storage_iterator<container_type>;
|
||||
/*! @brief Constant random access iterator type. */
|
||||
using const_iterator = internal::storage_iterator<const container_type, traits_type::page_size>;
|
||||
using const_iterator = internal::storage_iterator<const container_type>;
|
||||
/*! @brief Reverse iterator type. */
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
/*! @brief Constant reverse iterator type. */
|
||||
@@ -420,6 +422,8 @@ public:
|
||||
using reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::reverse_iterator, reverse_iterator>>;
|
||||
/*! @brief Constant extended reverse iterable storage proxy. */
|
||||
using const_reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_reverse_iterator, const_reverse_iterator>>;
|
||||
/*! @brief Storage deletion policy. */
|
||||
static constexpr deletion_policy storage_policy{traits_type::in_place_delete};
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
basic_storage()
|
||||
@@ -430,9 +434,12 @@ public:
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit basic_storage(const allocator_type &allocator)
|
||||
: base_type{type_id<value_type>(), deletion_policy{traits_type::in_place_delete}, allocator},
|
||||
: base_type{type_id<element_type>(), storage_policy, allocator},
|
||||
payload{allocator} {}
|
||||
|
||||
/*! @brief Default copy constructor, deleted on purpose. */
|
||||
basic_storage(const basic_storage &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
@@ -446,28 +453,33 @@ public:
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept
|
||||
basic_storage(basic_storage &&other, const allocator_type &allocator)
|
||||
: base_type{std::move(other), allocator},
|
||||
payload{std::move(other.payload), allocator} {
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || payload.get_allocator() == other.payload.get_allocator(), "Copying a storage is not allowed");
|
||||
// NOLINTNEXTLINE(bugprone-use-after-move)
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a storage is not allowed");
|
||||
}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
// NOLINTNEXTLINE(bugprone-exception-escape)
|
||||
~basic_storage() override {
|
||||
shrink_to_size(0u);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator, deleted on purpose.
|
||||
* @return This storage.
|
||||
*/
|
||||
basic_storage &operator=(const basic_storage &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
* @param other The instance to move from.
|
||||
* @return This storage.
|
||||
*/
|
||||
basic_storage &operator=(basic_storage &&other) noexcept {
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || payload.get_allocator() == other.payload.get_allocator(), "Copying a storage is not allowed");
|
||||
|
||||
shrink_to_size(0u);
|
||||
base_type::operator=(std::move(other));
|
||||
payload = std::move(other.payload);
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a storage is not allowed");
|
||||
swap(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -475,10 +487,10 @@ public:
|
||||
* @brief Exchanges the contents with those of a given storage.
|
||||
* @param other Storage to exchange the content with.
|
||||
*/
|
||||
void swap(basic_storage &other) {
|
||||
void swap(basic_storage &other) noexcept {
|
||||
using std::swap;
|
||||
base_type::swap(other);
|
||||
swap(payload, other.payload);
|
||||
base_type::swap(other);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -697,7 +709,7 @@ public:
|
||||
* @param first An iterator to the first element of the range of entities.
|
||||
* @param last An iterator past the last element of the range of entities.
|
||||
* @param value An instance of the object to construct.
|
||||
* @return Iterator pointing to the last element inserted, if any.
|
||||
* @return Iterator pointing to the first element inserted, if any.
|
||||
*/
|
||||
template<typename It>
|
||||
iterator insert(It first, It last, const value_type &value = {}) {
|
||||
@@ -734,17 +746,17 @@ public:
|
||||
* @brief Returns an iterable object to use to _visit_ a storage.
|
||||
*
|
||||
* The iterable object returns a tuple that contains the current entity and
|
||||
* a reference to its component.
|
||||
* a reference to its element.
|
||||
*
|
||||
* @return An iterable object to use to _visit_ the storage.
|
||||
*/
|
||||
[[nodiscard]] iterable each() noexcept {
|
||||
return {internal::extended_storage_iterator{base_type::begin(), begin()}, internal::extended_storage_iterator{base_type::end(), end()}};
|
||||
return iterable{{base_type::begin(), begin()}, {base_type::end(), end()}};
|
||||
}
|
||||
|
||||
/*! @copydoc each */
|
||||
[[nodiscard]] const_iterable each() const noexcept {
|
||||
return {internal::extended_storage_iterator{base_type::cbegin(), cbegin()}, internal::extended_storage_iterator{base_type::cend(), cend()}};
|
||||
return const_iterable{{base_type::cbegin(), cbegin()}, {base_type::cend(), cend()}};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -755,12 +767,12 @@ public:
|
||||
* @return A reverse iterable object to use to _visit_ the storage.
|
||||
*/
|
||||
[[nodiscard]] reverse_iterable reach() noexcept {
|
||||
return {internal::extended_storage_iterator{base_type::rbegin(), rbegin()}, internal::extended_storage_iterator{base_type::rend(), rend()}};
|
||||
return reverse_iterable{{base_type::rbegin(), rbegin()}, {base_type::rend(), rend()}};
|
||||
}
|
||||
|
||||
/*! @copydoc reach */
|
||||
[[nodiscard]] const_reverse_iterable reach() const noexcept {
|
||||
return {internal::extended_storage_iterator{base_type::crbegin(), crbegin()}, internal::extended_storage_iterator{base_type::crend(), crend()}};
|
||||
return const_reverse_iterable{{base_type::crbegin(), crbegin()}, {base_type::crend(), crend()}};
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -773,20 +785,21 @@ class basic_storage<Type, Entity, Allocator, std::enable_if_t<component_traits<T
|
||||
: 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>;
|
||||
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Base type. */
|
||||
using base_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
|
||||
/*! @brief Element type. */
|
||||
using element_type = Type;
|
||||
/*! @brief Type of the objects assigned to entities. */
|
||||
using value_type = Type;
|
||||
/*! @brief Component traits. */
|
||||
using traits_type = component_traits<value_type>;
|
||||
using value_type = void;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Extended iterable storage proxy. */
|
||||
using iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::iterator>>;
|
||||
/*! @brief Constant extended iterable storage proxy. */
|
||||
@@ -795,6 +808,8 @@ public:
|
||||
using reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::reverse_iterator>>;
|
||||
/*! @brief Constant extended reverse iterable storage proxy. */
|
||||
using const_reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_reverse_iterator>>;
|
||||
/*! @brief Storage deletion policy. */
|
||||
static constexpr deletion_policy storage_policy{traits_type::in_place_delete};
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
basic_storage()
|
||||
@@ -805,7 +820,10 @@ public:
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit basic_storage(const allocator_type &allocator)
|
||||
: base_type{type_id<value_type>(), deletion_policy{traits_type::in_place_delete}, allocator} {}
|
||||
: base_type{type_id<element_type>(), storage_policy, allocator} {}
|
||||
|
||||
/*! @brief Default copy constructor, deleted on purpose. */
|
||||
basic_storage(const basic_storage &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
@@ -818,9 +836,18 @@ public:
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept
|
||||
basic_storage(basic_storage &&other, const allocator_type &allocator)
|
||||
: base_type{std::move(other), allocator} {}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~basic_storage() override = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator, deleted on purpose.
|
||||
* @return This storage.
|
||||
*/
|
||||
basic_storage &operator=(const basic_storage &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
* @param other The instance to move from.
|
||||
@@ -834,7 +861,7 @@ public:
|
||||
*/
|
||||
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
|
||||
// std::allocator<void> has no cross constructors (waiting for C++20)
|
||||
if constexpr(std::is_void_v<value_type> && !std::is_constructible_v<allocator_type, typename base_type::allocator_type>) {
|
||||
if constexpr(std::is_void_v<element_type> && !std::is_constructible_v<allocator_type, typename base_type::allocator_type>) {
|
||||
return allocator_type{};
|
||||
} else {
|
||||
return allocator_type{base_type::get_allocator()};
|
||||
@@ -851,21 +878,16 @@ public:
|
||||
* @param entt A valid identifier.
|
||||
*/
|
||||
void get([[maybe_unused]] const entity_type entt) const noexcept {
|
||||
ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity");
|
||||
ENTT_ASSERT(base_type::contains(entt), "Invalid entity");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an empty tuple.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to use an entity that doesn't belong to the storage results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @param entt A valid identifier.
|
||||
* @return Returns an empty tuple.
|
||||
*/
|
||||
[[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const noexcept {
|
||||
ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity");
|
||||
ENTT_ASSERT(base_type::contains(entt), "Invalid entity");
|
||||
return std::tuple{};
|
||||
}
|
||||
|
||||
@@ -892,7 +914,7 @@ public:
|
||||
*/
|
||||
template<typename... Func>
|
||||
void patch([[maybe_unused]] const entity_type entt, Func &&...func) {
|
||||
ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity");
|
||||
ENTT_ASSERT(base_type::contains(entt), "Invalid entity");
|
||||
(std::forward<Func>(func)(), ...);
|
||||
}
|
||||
|
||||
@@ -918,12 +940,12 @@ public:
|
||||
* @return An iterable object to use to _visit_ the storage.
|
||||
*/
|
||||
[[nodiscard]] iterable each() noexcept {
|
||||
return {internal::extended_storage_iterator{base_type::begin()}, internal::extended_storage_iterator{base_type::end()}};
|
||||
return iterable{base_type::begin(), base_type::end()};
|
||||
}
|
||||
|
||||
/*! @copydoc each */
|
||||
[[nodiscard]] const_iterable each() const noexcept {
|
||||
return {internal::extended_storage_iterator{base_type::cbegin()}, internal::extended_storage_iterator{base_type::cend()}};
|
||||
return const_iterable{base_type::cbegin(), base_type::cend()};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -934,12 +956,12 @@ public:
|
||||
* @return A reverse iterable object to use to _visit_ the storage.
|
||||
*/
|
||||
[[nodiscard]] reverse_iterable reach() noexcept {
|
||||
return {internal::extended_storage_iterator{base_type::rbegin()}, internal::extended_storage_iterator{base_type::rend()}};
|
||||
return reverse_iterable{{base_type::rbegin()}, {base_type::rend()}};
|
||||
}
|
||||
|
||||
/*! @copydoc reach */
|
||||
[[nodiscard]] const_reverse_iterable reach() const noexcept {
|
||||
return {internal::extended_storage_iterator{base_type::crbegin()}, internal::extended_storage_iterator{base_type::crend()}};
|
||||
return const_reverse_iterable{{base_type::crbegin()}, {base_type::crend()}};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -953,15 +975,27 @@ class basic_storage<Entity, Entity, Allocator>
|
||||
: public basic_sparse_set<Entity, Allocator> {
|
||||
using alloc_traits = std::allocator_traits<Allocator>;
|
||||
static_assert(std::is_same_v<typename alloc_traits::value_type, Entity>, "Invalid value type");
|
||||
using underlying_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
|
||||
using underlying_iterator = typename underlying_type::basic_iterator;
|
||||
using underlying_iterator = typename basic_sparse_set<Entity, Allocator>::basic_iterator;
|
||||
using traits_type = entt_traits<Entity>;
|
||||
|
||||
auto entity_at(const std::size_t pos) const noexcept {
|
||||
ENTT_ASSERT(pos < underlying_type::traits_type::to_entity(null), "Invalid element");
|
||||
return underlying_type::traits_type::combine(static_cast<typename underlying_type::traits_type::entity_type>(pos), {});
|
||||
auto next() noexcept {
|
||||
entity_type entt = null;
|
||||
|
||||
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);
|
||||
|
||||
return entt;
|
||||
}
|
||||
|
||||
protected:
|
||||
/*! @brief Erases all entities of a storage. */
|
||||
void pop_all() override {
|
||||
base_type::pop_all();
|
||||
placeholder = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns an entity to a storage.
|
||||
* @param hint A valid identifier.
|
||||
@@ -972,16 +1006,18 @@ protected:
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Base type. */
|
||||
using base_type = basic_sparse_set<Entity, Allocator>;
|
||||
/*! @brief Element type. */
|
||||
using element_type = Entity;
|
||||
/*! @brief Type of the objects assigned to entities. */
|
||||
using value_type = Entity;
|
||||
using value_type = void;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Extended iterable storage proxy. */
|
||||
using iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::iterator>>;
|
||||
/*! @brief Constant extended iterable storage proxy. */
|
||||
@@ -990,6 +1026,8 @@ public:
|
||||
using reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::reverse_iterator>>;
|
||||
/*! @brief Constant extended reverse iterable storage proxy. */
|
||||
using const_reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_reverse_iterator>>;
|
||||
/*! @brief Storage deletion policy. */
|
||||
static constexpr deletion_policy storage_policy = deletion_policy::swap_only;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
basic_storage()
|
||||
@@ -1001,22 +1039,36 @@ public:
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit basic_storage(const allocator_type &allocator)
|
||||
: base_type{type_id<void>(), deletion_policy::swap_only, allocator} {}
|
||||
: base_type{type_id<void>(), storage_policy, allocator} {}
|
||||
|
||||
/*! @brief Default copy constructor, deleted on purpose. */
|
||||
basic_storage(const basic_storage &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
basic_storage(basic_storage &&other) noexcept
|
||||
: base_type{std::move(other)} {}
|
||||
: base_type{std::move(other)},
|
||||
placeholder{other.placeholder} {}
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept
|
||||
: base_type{std::move(other), allocator} {}
|
||||
basic_storage(basic_storage &&other, const allocator_type &allocator)
|
||||
: base_type{std::move(other), allocator},
|
||||
placeholder{other.placeholder} {}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~basic_storage() override = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator, deleted on purpose.
|
||||
* @return This storage.
|
||||
*/
|
||||
basic_storage &operator=(const basic_storage &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
@@ -1024,6 +1076,7 @@ public:
|
||||
* @return This storage.
|
||||
*/
|
||||
basic_storage &operator=(basic_storage &&other) noexcept {
|
||||
placeholder = other.placeholder;
|
||||
base_type::operator=(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
@@ -1043,11 +1096,6 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Returns an empty tuple.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to use an entity that doesn't belong to the storage results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @param entt A valid identifier.
|
||||
* @return Returns an empty tuple.
|
||||
*/
|
||||
@@ -1062,7 +1110,7 @@ public:
|
||||
*/
|
||||
entity_type emplace() {
|
||||
const auto len = base_type::free_list();
|
||||
const auto entt = (len == base_type::size()) ? entity_at(len) : base_type::data()[len];
|
||||
const auto entt = (len == base_type::size()) ? next() : base_type::data()[len];
|
||||
return *base_type::try_emplace(entt, true);
|
||||
}
|
||||
|
||||
@@ -1076,22 +1124,13 @@ public:
|
||||
* @return A valid identifier.
|
||||
*/
|
||||
entity_type emplace(const entity_type hint) {
|
||||
if(hint == null || hint == tombstone) {
|
||||
return emplace();
|
||||
} else if(const auto curr = underlying_type::traits_type::construct(underlying_type::traits_type::to_entity(hint), base_type::current(hint)); curr == tombstone) {
|
||||
const auto pos = static_cast<size_type>(underlying_type::traits_type::to_entity(hint));
|
||||
const auto entt = *base_type::try_emplace(hint, true);
|
||||
|
||||
while(!(pos < base_type::size())) {
|
||||
base_type::try_emplace(entity_at(base_type::size() - 1u), false);
|
||||
if(hint != null && hint != tombstone) {
|
||||
if(const auto curr = traits_type::construct(traits_type::to_entity(hint), base_type::current(hint)); curr == tombstone || !(base_type::index(curr) < base_type::free_list())) {
|
||||
return *base_type::try_emplace(hint, true);
|
||||
}
|
||||
|
||||
return entt;
|
||||
} else if(const auto idx = base_type::index(curr); idx < base_type::free_list()) {
|
||||
return emplace();
|
||||
} else {
|
||||
return *base_type::try_emplace(hint, true);
|
||||
}
|
||||
|
||||
return emplace();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1119,39 +1158,10 @@ public:
|
||||
}
|
||||
|
||||
for(; first != last; ++first) {
|
||||
*first = *base_type::try_emplace(entity_at(base_type::free_list()), true);
|
||||
*first = *base_type::try_emplace(next(), true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Makes all elements in a range contiguous.
|
||||
* @tparam It Type of forward iterator.
|
||||
* @param first An iterator to the first element of the range to pack.
|
||||
* @param last An iterator past the last element of the range to pack.
|
||||
* @return The number of elements within the newly created range.
|
||||
*/
|
||||
template<typename It>
|
||||
[[deprecated("use sort_as instead")]] size_type pack(It first, It last) {
|
||||
base_type::sort_as(first, last);
|
||||
return static_cast<size_type>(std::distance(first, last));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements considered still in use.
|
||||
* @return The number of elements considered still in use.
|
||||
*/
|
||||
[[deprecated("use free_list() instead")]] [[nodiscard]] size_type in_use() const noexcept {
|
||||
return base_type::free_list();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the number of elements considered still in use.
|
||||
* @param len The number of elements considered still in use.
|
||||
*/
|
||||
[[deprecated("use free_list(len) instead")]] void in_use(const size_type len) noexcept {
|
||||
base_type::free_list(len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterable object to use to _visit_ a storage.
|
||||
*
|
||||
@@ -1160,12 +1170,13 @@ public:
|
||||
* @return An iterable object to use to _visit_ the storage.
|
||||
*/
|
||||
[[nodiscard]] iterable each() noexcept {
|
||||
return {internal::extended_storage_iterator{base_type::begin(0)}, internal::extended_storage_iterator{base_type::end(0)}};
|
||||
return std::as_const(*this).each();
|
||||
}
|
||||
|
||||
/*! @copydoc each */
|
||||
[[nodiscard]] const_iterable each() const noexcept {
|
||||
return {internal::extended_storage_iterator{base_type::cbegin(0)}, internal::extended_storage_iterator{base_type::cend(0)}};
|
||||
const auto it = base_type::cend();
|
||||
return const_iterable{it - base_type::free_list(), it};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1176,13 +1187,17 @@ public:
|
||||
* @return A reverse iterable object to use to _visit_ the storage.
|
||||
*/
|
||||
[[nodiscard]] reverse_iterable reach() noexcept {
|
||||
return {internal::extended_storage_iterator{base_type::rbegin()}, internal::extended_storage_iterator{base_type::rend(0)}};
|
||||
return std::as_const(*this).reach();
|
||||
}
|
||||
|
||||
/*! @copydoc reach */
|
||||
[[nodiscard]] const_reverse_iterable reach() const noexcept {
|
||||
return {internal::extended_storage_iterator{base_type::crbegin()}, internal::extended_storage_iterator{base_type::crend(0)}};
|
||||
const auto it = base_type::crbegin();
|
||||
return const_reverse_iterable{it, it + base_type::free_list()};
|
||||
}
|
||||
|
||||
private:
|
||||
size_type placeholder{};
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,9 +4,11 @@
|
||||
#include "config/version.h"
|
||||
#include "container/dense_map.hpp"
|
||||
#include "container/dense_set.hpp"
|
||||
#include "container/table.hpp"
|
||||
#include "core/algorithm.hpp"
|
||||
#include "core/any.hpp"
|
||||
#include "core/attribute.h"
|
||||
#include "core/bit.hpp"
|
||||
#include "core/compressed_pair.hpp"
|
||||
#include "core/enum.hpp"
|
||||
#include "core/family.hpp"
|
||||
@@ -15,6 +17,7 @@
|
||||
#include "core/iterator.hpp"
|
||||
#include "core/memory.hpp"
|
||||
#include "core/monostate.hpp"
|
||||
#include "core/ranges.hpp"
|
||||
#include "core/tuple.hpp"
|
||||
#include "core/type_info.hpp"
|
||||
#include "core/type_traits.hpp"
|
||||
@@ -27,6 +30,7 @@
|
||||
#include "entity/mixin.hpp"
|
||||
#include "entity/observer.hpp"
|
||||
#include "entity/organizer.hpp"
|
||||
#include "entity/ranges.hpp"
|
||||
#include "entity/registry.hpp"
|
||||
#include "entity/runtime_view.hpp"
|
||||
#include "entity/snapshot.hpp"
|
||||
@@ -50,7 +54,6 @@
|
||||
#include "meta/template.hpp"
|
||||
#include "meta/type_traits.hpp"
|
||||
#include "meta/utility.hpp"
|
||||
#include "platform/android-ndk-r17.hpp"
|
||||
#include "poly/poly.hpp"
|
||||
#include "process/process.hpp"
|
||||
#include "process/scheduler.hpp"
|
||||
|
||||
@@ -28,13 +28,9 @@ public:
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using iterator_concept = std::forward_iterator_tag;
|
||||
|
||||
constexpr edge_iterator() noexcept
|
||||
: it{},
|
||||
vert{},
|
||||
pos{},
|
||||
last{},
|
||||
offset{} {}
|
||||
constexpr edge_iterator() noexcept = default;
|
||||
|
||||
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
|
||||
constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept
|
||||
: it{std::move(base)},
|
||||
vert{vertices},
|
||||
@@ -66,10 +62,10 @@ public:
|
||||
friend constexpr bool operator==(const edge_iterator<Type> &, const edge_iterator<Type> &) noexcept;
|
||||
|
||||
private:
|
||||
It it;
|
||||
size_type vert;
|
||||
size_type pos;
|
||||
size_type last;
|
||||
It it{};
|
||||
size_type vert{};
|
||||
size_type pos{};
|
||||
size_type last{};
|
||||
size_type offset{};
|
||||
};
|
||||
|
||||
@@ -111,16 +107,17 @@ public:
|
||||
using vertex_iterator = iota_iterator<vertex_type>;
|
||||
/*! @brief Edge iterator type. */
|
||||
using edge_iterator = internal::edge_iterator<typename container_type::const_iterator>;
|
||||
/*! @brief Out edge iterator type. */
|
||||
/*! @brief Out-edge iterator type. */
|
||||
using out_edge_iterator = edge_iterator;
|
||||
/*! @brief In edge iterator type. */
|
||||
/*! @brief In-edge iterator type. */
|
||||
using in_edge_iterator = edge_iterator;
|
||||
/*! @brief Graph category tag. */
|
||||
using graph_category = Category;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
adjacency_matrix() noexcept(noexcept(allocator_type{}))
|
||||
: adjacency_matrix{0u} {}
|
||||
: adjacency_matrix{0u} {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructs an empty container with a given allocator.
|
||||
@@ -139,12 +136,8 @@ public:
|
||||
: matrix{vertices * vertices, allocator},
|
||||
vert{vertices} {}
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
* @param other The instance to copy from.
|
||||
*/
|
||||
adjacency_matrix(const adjacency_matrix &other)
|
||||
: adjacency_matrix{other, other.get_allocator()} {}
|
||||
/*! @brief Default copy constructor. */
|
||||
adjacency_matrix(const adjacency_matrix &) = default;
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended copy constructor.
|
||||
@@ -155,12 +148,8 @@ public:
|
||||
: matrix{other.matrix, allocator},
|
||||
vert{other.vert} {}
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
adjacency_matrix(adjacency_matrix &&other) noexcept
|
||||
: adjacency_matrix{std::move(other), other.get_allocator()} {}
|
||||
/*! @brief Default move constructor. */
|
||||
adjacency_matrix(adjacency_matrix &&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
@@ -169,29 +158,22 @@ public:
|
||||
*/
|
||||
adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator)
|
||||
: matrix{std::move(other.matrix), allocator},
|
||||
vert{std::exchange(other.vert, 0u)} {}
|
||||
vert{other.vert} {}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~adjacency_matrix() = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator.
|
||||
* @param other The instance to copy from.
|
||||
* @return This container.
|
||||
*/
|
||||
adjacency_matrix &operator=(const adjacency_matrix &other) {
|
||||
matrix = other.matrix;
|
||||
vert = other.vert;
|
||||
return *this;
|
||||
}
|
||||
adjacency_matrix &operator=(const adjacency_matrix &) = default;
|
||||
|
||||
/**
|
||||
* @brief Default move assignment operator.
|
||||
* @param other The instance to move from.
|
||||
* @return This container.
|
||||
*/
|
||||
adjacency_matrix &operator=(adjacency_matrix &&other) noexcept {
|
||||
matrix = std::move(other.matrix);
|
||||
vert = std::exchange(other.vert, 0u);
|
||||
return *this;
|
||||
}
|
||||
adjacency_matrix &operator=(adjacency_matrix &&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Returns the associated allocator.
|
||||
@@ -211,12 +193,25 @@ public:
|
||||
* @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) {
|
||||
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.
|
||||
*
|
||||
* @warning
|
||||
* Potentially expensive, try to avoid it on hot paths.
|
||||
*
|
||||
* @return True if the adjacency matrix is empty, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool empty() const noexcept {
|
||||
const auto iterable = edges();
|
||||
return (iterable.begin() == iterable.end());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of vertices.
|
||||
* @return The number of vertices.
|
||||
@@ -244,9 +239,9 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterable object to visit all out edges of a vertex.
|
||||
* @param vertex The vertex of which to return all out edges.
|
||||
* @return An iterable object to visit all out edges of a vertex.
|
||||
* @brief Returns an iterable object to visit all out-edges of a vertex.
|
||||
* @param vertex The vertex of which to return all out-edges.
|
||||
* @return An iterable object to visit all out-edges of a vertex.
|
||||
*/
|
||||
[[nodiscard]] iterable_adaptor<out_edge_iterator> out_edges(const vertex_type vertex) const noexcept {
|
||||
const auto it = matrix.cbegin();
|
||||
@@ -256,9 +251,9 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterable object to visit all in edges of a vertex.
|
||||
* @param vertex The vertex of which to return all in edges.
|
||||
* @return An iterable object to visit all in edges of a vertex.
|
||||
* @brief Returns an iterable object to visit all in-edges of a vertex.
|
||||
* @param vertex The vertex of which to return all in-edges.
|
||||
* @return An iterable object to visit all in-edges of a vertex.
|
||||
*/
|
||||
[[nodiscard]] iterable_adaptor<in_edge_iterator> in_edges(const vertex_type vertex) const noexcept {
|
||||
const auto it = matrix.cbegin();
|
||||
|
||||
@@ -29,9 +29,9 @@ template<typename Allocator>
|
||||
class basic_flow {
|
||||
using alloc_traits = std::allocator_traits<Allocator>;
|
||||
static_assert(std::is_same_v<typename alloc_traits::value_type, id_type>, "Invalid value type");
|
||||
using task_container_type = dense_set<id_type, identity, std::equal_to<id_type>, typename alloc_traits::template rebind_alloc<id_type>>;
|
||||
using task_container_type = dense_set<id_type, identity, std::equal_to<>, typename alloc_traits::template rebind_alloc<id_type>>;
|
||||
using ro_rw_container_type = std::vector<std::pair<std::size_t, bool>, typename alloc_traits::template rebind_alloc<std::pair<std::size_t, bool>>>;
|
||||
using deps_container_type = dense_map<id_type, ro_rw_container_type, identity, std::equal_to<id_type>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, ro_rw_container_type>>>;
|
||||
using deps_container_type = dense_map<id_type, ro_rw_container_type, identity, std::equal_to<>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, ro_rw_container_type>>>;
|
||||
using adjacency_matrix_type = adjacency_matrix<directed_tag, typename alloc_traits::template rebind_alloc<std::size_t>>;
|
||||
|
||||
void emplace(const id_type res, const bool is_rw) {
|
||||
@@ -135,8 +135,7 @@ public:
|
||||
explicit basic_flow(const allocator_type &allocator)
|
||||
: index{0u, allocator},
|
||||
vertices{allocator},
|
||||
deps{allocator},
|
||||
sync_on{} {}
|
||||
deps{allocator} {}
|
||||
|
||||
/*! @brief Default copy constructor. */
|
||||
basic_flow(const basic_flow &) = default;
|
||||
@@ -166,6 +165,9 @@ public:
|
||||
deps{std::move(other.deps), allocator},
|
||||
sync_on{other.sync_on} {}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~basic_flow() = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator.
|
||||
* @return This flow builder.
|
||||
@@ -207,7 +209,7 @@ public:
|
||||
* @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) {
|
||||
void swap(basic_flow &other) noexcept {
|
||||
using std::swap;
|
||||
std::swap(index, other.index);
|
||||
std::swap(vertices, other.vertices);
|
||||
@@ -215,6 +217,14 @@ public:
|
||||
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.
|
||||
*/
|
||||
[[nodiscard]] bool empty() const noexcept {
|
||||
return vertices.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of tasks.
|
||||
* @return The number of tasks.
|
||||
@@ -333,7 +343,7 @@ private:
|
||||
compressed_pair<size_type, allocator_type> index;
|
||||
task_container_type vertices;
|
||||
deps_container_type deps;
|
||||
size_type sync_on;
|
||||
size_type sync_on{};
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -38,9 +38,19 @@ public:
|
||||
|
||||
/*! @brief Default constructor, deleted on purpose. */
|
||||
locator() = delete;
|
||||
|
||||
/*! @brief Default copy constructor, deleted on purpose. */
|
||||
locator(const locator &) = delete;
|
||||
|
||||
/*! @brief Default destructor, deleted on purpose. */
|
||||
~locator() = delete;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator, deleted on purpose.
|
||||
* @return This locator.
|
||||
*/
|
||||
locator &operator=(const locator &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Checks whether a service locator contains a value.
|
||||
* @return True if the service locator contains a value, false otherwise.
|
||||
@@ -139,6 +149,7 @@ public:
|
||||
|
||||
private:
|
||||
// std::shared_ptr because of its type erased allocator which is useful here
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
inline static std::shared_ptr<Service> service{};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// IWYU pragma: always_keep
|
||||
|
||||
#ifndef ENTT_META_CONTAINER_HPP
|
||||
#define ENTT_META_CONTAINER_HPP
|
||||
|
||||
@@ -60,14 +62,14 @@ template<typename Type>
|
||||
struct basic_meta_sequence_container_traits {
|
||||
static_assert(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>, "Unexpected type");
|
||||
|
||||
/*! @brief True in case of key-only containers, false otherwise. */
|
||||
static constexpr bool fixed_size = internal::fixed_size_sequence_container_v<Type>;
|
||||
|
||||
/*! @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 Returns the number of elements in a container.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
@@ -129,8 +131,8 @@ struct basic_meta_sequence_container_traits {
|
||||
* @return An iterator to the first element of the container.
|
||||
*/
|
||||
static iterator begin(const meta_ctx &area, void *container, const void *as_const) {
|
||||
return container ? iterator{area, static_cast<Type *>(container)->begin()}
|
||||
: iterator{area, static_cast<const Type *>(as_const)->begin()};
|
||||
return (container != nullptr) ? iterator{area, static_cast<Type *>(container)->begin()}
|
||||
: iterator{area, static_cast<const Type *>(as_const)->begin()};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,8 +143,8 @@ struct basic_meta_sequence_container_traits {
|
||||
* @return An iterator that is past the last element of the container.
|
||||
*/
|
||||
static iterator end(const meta_ctx &area, void *container, const void *as_const) {
|
||||
return container ? iterator{area, static_cast<Type *>(container)->end()}
|
||||
: iterator{area, static_cast<const Type *>(as_const)->end()};
|
||||
return (container != nullptr) ? iterator{area, static_cast<Type *>(container)->end()}
|
||||
: iterator{area, static_cast<const Type *>(as_const)->end()};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,14 +159,14 @@ struct basic_meta_sequence_container_traits {
|
||||
* @param it Iterator before which the element will be inserted.
|
||||
* @return A possibly invalid iterator to the inserted element.
|
||||
*/
|
||||
[[nodiscard]] static iterator insert(const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const void *value, [[maybe_unused]] const void *cref, [[maybe_unused]] const iterator &it) {
|
||||
[[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{area};
|
||||
return iterator{};
|
||||
} else {
|
||||
auto *const non_const = any_cast<typename Type::iterator>(&it.base());
|
||||
return {area, static_cast<Type *>(container)->insert(
|
||||
non_const ? *non_const : any_cast<const typename Type::const_iterator &>(it.base()),
|
||||
value ? *static_cast<const typename Type::value_type *>(value) : *static_cast<const std::remove_reference_t<typename Type::const_reference> *>(cref))};
|
||||
(value != nullptr) ? *static_cast<const typename Type::value_type *>(value) : *static_cast<const std::remove_reference_t<typename Type::const_reference> *>(cref))};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,9 +177,9 @@ struct basic_meta_sequence_container_traits {
|
||||
* @param it An opaque iterator to the element to erase.
|
||||
* @return A possibly invalid iterator following the last removed element.
|
||||
*/
|
||||
[[nodiscard]] static iterator erase(const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const iterator &it) {
|
||||
[[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{area};
|
||||
return iterator{};
|
||||
} else {
|
||||
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()))};
|
||||
@@ -193,14 +195,14 @@ template<typename Type>
|
||||
struct basic_meta_associative_container_traits {
|
||||
static_assert(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>, "Unexpected type");
|
||||
|
||||
/*! @brief True in case of key-only containers, false otherwise. */
|
||||
static constexpr bool key_only = internal::key_only_associative_container_v<Type>;
|
||||
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename meta_associative_container::size_type;
|
||||
/*! @brief Meta iterator type. */
|
||||
using iterator = typename meta_associative_container::iterator;
|
||||
|
||||
/*! @brief True in case of key-only containers, false otherwise. */
|
||||
static constexpr bool key_only = internal::key_only_associative_container_v<Type>;
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements in a container.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
@@ -243,8 +245,8 @@ struct basic_meta_associative_container_traits {
|
||||
* @return An iterator to the first element of the container.
|
||||
*/
|
||||
static iterator begin(const meta_ctx &area, void *container, const void *as_const) {
|
||||
return container ? iterator{area, std::bool_constant<key_only>{}, static_cast<Type *>(container)->begin()}
|
||||
: iterator{area, std::bool_constant<key_only>{}, static_cast<const Type *>(as_const)->begin()};
|
||||
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()};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -255,8 +257,8 @@ struct basic_meta_associative_container_traits {
|
||||
* @return An iterator that is past the last element of the container.
|
||||
*/
|
||||
static iterator end(const meta_ctx &area, void *container, const void *as_const) {
|
||||
return container ? iterator{area, std::bool_constant<key_only>{}, static_cast<Type *>(container)->end()}
|
||||
: iterator{area, std::bool_constant<key_only>{}, static_cast<const Type *>(as_const)->end()};
|
||||
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()};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -293,8 +295,8 @@ struct basic_meta_associative_container_traits {
|
||||
* @return An iterator to the element with the given key, if any.
|
||||
*/
|
||||
static iterator find(const meta_ctx &area, void *container, const void *as_const, const void *key) {
|
||||
return container ? iterator{area, std::bool_constant<key_only>{}, static_cast<Type *>(container)->find(*static_cast<const typename Type::key_type *>(key))}
|
||||
: iterator{area, std::bool_constant<key_only>{}, static_cast<const Type *>(as_const)->find(*static_cast<const typename Type::key_type *>(key))};
|
||||
return (container != nullptr) ? iterator{area, std::bool_constant<key_only>{}, static_cast<Type *>(container)->find(*static_cast<const typename Type::key_type *>(key))}
|
||||
: iterator{area, std::bool_constant<key_only>{}, static_cast<const Type *>(as_const)->find(*static_cast<const typename Type::key_type *>(key))};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
#define ENTT_META_FACTORY_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/bit.hpp"
|
||||
#include "../core/fwd.hpp"
|
||||
#include "../core/type_info.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
@@ -25,52 +27,154 @@ namespace entt {
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
[[nodiscard]] inline decltype(auto) owner(meta_ctx &ctx, const type_info &info) {
|
||||
auto &&context = internal::meta_context::from(ctx);
|
||||
ENTT_ASSERT(context.value.contains(info.hash()), "Type not available");
|
||||
return context.value[info.hash()];
|
||||
}
|
||||
class basic_meta_factory {
|
||||
using invoke_type = std::remove_pointer_t<decltype(meta_func_node::invoke)>;
|
||||
|
||||
inline meta_data_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_data_node node) {
|
||||
return parent.details->data.insert_or_assign(id, std::move(node)).first->second;
|
||||
}
|
||||
|
||||
inline meta_func_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_func_node node) {
|
||||
if(auto it = parent.details->func.find(id); it != parent.details->func.end()) {
|
||||
for(auto *curr = &it->second; curr; curr = curr->next.get()) {
|
||||
if(curr->invoke == node.invoke) {
|
||||
node.next = std::move(curr->next);
|
||||
*curr = std::move(node);
|
||||
return *curr;
|
||||
}
|
||||
}
|
||||
|
||||
// locally overloaded function
|
||||
node.next = std::make_shared<meta_func_node>(std::move(parent.details->func[id]));
|
||||
auto *find_member_or_assert() {
|
||||
auto *member = find_member<&meta_data_node::id>(details->data, bucket);
|
||||
ENTT_ASSERT(member != nullptr, "Cannot find member");
|
||||
return member;
|
||||
}
|
||||
|
||||
return parent.details->func.insert_or_assign(id, std::move(node)).first->second;
|
||||
}
|
||||
auto *find_overload_or_assert() {
|
||||
auto *overload = find_overload(find_member<&meta_func_node::id>(details->func, bucket), invoke);
|
||||
ENTT_ASSERT(overload != nullptr, "Cannot find overload");
|
||||
return overload;
|
||||
}
|
||||
|
||||
void reset_bucket(const id_type id, invoke_type *const ref = nullptr) {
|
||||
invoke = ref;
|
||||
bucket = id;
|
||||
}
|
||||
|
||||
protected:
|
||||
void type(const id_type id) noexcept {
|
||||
reset_bucket(parent);
|
||||
auto &&elem = meta_context::from(*ctx).value[parent];
|
||||
ENTT_ASSERT(elem.id == id || !resolve(*ctx, id), "Duplicate identifier");
|
||||
elem.id = id;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
void insert_or_assign(Type 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);
|
||||
} 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);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
void dtor(meta_dtor_node node) {
|
||||
reset_bucket(parent);
|
||||
meta_context::from(*ctx).value[parent].dtor = node;
|
||||
}
|
||||
|
||||
void data(meta_data_node 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));
|
||||
} else if(member->set != node.set || member->get != node.get) {
|
||||
*member = std::move(node);
|
||||
}
|
||||
}
|
||||
|
||||
void func(meta_func_node 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));
|
||||
} 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));
|
||||
}
|
||||
}
|
||||
|
||||
void prop(meta_prop_node node) {
|
||||
std::vector<meta_prop_node> *container = nullptr;
|
||||
|
||||
if(bucket == parent) {
|
||||
container = &details->prop;
|
||||
} else if(invoke == nullptr) {
|
||||
container = &find_member_or_assert()->prop;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
void custom(meta_custom_node node) {
|
||||
if(bucket == parent) {
|
||||
meta_context::from(*ctx).value[bucket].custom = std::move(node);
|
||||
} else if(invoke == nullptr) {
|
||||
find_member_or_assert()->custom = std::move(node);
|
||||
} else {
|
||||
find_overload_or_assert()->custom = std::move(node);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
basic_meta_factory(const id_type id, meta_ctx &area)
|
||||
: 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>();
|
||||
}
|
||||
|
||||
details = elem.details.get();
|
||||
}
|
||||
|
||||
private:
|
||||
meta_ctx *ctx{};
|
||||
id_type parent{};
|
||||
id_type bucket{};
|
||||
invoke_type *invoke{};
|
||||
meta_type_descriptor *details{};
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Basic meta factory to be used for reflection purposes.
|
||||
* @brief Meta factory to be used for reflection purposes.
|
||||
* @tparam Type Reflected type for which the factory was created.
|
||||
*/
|
||||
template<typename Type>
|
||||
class meta_factory {
|
||||
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");
|
||||
|
||||
auto &&elem = internal::meta_extend(
|
||||
internal::owner(*ctx, *info),
|
||||
id,
|
||||
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,
|
||||
@@ -78,42 +182,27 @@ class meta_factory {
|
||||
&meta_arg<type_list<type_list_element_t<type_list_element_t<Index, args_type>::size != 1u, type_list_element_t<Index, args_type>>...>>,
|
||||
+[](meta_handle instance, meta_any value) { return (meta_setter<Type, value_list_element_v<Index, Setter>>(*instance.operator->(), value.as_ref()) || ...); },
|
||||
&meta_getter<Type, Getter, Policy>});
|
||||
|
||||
bucket = &elem.prop;
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Default constructor. */
|
||||
meta_factory() noexcept
|
||||
: meta_factory{locator<meta_ctx>::value_or()} {}
|
||||
: internal::basic_meta_factory{type_id<Type>(), 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
|
||||
: ctx{&area},
|
||||
bucket{},
|
||||
info{&type_id<Type>()} {
|
||||
auto &&elem = internal::owner(*ctx, *info);
|
||||
|
||||
if(!elem.details) {
|
||||
elem.details = std::make_shared<internal::meta_type_descriptor>();
|
||||
}
|
||||
|
||||
bucket = &elem.details->prop;
|
||||
}
|
||||
: internal::basic_meta_factory{type_id<Type>().hash(), area} {}
|
||||
|
||||
/**
|
||||
* @brief Assigns a custom unique identifier to a meta type.
|
||||
* @param id A custom unique identifier.
|
||||
* @return A meta factory for the given type.
|
||||
*/
|
||||
auto type(const id_type id) noexcept {
|
||||
auto &&elem = internal::owner(*ctx, *info);
|
||||
ENTT_ASSERT(elem.id == id || !resolve(*ctx, id), "Duplicate identifier");
|
||||
bucket = &elem.details->prop;
|
||||
elem.id = id;
|
||||
meta_factory type(const id_type id) noexcept {
|
||||
base_type::type(id);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -126,11 +215,10 @@ public:
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template<typename Base>
|
||||
auto base() noexcept {
|
||||
meta_factory base() noexcept {
|
||||
static_assert(!std::is_same_v<Type, Base> && std::is_base_of_v<Base, Type>, "Invalid base type");
|
||||
auto *const op = +[](const void *instance) noexcept { return static_cast<const void *>(static_cast<const Base *>(static_cast<const Type *>(instance))); };
|
||||
internal::owner(*ctx, *info).details->base.insert_or_assign(type_id<Base>().hash(), internal::meta_base_node{&internal::resolve<Base>, op});
|
||||
bucket = nullptr;
|
||||
base_type::insert_or_assign(internal::meta_base_node{type_id<Base>().hash(), &internal::resolve<Base>, op});
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -150,8 +238,7 @@ public:
|
||||
auto conv() noexcept {
|
||||
using conv_type = std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(Candidate), Type &>>>;
|
||||
auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, std::invoke(Candidate, *static_cast<const Type *>(instance))); };
|
||||
internal::owner(*ctx, *info).details->conv.insert_or_assign(type_id<conv_type>().hash(), internal::meta_conv_node{op});
|
||||
bucket = nullptr;
|
||||
base_type::insert_or_assign(internal::meta_conv_node{type_id<conv_type>().hash(), op});
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -165,11 +252,10 @@ public:
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template<typename To>
|
||||
auto conv() noexcept {
|
||||
meta_factory conv() noexcept {
|
||||
using conv_type = std::remove_cv_t<std::remove_reference_t<To>>;
|
||||
auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, static_cast<To>(*static_cast<const Type *>(instance))); };
|
||||
internal::owner(*ctx, *info).details->conv.insert_or_assign(type_id<conv_type>().hash(), internal::meta_conv_node{op});
|
||||
bucket = nullptr;
|
||||
base_type::insert_or_assign(internal::meta_conv_node{type_id<conv_type>().hash(), op});
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -187,12 +273,11 @@ public:
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template<auto Candidate, typename Policy = as_is_t>
|
||||
auto ctor() noexcept {
|
||||
meta_factory ctor() noexcept {
|
||||
using descriptor = meta_function_helper_t<Type, decltype(Candidate)>;
|
||||
static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy");
|
||||
static_assert(std::is_same_v<std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>, Type>, "The function doesn't return an object of the required type");
|
||||
internal::owner(*ctx, *info).details->ctor.insert_or_assign(type_id<typename descriptor::args_type>().hash(), internal::meta_ctor_node{descriptor::args_type::size, &meta_arg<typename descriptor::args_type>, &meta_construct<Type, Candidate, Policy>});
|
||||
bucket = nullptr;
|
||||
base_type::insert_or_assign(internal::meta_ctor_node{type_id<typename descriptor::args_type>().hash(), descriptor::args_type::size, &meta_arg<typename descriptor::args_type>, &meta_construct<Type, Candidate, Policy>});
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -207,14 +292,13 @@ public:
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template<typename... Args>
|
||||
auto ctor() noexcept {
|
||||
meta_factory ctor() noexcept {
|
||||
// default constructor is already implicitly generated, no need for redundancy
|
||||
if constexpr(sizeof...(Args) != 0u) {
|
||||
using descriptor = meta_function_helper_t<Type, Type (*)(Args...)>;
|
||||
internal::owner(*ctx, *info).details->ctor.insert_or_assign(type_id<typename descriptor::args_type>().hash(), internal::meta_ctor_node{descriptor::args_type::size, &meta_arg<typename descriptor::args_type>, &meta_construct<Type, Args...>});
|
||||
base_type::insert_or_assign(internal::meta_ctor_node{type_id<typename descriptor::args_type>().hash(), descriptor::args_type::size, &meta_arg<typename descriptor::args_type>, &meta_construct<Type, Args...>});
|
||||
}
|
||||
|
||||
bucket = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -237,11 +321,10 @@ public:
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template<auto Func>
|
||||
auto dtor() noexcept {
|
||||
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)); };
|
||||
internal::owner(*ctx, *info).dtor = internal::meta_dtor_node{op};
|
||||
bucket = nullptr;
|
||||
base_type::dtor(internal::meta_dtor_node{op});
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -259,15 +342,14 @@ public:
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template<auto Data, typename Policy = as_is_t>
|
||||
auto data(const id_type id) noexcept {
|
||||
meta_factory data(const id_type id) noexcept {
|
||||
if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
|
||||
using data_type = std::invoke_result_t<decltype(Data), Type &>;
|
||||
static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
|
||||
|
||||
auto &&elem = internal::meta_extend(
|
||||
internal::owner(*ctx, *info),
|
||||
id,
|
||||
base_type::data(
|
||||
internal::meta_data_node{
|
||||
id,
|
||||
/* 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,
|
||||
@@ -275,8 +357,6 @@ public:
|
||||
&meta_arg<type_list<std::remove_cv_t<std::remove_reference_t<data_type>>>>,
|
||||
&meta_setter<Type, Data>,
|
||||
&meta_getter<Type, Data, Policy>});
|
||||
|
||||
bucket = &elem.prop;
|
||||
} else {
|
||||
using data_type = std::remove_pointer_t<decltype(Data)>;
|
||||
|
||||
@@ -286,18 +366,15 @@ public:
|
||||
static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
|
||||
}
|
||||
|
||||
auto &&elem = internal::meta_extend(
|
||||
internal::owner(*ctx, *info),
|
||||
id,
|
||||
base_type::data(
|
||||
internal::meta_data_node{
|
||||
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,
|
||||
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>>>>,
|
||||
&meta_setter<Type, Data>,
|
||||
&meta_getter<Type, Data, Policy>});
|
||||
|
||||
bucket = &elem.prop;
|
||||
}
|
||||
|
||||
return *this;
|
||||
@@ -324,15 +401,14 @@ public:
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template<auto Setter, auto Getter, typename Policy = as_is_t>
|
||||
auto data(const id_type id) noexcept {
|
||||
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");
|
||||
|
||||
if constexpr(std::is_same_v<decltype(Setter), std::nullptr_t>) {
|
||||
auto &&elem = internal::meta_extend(
|
||||
internal::owner(*ctx, *info),
|
||||
id,
|
||||
base_type::data(
|
||||
internal::meta_data_node{
|
||||
id,
|
||||
/* this is never static */
|
||||
internal::meta_traits::is_const,
|
||||
0u,
|
||||
@@ -340,15 +416,12 @@ public:
|
||||
&meta_arg<type_list<>>,
|
||||
&meta_setter<Type, Setter>,
|
||||
&meta_getter<Type, Getter, Policy>});
|
||||
|
||||
bucket = &elem.prop;
|
||||
} else {
|
||||
using args_type = typename meta_function_helper_t<Type, decltype(Setter)>::args_type;
|
||||
|
||||
auto &&elem = internal::meta_extend(
|
||||
internal::owner(*ctx, *info),
|
||||
id,
|
||||
base_type::data(
|
||||
internal::meta_data_node{
|
||||
id,
|
||||
/* this is never static nor const */
|
||||
internal::meta_traits::is_none,
|
||||
1u,
|
||||
@@ -356,8 +429,6 @@ public:
|
||||
&meta_arg<type_list<type_list_element_t<args_type::size != 1u, args_type>>>,
|
||||
&meta_setter<Type, Setter>,
|
||||
&meta_getter<Type, Getter, Policy>});
|
||||
|
||||
bucket = &elem.prop;
|
||||
}
|
||||
|
||||
return *this;
|
||||
@@ -381,7 +452,7 @@ public:
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template<typename Setter, auto Getter, typename Policy = as_is_t>
|
||||
auto data(const id_type id) noexcept {
|
||||
meta_factory data(const id_type id) noexcept {
|
||||
data<Setter, Getter, Policy>(id, std::make_index_sequence<Setter::size>{});
|
||||
return *this;
|
||||
}
|
||||
@@ -400,26 +471,24 @@ public:
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template<auto Candidate, typename Policy = as_is_t>
|
||||
auto func(const id_type id) noexcept {
|
||||
meta_factory func(const id_type id) noexcept {
|
||||
using descriptor = meta_function_helper_t<Type, decltype(Candidate)>;
|
||||
static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy");
|
||||
|
||||
auto &&elem = internal::meta_extend(
|
||||
internal::owner(*ctx, *info),
|
||||
id,
|
||||
base_type::func(
|
||||
internal::meta_func_node{
|
||||
id,
|
||||
(descriptor::is_const ? internal::meta_traits::is_const : internal::meta_traits::is_none) | (descriptor::is_static ? internal::meta_traits::is_static : internal::meta_traits::is_none),
|
||||
descriptor::args_type::size,
|
||||
&internal::resolve<std::conditional_t<std::is_same_v<Policy, as_void_t>, void, std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>>>,
|
||||
&meta_arg<typename descriptor::args_type>,
|
||||
&meta_invoke<Type, Candidate, Policy>});
|
||||
|
||||
bucket = &elem.prop;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns a property to the last meta object created.
|
||||
* @brief Assigns a property to the last created meta object.
|
||||
*
|
||||
* Both the key and the value (if any) must be at least copy constructible.
|
||||
*
|
||||
@@ -429,24 +498,44 @@ public:
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template<typename... Value>
|
||||
meta_factory prop(id_type id, [[maybe_unused]] Value &&...value) {
|
||||
ENTT_ASSERT(bucket != nullptr, "Meta object does not support properties");
|
||||
|
||||
[[deprecated("use ::custom() instead")]] meta_factory prop(id_type id, [[maybe_unused]] Value &&...value) {
|
||||
if constexpr(sizeof...(Value) == 0u) {
|
||||
(*bucket)[id] = internal::meta_prop_node{&internal::resolve<void>};
|
||||
base_type::prop(internal::meta_prop_node{id, &internal::resolve<void>});
|
||||
} else {
|
||||
(*bucket)[id] = internal::meta_prop_node{
|
||||
&internal::resolve<std::decay_t<Value>>...,
|
||||
std::make_shared<std::decay_t<Value>>(std::forward<Value>(value))...};
|
||||
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;
|
||||
}
|
||||
|
||||
private:
|
||||
meta_ctx *ctx;
|
||||
dense_map<id_type, internal::meta_prop_node, identity> *bucket;
|
||||
const type_info *info;
|
||||
/**
|
||||
* @brief Sets traits on the last created meta object.
|
||||
*
|
||||
* The assigned value must be an enum and intended as a bitmask.
|
||||
*
|
||||
* @tparam Value Type of the traits value.
|
||||
* @param value Traits value.
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template<typename Value>
|
||||
meta_factory traits(const Value value) {
|
||||
static_assert(std::is_enum_v<Value>, "Invalid enum type");
|
||||
base_type::traits(internal::user_to_meta_traits(value));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets user defined data that will never be used by the library.
|
||||
* @tparam Value Type of user defined data to store.
|
||||
* @tparam Args Types of arguments to use to construct the user data.
|
||||
* @param args Parameters to use to initialize the user data.
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template<typename Value, typename... Args>
|
||||
meta_factory custom(Args &&...args) {
|
||||
base_type::custom(internal::meta_custom_node{type_id<Value>().hash(), std::make_shared<Value>(std::forward<Args>(args)...)});
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,6 +13,8 @@ struct meta_handle;
|
||||
|
||||
struct meta_prop;
|
||||
|
||||
struct meta_custom;
|
||||
|
||||
struct meta_data;
|
||||
|
||||
struct meta_func;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef ENTT_META_META_HPP
|
||||
#define ENTT_META_META_HPP
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
@@ -37,8 +38,7 @@ public:
|
||||
using iterator = meta_iterator;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
meta_sequence_container() noexcept
|
||||
: meta_sequence_container{locator<meta_ctx>::value_or()} {}
|
||||
meta_sequence_container() = default;
|
||||
|
||||
/**
|
||||
* @brief Context aware constructor.
|
||||
@@ -70,18 +70,18 @@ public:
|
||||
|
||||
[[nodiscard]] inline meta_type value_type() const noexcept;
|
||||
[[nodiscard]] inline size_type size() const noexcept;
|
||||
inline bool resize(const size_type);
|
||||
inline bool resize(size_type);
|
||||
inline bool clear();
|
||||
inline bool reserve(const size_type);
|
||||
inline bool reserve(size_type);
|
||||
[[nodiscard]] inline iterator begin();
|
||||
[[nodiscard]] inline iterator end();
|
||||
inline iterator insert(iterator, meta_any);
|
||||
inline iterator erase(iterator);
|
||||
[[nodiscard]] inline meta_any operator[](const size_type);
|
||||
inline iterator insert(const iterator &, meta_any);
|
||||
inline iterator erase(const iterator &);
|
||||
[[nodiscard]] inline meta_any operator[](size_type);
|
||||
[[nodiscard]] inline explicit operator bool() const noexcept;
|
||||
|
||||
private:
|
||||
const meta_ctx *ctx{};
|
||||
const meta_ctx *ctx{&locator<meta_ctx>::value_or()};
|
||||
internal::meta_type_node (*value_type_node)(const internal::meta_context &){};
|
||||
internal::meta_type_node (*const_reference_node)(const internal::meta_context &){};
|
||||
size_type (*size_fn)(const void *){};
|
||||
@@ -107,8 +107,7 @@ public:
|
||||
using iterator = meta_iterator;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
meta_associative_container() noexcept
|
||||
: meta_associative_container{locator<meta_ctx>::value_or()} {}
|
||||
meta_associative_container() = default;
|
||||
|
||||
/**
|
||||
* @brief Context aware constructor.
|
||||
@@ -143,13 +142,12 @@ public:
|
||||
data = &instance;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool key_only() const noexcept;
|
||||
[[nodiscard]] inline meta_type key_type() const noexcept;
|
||||
[[nodiscard]] inline meta_type mapped_type() const noexcept;
|
||||
[[nodiscard]] inline meta_type value_type() const noexcept;
|
||||
[[nodiscard]] inline size_type size() const noexcept;
|
||||
inline bool clear();
|
||||
inline bool reserve(const size_type);
|
||||
inline bool reserve(size_type);
|
||||
[[nodiscard]] inline iterator begin();
|
||||
[[nodiscard]] inline iterator end();
|
||||
inline bool insert(meta_any, meta_any);
|
||||
@@ -158,7 +156,7 @@ public:
|
||||
[[nodiscard]] inline explicit operator bool() const noexcept;
|
||||
|
||||
private:
|
||||
const meta_ctx *ctx{};
|
||||
const meta_ctx *ctx{&locator<meta_ctx>::value_or()};
|
||||
internal::meta_type_node (*key_type_node)(const internal::meta_context &){};
|
||||
internal::meta_type_node (*mapped_type_node)(const internal::meta_context &){};
|
||||
internal::meta_type_node (*value_type_node)(const internal::meta_context &){};
|
||||
@@ -215,31 +213,27 @@ class meta_any {
|
||||
}
|
||||
|
||||
void release() {
|
||||
if(node.dtor.dtor && (storage.policy() == any_policy::owner)) {
|
||||
if((node.dtor.dtor != nullptr) && (storage.policy() == any_policy::owner)) {
|
||||
node.dtor.dtor(storage.data());
|
||||
}
|
||||
}
|
||||
|
||||
meta_any(const meta_ctx &area, const meta_any &other, any ref) noexcept
|
||||
meta_any(const meta_any &other, any ref) noexcept
|
||||
: storage{std::move(ref)},
|
||||
ctx{&area},
|
||||
ctx{other.ctx},
|
||||
node{storage ? other.node : internal::meta_type_node{}},
|
||||
vtable{storage ? other.vtable : &basic_vtable<void>} {}
|
||||
|
||||
public:
|
||||
/*! Default constructor. */
|
||||
meta_any() noexcept
|
||||
: meta_any{meta_ctx_arg, locator<meta_ctx>::value_or()} {}
|
||||
meta_any() = default;
|
||||
|
||||
/**
|
||||
* @brief Context aware constructor.
|
||||
* @param area The context from which to search for meta types.
|
||||
*/
|
||||
meta_any(meta_ctx_arg_t, const meta_ctx &area) noexcept
|
||||
: storage{},
|
||||
ctx{&area},
|
||||
node{},
|
||||
vtable{&basic_vtable<void>} {}
|
||||
meta_any(meta_ctx_arg_t, const meta_ctx &area)
|
||||
: ctx{&area} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a wrapper by directly initializing the new object.
|
||||
@@ -290,10 +284,10 @@ public:
|
||||
* @param other The instance to copy from.
|
||||
*/
|
||||
meta_any(const meta_ctx &area, const meta_any &other)
|
||||
: meta_any{other} {
|
||||
ctx = &area;
|
||||
node = node.resolve ? node.resolve(internal::meta_context::from(*ctx)) : node;
|
||||
}
|
||||
: storage{other.storage},
|
||||
ctx{&area},
|
||||
node{(other.node.resolve != nullptr) ? other.node.resolve(internal::meta_context::from(*ctx)) : other.node},
|
||||
vtable{other.vtable} {}
|
||||
|
||||
/**
|
||||
* @brief Context aware move constructor.
|
||||
@@ -301,10 +295,10 @@ public:
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
meta_any(const meta_ctx &area, meta_any &&other)
|
||||
: meta_any{std::move(other)} {
|
||||
ctx = &area;
|
||||
node = node.resolve ? node.resolve(internal::meta_context::from(*ctx)) : node;
|
||||
}
|
||||
: storage{std::move(other.storage)},
|
||||
ctx{&area},
|
||||
node{(other.node.resolve != nullptr) ? std::exchange(other.node, internal::meta_type_node{}).resolve(internal::meta_context::from(*ctx)) : std::exchange(other.node, internal::meta_type_node{})},
|
||||
vtable{std::exchange(other.vtable, &basic_vtable<void>)} {}
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
@@ -333,11 +327,14 @@ public:
|
||||
* @return This meta any object.
|
||||
*/
|
||||
meta_any &operator=(const meta_any &other) {
|
||||
release();
|
||||
storage = other.storage;
|
||||
ctx = other.ctx;
|
||||
node = other.node;
|
||||
vtable = other.vtable;
|
||||
if(this != &other) {
|
||||
release();
|
||||
storage = other.storage;
|
||||
ctx = other.ctx;
|
||||
node = other.node;
|
||||
vtable = other.vtable;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -347,6 +344,8 @@ public:
|
||||
* @return This meta any object.
|
||||
*/
|
||||
meta_any &operator=(meta_any &&other) noexcept {
|
||||
ENTT_ASSERT(this != &other, "Self move assignment");
|
||||
|
||||
release();
|
||||
storage = std::move(other.storage);
|
||||
ctx = other.ctx;
|
||||
@@ -361,9 +360,8 @@ public:
|
||||
* @param value An instance of an object to use to initialize the wrapper.
|
||||
* @return This meta any object.
|
||||
*/
|
||||
template<typename Type>
|
||||
std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_any>, meta_any &>
|
||||
operator=(Type &&value) {
|
||||
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_any>>>
|
||||
meta_any &operator=(Type &&value) {
|
||||
emplace<std::decay_t<Type>>(std::forward<Type>(value));
|
||||
return *this;
|
||||
}
|
||||
@@ -389,11 +387,11 @@ public:
|
||||
* @return A wrapper containing the returned value, if any.
|
||||
*/
|
||||
template<typename... Args>
|
||||
meta_any invoke(const id_type id, Args &&...args) const;
|
||||
meta_any invoke(id_type id, Args &&...args) const;
|
||||
|
||||
/*! @copydoc invoke */
|
||||
template<typename... Args>
|
||||
meta_any invoke(const id_type id, Args &&...args);
|
||||
meta_any invoke(id_type id, Args &&...args);
|
||||
|
||||
/**
|
||||
* @brief Sets the value of a given variable.
|
||||
@@ -403,17 +401,17 @@ public:
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
template<typename Type>
|
||||
bool set(const id_type id, Type &&value);
|
||||
bool set(id_type id, Type &&value);
|
||||
|
||||
/**
|
||||
* @brief Gets the value of a given variable.
|
||||
* @param id Unique identifier.
|
||||
* @return A wrapper containing the value of the underlying variable.
|
||||
*/
|
||||
[[nodiscard]] meta_any get(const id_type id) const;
|
||||
[[nodiscard]] meta_any get(id_type id) const;
|
||||
|
||||
/*! @copydoc get */
|
||||
[[nodiscard]] meta_any get(const id_type id);
|
||||
[[nodiscard]] meta_any get(id_type id);
|
||||
|
||||
/**
|
||||
* @brief Tries to cast an instance to a given type.
|
||||
@@ -443,7 +441,7 @@ public:
|
||||
* @return A reference to the contained instance.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] Type cast() const {
|
||||
[[nodiscard]] std::remove_const_t<Type> cast() const {
|
||||
auto *const instance = try_cast<std::remove_reference_t<Type>>();
|
||||
ENTT_ASSERT(instance, "Invalid instance");
|
||||
return static_cast<Type>(*instance);
|
||||
@@ -451,7 +449,7 @@ public:
|
||||
|
||||
/*! @copydoc cast */
|
||||
template<typename Type>
|
||||
[[nodiscard]] Type cast() {
|
||||
[[nodiscard]] std::remove_const_t<Type> cast() {
|
||||
// forces const on non-reference types to make them work also with wrappers for const references
|
||||
auto *const instance = try_cast<std::remove_reference_t<const Type>>();
|
||||
ENTT_ASSERT(instance, "Invalid instance");
|
||||
@@ -588,7 +586,7 @@ public:
|
||||
|
||||
/*! @copydoc any::operator== */
|
||||
[[nodiscard]] bool operator==(const meta_any &other) const noexcept {
|
||||
return (ctx == other.ctx) && ((!node.info && !other.node.info) || (node.info && other.node.info && *node.info == *other.node.info && storage == other.storage));
|
||||
return (ctx == other.ctx) && (((node.info == nullptr) && (other.node.info == nullptr)) || ((node.info != nullptr) && (other.node.info != nullptr) && *node.info == *other.node.info && storage == other.storage));
|
||||
}
|
||||
|
||||
/*! @copydoc any::operator!= */
|
||||
@@ -598,17 +596,12 @@ public:
|
||||
|
||||
/*! @copydoc any::as_ref */
|
||||
[[nodiscard]] meta_any as_ref() noexcept {
|
||||
return meta_any{*ctx, *this, storage.as_ref()};
|
||||
return meta_any{*this, storage.as_ref()};
|
||||
}
|
||||
|
||||
/*! @copydoc any::as_ref */
|
||||
[[nodiscard]] meta_any as_ref() const noexcept {
|
||||
return meta_any{*ctx, *this, storage.as_ref()};
|
||||
}
|
||||
|
||||
/*! @copydoc any::owner */
|
||||
[[deprecated("use policy() and meta_any_policy instead")]] [[nodiscard]] bool owner() const noexcept {
|
||||
return (storage.policy() == any_policy::owner);
|
||||
return meta_any{*this, storage.as_ref()};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -620,10 +613,10 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
any storage;
|
||||
const meta_ctx *ctx;
|
||||
internal::meta_type_node node;
|
||||
vtable_type *vtable;
|
||||
any storage{};
|
||||
const meta_ctx *ctx{&locator<meta_ctx>::value_or()};
|
||||
internal::meta_type_node node{};
|
||||
vtable_type *vtable{&basic_vtable<void>};
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -657,28 +650,27 @@ template<typename Type>
|
||||
*/
|
||||
struct meta_handle {
|
||||
/*! Default constructor. */
|
||||
meta_handle() noexcept
|
||||
: meta_handle{meta_ctx_arg, locator<meta_ctx>::value_or()} {}
|
||||
meta_handle() = default;
|
||||
|
||||
/**
|
||||
* @brief Context aware constructor.
|
||||
* @param area The context from which to search for meta types.
|
||||
*/
|
||||
meta_handle(meta_ctx_arg_t, const meta_ctx &area) noexcept
|
||||
meta_handle(meta_ctx_arg_t, const meta_ctx &area)
|
||||
: any{meta_ctx_arg, area} {}
|
||||
|
||||
/**
|
||||
* @brief Creates a handle that points to an unmanaged object.
|
||||
* @param value An instance of an object to use to initialize the handle.
|
||||
*/
|
||||
meta_handle(meta_any &value) noexcept
|
||||
meta_handle(meta_any &value)
|
||||
: any{value.as_ref()} {}
|
||||
|
||||
/**
|
||||
* @brief Creates a handle that points to an unmanaged object.
|
||||
* @param value An instance of an object to use to initialize the handle.
|
||||
*/
|
||||
meta_handle(const meta_any &value) noexcept
|
||||
meta_handle(const meta_any &value)
|
||||
: any{value.as_ref()} {}
|
||||
|
||||
/**
|
||||
@@ -688,7 +680,7 @@ struct meta_handle {
|
||||
* @param value An instance of an object to use to initialize the handle.
|
||||
*/
|
||||
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_handle>>>
|
||||
meta_handle(const meta_ctx &ctx, Type &value) noexcept
|
||||
meta_handle(const meta_ctx &ctx, Type &value)
|
||||
: any{ctx, std::in_place_type<Type &>, value} {}
|
||||
|
||||
/**
|
||||
@@ -697,7 +689,7 @@ struct meta_handle {
|
||||
* @param value An instance of an object to use to initialize the handle.
|
||||
*/
|
||||
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_handle>>>
|
||||
meta_handle(Type &value) noexcept
|
||||
meta_handle(Type &value)
|
||||
: meta_handle{locator<meta_ctx>::value_or(), value} {}
|
||||
|
||||
/**
|
||||
@@ -722,6 +714,9 @@ struct meta_handle {
|
||||
/*! @brief Default move constructor. */
|
||||
meta_handle(meta_handle &&) = default;
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~meta_handle() = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator, deleted on purpose.
|
||||
* @return This meta handle.
|
||||
@@ -766,23 +761,21 @@ struct meta_handle {
|
||||
}
|
||||
|
||||
private:
|
||||
meta_any any;
|
||||
meta_any any{meta_ctx_arg, locator<meta_ctx>::value_or()};
|
||||
};
|
||||
|
||||
/*! @brief Opaque wrapper for properties of any type. */
|
||||
struct meta_prop {
|
||||
/*! @brief Default constructor. */
|
||||
meta_prop() noexcept
|
||||
: node{},
|
||||
ctx{} {}
|
||||
meta_prop() noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Context aware constructor for meta objects.
|
||||
* @param area The context from which to search for meta types.
|
||||
* @param curr The underlying node with which to construct the instance.
|
||||
*/
|
||||
meta_prop(const meta_ctx &area, const internal::meta_prop_node &curr) noexcept
|
||||
: node{&curr},
|
||||
meta_prop(const meta_ctx &area, internal::meta_prop_node curr) noexcept
|
||||
: node{std::move(curr)},
|
||||
ctx{&area} {}
|
||||
|
||||
/**
|
||||
@@ -790,7 +783,7 @@ struct meta_prop {
|
||||
* @return A wrapper containing the value stored with the property.
|
||||
*/
|
||||
[[nodiscard]] meta_any value() const {
|
||||
return node->value ? node->type(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, node->value.get()) : meta_any{meta_ctx_arg, *ctx};
|
||||
return node.value ? node.type(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, node.value.get()) : meta_any{meta_ctx_arg, *ctx};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -798,7 +791,7 @@ struct meta_prop {
|
||||
* @return A wrapper containing the value stored with the property.
|
||||
*/
|
||||
[[nodiscard]] meta_any value() {
|
||||
return node->value ? node->type(internal::meta_context::from(*ctx)).from_void(*ctx, node->value.get(), nullptr) : meta_any{meta_ctx_arg, *ctx};
|
||||
return node.value ? node.type(internal::meta_context::from(*ctx)).from_void(*ctx, node.value.get(), nullptr) : meta_any{meta_ctx_arg, *ctx};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -806,7 +799,7 @@ struct meta_prop {
|
||||
* @return True if the object is valid, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] explicit operator bool() const noexcept {
|
||||
return (node != nullptr);
|
||||
return static_cast<bool>(node.type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -815,12 +808,12 @@ struct meta_prop {
|
||||
* @return True if the objects refer to the same type, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool operator==(const meta_prop &other) const noexcept {
|
||||
return (ctx == other.ctx && node == other.node);
|
||||
return (ctx == other.ctx && node.value == other.node.value);
|
||||
}
|
||||
|
||||
private:
|
||||
const internal::meta_prop_node *node;
|
||||
const meta_ctx *ctx;
|
||||
internal::meta_prop_node node{};
|
||||
const meta_ctx *ctx{};
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -833,15 +826,48 @@ private:
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/*! @brief Opaque wrapper for user defined data of any type. */
|
||||
struct meta_custom {
|
||||
/*! @brief Default constructor. */
|
||||
meta_custom() noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Basic constructor for meta objects.
|
||||
* @param curr The underlying node with which to construct the instance.
|
||||
*/
|
||||
meta_custom(internal::meta_custom_node curr) noexcept
|
||||
: node{std::move(curr)} {}
|
||||
|
||||
/**
|
||||
* @brief Generic conversion operator.
|
||||
* @tparam Type Type to which conversion is requested.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] operator Type *() const noexcept {
|
||||
return (type_id<Type>().hash() == node.type) ? std::static_pointer_cast<Type>(node.value).get() : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generic conversion operator.
|
||||
* @tparam Type Type to which conversion is requested.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] operator Type &() const noexcept {
|
||||
ENTT_ASSERT(type_id<Type>().hash() == node.type, "Invalid type");
|
||||
return *std::static_pointer_cast<Type>(node.value);
|
||||
}
|
||||
|
||||
private:
|
||||
internal::meta_custom_node node{};
|
||||
};
|
||||
|
||||
/*! @brief Opaque wrapper for data members. */
|
||||
struct meta_data {
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename internal::meta_data_node::size_type;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
meta_data() noexcept
|
||||
: node{},
|
||||
ctx{} {}
|
||||
meta_data() noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Context aware constructor for meta objects.
|
||||
@@ -887,6 +913,7 @@ struct meta_data {
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
template<typename Type>
|
||||
// NOLINTNEXTLINE(modernize-use-nodiscard)
|
||||
bool set(meta_handle instance, Type &&value) const {
|
||||
return node->set && node->set(meta_handle{*ctx, std::move(instance)}, meta_any{*ctx, std::forward<Type>(value)});
|
||||
}
|
||||
@@ -905,13 +932,13 @@ struct meta_data {
|
||||
* @param index Index of the setter of which to return the accepted type.
|
||||
* @return The type accepted by the i-th setter.
|
||||
*/
|
||||
[[nodiscard]] inline meta_type arg(const size_type index) const noexcept;
|
||||
[[nodiscard]] inline meta_type arg(size_type index) const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Returns a range to visit registered meta properties.
|
||||
* @return An iterable range to visit registered meta properties.
|
||||
*/
|
||||
[[nodiscard]] meta_range<meta_prop, typename decltype(internal::meta_data_node::prop)::const_iterator> prop() const noexcept {
|
||||
[[nodiscard]] [[deprecated("use ::custom() instead")]] meta_range<meta_prop, typename decltype(internal::meta_data_node::prop)::const_iterator> prop() const noexcept {
|
||||
return {{*ctx, node->prop.cbegin()}, {*ctx, node->prop.cend()}};
|
||||
}
|
||||
|
||||
@@ -920,9 +947,32 @@ struct meta_data {
|
||||
* @param key The key to use to search for a property.
|
||||
* @return The registered meta property for the given key, if any.
|
||||
*/
|
||||
[[nodiscard]] meta_prop prop(const id_type key) const {
|
||||
const auto it = node->prop.find(key);
|
||||
return it != node->prop.cend() ? meta_prop{*ctx, it->second} : meta_prop{};
|
||||
[[nodiscard]] [[deprecated("use ::custom() instead")]] meta_prop prop(const id_type key) const {
|
||||
for(auto &&elem: node->prop) {
|
||||
if(elem.id == key) {
|
||||
return meta_prop{*ctx, elem};
|
||||
}
|
||||
}
|
||||
|
||||
return meta_prop{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns all meta traits for a given meta object.
|
||||
* @tparam Type The type to convert the meta traits to.
|
||||
* @return The registered meta traits, if any.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] Type traits() const noexcept {
|
||||
return internal::meta_to_user_traits<Type>(node->traits);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns user defined data for a given meta object.
|
||||
* @return User defined arbitrary data.
|
||||
*/
|
||||
[[nodiscard]] meta_custom custom() const noexcept {
|
||||
return {node->custom};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -939,8 +989,8 @@ struct meta_data {
|
||||
}
|
||||
|
||||
private:
|
||||
const internal::meta_data_node *node;
|
||||
const meta_ctx *ctx;
|
||||
const internal::meta_data_node *node{};
|
||||
const meta_ctx *ctx{};
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -959,9 +1009,7 @@ struct meta_func {
|
||||
using size_type = typename internal::meta_func_node::size_type;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
meta_func() noexcept
|
||||
: node{},
|
||||
ctx{} {}
|
||||
meta_func() noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Context aware constructor for meta objects.
|
||||
@@ -1007,14 +1055,10 @@ struct meta_func {
|
||||
* @param index Index of the argument of which to return the type.
|
||||
* @return The type of the i-th argument of a member function.
|
||||
*/
|
||||
[[nodiscard]] inline meta_type arg(const size_type index) const noexcept;
|
||||
[[nodiscard]] inline meta_type arg(size_type index) const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Invokes the underlying function, if possible.
|
||||
*
|
||||
* @warning
|
||||
* The context of the arguments is **never** changed.
|
||||
*
|
||||
* @param instance An opaque instance of the underlying type.
|
||||
* @param args Parameters to use to invoke the function.
|
||||
* @param sz Number of parameters to use to invoke the function.
|
||||
@@ -1032,13 +1076,14 @@ struct meta_func {
|
||||
* @return A wrapper containing the returned value, if any.
|
||||
*/
|
||||
template<typename... Args>
|
||||
// NOLINTNEXTLINE(modernize-use-nodiscard)
|
||||
meta_any invoke(meta_handle instance, Args &&...args) const {
|
||||
meta_any arguments[sizeof...(Args) + !sizeof...(Args)]{{*ctx, std::forward<Args>(args)}...};
|
||||
return invoke(std::move(instance), arguments, sizeof...(Args));
|
||||
std::array<meta_any, sizeof...(Args)> arguments{meta_any{*ctx, std::forward<Args>(args)}...};
|
||||
return invoke(std::move(instance), arguments.data(), sizeof...(Args));
|
||||
}
|
||||
|
||||
/*! @copydoc meta_data::prop */
|
||||
[[nodiscard]] meta_range<meta_prop, typename decltype(internal::meta_func_node::prop)::const_iterator> prop() const noexcept {
|
||||
[[nodiscard]] [[deprecated("use ::custom() instead")]] meta_range<meta_prop, typename decltype(internal::meta_func_node::prop)::const_iterator> prop() const noexcept {
|
||||
return {{*ctx, node->prop.cbegin()}, {*ctx, node->prop.cend()}};
|
||||
}
|
||||
|
||||
@@ -1047,9 +1092,25 @@ struct meta_func {
|
||||
* @param key The key to use to search for a property.
|
||||
* @return The registered meta property for the given key, if any.
|
||||
*/
|
||||
[[nodiscard]] meta_prop prop(const id_type key) const {
|
||||
const auto it = node->prop.find(key);
|
||||
return it != node->prop.cend() ? meta_prop{*ctx, it->second} : meta_prop{};
|
||||
[[nodiscard]] [[deprecated("use ::custom() instead")]] meta_prop prop(const id_type key) const {
|
||||
for(auto &&elem: node->prop) {
|
||||
if(elem.id == key) {
|
||||
return meta_prop{*ctx, elem};
|
||||
}
|
||||
}
|
||||
|
||||
return meta_prop{};
|
||||
}
|
||||
|
||||
/*! @copydoc meta_data::traits */
|
||||
template<typename Type>
|
||||
[[nodiscard]] Type traits() const noexcept {
|
||||
return internal::meta_to_user_traits<Type>(node->traits);
|
||||
}
|
||||
|
||||
/*! @copydoc meta_data::custom */
|
||||
[[nodiscard]] meta_custom custom() const noexcept {
|
||||
return {node->custom};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1074,8 +1135,8 @@ struct meta_func {
|
||||
}
|
||||
|
||||
private:
|
||||
const internal::meta_func_node *node;
|
||||
const meta_ctx *ctx;
|
||||
const internal::meta_func_node *node{};
|
||||
const meta_ctx *ctx{};
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1107,16 +1168,18 @@ class meta_type {
|
||||
size_type match{};
|
||||
size_type pos{};
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) - waiting for C++20 (and std::span)
|
||||
for(; pos < sz && args[pos]; ++pos) {
|
||||
const auto other = curr->arg(*ctx, pos);
|
||||
const auto type = args[pos].type();
|
||||
|
||||
if(const auto &info = other.info(); info == type.info()) {
|
||||
++match;
|
||||
} else if(!((type.node.details && (type.node.details->base.contains(info.hash()) || type.node.details->conv.contains(info.hash()))) || (type.node.conversion_helper && other.node.conversion_helper))) {
|
||||
} else if(!(type.node.conversion_helper && other.node.conversion_helper) && !(type.node.details && (internal::find_member<&internal::meta_base_node::type>(type.node.details->base, info.hash()) || internal::find_member<&internal::meta_conv_node::type>(type.node.details->conv, info.hash())))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
|
||||
if(pos == sz) {
|
||||
if(!candidate || match > same) {
|
||||
@@ -1146,17 +1209,15 @@ public:
|
||||
using size_type = typename internal::meta_type_node::size_type;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
meta_type() noexcept
|
||||
: node{},
|
||||
ctx{} {}
|
||||
meta_type() noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Context aware constructor for meta objects.
|
||||
* @param area The context from which to search for meta types.
|
||||
* @param curr The underlying node with which to construct the instance.
|
||||
*/
|
||||
meta_type(const meta_ctx &area, const internal::meta_type_node &curr) noexcept
|
||||
: node{curr},
|
||||
meta_type(const meta_ctx &area, internal::meta_type_node curr) noexcept
|
||||
: node{std::move(curr)},
|
||||
ctx{&area} {}
|
||||
|
||||
/**
|
||||
@@ -1165,7 +1226,7 @@ public:
|
||||
* @param curr The underlying node with which to construct the instance.
|
||||
*/
|
||||
meta_type(const meta_ctx &area, const internal::meta_base_node &curr) noexcept
|
||||
: meta_type{area, curr.type(internal::meta_context::from(area))} {}
|
||||
: meta_type{area, curr.resolve(internal::meta_context::from(area))} {}
|
||||
|
||||
/**
|
||||
* @brief Returns the type info object of the underlying type.
|
||||
@@ -1245,7 +1306,7 @@ public:
|
||||
* @return True if the underlying type is a pointer, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool is_pointer() const noexcept {
|
||||
return node.info && (node.info->hash() != remove_pointer().info().hash());
|
||||
return static_cast<bool>(node.traits & internal::meta_traits::is_pointer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1254,7 +1315,7 @@ public:
|
||||
* doesn't refer to a pointer type.
|
||||
*/
|
||||
[[nodiscard]] meta_type remove_pointer() const noexcept {
|
||||
return {*ctx, node.remove_pointer(internal::meta_context::from(*ctx))}; // NOLINT
|
||||
return {*ctx, node.remove_pointer(internal::meta_context::from(*ctx))};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1304,7 +1365,7 @@ public:
|
||||
* @return The tag for the class template of the underlying type.
|
||||
*/
|
||||
[[nodiscard]] inline meta_type template_type() const noexcept {
|
||||
return node.templ.type ? meta_type{*ctx, node.templ.type(internal::meta_context::from(*ctx))} : meta_type{};
|
||||
return (node.templ.resolve != nullptr) ? meta_type{*ctx, node.templ.resolve(internal::meta_context::from(*ctx))} : meta_type{};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1360,7 +1421,7 @@ public:
|
||||
*/
|
||||
[[nodiscard]] meta_data data(const id_type id) const {
|
||||
const auto *elem = internal::look_for<&internal::meta_type_descriptor::data>(internal::meta_context::from(*ctx), node, id);
|
||||
return elem ? meta_data{*ctx, *elem} : meta_data{};
|
||||
return (elem != nullptr) ? meta_data{*ctx, *elem} : meta_data{};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1382,27 +1443,23 @@ public:
|
||||
*/
|
||||
[[nodiscard]] meta_func func(const id_type id) const {
|
||||
const auto *elem = internal::look_for<&internal::meta_type_descriptor::func>(internal::meta_context::from(*ctx), node, id);
|
||||
return elem ? meta_func{*ctx, *elem} : meta_func{};
|
||||
return (elem != nullptr) ? meta_func{*ctx, *elem} : meta_func{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates an instance of the underlying type, if possible.
|
||||
*
|
||||
* @warning
|
||||
* The context of the arguments is **never** changed.
|
||||
*
|
||||
* @param args Parameters to use to construct the instance.
|
||||
* @param sz Number of parameters to use to construct the instance.
|
||||
* @return A wrapper containing the new instance, if any.
|
||||
*/
|
||||
[[nodiscard]] meta_any construct(meta_any *const args, const size_type sz) const {
|
||||
if(node.details) {
|
||||
if(const auto *candidate = lookup(args, sz, false, [first = node.details->ctor.cbegin(), last = node.details->ctor.cend()]() mutable { return first == last ? nullptr : &(first++)->second; }); candidate) {
|
||||
if(const auto *candidate = lookup(args, sz, false, [first = node.details->ctor.cbegin(), last = node.details->ctor.cend()]() mutable { return first == last ? nullptr : &*(first++); }); candidate) {
|
||||
return candidate->invoke(*ctx, args);
|
||||
}
|
||||
}
|
||||
|
||||
if(sz == 0u && node.default_constructor) {
|
||||
if(sz == 0u && (node.default_constructor != nullptr)) {
|
||||
return node.default_constructor(*ctx);
|
||||
}
|
||||
|
||||
@@ -1417,30 +1474,26 @@ public:
|
||||
*/
|
||||
template<typename... Args>
|
||||
[[nodiscard]] meta_any construct(Args &&...args) const {
|
||||
meta_any arguments[sizeof...(Args) + !sizeof...(Args)]{{*ctx, std::forward<Args>(args)}...};
|
||||
return construct(arguments, sizeof...(Args));
|
||||
std::array<meta_any, sizeof...(Args)> arguments{meta_any{*ctx, std::forward<Args>(args)}...};
|
||||
return construct(arguments.data(), sizeof...(Args));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wraps an opaque element of the underlying type.
|
||||
* @param element A valid pointer to an element of the underlying type.
|
||||
* @param elem A valid pointer to an element of the underlying type.
|
||||
* @return A wrapper that references the given instance.
|
||||
*/
|
||||
[[nodiscard]] meta_any from_void(void *element) const {
|
||||
return (element && node.from_void) ? node.from_void(*ctx, element, nullptr) : meta_any{meta_ctx_arg, *ctx};
|
||||
[[nodiscard]] meta_any from_void(void *elem) const {
|
||||
return ((elem != nullptr) && (node.from_void != nullptr)) ? node.from_void(*ctx, elem, nullptr) : meta_any{meta_ctx_arg, *ctx};
|
||||
}
|
||||
|
||||
/*! @copydoc from_void */
|
||||
[[nodiscard]] meta_any from_void(const void *element) const {
|
||||
return (element && node.from_void) ? node.from_void(*ctx, nullptr, element) : meta_any{meta_ctx_arg, *ctx};
|
||||
[[nodiscard]] meta_any from_void(const void *elem) const {
|
||||
return ((elem != nullptr) && (node.from_void != nullptr)) ? node.from_void(*ctx, nullptr, elem) : meta_any{meta_ctx_arg, *ctx};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Invokes a function given an identifier, if possible.
|
||||
*
|
||||
* @warning
|
||||
* The context of the arguments is **never** changed.
|
||||
*
|
||||
* @param id Unique identifier.
|
||||
* @param instance An opaque instance of the underlying type.
|
||||
* @param args Parameters to use to invoke the function.
|
||||
@@ -1449,8 +1502,8 @@ public:
|
||||
*/
|
||||
meta_any invoke(const id_type id, meta_handle instance, meta_any *const args, const size_type sz) const {
|
||||
if(node.details) {
|
||||
if(auto it = node.details->func.find(id); it != node.details->func.cend()) {
|
||||
if(const auto *candidate = lookup(args, sz, instance && (instance->data() == nullptr), [curr = &it->second]() mutable { return curr ? std::exchange(curr, curr->next.get()) : nullptr; }); candidate) {
|
||||
if(auto *elem = internal::find_member<&internal::meta_func_node::id>(node.details->func, id); elem != nullptr) {
|
||||
if(const auto *candidate = lookup(args, sz, instance && (instance->data() == nullptr), [curr = elem]() mutable { return (curr != nullptr) ? std::exchange(curr, curr->next.get()) : nullptr; }); candidate) {
|
||||
return candidate->invoke(*ctx, meta_handle{*ctx, std::move(instance)}, args);
|
||||
}
|
||||
}
|
||||
@@ -1474,9 +1527,10 @@ public:
|
||||
* @return A wrapper containing the returned value, if any.
|
||||
*/
|
||||
template<typename... Args>
|
||||
// NOLINTNEXTLINE(modernize-use-nodiscard)
|
||||
meta_any invoke(const id_type id, meta_handle instance, Args &&...args) const {
|
||||
meta_any arguments[sizeof...(Args) + !sizeof...(Args)]{{*ctx, std::forward<Args>(args)}...};
|
||||
return invoke(id, std::move(instance), arguments, sizeof...(Args));
|
||||
std::array<meta_any, sizeof...(Args)> arguments{meta_any{*ctx, std::forward<Args>(args)}...};
|
||||
return invoke(id, std::move(instance), arguments.data(), sizeof...(Args));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1508,7 +1562,7 @@ public:
|
||||
* @brief Returns a range to visit registered top-level meta properties.
|
||||
* @return An iterable range to visit registered top-level meta properties.
|
||||
*/
|
||||
[[nodiscard]] meta_range<meta_prop, typename decltype(internal::meta_type_descriptor::prop)::const_iterator> prop() const noexcept {
|
||||
[[nodiscard]] [[deprecated("use ::custom() instead")]] meta_range<meta_prop, typename decltype(internal::meta_type_descriptor::prop)::const_iterator> prop() const noexcept {
|
||||
using range_type = meta_range<meta_prop, typename decltype(internal::meta_type_descriptor::prop)::const_iterator>;
|
||||
return node.details ? range_type{{*ctx, node.details->prop.cbegin()}, {*ctx, node.details->prop.cend()}} : range_type{};
|
||||
}
|
||||
@@ -1518,9 +1572,20 @@ public:
|
||||
* @param key The key to use to search for a property.
|
||||
* @return The registered meta property for the given key, if any.
|
||||
*/
|
||||
[[nodiscard]] meta_prop prop(const id_type key) const {
|
||||
[[nodiscard]] [[deprecated("use ::custom() instead")]] meta_prop prop(const id_type key) const {
|
||||
const auto *elem = internal::look_for<&internal::meta_type_descriptor::prop>(internal::meta_context::from(*ctx), node, key);
|
||||
return elem ? meta_prop{*ctx, *elem} : meta_prop{};
|
||||
return (elem != nullptr) ? meta_prop{*ctx, *elem} : meta_prop{};
|
||||
}
|
||||
|
||||
/*! @copydoc meta_data::traits */
|
||||
template<typename Type>
|
||||
[[nodiscard]] Type traits() const noexcept {
|
||||
return internal::meta_to_user_traits<Type>(node.traits);
|
||||
}
|
||||
|
||||
/*! @copydoc meta_data::custom */
|
||||
[[nodiscard]] meta_custom custom() const noexcept {
|
||||
return {node.custom};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1533,12 +1598,12 @@ public:
|
||||
|
||||
/*! @copydoc meta_prop::operator== */
|
||||
[[nodiscard]] bool operator==(const meta_type &other) const noexcept {
|
||||
return (ctx == other.ctx) && ((!node.info && !other.node.info) || (node.info && other.node.info && *node.info == *other.node.info));
|
||||
return (ctx == other.ctx) && (((node.info == nullptr) && (other.node.info == nullptr)) || ((node.info != nullptr) && (other.node.info != nullptr) && *node.info == *other.node.info));
|
||||
}
|
||||
|
||||
private:
|
||||
internal::meta_type_node node;
|
||||
const meta_ctx *ctx;
|
||||
internal::meta_type_node node{};
|
||||
const meta_ctx *ctx{};
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1552,10 +1617,11 @@ private:
|
||||
}
|
||||
|
||||
[[nodiscard]] inline meta_type meta_any::type() const noexcept {
|
||||
return node.info ? meta_type{*ctx, node} : meta_type{};
|
||||
return (node.info != nullptr) ? meta_type{*ctx, node} : meta_type{};
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
// NOLINTNEXTLINE(modernize-use-nodiscard)
|
||||
meta_any meta_any::invoke(const id_type id, Args &&...args) const {
|
||||
return type().invoke(id, *this, std::forward<Args>(args)...);
|
||||
}
|
||||
@@ -1637,18 +1703,14 @@ class meta_sequence_container::meta_iterator final {
|
||||
}
|
||||
|
||||
public:
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = meta_any;
|
||||
using pointer = input_iterator_pointer<value_type>;
|
||||
using reference = value_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using iterator_concept = std::bidirectional_iterator_tag;
|
||||
|
||||
meta_iterator() noexcept
|
||||
: meta_iterator{locator<meta_ctx>::value_or()} {}
|
||||
|
||||
meta_iterator(const meta_ctx &area) noexcept
|
||||
: ctx{&area} {}
|
||||
meta_iterator() = default;
|
||||
|
||||
template<typename It>
|
||||
meta_iterator(const meta_ctx &area, It iter) noexcept
|
||||
@@ -1705,7 +1767,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
const meta_ctx *ctx{};
|
||||
const meta_ctx *ctx{&locator<meta_ctx>::value_or()};
|
||||
vtable_type *vtable{};
|
||||
any handle{};
|
||||
};
|
||||
@@ -1728,18 +1790,14 @@ class meta_associative_container::meta_iterator final {
|
||||
}
|
||||
|
||||
public:
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = std::pair<meta_any, meta_any>;
|
||||
using pointer = input_iterator_pointer<value_type>;
|
||||
using reference = value_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using iterator_concept = std::forward_iterator_tag;
|
||||
|
||||
meta_iterator() noexcept
|
||||
: meta_iterator{locator<meta_ctx>::value_or()} {}
|
||||
|
||||
meta_iterator(const meta_ctx &area) noexcept
|
||||
: ctx{&area} {}
|
||||
meta_iterator() = default;
|
||||
|
||||
template<bool KeyOnly, typename It>
|
||||
meta_iterator(const meta_ctx &area, std::bool_constant<KeyOnly>, It iter) noexcept
|
||||
@@ -1781,7 +1839,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
const meta_ctx *ctx{};
|
||||
const meta_ctx *ctx{&locator<meta_ctx>::value_or()};
|
||||
vtable_type *vtable{};
|
||||
any handle{};
|
||||
};
|
||||
@@ -1792,7 +1850,7 @@ private:
|
||||
* @return The meta value type of the container.
|
||||
*/
|
||||
[[nodiscard]] inline meta_type meta_sequence_container::value_type() const noexcept {
|
||||
return value_type_node ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{};
|
||||
return (value_type_node != nullptr) ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1851,14 +1909,14 @@ inline bool meta_sequence_container::reserve(const size_type sz) {
|
||||
* @param value Element value to insert.
|
||||
* @return A possibly invalid iterator to the inserted element.
|
||||
*/
|
||||
inline meta_sequence_container::iterator meta_sequence_container::insert(iterator it, meta_any value) {
|
||||
inline meta_sequence_container::iterator meta_sequence_container::insert(const iterator &it, meta_any value) {
|
||||
// this abomination is necessary because only on macos value_type and const_reference are different types for std::vector<bool>
|
||||
if(const auto vtype = value_type_node(internal::meta_context::from(*ctx)); !const_only && (value.allow_cast({*ctx, vtype}) || value.allow_cast({*ctx, const_reference_node(internal::meta_context::from(*ctx))}))) {
|
||||
const bool is_value_type = (value.type().info() == *vtype.info);
|
||||
return insert_fn(*ctx, const_cast<void *>(data), is_value_type ? std::as_const(value).data() : nullptr, is_value_type ? nullptr : std::as_const(value).data(), it);
|
||||
}
|
||||
|
||||
return iterator{*ctx};
|
||||
return iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1866,13 +1924,12 @@ inline meta_sequence_container::iterator meta_sequence_container::insert(iterato
|
||||
* @param it Iterator to the element to remove.
|
||||
* @return A possibly invalid iterator following the last removed element.
|
||||
*/
|
||||
inline meta_sequence_container::iterator meta_sequence_container::erase(iterator it) {
|
||||
return const_only ? iterator{*ctx} : erase_fn(*ctx, const_cast<void *>(data), it);
|
||||
inline meta_sequence_container::iterator meta_sequence_container::erase(const iterator &it) {
|
||||
return const_only ? iterator{} : erase_fn(*ctx, const_cast<void *>(data), it);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the element at a given location of a container
|
||||
* (no bounds checking is performed).
|
||||
* @brief Returns a reference to the element at a given location of a container.
|
||||
* @param pos The position of the element to return.
|
||||
* @return A reference to the requested element properly wrapped.
|
||||
*/
|
||||
@@ -1890,20 +1947,12 @@ inline meta_sequence_container::iterator meta_sequence_container::erase(iterator
|
||||
return (data != nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true if a container is also key-only, false otherwise.
|
||||
* @return True if the associative container is also key-only, false otherwise.
|
||||
*/
|
||||
[[deprecated("use mapped_type() instead")]] [[nodiscard]] inline bool meta_associative_container::key_only() const noexcept {
|
||||
return (mapped_type_node == nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the meta key type of a container.
|
||||
* @return The meta key type of the a container.
|
||||
*/
|
||||
[[nodiscard]] inline meta_type meta_associative_container::key_type() const noexcept {
|
||||
return key_type_node ? meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))} : meta_type{};
|
||||
return (key_type_node != nullptr) ? meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))} : meta_type{};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1911,12 +1960,12 @@ inline meta_sequence_container::iterator meta_sequence_container::erase(iterator
|
||||
* @return The meta mapped type of the a container.
|
||||
*/
|
||||
[[nodiscard]] inline meta_type meta_associative_container::mapped_type() const noexcept {
|
||||
return mapped_type_node ? meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))} : meta_type{};
|
||||
return (mapped_type_node != nullptr) ? meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))} : meta_type{};
|
||||
}
|
||||
|
||||
/*! @copydoc meta_sequence_container::value_type */
|
||||
[[nodiscard]] inline meta_type meta_associative_container::value_type() const noexcept {
|
||||
return value_type_node ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{};
|
||||
return (value_type_node != nullptr) ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{};
|
||||
}
|
||||
|
||||
/*! @copydoc meta_sequence_container::size */
|
||||
@@ -1952,7 +2001,7 @@ inline bool meta_associative_container::reserve(const size_type sz) {
|
||||
*/
|
||||
inline bool meta_associative_container::insert(meta_any key, meta_any value = {}) {
|
||||
return !const_only && key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))})
|
||||
&& (!mapped_type_node || value.allow_cast(meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))}))
|
||||
&& ((mapped_type_node == nullptr) || value.allow_cast(meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))}))
|
||||
&& insert_fn(const_cast<void *>(data), std::as_const(key).data(), std::as_const(value).data());
|
||||
}
|
||||
|
||||
@@ -1971,7 +2020,7 @@ inline meta_associative_container::size_type meta_associative_container::erase(m
|
||||
* @return An iterator to the element with the given key, if any.
|
||||
*/
|
||||
[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::find(meta_any key) {
|
||||
return key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))}) ? find_fn(*ctx, const_only ? nullptr : const_cast<void *>(data), data, std::as_const(key).data()) : iterator{*ctx};
|
||||
return key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))}) ? find_fn(*ctx, const_only ? nullptr : const_cast<void *>(data), data, std::as_const(key).data()) : iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../config/config.h"
|
||||
#include "../container/dense_map.hpp"
|
||||
#include "../core/attribute.h"
|
||||
#include "../core/bit.hpp"
|
||||
#include "../core/enum.hpp"
|
||||
#include "../core/fwd.hpp"
|
||||
#include "../core/type_info.hpp"
|
||||
@@ -35,31 +36,58 @@ enum class meta_traits : std::uint32_t {
|
||||
is_array = 0x0020,
|
||||
is_enum = 0x0040,
|
||||
is_class = 0x0080,
|
||||
is_meta_pointer_like = 0x0100,
|
||||
is_meta_sequence_container = 0x0200,
|
||||
is_meta_associative_container = 0x0400,
|
||||
_entt_enum_as_bitmask
|
||||
is_pointer = 0x0100,
|
||||
is_meta_pointer_like = 0x0200,
|
||||
is_meta_sequence_container = 0x0400,
|
||||
is_meta_associative_container = 0x0800,
|
||||
_user_defined_traits = 0xFFFF,
|
||||
_entt_enum_as_bitmask = 0xFFFF
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] auto meta_to_user_traits(const meta_traits traits) noexcept {
|
||||
static_assert(std::is_enum_v<Type>, "Invalid enum type");
|
||||
constexpr auto shift = popcount(static_cast<std::underlying_type_t<meta_traits>>(meta_traits::_user_defined_traits));
|
||||
return Type{static_cast<std::underlying_type_t<Type>>(static_cast<std::underlying_type_t<meta_traits>>(traits) >> shift)};
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] auto user_to_meta_traits(const Type value) noexcept {
|
||||
static_assert(std::is_enum_v<Type>, "Invalid enum type");
|
||||
constexpr auto shift = popcount(static_cast<std::underlying_type_t<meta_traits>>(meta_traits::_user_defined_traits));
|
||||
const auto traits = static_cast<std::underlying_type_t<internal::meta_traits>>(static_cast<std::underlying_type_t<Type>>(value));
|
||||
ENTT_ASSERT(traits < ((~static_cast<std::underlying_type_t<meta_traits>>(meta_traits::_user_defined_traits)) >> shift), "Invalid traits");
|
||||
return meta_traits{traits << shift};
|
||||
}
|
||||
|
||||
struct meta_type_node;
|
||||
|
||||
struct meta_custom_node {
|
||||
id_type type{};
|
||||
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 {
|
||||
meta_type_node (*type)(const meta_context &) noexcept {};
|
||||
id_type type{};
|
||||
meta_type_node (*resolve)(const meta_context &) noexcept {};
|
||||
const void *(*cast)(const void *) noexcept {};
|
||||
};
|
||||
|
||||
struct meta_conv_node {
|
||||
id_type type{};
|
||||
meta_any (*conv)(const meta_ctx &, const void *){};
|
||||
};
|
||||
|
||||
struct meta_ctor_node {
|
||||
using size_type = std::size_t;
|
||||
|
||||
id_type id{};
|
||||
size_type arity{0u};
|
||||
meta_type (*arg)(const meta_ctx &, const size_type) noexcept {};
|
||||
meta_any (*invoke)(const meta_ctx &, meta_any *const){};
|
||||
@@ -72,42 +100,46 @@ struct meta_dtor_node {
|
||||
struct meta_data_node {
|
||||
using size_type = std::size_t;
|
||||
|
||||
id_type id{};
|
||||
meta_traits traits{meta_traits::is_none};
|
||||
size_type arity{0u};
|
||||
meta_type_node (*type)(const meta_context &) noexcept {};
|
||||
meta_type (*arg)(const meta_ctx &, const size_type) noexcept {};
|
||||
bool (*set)(meta_handle, meta_any){};
|
||||
meta_any (*get)(const meta_ctx &, meta_handle){};
|
||||
dense_map<id_type, meta_prop_node, identity> prop{};
|
||||
meta_custom_node custom{};
|
||||
std::vector<meta_prop_node> prop{};
|
||||
};
|
||||
|
||||
struct meta_func_node {
|
||||
using size_type = std::size_t;
|
||||
|
||||
id_type id{};
|
||||
meta_traits traits{meta_traits::is_none};
|
||||
size_type arity{0u};
|
||||
meta_type_node (*ret)(const meta_context &) noexcept {};
|
||||
meta_type (*arg)(const meta_ctx &, const size_type) noexcept {};
|
||||
meta_any (*invoke)(const meta_ctx &, meta_handle, meta_any *const){};
|
||||
std::shared_ptr<meta_func_node> next{};
|
||||
dense_map<id_type, meta_prop_node, identity> prop{};
|
||||
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 (*type)(const meta_context &) noexcept {};
|
||||
meta_type_node (*resolve)(const meta_context &) noexcept {};
|
||||
meta_type_node (*arg)(const meta_context &, const size_type) noexcept {};
|
||||
};
|
||||
|
||||
struct meta_type_descriptor {
|
||||
dense_map<id_type, meta_ctor_node, identity> ctor{};
|
||||
dense_map<id_type, meta_base_node, identity> base{};
|
||||
dense_map<id_type, meta_conv_node, identity> conv{};
|
||||
dense_map<id_type, meta_data_node, identity> data{};
|
||||
dense_map<id_type, meta_func_node, identity> func{};
|
||||
dense_map<id_type, meta_prop_node, identity> prop{};
|
||||
std::vector<meta_ctor_node> ctor{};
|
||||
std::vector<meta_base_node> base{};
|
||||
std::vector<meta_conv_node> conv{};
|
||||
std::vector<meta_data_node> data{};
|
||||
std::vector<meta_func_node> func{};
|
||||
std::vector<meta_prop_node> prop{};
|
||||
};
|
||||
|
||||
struct meta_type_node {
|
||||
@@ -124,24 +156,43 @@ struct meta_type_node {
|
||||
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{};
|
||||
};
|
||||
|
||||
template<auto Member, typename Type, typename Value>
|
||||
[[nodiscard]] auto *find_member(Type &from, const Value value) {
|
||||
for(auto &&elem: from) {
|
||||
if((elem.*Member) == value) {
|
||||
return &elem;
|
||||
}
|
||||
}
|
||||
|
||||
return static_cast<typename Type::value_type *>(nullptr);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline auto *find_overload(meta_func_node *curr, std::remove_pointer_t<decltype(meta_func_node::invoke)> *const ref) {
|
||||
while((curr != nullptr) && (curr->invoke != ref)) { curr = curr->next.get(); }
|
||||
return curr;
|
||||
}
|
||||
|
||||
template<auto Member>
|
||||
auto *look_for(const meta_context &context, const meta_type_node &node, const id_type id) {
|
||||
[[nodiscard]] auto *look_for(const meta_context &context, const meta_type_node &node, const id_type id) {
|
||||
using value_type = typename std::remove_reference_t<decltype((node.details.get()->*Member))>::value_type;
|
||||
|
||||
if(node.details) {
|
||||
if(const auto it = (node.details.get()->*Member).find(id); it != (node.details.get()->*Member).cend()) {
|
||||
return &it->second;
|
||||
if(auto *member = find_member<&value_type::id>((node.details.get()->*Member), id); member != nullptr) {
|
||||
return member;
|
||||
}
|
||||
|
||||
for(auto &&curr: node.details->base) {
|
||||
if(auto *elem = look_for<Member>(context, curr.second.type(context), id); elem) {
|
||||
if(auto *elem = look_for<Member>(context, curr.resolve(context), id); elem) {
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return static_cast<typename std::remove_reference_t<decltype(node.details.get()->*Member)>::mapped_type *>(nullptr);
|
||||
return static_cast<value_type *>(nullptr);
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
@@ -157,13 +208,13 @@ template<typename... Args>
|
||||
}
|
||||
|
||||
[[nodiscard]] inline const void *try_cast(const meta_context &context, const meta_type_node &from, const meta_type_node &to, const void *instance) noexcept {
|
||||
if(from.info && to.info && *from.info == *to.info) {
|
||||
if((from.info != nullptr) && (to.info != nullptr) && *from.info == *to.info) {
|
||||
return instance;
|
||||
}
|
||||
|
||||
if(from.details) {
|
||||
for(auto &&curr: from.details->base) {
|
||||
if(const void *elem = try_cast(context, curr.second.type(context), to, curr.second.cast(instance)); elem) {
|
||||
if(const void *elem = try_cast(context, curr.resolve(context), to, curr.cast(instance)); elem) {
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
@@ -179,12 +230,14 @@ template<typename Func>
|
||||
}
|
||||
|
||||
if(from.details) {
|
||||
if(auto it = from.details->conv.find(to.hash()); it != from.details->conv.cend()) {
|
||||
return func(instance, it->second);
|
||||
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.second.type(context), to, arithmetic_or_enum, curr.second.cast(instance), func); other) {
|
||||
if(auto other = try_convert(context, curr.resolve(context), to, arithmetic_or_enum, curr.cast(instance), func); other) {
|
||||
return other;
|
||||
}
|
||||
}
|
||||
@@ -219,6 +272,7 @@ template<typename Type>
|
||||
| (std::is_array_v<Type> ? meta_traits::is_array : meta_traits::is_none)
|
||||
| (std::is_enum_v<Type> ? meta_traits::is_enum : meta_traits::is_none)
|
||||
| (std::is_class_v<Type> ? meta_traits::is_class : meta_traits::is_none)
|
||||
| (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),
|
||||
@@ -233,22 +287,22 @@ template<typename Type>
|
||||
}
|
||||
|
||||
if constexpr(std::is_arithmetic_v<Type>) {
|
||||
node.conversion_helper = +[](void *bin, const void *value) {
|
||||
return bin ? static_cast<double>(*static_cast<Type *>(bin) = static_cast<Type>(*static_cast<const double *>(value))) : static_cast<double>(*static_cast<const Type *>(value));
|
||||
node.conversion_helper = +[](void *lhs, const void *rhs) {
|
||||
return lhs ? static_cast<double>(*static_cast<Type *>(lhs) = static_cast<Type>(*static_cast<const double *>(rhs))) : static_cast<double>(*static_cast<const Type *>(rhs));
|
||||
};
|
||||
} else if constexpr(std::is_enum_v<Type>) {
|
||||
node.conversion_helper = +[](void *bin, const void *value) {
|
||||
return bin ? static_cast<double>(*static_cast<Type *>(bin) = static_cast<Type>(static_cast<std::underlying_type_t<Type>>(*static_cast<const double *>(value)))) : static_cast<double>(*static_cast<const Type *>(value));
|
||||
node.conversion_helper = +[](void *lhs, const void *rhs) {
|
||||
return lhs ? static_cast<double>(*static_cast<Type *>(lhs) = static_cast<Type>(static_cast<std::underlying_type_t<Type>>(*static_cast<const double *>(rhs)))) : static_cast<double>(*static_cast<const Type *>(rhs));
|
||||
};
|
||||
}
|
||||
|
||||
if constexpr(!std::is_void_v<Type> && !std::is_function_v<Type>) {
|
||||
node.from_void = +[](const meta_ctx &ctx, void *element, const void *as_const) {
|
||||
if(element) {
|
||||
return meta_any{ctx, std::in_place_type<std::decay_t<Type> &>, *static_cast<std::decay_t<Type> *>(element)};
|
||||
node.from_void = +[](const meta_ctx &ctx, void *elem, const void *celem) {
|
||||
if(elem) {
|
||||
return meta_any{ctx, std::in_place_type<std::decay_t<Type> &>, *static_cast<std::decay_t<Type> *>(elem)};
|
||||
}
|
||||
|
||||
return meta_any{ctx, std::in_place_type<const std::decay_t<Type> &>, *static_cast<const std::decay_t<Type> *>(as_const)};
|
||||
return meta_any{ctx, std::in_place_type<const std::decay_t<Type> &>, *static_cast<const std::decay_t<Type> *>(celem)};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// IWYU pragma: always_keep
|
||||
|
||||
#ifndef ENTT_META_POINTER_HPP
|
||||
#define ENTT_META_POINTER_HPP
|
||||
|
||||
@@ -21,6 +23,7 @@ struct is_meta_pointer_like<Type *>
|
||||
* @tparam N Number of elements of the array.
|
||||
*/
|
||||
template<typename Type, std::size_t N>
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
|
||||
struct is_meta_pointer_like<Type (*)[N]>
|
||||
: std::false_type {};
|
||||
|
||||
|
||||
@@ -13,12 +13,14 @@ namespace entt {
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
struct meta_base_node;
|
||||
|
||||
template<typename Type, typename It>
|
||||
struct meta_range_iterator final {
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = std::pair<id_type, Type>;
|
||||
using pointer = input_iterator_pointer<value_type>;
|
||||
using reference = value_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using iterator_concept = std::random_access_iterator_tag;
|
||||
|
||||
@@ -67,7 +69,13 @@ struct meta_range_iterator final {
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
|
||||
return {it[value].first, Type{*ctx, it[value].second}};
|
||||
if constexpr(std::is_same_v<It, typename decltype(meta_context::value)::const_iterator>) {
|
||||
return {it[value].first, Type{*ctx, it[value].second}};
|
||||
} else if constexpr(std::is_same_v<typename std::iterator_traits<It>::value_type, meta_base_node>) {
|
||||
return {it[value].type, Type{*ctx, it[value]}};
|
||||
} else {
|
||||
return {it[value].id, Type{*ctx, it[value]}};
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr pointer operator->() const noexcept {
|
||||
@@ -75,7 +83,7 @@ struct meta_range_iterator final {
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr reference operator*() const noexcept {
|
||||
return {it->first, Type{*ctx, it->second}};
|
||||
return operator[](0);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
|
||||
@@ -85,7 +85,7 @@ template<typename Type>
|
||||
[[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const type_info &info) noexcept {
|
||||
auto &&context = internal::meta_context::from(ctx);
|
||||
const auto *elem = internal::try_resolve(context, info);
|
||||
return elem ? meta_type{ctx, *elem} : meta_type{};
|
||||
return (elem != nullptr) ? meta_type{ctx, *elem} : meta_type{};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// IWYU pragma: always_keep
|
||||
|
||||
#ifndef ENTT_META_TEMPLATE_HPP
|
||||
#define ENTT_META_TEMPLATE_HPP
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ class meta_function_helper {
|
||||
template<typename Ret, typename... Args, typename Class>
|
||||
static constexpr meta_function_descriptor<Type, Ret (Class::*)(Args...)> get_rid_of_noexcept(Ret (Class::*)(Args...));
|
||||
|
||||
template<typename Ret, typename Class>
|
||||
template<typename Ret, typename Class, typename = std::enable_if_t<std::is_member_object_pointer_v<Ret Class::*>>>
|
||||
static constexpr meta_function_descriptor<Type, Ret Class::*> get_rid_of_noexcept(Ret Class::*);
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
@@ -327,15 +327,16 @@ 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 *args, std::index_sequence<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...>) {
|
||||
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>>()...);
|
||||
}
|
||||
} 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>>() && ...)) { // NOLINT
|
||||
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>>()...);
|
||||
}
|
||||
} else {
|
||||
@@ -343,15 +344,18 @@ template<typename Type, typename Policy, typename Candidate, std::size_t... Inde
|
||||
return meta_invoke_with_args<Policy>(ctx, 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};
|
||||
}
|
||||
|
||||
template<typename Type, typename... Args, std::size_t... Index>
|
||||
[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, meta_any *const args, std::index_sequence<Index...>) {
|
||||
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) - waiting for C++20 (and std::span)
|
||||
if(((args + Index)->allow_cast<Args>() && ...)) {
|
||||
return meta_any{ctx, std::in_place_type<Type>, (args + Index)->cast<Args>()...};
|
||||
}
|
||||
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
|
||||
return meta_any{meta_ctx_arg, ctx};
|
||||
}
|
||||
@@ -479,6 +483,7 @@ template<typename Type, typename Policy = as_is_t, typename Candidate>
|
||||
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>{});
|
||||
} 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>{});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
#ifndef ENTT_PLATFORM_ANDROID_NDK_R17_HPP
|
||||
#define ENTT_PLATFORM_ANDROID_NDK_R17_HPP
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
#ifdef __ANDROID__
|
||||
# include <android/ndk-version.h>
|
||||
# if __NDK_MAJOR__ == 17
|
||||
|
||||
# include <functional>
|
||||
# include <type_traits>
|
||||
# include <utility>
|
||||
|
||||
namespace std {
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename Func, typename... Args>
|
||||
constexpr auto is_invocable(int) -> decltype(std::invoke(std::declval<Func>(), std::declval<Args>()...), std::true_type{});
|
||||
|
||||
template<typename, typename...>
|
||||
constexpr std::false_type is_invocable(...);
|
||||
|
||||
template<typename Ret, typename Func, typename... Args>
|
||||
constexpr auto is_invocable_r(int)
|
||||
-> std::enable_if_t<decltype(std::is_convertible_v<decltype(std::invoke(std::declval<Func>(), std::declval<Args>()...)), Ret>, std::true_type>;
|
||||
|
||||
|
||||
template<typename, typename, typename...>
|
||||
constexpr std::false_type is_invocable_r(...);
|
||||
|
||||
} // namespace internal
|
||||
|
||||
template<typename Func, typename... Args>
|
||||
struct is_invocable: decltype(internal::is_invocable<Func, Args...>(0)) {};
|
||||
|
||||
template<typename Func, typename... Argsv>
|
||||
inline constexpr bool is_invocable_v = std::is_invocable<Func, Args...>::value;
|
||||
|
||||
template<typename Ret, typename Func, typename... Args>
|
||||
struct is_invocable_r: decltype(internal::is_invocable_r<Ret, Func, Args...>(0)) {};
|
||||
|
||||
template<typename Ret, typename Func, typename... Args>
|
||||
inline constexpr bool is_invocable_r_v = std::is_invocable_r<Ret, Func, Args...>::value;
|
||||
|
||||
template<typename Func, typename... Args>
|
||||
struct invoke_result {
|
||||
using type = decltype(std::invoke(std::declval<Func>(), std::declval<Args>()...));
|
||||
};
|
||||
|
||||
template<typename Func, typename... Args>
|
||||
using invoke_result_t = typename std::invoke_result<Func, Args...>::type;
|
||||
|
||||
} // namespace std
|
||||
|
||||
# endif
|
||||
#endif
|
||||
/*! @endcond */
|
||||
|
||||
#endif
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
|
||||
template<typename, std::size_t Len = sizeof(double[2]), std::size_t = alignof(double[2])>
|
||||
class basic_poly;
|
||||
|
||||
|
||||
@@ -30,11 +30,11 @@ struct poly_inspector {
|
||||
* @return A poly inspector convertible to any type.
|
||||
*/
|
||||
template<std::size_t Member, typename... Args>
|
||||
poly_inspector invoke(Args &&...args) const;
|
||||
[[nodiscard]] poly_inspector invoke(Args &&...args) const;
|
||||
|
||||
/*! @copydoc invoke */
|
||||
template<std::size_t Member, typename... Args>
|
||||
poly_inspector invoke(Args &&...args);
|
||||
[[nodiscard]] poly_inspector invoke(Args &&...args);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -47,20 +47,21 @@ template<typename Concept, std::size_t Len, std::size_t Align>
|
||||
class poly_vtable {
|
||||
using inspector = typename Concept::template type<poly_inspector>;
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
static auto vtable_entry(Ret (*)(inspector &, Args...)) -> Ret (*)(basic_any<Len, Align> &, Args...);
|
||||
template<typename Ret, typename Clazz, typename... Args>
|
||||
static auto vtable_entry(Ret (*)(Clazz &, Args...))
|
||||
-> std::enable_if_t<std::is_base_of_v<std::remove_const_t<Clazz>, inspector>, Ret (*)(constness_as_t<basic_any<Len, Align>, Clazz> &, Args...)>;
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
static auto vtable_entry(Ret (*)(const inspector &, Args...)) -> Ret (*)(const basic_any<Len, Align> &, Args...);
|
||||
static auto vtable_entry(Ret (*)(Args...))
|
||||
-> Ret (*)(const basic_any<Len, Align> &, Args...);
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
static auto vtable_entry(Ret (*)(Args...)) -> Ret (*)(const basic_any<Len, Align> &, Args...);
|
||||
template<typename Ret, typename Clazz, typename... Args>
|
||||
static auto vtable_entry(Ret (Clazz::*)(Args...))
|
||||
-> std::enable_if_t<std::is_base_of_v<Clazz, inspector>, Ret (*)(basic_any<Len, Align> &, Args...)>;
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
static auto vtable_entry(Ret (inspector::*)(Args...)) -> Ret (*)(basic_any<Len, Align> &, Args...);
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
static auto vtable_entry(Ret (inspector::*)(Args...) const) -> Ret (*)(const basic_any<Len, Align> &, Args...);
|
||||
template<typename Ret, typename Clazz, typename... Args>
|
||||
static auto vtable_entry(Ret (Clazz::*)(Args...) const)
|
||||
-> std::enable_if_t<std::is_base_of_v<Clazz, inspector>, Ret (*)(const basic_any<Len, Align> &, Args...)>;
|
||||
|
||||
template<auto... Candidate>
|
||||
static auto make_vtable(value_list<Candidate...>) noexcept
|
||||
@@ -199,9 +200,7 @@ public:
|
||||
using vtable_type = typename poly_vtable<Concept, Len, Align>::type;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
basic_poly() noexcept
|
||||
: storage{},
|
||||
vtable{} {}
|
||||
basic_poly() noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Constructs a poly by directly initializing the new object.
|
||||
@@ -303,8 +302,8 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
basic_any<Len, Align> storage;
|
||||
vtable_type vtable;
|
||||
basic_any<Len, Align> storage{};
|
||||
vtable_type vtable{};
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -110,7 +110,8 @@ class process {
|
||||
static_cast<Target *>(this)->aborted();
|
||||
}
|
||||
|
||||
void next(...) const noexcept {}
|
||||
template<typename... Args>
|
||||
void next(Args &&...) const noexcept {}
|
||||
|
||||
protected:
|
||||
/**
|
||||
@@ -165,8 +166,29 @@ 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() noexcept {
|
||||
virtual ~process() {
|
||||
static_assert(std::is_base_of_v<process, Derived>, "Incorrect use of the class template");
|
||||
}
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ template<typename Delta>
|
||||
struct basic_process_handler {
|
||||
virtual ~basic_process_handler() = default;
|
||||
|
||||
virtual bool update(const Delta, void *) = 0;
|
||||
virtual void abort(const bool) = 0;
|
||||
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;
|
||||
@@ -109,6 +109,9 @@ public:
|
||||
explicit basic_scheduler(const allocator_type &allocator)
|
||||
: handlers{allocator, allocator} {}
|
||||
|
||||
/*! @brief Default copy constructor, deleted on purpose. */
|
||||
basic_scheduler(const basic_scheduler &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
@@ -121,19 +124,28 @@ public:
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
basic_scheduler(basic_scheduler &&other, const allocator_type &allocator) noexcept
|
||||
basic_scheduler(basic_scheduler &&other, const allocator_type &allocator)
|
||||
: handlers{container_type{std::move(other.handlers.first()), allocator}, allocator} {
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying a scheduler is not allowed");
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a scheduler is not allowed");
|
||||
}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~basic_scheduler() = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator, deleted on purpose.
|
||||
* @return This process scheduler.
|
||||
*/
|
||||
basic_scheduler &operator=(const basic_scheduler &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
* @param other The instance to move from.
|
||||
* @return This scheduler.
|
||||
* @return This process scheduler.
|
||||
*/
|
||||
basic_scheduler &operator=(basic_scheduler &&other) noexcept {
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying a scheduler is not allowed");
|
||||
handlers = std::move(other.handlers);
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a scheduler is not allowed");
|
||||
swap(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -141,7 +153,7 @@ public:
|
||||
* @brief Exchanges the contents with those of a given scheduler.
|
||||
* @param other Scheduler to exchange the content with.
|
||||
*/
|
||||
void swap(basic_scheduler &other) {
|
||||
void swap(basic_scheduler &other) noexcept {
|
||||
using std::swap;
|
||||
swap(handlers, other.handlers);
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ public:
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr reference operator*() const noexcept {
|
||||
return (*this)[0];
|
||||
return operator[](0);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr pointer operator->() const noexcept {
|
||||
@@ -154,17 +154,17 @@ class resource_cache {
|
||||
using alloc_traits = std::allocator_traits<Allocator>;
|
||||
static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
|
||||
using container_allocator = typename alloc_traits::template rebind_alloc<std::pair<const id_type, typename Loader::result_type>>;
|
||||
using container_type = dense_map<id_type, typename Loader::result_type, identity, std::equal_to<id_type>, container_allocator>;
|
||||
using container_type = dense_map<id_type, typename Loader::result_type, identity, std::equal_to<>, container_allocator>;
|
||||
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Resource type. */
|
||||
using value_type = Type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Loader type. */
|
||||
using loader_type = Loader;
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Input iterator type. */
|
||||
using iterator = internal::resource_cache_iterator<Type, typename container_type::iterator>;
|
||||
/*! @brief Constant input iterator type. */
|
||||
@@ -201,7 +201,7 @@ public:
|
||||
: pool{std::piecewise_construct, std::forward_as_tuple(other.pool.first(), allocator), std::forward_as_tuple(other.pool.second())} {}
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
resource_cache(resource_cache &&) = default;
|
||||
resource_cache(resource_cache &&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
@@ -211,6 +211,9 @@ public:
|
||||
resource_cache(resource_cache &&other, const allocator_type &allocator)
|
||||
: pool{std::piecewise_construct, std::forward_as_tuple(std::move(other.pool.first()), allocator), std::forward_as_tuple(std::move(other.pool.second()))} {}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~resource_cache() = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator.
|
||||
* @return This cache.
|
||||
@@ -221,7 +224,7 @@ public:
|
||||
* @brief Default move assignment operator.
|
||||
* @return This cache.
|
||||
*/
|
||||
resource_cache &operator=(resource_cache &&) = default;
|
||||
resource_cache &operator=(resource_cache &&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Returns the associated allocator.
|
||||
|
||||
@@ -37,8 +37,8 @@ public:
|
||||
: value{} {}
|
||||
|
||||
/**
|
||||
* @brief Creates a handle from a weak pointer, namely a resource.
|
||||
* @param res A weak pointer to a resource.
|
||||
* @brief Creates a new resource handle.
|
||||
* @param res A handle to a resource.
|
||||
*/
|
||||
explicit resource(handle_type res) noexcept
|
||||
: value{std::move(res)} {}
|
||||
@@ -77,6 +77,9 @@ public:
|
||||
resource(resource<Other> &&other) noexcept
|
||||
: value{std::move(other.value)} {}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~resource() = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator.
|
||||
* @return This resource handle.
|
||||
@@ -95,9 +98,8 @@ public:
|
||||
* @param other The handle to copy from.
|
||||
* @return This resource handle.
|
||||
*/
|
||||
template<typename Other>
|
||||
std::enable_if_t<is_acceptable_v<Other>, resource &>
|
||||
operator=(const resource<Other> &other) noexcept {
|
||||
template<typename Other, typename = std::enable_if_t<is_acceptable_v<Other>>>
|
||||
resource &operator=(const resource<Other> &other) noexcept {
|
||||
value = other.value;
|
||||
return *this;
|
||||
}
|
||||
@@ -108,13 +110,21 @@ public:
|
||||
* @param other The handle to move from.
|
||||
* @return This resource handle.
|
||||
*/
|
||||
template<typename Other>
|
||||
std::enable_if_t<is_acceptable_v<Other>, resource &>
|
||||
operator=(resource<Other> &&other) noexcept {
|
||||
template<typename Other, typename = std::enable_if_t<is_acceptable_v<Other>>>
|
||||
resource &operator=(resource<Other> &&other) noexcept {
|
||||
value = std::move(other.value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exchanges the content with that of a given resource.
|
||||
* @param other Resource to exchange the content with.
|
||||
*/
|
||||
void swap(resource &other) noexcept {
|
||||
using std::swap;
|
||||
swap(value, other.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the managed resource.
|
||||
*
|
||||
@@ -148,6 +158,19 @@ public:
|
||||
return static_cast<bool>(value);
|
||||
}
|
||||
|
||||
/*! @brief Releases the ownership of the managed resource. */
|
||||
void reset() {
|
||||
value.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Replaces the managed resource.
|
||||
* @param other A handle to a resource.
|
||||
*/
|
||||
void reset(handle_type other) {
|
||||
value = std::move(other);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the underlying resource handle.
|
||||
* @return The underlying resource handle.
|
||||
|
||||
@@ -27,7 +27,7 @@ constexpr auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)
|
||||
template<typename Class, typename Ret, typename... Args, typename... Other>
|
||||
constexpr auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...);
|
||||
|
||||
template<typename Class, typename Type, typename... Other>
|
||||
template<typename Class, typename Type, typename... Other, typename = std::enable_if_t<std::is_member_object_pointer_v<Type Class::*>>>
|
||||
constexpr auto function_pointer(Type Class::*, Other &&...) -> Type (*)();
|
||||
|
||||
template<typename... Type>
|
||||
@@ -64,47 +64,35 @@ class delegate;
|
||||
*/
|
||||
template<typename Ret, typename... Args>
|
||||
class delegate<Ret(Args...)> {
|
||||
using return_type = std::remove_const_t<Ret>;
|
||||
using delegate_type = return_type(const void *, Args...);
|
||||
|
||||
template<auto Candidate, std::size_t... Index>
|
||||
[[nodiscard]] auto wrap(std::index_sequence<Index...>) noexcept {
|
||||
return [](const void *, Args... args) -> Ret {
|
||||
return [](const void *, Args... args) -> return_type {
|
||||
[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
|
||||
|
||||
if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), type_list_element_t<Index, type_list<Args...>>...>) {
|
||||
return static_cast<Ret>(std::invoke(Candidate, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
|
||||
} else {
|
||||
constexpr auto offset = sizeof...(Args) - sizeof...(Index);
|
||||
return static_cast<Ret>(std::invoke(Candidate, std::forward<type_list_element_t<Index + offset, type_list<Args...>>>(std::get<Index + offset>(arguments))...));
|
||||
}
|
||||
[[maybe_unused]] constexpr auto offset = !std::is_invocable_r_v<Ret, decltype(Candidate), type_list_element_t<Index, type_list<Args...>>...> * (sizeof...(Args) - sizeof...(Index));
|
||||
return static_cast<Ret>(std::invoke(Candidate, std::forward<type_list_element_t<Index + offset, type_list<Args...>>>(std::get<Index + offset>(arguments))...));
|
||||
};
|
||||
}
|
||||
|
||||
template<auto Candidate, typename Type, std::size_t... Index>
|
||||
[[nodiscard]] auto wrap(Type &, std::index_sequence<Index...>) noexcept {
|
||||
return [](const void *payload, Args... args) -> Ret {
|
||||
[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
|
||||
return [](const void *payload, Args... args) -> return_type {
|
||||
Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
|
||||
|
||||
if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type &, type_list_element_t<Index, type_list<Args...>>...>) {
|
||||
return static_cast<Ret>(std::invoke(Candidate, *curr, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
|
||||
} else {
|
||||
constexpr auto offset = sizeof...(Args) - sizeof...(Index);
|
||||
return static_cast<Ret>(std::invoke(Candidate, *curr, std::forward<type_list_element_t<Index + offset, type_list<Args...>>>(std::get<Index + offset>(arguments))...));
|
||||
}
|
||||
[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
|
||||
[[maybe_unused]] constexpr auto offset = !std::is_invocable_r_v<Ret, decltype(Candidate), Type &, type_list_element_t<Index, type_list<Args...>>...> * (sizeof...(Args) - sizeof...(Index));
|
||||
return static_cast<Ret>(std::invoke(Candidate, *curr, std::forward<type_list_element_t<Index + offset, type_list<Args...>>>(std::get<Index + offset>(arguments))...));
|
||||
};
|
||||
}
|
||||
|
||||
template<auto Candidate, typename Type, std::size_t... Index>
|
||||
[[nodiscard]] auto wrap(Type *, std::index_sequence<Index...>) noexcept {
|
||||
return [](const void *payload, Args... args) -> Ret {
|
||||
[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
|
||||
return [](const void *payload, Args... args) -> return_type {
|
||||
Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
|
||||
|
||||
if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type *, type_list_element_t<Index, type_list<Args...>>...>) {
|
||||
return static_cast<Ret>(std::invoke(Candidate, curr, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
|
||||
} else {
|
||||
constexpr auto offset = sizeof...(Args) - sizeof...(Index);
|
||||
return static_cast<Ret>(std::invoke(Candidate, curr, std::forward<type_list_element_t<Index + offset, type_list<Args...>>>(std::get<Index + offset>(arguments))...));
|
||||
}
|
||||
[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
|
||||
[[maybe_unused]] constexpr auto offset = !std::is_invocable_r_v<Ret, decltype(Candidate), Type *, type_list_element_t<Index, type_list<Args...>>...> * (sizeof...(Args) - sizeof...(Index));
|
||||
return static_cast<Ret>(std::invoke(Candidate, curr, std::forward<type_list_element_t<Index + offset, type_list<Args...>>>(std::get<Index + offset>(arguments))...));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -117,9 +105,7 @@ public:
|
||||
using result_type = Ret;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
delegate() noexcept
|
||||
: instance{nullptr},
|
||||
fn{nullptr} {}
|
||||
delegate() noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Constructs a delegate with a given object or payload, if any.
|
||||
@@ -151,7 +137,7 @@ public:
|
||||
instance = nullptr;
|
||||
|
||||
if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Args...>) {
|
||||
fn = [](const void *, Args... args) -> Ret {
|
||||
fn = [](const void *, Args... args) -> return_type {
|
||||
return Ret(std::invoke(Candidate, std::forward<Args>(args)...));
|
||||
};
|
||||
} else if constexpr(std::is_member_pointer_v<decltype(Candidate)>) {
|
||||
@@ -181,7 +167,7 @@ public:
|
||||
instance = &value_or_instance;
|
||||
|
||||
if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type &, Args...>) {
|
||||
fn = [](const void *payload, Args... args) -> Ret {
|
||||
fn = [](const void *payload, Args... args) -> return_type {
|
||||
Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
|
||||
return Ret(std::invoke(Candidate, *curr, std::forward<Args>(args)...));
|
||||
};
|
||||
@@ -205,7 +191,7 @@ public:
|
||||
instance = value_or_instance;
|
||||
|
||||
if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type *, Args...>) {
|
||||
fn = [](const void *payload, Args... args) -> Ret {
|
||||
fn = [](const void *payload, Args... args) -> return_type {
|
||||
Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
|
||||
return Ret(std::invoke(Candidate, curr, std::forward<Args>(args)...));
|
||||
};
|
||||
@@ -295,8 +281,8 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
const void *instance;
|
||||
function_type *fn;
|
||||
const void *instance{};
|
||||
delegate_type *fn{};
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,7 +25,7 @@ struct basic_dispatcher_handler {
|
||||
virtual void publish() = 0;
|
||||
virtual void disconnect(void *) = 0;
|
||||
virtual void clear() noexcept = 0;
|
||||
virtual std::size_t size() const noexcept = 0;
|
||||
[[nodiscard]] virtual std::size_t size() const noexcept = 0;
|
||||
};
|
||||
|
||||
template<typename Type, typename Allocator>
|
||||
@@ -115,7 +115,7 @@ class basic_dispatcher {
|
||||
|
||||
using alloc_traits = std::allocator_traits<Allocator>;
|
||||
using container_allocator = typename alloc_traits::template rebind_alloc<std::pair<const key_type, mapped_type>>;
|
||||
using container_type = dense_map<key_type, mapped_type, identity, std::equal_to<key_type>, container_allocator>;
|
||||
using container_type = dense_map<key_type, mapped_type, identity, std::equal_to<>, container_allocator>;
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] handler_type<Type> &assure(const id_type id) {
|
||||
@@ -158,6 +158,9 @@ public:
|
||||
explicit basic_dispatcher(const allocator_type &allocator)
|
||||
: pools{allocator, allocator} {}
|
||||
|
||||
/*! @brief Default copy constructor, deleted on purpose. */
|
||||
basic_dispatcher(const basic_dispatcher &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
@@ -170,19 +173,28 @@ public:
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
basic_dispatcher(basic_dispatcher &&other, const allocator_type &allocator) noexcept
|
||||
basic_dispatcher(basic_dispatcher &&other, const allocator_type &allocator)
|
||||
: pools{container_type{std::move(other.pools.first()), allocator}, allocator} {
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || pools.second() == other.pools.second(), "Copying a dispatcher is not allowed");
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a dispatcher is not allowed");
|
||||
}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~basic_dispatcher() = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator, deleted on purpose.
|
||||
* @return This dispatcher.
|
||||
*/
|
||||
basic_dispatcher &operator=(const basic_dispatcher &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
* @param other The instance to move from.
|
||||
* @return This dispatcher.
|
||||
*/
|
||||
basic_dispatcher &operator=(basic_dispatcher &&other) noexcept {
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || pools.second() == other.pools.second(), "Copying a dispatcher is not allowed");
|
||||
pools = std::move(other.pools);
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a dispatcher is not allowed");
|
||||
swap(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -190,7 +202,7 @@ public:
|
||||
* @brief Exchanges the contents with those of a given dispatcher.
|
||||
* @param other Dispatcher to exchange the content with.
|
||||
*/
|
||||
void swap(basic_dispatcher &other) {
|
||||
void swap(basic_dispatcher &other) noexcept {
|
||||
using std::swap;
|
||||
swap(pools, other.pools);
|
||||
}
|
||||
@@ -210,7 +222,7 @@ public:
|
||||
* @return The number of pending events for the given type.
|
||||
*/
|
||||
template<typename Type>
|
||||
size_type size(const id_type id = type_hash<Type>::value()) const noexcept {
|
||||
[[nodiscard]] size_type size(const id_type id = type_hash<Type>::value()) const noexcept {
|
||||
const auto *cpool = assure<std::decay_t<Type>>(id);
|
||||
return cpool ? cpool->size() : 0u;
|
||||
}
|
||||
@@ -219,7 +231,7 @@ public:
|
||||
* @brief Returns the total number of pending events.
|
||||
* @return The total number of pending events.
|
||||
*/
|
||||
size_type size() const noexcept {
|
||||
[[nodiscard]] size_type size() const noexcept {
|
||||
size_type count{};
|
||||
|
||||
for(auto &&cpool: pools.first()) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user