Compare commits
1298 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7821307565 | ||
|
|
27efd6fdf4 | ||
|
|
4dc3f650e8 | ||
|
|
d5a2e353cd | ||
|
|
3fd8b84d04 | ||
|
|
2909e7ab1f | ||
|
|
60703f6845 | ||
|
|
062c95fc11 | ||
|
|
15ce55bc60 | ||
|
|
2d3fa3e51a | ||
|
|
7f49768739 | ||
|
|
fedcb920ce | ||
|
|
6e0c2baf9d | ||
|
|
20aa62c584 | ||
|
|
9fbd167bd0 | ||
|
|
2562bf76f6 | ||
|
|
b5ac3c5f41 | ||
|
|
7af8d015bc | ||
|
|
f01fe06b7a | ||
|
|
5618fd9549 | ||
|
|
76397ad2e6 | ||
|
|
6ca1bca26c | ||
|
|
b0b5b2ec4d | ||
|
|
1c460130a5 | ||
|
|
31b2a2129c | ||
|
|
f3a6fdbc02 | ||
|
|
f6232603c4 | ||
|
|
15d63ec358 | ||
|
|
9800bfa680 | ||
|
|
6b91bbab28 | ||
|
|
b7e929b0cc | ||
|
|
f38ab24b7a | ||
|
|
b698e53131 | ||
|
|
346165e4f9 | ||
|
|
9708727269 | ||
|
|
0f6e1d8aef | ||
|
|
51932b4459 | ||
|
|
2b092c2fce | ||
|
|
b152c63dd6 | ||
|
|
f920a84ac0 | ||
|
|
4e9cc9773d | ||
|
|
4d32c1ba58 | ||
|
|
2776f9bad9 | ||
|
|
fc381b72d3 | ||
|
|
a77a223223 | ||
|
|
b249fa252d | ||
|
|
b32b8002d3 | ||
|
|
8177f8934c | ||
|
|
ac4c38a10a | ||
|
|
854dedfda0 | ||
|
|
52f8e6db30 | ||
|
|
140d0dab6a | ||
|
|
1ff2db8aa2 | ||
|
|
34f700fcca | ||
|
|
8c53e0514e | ||
|
|
b6cea84910 | ||
|
|
f4d9a7729f | ||
|
|
47ab69f394 | ||
|
|
aab9277d8d | ||
|
|
6d21fdcc78 | ||
|
|
1d9f771af3 | ||
|
|
18330eb330 | ||
|
|
6f38fdaea6 | ||
|
|
bd75e33694 | ||
|
|
010f0b9402 | ||
|
|
776c541d1c | ||
|
|
f20abd5767 | ||
|
|
8cb0c8802d | ||
|
|
a4a0abdbd6 | ||
|
|
4bd0f26a26 | ||
|
|
5ea8f0382b | ||
|
|
8beaea89f2 | ||
|
|
433fca98a3 | ||
|
|
24aac47ce5 | ||
|
|
d5518c63e9 | ||
|
|
4a06c41a33 | ||
|
|
4d63ac5c88 | ||
|
|
6e31855717 | ||
|
|
76e668034c | ||
|
|
28424709d5 | ||
|
|
fc3df2b0ac | ||
|
|
27698a2a0a | ||
|
|
9b7aa49adc | ||
|
|
8022cf4200 | ||
|
|
bd06d384e0 | ||
|
|
17913a7a14 | ||
|
|
ef914bad8c | ||
|
|
867656f549 | ||
|
|
6e665b27bf | ||
|
|
f34dbcd7d8 | ||
|
|
6b608f51fb | ||
|
|
cbf853fa75 | ||
|
|
14450df1ee | ||
|
|
27a46a97a6 | ||
|
|
d2344e8e62 | ||
|
|
4b5a6cd2c6 | ||
|
|
f3ce121be3 | ||
|
|
eef8b42c77 | ||
|
|
03551c242c | ||
|
|
f5a8feb47f | ||
|
|
3208281d94 | ||
|
|
2271fb8c0f | ||
|
|
feb0bb02ae | ||
|
|
6c99ca50e7 | ||
|
|
cadcd9ce43 | ||
|
|
0bf1817b9a | ||
|
|
cd887237b8 | ||
|
|
218cb02492 | ||
|
|
5a2be7d6eb | ||
|
|
c6f3a5408e | ||
|
|
b6d0921177 | ||
|
|
6ac7a02172 | ||
|
|
a35612a4f8 | ||
|
|
4d9ff926b6 | ||
|
|
636749eff3 | ||
|
|
685467acba | ||
|
|
9605af3f54 | ||
|
|
a3db63342d | ||
|
|
57ec8d9ca3 | ||
|
|
f11d93ab25 | ||
|
|
b3489857ad | ||
|
|
205e21e313 | ||
|
|
cd59efab8f | ||
|
|
72304e6781 | ||
|
|
bdd213009c | ||
|
|
5f576667d3 | ||
|
|
1ec2f3eeb8 | ||
|
|
b1a5b4b982 | ||
|
|
9e4f40fee6 | ||
|
|
fa0add3c02 | ||
|
|
937ed81fa2 | ||
|
|
daa83a1885 | ||
|
|
eb290d8bd4 | ||
|
|
dad6061cbb | ||
|
|
07777c78aa | ||
|
|
7a12dc6533 | ||
|
|
80c8c76fdb | ||
|
|
1931ade325 | ||
|
|
2e264dffb3 | ||
|
|
09197a91a0 | ||
|
|
70053f9301 | ||
|
|
91a193141f | ||
|
|
5d8a1e3ee1 | ||
|
|
9c85cd8d55 | ||
|
|
1fb1027036 | ||
|
|
bdea65d500 | ||
|
|
88f65c8d5d | ||
|
|
8355f12398 | ||
|
|
f3120f7e7d | ||
|
|
a74b1b8220 | ||
|
|
b81e40e07e | ||
|
|
e4df106417 | ||
|
|
d0a16cd852 | ||
|
|
b274d49b3f | ||
|
|
3d9651b783 | ||
|
|
dfd7cb90d0 | ||
|
|
c33bee892d | ||
|
|
a986f68e45 | ||
|
|
c0f1a691f9 | ||
|
|
711e82b688 | ||
|
|
5190db0222 | ||
|
|
b00830d382 | ||
|
|
cd24c0aac5 | ||
|
|
a927d6db07 | ||
|
|
294a606cf9 | ||
|
|
735db1e6af | ||
|
|
3ec5e81d81 | ||
|
|
4664d0601f | ||
|
|
43fcc94682 | ||
|
|
4cdff43781 | ||
|
|
9d23522086 | ||
|
|
feed10c9f6 | ||
|
|
9732092c0f | ||
|
|
aa46b2504c | ||
|
|
c3e36e7148 | ||
|
|
c912b85f6e | ||
|
|
4f700f90ec | ||
|
|
f2c417435c | ||
|
|
48c374be2a | ||
|
|
4cb4553796 | ||
|
|
3db20d0aa5 | ||
|
|
9e62e1dab6 | ||
|
|
42529d0ab2 | ||
|
|
637d6bbabc | ||
|
|
f8eba58e19 | ||
|
|
d58c5766bc | ||
|
|
5b2c136a7c | ||
|
|
8c532fb377 | ||
|
|
c5552d85c3 | ||
|
|
d5c162e6a0 | ||
|
|
95b73d94f7 | ||
|
|
9f80b14f88 | ||
|
|
1a6565d5f9 | ||
|
|
9c60708203 | ||
|
|
e0cac58500 | ||
|
|
60e2557c45 | ||
|
|
228066c1f6 | ||
|
|
0744937811 | ||
|
|
a383c0c0a5 | ||
|
|
b6da31d933 | ||
|
|
5985531d9e | ||
|
|
847099932d | ||
|
|
d43416102b | ||
|
|
9e73c3c44a | ||
|
|
d152153547 | ||
|
|
5016dd8bb0 | ||
|
|
a3fdc96b0d | ||
|
|
b5fbe50fa1 | ||
|
|
e9039ece28 | ||
|
|
60e8763fe8 | ||
|
|
e3475c026c | ||
|
|
24c95e493f | ||
|
|
8ce80145de | ||
|
|
838d0e5f76 | ||
|
|
7fecbf7e03 | ||
|
|
e04f2bdb24 | ||
|
|
c26a24e713 | ||
|
|
c785260783 | ||
|
|
8372292841 | ||
|
|
2ce6317c9a | ||
|
|
16d43d2975 | ||
|
|
d7d8752a84 | ||
|
|
339df18c06 | ||
|
|
757eb5f10c | ||
|
|
580f1edb91 | ||
|
|
6e03ca088e | ||
|
|
c75a4804da | ||
|
|
3487196d0d | ||
|
|
adbcc8dca9 | ||
|
|
8b79ee586a | ||
|
|
abfd8e035c | ||
|
|
9bad12064c | ||
|
|
a5608bf7bb | ||
|
|
b84a4bed29 | ||
|
|
94582777e8 | ||
|
|
c2b5be0836 | ||
|
|
8e13ede4d5 | ||
|
|
857d70528d | ||
|
|
b16e4c1ec7 | ||
|
|
b8b41b3ac7 | ||
|
|
f13dcae6d3 | ||
|
|
1724e8cfeb | ||
|
|
e53601e860 | ||
|
|
5e8544cfc6 | ||
|
|
dc184c10e2 | ||
|
|
f89e8560a3 | ||
|
|
f4a54cc0da | ||
|
|
6aa4c770e6 | ||
|
|
afe942ef2e | ||
|
|
9b4c920d1f | ||
|
|
00d0ff2e45 | ||
|
|
3f5b6a1693 | ||
|
|
d98821e976 | ||
|
|
223c8ab9b9 | ||
|
|
76a6a22527 | ||
|
|
ee7e025680 | ||
|
|
68125c4187 | ||
|
|
fd1abc952f | ||
|
|
5c04c872c5 | ||
|
|
8405a78bd8 | ||
|
|
0e4f5a6870 | ||
|
|
a4f2ba1ab8 | ||
|
|
3c8ddbb0a5 | ||
|
|
1dbe025d36 | ||
|
|
16d55c9f59 | ||
|
|
f3bd033bf6 | ||
|
|
ba87e22af0 | ||
|
|
9d2bbac695 | ||
|
|
5e0f4e6ef0 | ||
|
|
9a362b39bf | ||
|
|
fb624bab50 | ||
|
|
e0661a55f6 | ||
|
|
1773cc9eb5 | ||
|
|
dd7c8f2ad6 | ||
|
|
592ff17c5f | ||
|
|
6d6d83afdb | ||
|
|
134640411a | ||
|
|
6e1a5c0353 | ||
|
|
b1a884f898 | ||
|
|
1717b3683c | ||
|
|
a00f9e5f77 | ||
|
|
e210c31bf2 | ||
|
|
93094740b2 | ||
|
|
8ee3c333fb | ||
|
|
302b246475 | ||
|
|
90b9186fd6 | ||
|
|
29e2f13e1e | ||
|
|
3d9da1dbce | ||
|
|
62a13526c9 | ||
|
|
4dcc0e8666 | ||
|
|
7e98afe6fe | ||
|
|
e835bfbec7 | ||
|
|
577ff97980 | ||
|
|
76516e0320 | ||
|
|
385155f043 | ||
|
|
29b2552cf2 | ||
|
|
ee1e1a4cd5 | ||
|
|
a0c85add5b | ||
|
|
94472a6782 | ||
|
|
cdc47832ef | ||
|
|
acae98324b | ||
|
|
4786178705 | ||
|
|
2e375a4482 | ||
|
|
e58735a6fd | ||
|
|
0f1fff7ac8 | ||
|
|
0ef43f5d57 | ||
|
|
7a2c5753c5 | ||
|
|
2a3ae06d85 | ||
|
|
f009d99e5e | ||
|
|
206d2f623d | ||
|
|
273035ff49 | ||
|
|
3540fbab74 | ||
|
|
4983d40358 | ||
|
|
fd970971af | ||
|
|
c8a0701597 | ||
|
|
ad99c4eb14 | ||
|
|
67d7b0080e | ||
|
|
2bf66e5b42 | ||
|
|
88d0b5a17d | ||
|
|
c042e3d985 | ||
|
|
2defe1c2c4 | ||
|
|
0bc946b333 | ||
|
|
98dd0fbad7 | ||
|
|
4bb37f7e25 | ||
|
|
23703f5c01 | ||
|
|
215a22f425 | ||
|
|
e742fe7bf9 | ||
|
|
57d1754bc8 | ||
|
|
b95bb9bedb | ||
|
|
39f6349878 | ||
|
|
678131429b | ||
|
|
bb70e8b14e | ||
|
|
b9c86b3f99 | ||
|
|
0ec50842a7 | ||
|
|
a678133e47 | ||
|
|
b0a5f4f3e8 | ||
|
|
642f4e70b5 | ||
|
|
efd03711cc | ||
|
|
4c067ee13d | ||
|
|
5cb7fc2610 | ||
|
|
05ba05393c | ||
|
|
2206d3f921 | ||
|
|
1f7f613800 | ||
|
|
8d92440451 | ||
|
|
5fca9d4c94 | ||
|
|
663353cbdb | ||
|
|
8550a9f159 | ||
|
|
f01dbd68f3 | ||
|
|
5913829e21 | ||
|
|
c201a44f1a | ||
|
|
382be6c4e1 | ||
|
|
aad20ed035 | ||
|
|
c24adea40f | ||
|
|
223e2376b6 | ||
|
|
0ce65cec6b | ||
|
|
7ddef2586c | ||
|
|
4d9f137980 | ||
|
|
03aabbfa07 | ||
|
|
fe0d412b98 | ||
|
|
826eb7ed65 | ||
|
|
2dc121e3df | ||
|
|
75bff4d67f | ||
|
|
9d33ae86be | ||
|
|
173c4e1c78 | ||
|
|
2d0b451ed0 | ||
|
|
35f2dbad09 | ||
|
|
872fc4751b | ||
|
|
504959850c | ||
|
|
2466c7123e | ||
|
|
0f9b4f1b6c | ||
|
|
38f63135a8 | ||
|
|
b0ad2ddbe7 | ||
|
|
94b3944003 | ||
|
|
aa1bfebc46 | ||
|
|
62dd41f0a1 | ||
|
|
bb54e2542c | ||
|
|
639191af23 | ||
|
|
4670a939bf | ||
|
|
391dcf9639 | ||
|
|
7378abdedb | ||
|
|
43c5c2dd0d | ||
|
|
ffa0a7f276 | ||
|
|
592147a499 | ||
|
|
02e2532e17 | ||
|
|
4891c8fcd2 | ||
|
|
ae20db416f | ||
|
|
283ad71283 | ||
|
|
25bbc4e053 | ||
|
|
913d313098 | ||
|
|
7efbafb8b4 | ||
|
|
3500757c97 | ||
|
|
c4c6ba7620 | ||
|
|
c53eaf939d | ||
|
|
d5613e8c65 | ||
|
|
7fada722e7 | ||
|
|
66ee7bab32 | ||
|
|
8848040203 | ||
|
|
33ef814a76 | ||
|
|
06e032bc6d | ||
|
|
25e844fadd | ||
|
|
08474c671b | ||
|
|
8f94041ea2 | ||
|
|
08fc2b3a4a | ||
|
|
37aec0ab84 | ||
|
|
61ad570ecc | ||
|
|
90e158e519 | ||
|
|
97d49b4bf3 | ||
|
|
4c3a35bc95 | ||
|
|
244b5d56ff | ||
|
|
755cb0f4e0 | ||
|
|
7b2b402d37 | ||
|
|
41481548b8 | ||
|
|
f2411a1db7 | ||
|
|
073cbae9d5 | ||
|
|
db8e22196d | ||
|
|
8a68fea16e | ||
|
|
6501ca9b3c | ||
|
|
0c7c6c58c0 | ||
|
|
f3f0ba6955 | ||
|
|
52da5cf894 | ||
|
|
2878e096e6 | ||
|
|
4f3ef4818a | ||
|
|
ffc0c5f2d3 | ||
|
|
9d97c83909 | ||
|
|
6a21e29397 | ||
|
|
8fdc24097e | ||
|
|
4e7368f79a | ||
|
|
1e1dee88b7 | ||
|
|
91e4ff6a08 | ||
|
|
4be9aac7a6 | ||
|
|
af059b5e21 | ||
|
|
fdb1978f89 | ||
|
|
8332e59c62 | ||
|
|
20cc263502 | ||
|
|
361d4ec4dd | ||
|
|
11f8ba5a8f | ||
|
|
c3bf842f9f | ||
|
|
33b598289b | ||
|
|
cfc7f99405 | ||
|
|
1d79550f3c | ||
|
|
db5ecd2c96 | ||
|
|
1280367a35 | ||
|
|
14706c8f9a | ||
|
|
b821df4279 | ||
|
|
07ff28d9dc | ||
|
|
d15fd2a8b7 | ||
|
|
6e4381934a | ||
|
|
46ccded145 | ||
|
|
01453b67fc | ||
|
|
7ec739b5c7 | ||
|
|
fd8279ee90 | ||
|
|
a6fea6727b | ||
|
|
e561e8cbdd | ||
|
|
8d39f89aff | ||
|
|
05644a742f | ||
|
|
f0f7fcf5b7 | ||
|
|
16bb54c524 | ||
|
|
70c3eaab80 | ||
|
|
104fb0f06c | ||
|
|
d93f96f48f | ||
|
|
8731eb91f4 | ||
|
|
a987609b2a | ||
|
|
40793e9485 | ||
|
|
874587a591 | ||
|
|
59a401fbe2 | ||
|
|
9c797ead28 | ||
|
|
b6bb7c1a84 | ||
|
|
995f7e8e19 | ||
|
|
9732f0547d | ||
|
|
9c9a71af74 | ||
|
|
be09b3a40a | ||
|
|
d79d08ef11 | ||
|
|
c7377cbbbf | ||
|
|
bbcc88217a | ||
|
|
c2838b4861 | ||
|
|
19c256d9a4 | ||
|
|
7b08533a76 | ||
|
|
baf0057628 | ||
|
|
99bf93dce6 | ||
|
|
776ec96565 | ||
|
|
d03c444eae | ||
|
|
de6678c47a | ||
|
|
b780198526 | ||
|
|
27f1e9e476 | ||
|
|
a9de9da75b | ||
|
|
4f0a119279 | ||
|
|
9d3b4b8d78 | ||
|
|
9a59de939e | ||
|
|
c5a2d00395 | ||
|
|
36c2087269 | ||
|
|
7178970524 | ||
|
|
d76fa92be3 | ||
|
|
e942348211 | ||
|
|
3e12f4f55f | ||
|
|
4ed51d3fd8 | ||
|
|
c9a5fda400 | ||
|
|
45d4bd0cf5 | ||
|
|
aafa8a9b0b | ||
|
|
b1e47fc7d4 | ||
|
|
b87b8cf272 | ||
|
|
e797fadaff | ||
|
|
316b5b9992 | ||
|
|
5aeae3a452 | ||
|
|
3f4f976396 | ||
|
|
7cd83ced17 | ||
|
|
f832a8f97c | ||
|
|
256eb727a5 | ||
|
|
3ae6613445 | ||
|
|
5979dd2e15 | ||
|
|
b203a31039 | ||
|
|
1f99b49617 | ||
|
|
efbf5e3111 | ||
|
|
b52fe64628 | ||
|
|
0d3511c05f | ||
|
|
ace3da4173 | ||
|
|
f65a21f88c | ||
|
|
47aeeabc51 | ||
|
|
049860b884 | ||
|
|
b7d23e1aec | ||
|
|
f1f33a3ac7 | ||
|
|
607e6d96a4 | ||
|
|
d8551ead86 | ||
|
|
29510716bb | ||
|
|
735e0af91a | ||
|
|
2666982645 | ||
|
|
768c8de48c | ||
|
|
ce6a04ae5a | ||
|
|
f8012d5055 | ||
|
|
966efd2397 | ||
|
|
9344798fa1 | ||
|
|
95643b6a56 | ||
|
|
6b2c88e542 | ||
|
|
0df38df1c6 | ||
|
|
73dd3dcc26 | ||
|
|
1e65fede6c | ||
|
|
b3c0d91eb3 | ||
|
|
eaa93d287e | ||
|
|
4517ae22e5 | ||
|
|
f4537ad272 | ||
|
|
c425199239 | ||
|
|
e15894872d | ||
|
|
54a15d7b51 | ||
|
|
b1b6470cc2 | ||
|
|
3a89263e73 | ||
|
|
533b755b11 | ||
|
|
c5d94545b5 | ||
|
|
2ae3e52f76 | ||
|
|
f20b9e3875 | ||
|
|
a1ca352818 | ||
|
|
6b96e2f2a9 | ||
|
|
b1b4c67b75 | ||
|
|
f775d3be67 | ||
|
|
09f7814503 | ||
|
|
745c5384bb | ||
|
|
5ff2b97f67 | ||
|
|
44f30dc01c | ||
|
|
4b307cb2b2 | ||
|
|
378d98096d | ||
|
|
2b12d29a2f | ||
|
|
7382a68505 | ||
|
|
e10d2cb37e | ||
|
|
c4e7a62ac3 | ||
|
|
f354378fec | ||
|
|
c638cb38b9 | ||
|
|
d255ecf0f9 | ||
|
|
4ea691118c | ||
|
|
bfd962cda2 | ||
|
|
719c233e77 | ||
|
|
bd78235b28 | ||
|
|
aa292ed113 | ||
|
|
b99d789543 | ||
|
|
5ef58cce5d | ||
|
|
31c3868bc8 | ||
|
|
071a97becb | ||
|
|
1df40ada5c | ||
|
|
a12b99ad49 | ||
|
|
e1d84d0b92 | ||
|
|
fb47ef66da | ||
|
|
9621348ad0 | ||
|
|
560e1429f1 | ||
|
|
18a945e7d6 | ||
|
|
16548fc3a1 | ||
|
|
4272d954ea | ||
|
|
77b5907a9f | ||
|
|
acae0533bc | ||
|
|
a5e259c19b | ||
|
|
12865f318e | ||
|
|
51704ee7ce | ||
|
|
ad6f4a3dd8 | ||
|
|
70ffa25c4c | ||
|
|
4aef168a50 | ||
|
|
40fe70f15f | ||
|
|
812351cfdb | ||
|
|
518e1abb26 | ||
|
|
9db64d3389 | ||
|
|
2ad1a44432 | ||
|
|
3047c73952 | ||
|
|
c8f5b1afa6 | ||
|
|
54be0c2d9c | ||
|
|
b5b4991ae6 | ||
|
|
3273c75057 | ||
|
|
f60f0cf279 | ||
|
|
9e7881ee08 | ||
|
|
9d45825947 | ||
|
|
c993388e5c | ||
|
|
b45cf33eef | ||
|
|
de376a0b25 | ||
|
|
c659fad434 | ||
|
|
5e7e4a4b11 | ||
|
|
7efbc3f72d | ||
|
|
8c93171303 | ||
|
|
d8f314df25 | ||
|
|
be0872e71e | ||
|
|
3b95cbd697 | ||
|
|
5ef07da451 | ||
|
|
5d98fc06c2 | ||
|
|
7f531e5e2d | ||
|
|
542a700f2c | ||
|
|
f3eaa1f74a | ||
|
|
06680c64eb | ||
|
|
4b1a19677a | ||
|
|
5792f43185 | ||
|
|
e89298eb52 | ||
|
|
22fada19ec | ||
|
|
ef7fa8ae0f | ||
|
|
72fa115160 | ||
|
|
3665cf11f3 | ||
|
|
9b5910547f | ||
|
|
04bcb51c73 | ||
|
|
774049c75e | ||
|
|
b8e52934ea | ||
|
|
5047aeacb5 | ||
|
|
6594da85f1 | ||
|
|
f532807d62 | ||
|
|
ffdc7893fe | ||
|
|
25a401042a | ||
|
|
22914d4687 | ||
|
|
7970a24359 | ||
|
|
8bbc113258 | ||
|
|
10c13e8fbe | ||
|
|
0fec57e76d | ||
|
|
e72bf9acb1 | ||
|
|
fc4287e9e1 | ||
|
|
5f184ed655 | ||
|
|
abad59f0ce | ||
|
|
cd3474e5b5 | ||
|
|
57d8072901 | ||
|
|
d7d0ba498c | ||
|
|
169fcdc323 | ||
|
|
a8a19b0c4d | ||
|
|
a669b84e6a | ||
|
|
c5e764750b | ||
|
|
7d2b7974fb | ||
|
|
ee955f7bef | ||
|
|
ad370d35c2 | ||
|
|
6a76afc061 | ||
|
|
c67f659041 | ||
|
|
dc5eea48ae | ||
|
|
cb974bf567 | ||
|
|
7b7d82e6f6 | ||
|
|
05c6898fc2 | ||
|
|
7ffa459a66 | ||
|
|
93e8e94e60 | ||
|
|
c4e2416621 | ||
|
|
9c25419b9a | ||
|
|
1879830df1 | ||
|
|
29298c0eb1 | ||
|
|
247abef1d7 | ||
|
|
6994d98d2a | ||
|
|
9a600ece2d | ||
|
|
f91226ef47 | ||
|
|
e366ffbd30 | ||
|
|
63b300d39d | ||
|
|
afb70d1570 | ||
|
|
49534eec0d | ||
|
|
3f1277f7bd | ||
|
|
26fad4c385 | ||
|
|
25b3afacf6 | ||
|
|
2d25bbb090 | ||
|
|
0eb834582d | ||
|
|
124a440527 | ||
|
|
5c704636ef | ||
|
|
31fd94cc3c | ||
|
|
573e43272a | ||
|
|
1d89434812 | ||
|
|
e0a1ef7c1b | ||
|
|
48ac0e0eb4 | ||
|
|
bcb6234d94 | ||
|
|
f967963264 | ||
|
|
b22c55dd21 | ||
|
|
4ff5a536ca | ||
|
|
fff5f578ac | ||
|
|
0f44c8c923 | ||
|
|
0b6ad03150 | ||
|
|
2450b0bc69 | ||
|
|
fc8eebf367 | ||
|
|
e4f51f2b7f | ||
|
|
2c2216a89e | ||
|
|
cafe851809 | ||
|
|
35e338cc99 | ||
|
|
8feeaaef7c | ||
|
|
e7a3c4e370 | ||
|
|
ea5c558bd4 | ||
|
|
94f0ed179f | ||
|
|
244c359491 | ||
|
|
1f24fea21a | ||
|
|
8deaa09b24 | ||
|
|
85bffb7143 | ||
|
|
325ca310d3 | ||
|
|
d903e268f0 | ||
|
|
f4b26756c5 | ||
|
|
fb3a34ee91 | ||
|
|
6902bb6c41 | ||
|
|
379819b2b0 | ||
|
|
59abfbfb5a | ||
|
|
6e2d871844 | ||
|
|
57ec3c85cb | ||
|
|
4afdf287f1 | ||
|
|
2810ac7cb9 | ||
|
|
e0d27f9bf8 | ||
|
|
de303c9990 | ||
|
|
1619e780f4 | ||
|
|
a1e37eca6b | ||
|
|
c345e7456c | ||
|
|
d166c026f7 | ||
|
|
5e639996d6 | ||
|
|
dac2ef5a9c | ||
|
|
71d7888e81 | ||
|
|
84a4df9c49 | ||
|
|
95bc203196 | ||
|
|
5a9f6d211c | ||
|
|
a29302faae | ||
|
|
75efa72c65 | ||
|
|
58a84665b0 | ||
|
|
a5263384d9 | ||
|
|
c0e6759c69 | ||
|
|
d754f74316 | ||
|
|
1df5399431 | ||
|
|
c284e6feed | ||
|
|
500239758a | ||
|
|
319ecd8084 | ||
|
|
d7891fabc0 | ||
|
|
e287dd0419 | ||
|
|
4dee9dde11 | ||
|
|
9bae6e67bc | ||
|
|
aa7a7ce25a | ||
|
|
a969468c57 | ||
|
|
a1e76fc638 | ||
|
|
d8ed4ca354 | ||
|
|
3248e3f91e | ||
|
|
f00687e6f9 | ||
|
|
5240c6b60a | ||
|
|
67604a88e1 | ||
|
|
4242dfb8b5 | ||
|
|
f96d8ee832 | ||
|
|
c147ec37c9 | ||
|
|
094ddbba36 | ||
|
|
634630ca2d | ||
|
|
d78c26f266 | ||
|
|
fabc6c9bd7 | ||
|
|
b6e8ddd2ad | ||
|
|
cf2bbae6e1 | ||
|
|
08799616d0 | ||
|
|
58bebf78d8 | ||
|
|
d534fad3ee | ||
|
|
871dc7a401 | ||
|
|
1fe7c78f7e | ||
|
|
22a65f80fc | ||
|
|
756ea8a388 | ||
|
|
12186cb401 | ||
|
|
aa9ffb9eef | ||
|
|
dcb5aed901 | ||
|
|
34f6a747a8 | ||
|
|
912cb2ad54 | ||
|
|
885488b3d6 | ||
|
|
3d3d3ef2d9 | ||
|
|
a7120b3400 | ||
|
|
51915205b0 | ||
|
|
4a3ee042ea | ||
|
|
88a1b8d0df | ||
|
|
7e18a0f966 | ||
|
|
c367082ddd | ||
|
|
9f94b5306d | ||
|
|
44ed10c50b | ||
|
|
1b22809412 | ||
|
|
bdabbaa63d | ||
|
|
c79c109b77 | ||
|
|
f1a2133820 | ||
|
|
17dc061490 | ||
|
|
3b8d82330d | ||
|
|
a20829e700 | ||
|
|
5be2fdc158 | ||
|
|
873b107e69 | ||
|
|
356bbbe53e | ||
|
|
e3ce4e1567 | ||
|
|
e02050c515 | ||
|
|
26930633f0 | ||
|
|
b7a485767f | ||
|
|
f54cdccd4f | ||
|
|
41c9a32f31 | ||
|
|
736ef35805 | ||
|
|
0128cbb4f3 | ||
|
|
ff0a407151 | ||
|
|
34f4403864 | ||
|
|
b1c78efb6c | ||
|
|
28f03ff9ce | ||
|
|
a5fe61adbb | ||
|
|
457f5e59ea | ||
|
|
422fd284e7 | ||
|
|
6f32225736 | ||
|
|
366bbceb02 | ||
|
|
7b7f81e08f | ||
|
|
cfe955f970 | ||
|
|
684ddc9de9 | ||
|
|
f5d38a9aed | ||
|
|
447e3693f2 | ||
|
|
909490bf63 | ||
|
|
d90363e4a4 | ||
|
|
ee5de744c3 | ||
|
|
d401c88a04 | ||
|
|
80563b9557 | ||
|
|
c74900057c | ||
|
|
78867d5c9b | ||
|
|
d435fc7792 | ||
|
|
e6f76e0f96 | ||
|
|
1c6b533609 | ||
|
|
5c3d8360c2 | ||
|
|
3882c7d9af | ||
|
|
15726218bd | ||
|
|
869bfc82cf | ||
|
|
0eb3d54b21 | ||
|
|
f83290f762 | ||
|
|
686a3b9d79 | ||
|
|
4d57d5c327 | ||
|
|
36c21cf7fa | ||
|
|
7ab10e1936 | ||
|
|
41467d35a4 | ||
|
|
d351252a12 | ||
|
|
c6cd4f701c | ||
|
|
65889cca44 | ||
|
|
f1914fd946 | ||
|
|
e53af7bef7 | ||
|
|
b910cd2615 | ||
|
|
58d331ca0e | ||
|
|
17f5b0a330 | ||
|
|
de386292bc | ||
|
|
88bf26a2f8 | ||
|
|
3caad4100d | ||
|
|
916203a240 | ||
|
|
62f1971f7b | ||
|
|
4fde96357d | ||
|
|
c3730b65fb | ||
|
|
1ea072cd38 | ||
|
|
bbe4582ee9 | ||
|
|
89ab5c328a | ||
|
|
3a4672793d | ||
|
|
0a0446f35c | ||
|
|
fc58ff74be | ||
|
|
fed6831cdc | ||
|
|
1605c8d9d3 | ||
|
|
d6641c7d8d | ||
|
|
5079f38e99 | ||
|
|
1eab2a4a80 | ||
|
|
c331107651 | ||
|
|
117b0bd675 | ||
|
|
9b4a6f8776 | ||
|
|
f4e6f2b375 | ||
|
|
5971fb7aa4 | ||
|
|
10dfe7e935 | ||
|
|
a9208a9565 | ||
|
|
1cc5b32cab | ||
|
|
f8a972a3c6 | ||
|
|
5b7cc20027 | ||
|
|
bd34e7f2c7 | ||
|
|
46fe29c3f6 | ||
|
|
c50e2815c8 | ||
|
|
fbfee632d5 | ||
|
|
77c59aabfa | ||
|
|
ebb1e8a728 | ||
|
|
1646217f09 | ||
|
|
645edfb2b8 | ||
|
|
61f28298c9 | ||
|
|
d19f97bf21 | ||
|
|
70c611a84f | ||
|
|
286428c19c | ||
|
|
6ec719bcfa | ||
|
|
11f9bb2d74 | ||
|
|
5a1ba5ad7d | ||
|
|
cf094e7ef5 | ||
|
|
31808bd9a2 | ||
|
|
61a5173a75 | ||
|
|
ed6fe9e657 | ||
|
|
e30fa85200 | ||
|
|
ca1069e182 | ||
|
|
70f73a0949 | ||
|
|
710fff0e3f | ||
|
|
660bc5843e | ||
|
|
13295a14ee | ||
|
|
9ce07ff617 | ||
|
|
b272e04bab | ||
|
|
28b11912ab | ||
|
|
b9f096d125 | ||
|
|
8c60faa1d0 | ||
|
|
1f93ea4eee | ||
|
|
7ca77e53f6 | ||
|
|
69397f3658 | ||
|
|
f907bc066a | ||
|
|
bda52701f5 | ||
|
|
d26f7684ce | ||
|
|
63d6c2bff6 | ||
|
|
cc45e73414 | ||
|
|
5d092bcb18 | ||
|
|
295c68841c | ||
|
|
2664b52559 | ||
|
|
dd36328331 | ||
|
|
c8c929e4a2 | ||
|
|
d1ef7bf155 | ||
|
|
1ab23f17d2 | ||
|
|
a72eb4693b | ||
|
|
67579d062b | ||
|
|
766a233f37 | ||
|
|
905671c236 | ||
|
|
27c1383e46 | ||
|
|
029ccc8f75 | ||
|
|
cde40d586d | ||
|
|
6a16a8a20b | ||
|
|
1a12dede6f | ||
|
|
35a78b65ea | ||
|
|
ada19432f6 | ||
|
|
4998e90870 | ||
|
|
471c11c6dc | ||
|
|
3e13e0b59b | ||
|
|
53cd105f2e | ||
|
|
24b31c3798 | ||
|
|
def82b534b | ||
|
|
a424f4ebf6 | ||
|
|
b8f0a8d8ec | ||
|
|
7941226eff | ||
|
|
86bbb2f6bb | ||
|
|
3c176f7258 | ||
|
|
3642c8a784 | ||
|
|
0e80d90a76 | ||
|
|
4fdf2dccd4 | ||
|
|
f8a997e6c5 | ||
|
|
40f676ed14 | ||
|
|
5e346748ea | ||
|
|
3ef61fe014 | ||
|
|
3885d280d3 | ||
|
|
f41b914197 | ||
|
|
e0684f6348 | ||
|
|
fb980a78c0 | ||
|
|
c2430ab48d | ||
|
|
d36d9cb39c | ||
|
|
0017c08bb6 | ||
|
|
e737ff7470 | ||
|
|
945dc40937 | ||
|
|
7ef0085115 | ||
|
|
d2fa68813f | ||
|
|
f22a09a9a2 | ||
|
|
b0aba79a56 | ||
|
|
7c23e4a2f6 | ||
|
|
7fe035ce4b | ||
|
|
3e7160edad | ||
|
|
aaeb686ec7 | ||
|
|
3fdf4884d8 | ||
|
|
1b23ff4b91 | ||
|
|
88dac318e5 | ||
|
|
520c2e660d | ||
|
|
f5d0d451b4 | ||
|
|
8af6fc0cc4 | ||
|
|
c04b97a313 | ||
|
|
1d85414dc2 | ||
|
|
c6533827f0 | ||
|
|
b5803451b4 | ||
|
|
3417d66b2b | ||
|
|
1e61204e80 | ||
|
|
19c4857ef1 | ||
|
|
66ea94898e | ||
|
|
ced6d21c3e | ||
|
|
429c7c45c7 | ||
|
|
c03b1111aa | ||
|
|
ebd7d3acdc | ||
|
|
5aeec60cfe | ||
|
|
620b4f7517 | ||
|
|
6d58004c11 | ||
|
|
df6d926dec | ||
|
|
e63af24cb5 | ||
|
|
068d9f8ae8 | ||
|
|
c19c848c46 | ||
|
|
0bf0a0a8fa | ||
|
|
743e8678ea | ||
|
|
a7ad1c06f4 | ||
|
|
b1af70e70f | ||
|
|
c87c3533ee | ||
|
|
4839a0ee6c | ||
|
|
a0f0c44e6d | ||
|
|
74691dc1d9 | ||
|
|
e4957badb7 | ||
|
|
46791c4c3a | ||
|
|
56c3917841 | ||
|
|
1fb13d3e93 | ||
|
|
535beb4e2d | ||
|
|
2d318b88c9 | ||
|
|
b7f0b76cef | ||
|
|
d30312f516 | ||
|
|
30772848e6 | ||
|
|
eca01a3979 | ||
|
|
35ef0b7ac1 | ||
|
|
19ccba3a6c | ||
|
|
207b7674ae | ||
|
|
631c55ba92 | ||
|
|
e7b30fd36d | ||
|
|
3e959007b9 | ||
|
|
07ec4ca230 | ||
|
|
6e4946b682 | ||
|
|
47ea16f17c | ||
|
|
722857fc07 | ||
|
|
2125b38381 | ||
|
|
289de7d57b | ||
|
|
25ecd8e797 | ||
|
|
319dfdb070 | ||
|
|
9dbbcac01e | ||
|
|
f545c8e058 | ||
|
|
c68fa6a654 | ||
|
|
d288ecd70d | ||
|
|
312d3aba84 | ||
|
|
4d2b2c6de5 | ||
|
|
80d55a226c | ||
|
|
d86a539350 | ||
|
|
0f7098d0e0 | ||
|
|
8c96be1e92 | ||
|
|
37f396bfe3 | ||
|
|
75894dc401 | ||
|
|
cdee000ce8 | ||
|
|
54ca62600b | ||
|
|
6f4280ed59 | ||
|
|
ddf56b78cd | ||
|
|
53a854f54f | ||
|
|
4896acac7d | ||
|
|
e3defeba2a | ||
|
|
62079908ce | ||
|
|
e65a8f2e5f | ||
|
|
9f27fb1e57 | ||
|
|
04d734e76c | ||
|
|
df50fa1b59 | ||
|
|
051872b8c8 | ||
|
|
57ab9e7be0 | ||
|
|
69d95ba759 | ||
|
|
9caf66d7c8 | ||
|
|
74cb0d40c5 | ||
|
|
deac7f34b8 | ||
|
|
a9883f27c6 | ||
|
|
85b1e57d8d | ||
|
|
b7d8e01867 | ||
|
|
390a56176f | ||
|
|
a1b888cce6 | ||
|
|
2107dd6891 | ||
|
|
1fca56afef | ||
|
|
c0762a6a5a | ||
|
|
f48de1bac9 | ||
|
|
c7dfce89e5 | ||
|
|
822fafcd42 | ||
|
|
1476d4ea9b | ||
|
|
c1c63777e8 | ||
|
|
2fab25ae89 | ||
|
|
75d4491522 | ||
|
|
c7866fb21b | ||
|
|
87987bacde | ||
|
|
bde0219fe6 | ||
|
|
ad64c849b8 | ||
|
|
b808bb83b7 | ||
|
|
d0090d35fb | ||
|
|
7a1a06a24b | ||
|
|
000b17881b | ||
|
|
068b6ed49b | ||
|
|
0187fb48aa | ||
|
|
35a2b38441 | ||
|
|
4747c9a4c8 | ||
|
|
7be8d83278 | ||
|
|
a5d6757d6f | ||
|
|
3f09d47c81 | ||
|
|
9c06d6ba0f | ||
|
|
b7c819bf48 | ||
|
|
9f31803ba7 | ||
|
|
1e7deff9c9 | ||
|
|
04ac15d8d9 | ||
|
|
3762189916 | ||
|
|
18d6e466d0 | ||
|
|
095ecf3142 | ||
|
|
433ed863e5 | ||
|
|
0dba68e754 | ||
|
|
1ab2815823 | ||
|
|
2af5a725e4 | ||
|
|
a86bf1332b | ||
|
|
831054bff1 | ||
|
|
f94de1c069 | ||
|
|
a3d9503a17 | ||
|
|
3f2b15f9f7 | ||
|
|
e48817d518 | ||
|
|
d11cebe30b | ||
|
|
77a5efb327 | ||
|
|
851006efee | ||
|
|
6fc6b2fb35 | ||
|
|
ed17a2c48b | ||
|
|
bd00e797a9 | ||
|
|
e645c4928a | ||
|
|
a425878e80 | ||
|
|
f3cd9d374d | ||
|
|
b3e93b084e | ||
|
|
314c189c49 | ||
|
|
2bb2c55662 | ||
|
|
d13c126e99 | ||
|
|
9b54ee37a6 | ||
|
|
e1ead9d3ee | ||
|
|
cf61068dc0 | ||
|
|
82863f8291 | ||
|
|
e4de59827f | ||
|
|
ccea4c920a | ||
|
|
89166f0e47 | ||
|
|
7a05a16c54 | ||
|
|
d0854646c7 | ||
|
|
1e9c9fe5f8 | ||
|
|
80fac8d8e5 | ||
|
|
c774b98389 | ||
|
|
3fd0403cc9 | ||
|
|
6eb3347a3b | ||
|
|
89bceaff75 | ||
|
|
dc25c9c1a2 | ||
|
|
e68ba5870c | ||
|
|
c68cb33751 | ||
|
|
59f807fd02 | ||
|
|
232ffebc1e | ||
|
|
3cea845a0f | ||
|
|
295f3b32e4 | ||
|
|
254da2c3c6 | ||
|
|
ecd3b8d933 | ||
|
|
c673b9b17c | ||
|
|
cd28de0d63 | ||
|
|
672f6a7112 | ||
|
|
3b50672b70 | ||
|
|
f0613b1c6c | ||
|
|
2197e160ef | ||
|
|
2dccd90166 | ||
|
|
2f873f2dd2 | ||
|
|
fde1a524ea | ||
|
|
0558010479 | ||
|
|
79a054a524 | ||
|
|
d94e443a14 | ||
|
|
3862184e88 | ||
|
|
f40fa3c2f6 | ||
|
|
01bc93459b | ||
|
|
151bd07391 | ||
|
|
935393aae0 | ||
|
|
fbfde43477 | ||
|
|
2ffbe115b7 | ||
|
|
645973eb79 | ||
|
|
1332307972 | ||
|
|
b700f5eb5d | ||
|
|
e60dbdc521 | ||
|
|
c66623b330 | ||
|
|
62246d8796 | ||
|
|
b35f131309 | ||
|
|
3dd82633a3 | ||
|
|
00231bf8a7 | ||
|
|
58d392e813 | ||
|
|
1d4d99d090 | ||
|
|
fe3edf2c83 | ||
|
|
0864ba0429 | ||
|
|
3a96980013 | ||
|
|
423f7a555d | ||
|
|
5db8ad53ac | ||
|
|
c2ab357802 | ||
|
|
4fb558f143 | ||
|
|
5762a8a086 | ||
|
|
ed4c675211 | ||
|
|
f157898462 | ||
|
|
6d20709e07 | ||
|
|
a9a9853c01 | ||
|
|
af14aa4c9e | ||
|
|
24d6b98817 | ||
|
|
899f4baa63 | ||
|
|
c1ab7ba025 | ||
|
|
9d38f60207 | ||
|
|
0efa25cf61 | ||
|
|
6316b60451 | ||
|
|
f268fb60a8 | ||
|
|
3520d6915c | ||
|
|
4da7a84518 | ||
|
|
382dfc3bb4 | ||
|
|
b6dcdc816e | ||
|
|
c9d544089a | ||
|
|
3eb5faeed5 | ||
|
|
7a328c7edf | ||
|
|
6567aa1951 | ||
|
|
92319f0111 | ||
|
|
782d86b6e7 | ||
|
|
c2cae37c1d | ||
|
|
1026d26ecb | ||
|
|
7156803dbe | ||
|
|
f54ed54247 | ||
|
|
f30b50195a | ||
|
|
c90ab9affd | ||
|
|
c2f6ca43f1 | ||
|
|
3e5e41d88f | ||
|
|
9eafc0431d | ||
|
|
0a82b777b2 | ||
|
|
32bcc01a46 | ||
|
|
9c3fe3546b | ||
|
|
83f8aed583 | ||
|
|
2fd6602740 | ||
|
|
a554d406e7 | ||
|
|
5f12f872e6 | ||
|
|
be4eb68a30 | ||
|
|
df5284d9e5 | ||
|
|
0e27d33e7e | ||
|
|
fe6e6ae738 | ||
|
|
9d29713eaa | ||
|
|
270d0277db | ||
|
|
0bd06c8d5d | ||
|
|
733f215ccf | ||
|
|
ad01a69fe5 | ||
|
|
dd9c1dade8 | ||
|
|
b8f70519f6 | ||
|
|
9b9d212dde | ||
|
|
3fe15969db | ||
|
|
ec4bf222c6 | ||
|
|
1173908ee4 | ||
|
|
2595b8a925 | ||
|
|
f4e2a8c76c | ||
|
|
66e1a05652 | ||
|
|
87283dc41f | ||
|
|
a802ebffed | ||
|
|
b84b09421e | ||
|
|
940fd09396 | ||
|
|
920338be59 | ||
|
|
bcd1155b77 | ||
|
|
1dc88109e8 | ||
|
|
262c1f53c1 | ||
|
|
4af0a3a0d1 | ||
|
|
be16418289 | ||
|
|
b54a52fbfe | ||
|
|
ae88159952 | ||
|
|
62c764f681 | ||
|
|
2c48cc10ae | ||
|
|
82f2866789 | ||
|
|
d56e5a269c | ||
|
|
1517b29513 | ||
|
|
bea7b43a1a | ||
|
|
2f878f8b5a | ||
|
|
fc68c1b290 | ||
|
|
9081c185da | ||
|
|
7c4493f237 | ||
|
|
da4e73ab85 | ||
|
|
f3e7f98b48 | ||
|
|
3925fc6124 | ||
|
|
c639130c1e | ||
|
|
75c3116008 | ||
|
|
e9e14eb49c | ||
|
|
d1558304f8 | ||
|
|
0531b530b1 | ||
|
|
f9d0178dd7 | ||
|
|
b66b8d37eb | ||
|
|
05ef4c29d8 | ||
|
|
9c3d756692 | ||
|
|
93651e46f5 | ||
|
|
ea901cbfa0 | ||
|
|
d5dc4f43ee | ||
|
|
498e02f154 | ||
|
|
d0ea8f4f96 | ||
|
|
dec3b7bb39 | ||
|
|
10bc8b05ad | ||
|
|
ad77b54dce | ||
|
|
b6724b0283 | ||
|
|
54270b1038 | ||
|
|
31dc732a74 | ||
|
|
f0e02d6d39 | ||
|
|
156d6e4ead | ||
|
|
4375c1c3d6 | ||
|
|
24a9cd67ee | ||
|
|
ba8d522c19 | ||
|
|
3ae46214a4 | ||
|
|
5119fe8d7b | ||
|
|
ed0319cdd8 | ||
|
|
bc50da6a7c | ||
|
|
52b3b4c249 | ||
|
|
74bab529d2 | ||
|
|
b1b143917b | ||
|
|
7beb4c85c4 | ||
|
|
f3beb5670a | ||
|
|
446c67b69a | ||
|
|
c4507bd172 | ||
|
|
61e872bb4e | ||
|
|
9f22a3e23a | ||
|
|
653dd5cd42 | ||
|
|
bc53ed3be9 | ||
|
|
f935bbccee | ||
|
|
c7d5053536 | ||
|
|
ea78f1d970 |
1
.bazelignore
Normal file
1
.bazelignore
Normal file
@@ -0,0 +1 @@
|
||||
test
|
||||
1
.bazeliskrc
Normal file
1
.bazeliskrc
Normal file
@@ -0,0 +1 @@
|
||||
USE_BAZEL_VERSION=6.x
|
||||
17
.bazelrc
Normal file
17
.bazelrc
Normal file
@@ -0,0 +1,17 @@
|
||||
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
|
||||
build:linux --cxxopt=-std=c++17
|
||||
build:macos --cxxopt=-std=c++17
|
||||
|
||||
common:ci --announce_rc
|
||||
common:ci --verbose_failures
|
||||
common:ci --keep_going
|
||||
test:ci --test_output=errors
|
||||
|
||||
try-import %workspace%/user.bazelrc
|
||||
26
.clang-tidy
Normal file
26
.clang-tidy
Normal file
@@ -0,0 +1,26 @@
|
||||
Checks: >
|
||||
bugprone-*,
|
||||
concurrency-*,
|
||||
cppcoreguidelines-*,
|
||||
misc-*,
|
||||
-misc-no-recursion,
|
||||
modernize-*,
|
||||
-modernize-use-trailing-return-type,
|
||||
performance-*,
|
||||
portability-*,
|
||||
readibility-*
|
||||
CheckOptions:
|
||||
- key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor
|
||||
value: true
|
||||
- key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions
|
||||
value: true
|
||||
- key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctionsWhenCopyIsDeleted
|
||||
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
|
||||
value: true
|
||||
- key: cppcoreguidelines-avoid-magic-numbers.IgnoreAllFloatingPointValues
|
||||
value: true
|
||||
18
.github/workflows/analyzer.yml
vendored
18
.github/workflows/analyzer.yml
vendored
@@ -12,14 +12,14 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
|
||||
env:
|
||||
IWYU: 0.18
|
||||
LLVM: 14
|
||||
IWYU: "0.20"
|
||||
LLVM: "16"
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install llvm/clang
|
||||
# see: https://apt.llvm.org/
|
||||
run: |
|
||||
@@ -39,17 +39,23 @@ jobs:
|
||||
git clone https://github.com/include-what-you-use/include-what-you-use.git --branch $IWYU --depth 1
|
||||
mkdir include-what-you-use/build
|
||||
cd include-what-you-use/build
|
||||
cmake -DCMAKE_C_COMPILER=clang-$LLVM -DCMAKE_CXX_COMPILER=clang++-$LLVM -DCMAKE_INSTALL_PREFIX=./ ..
|
||||
cmake -DCMAKE_C_COMPILER=clang-$LLVM \
|
||||
-DCMAKE_CXX_COMPILER=clang++-$LLVM \
|
||||
-DCMAKE_INSTALL_PREFIX=./ \
|
||||
..
|
||||
make -j4
|
||||
bin/include-what-you-use --version
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
run: |
|
||||
export PATH=$PATH:${GITHUB_WORKSPACE}/build/include-what-you-use/build/bin
|
||||
cmake -DENTT_BUILD_TESTING=ON \
|
||||
cmake -DCMAKE_C_COMPILER=clang-$LLVM \
|
||||
-DCMAKE_CXX_COMPILER=clang++-$LLVM \
|
||||
-DENTT_BUILD_TESTING=ON \
|
||||
-DENTT_BUILD_BENCHMARK=ON \
|
||||
-DENTT_BUILD_EXAMPLE=ON \
|
||||
-DENTT_BUILD_LIB=ON \
|
||||
-DENTT_BUILD_SNAPSHOT=ON \
|
||||
-DCMAKE_CXX_INCLUDE_WHAT_YOU_USE="include-what-you-use;-Xiwyu;--mapping_file=${GITHUB_WORKSPACE}/entt.imp;-Xiwyu;--no_fwd_decls;-Xiwyu;--verbose=1" ..
|
||||
-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
|
||||
|
||||
20
.github/workflows/bazel-release-archive.yml
vendored
Normal file
20
.github/workflows/bazel-release-archive.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: Bazel Release
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
# A release archive is required for bzlmod
|
||||
# See: https://blog.bazel.build/2023/02/15/github-archive-checksum.html
|
||||
bazel-release-archive:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- 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:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
20
.github/workflows/bazel.yml
vendored
Normal file
20
.github/workflows/bazel.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: bazel
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-latest
|
||||
- windows-latest
|
||||
- macos-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: bazelisk test --config=ci ...
|
||||
working-directory: test
|
||||
env:
|
||||
USE_BAZEL_VERSION: 6.x
|
||||
137
.github/workflows/build.yml
vendored
137
.github/workflows/build.yml
vendored
@@ -9,38 +9,61 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, ubuntu-20.04]
|
||||
compiler:
|
||||
- pkg: g++-7
|
||||
exe: g++-7
|
||||
- pkg: g++-8
|
||||
exe: g++-8
|
||||
- pkg: g++-9
|
||||
exe: g++-9
|
||||
- pkg: g++-10
|
||||
exe: g++-10
|
||||
- pkg: clang-8
|
||||
exe: clang++-8
|
||||
- pkg: clang-9
|
||||
exe: clang++-9
|
||||
- pkg: clang-10
|
||||
exe: clang++-10
|
||||
- pkg: clang-11
|
||||
exe: clang++-11
|
||||
- pkg: clang-12
|
||||
exe: clang++-12
|
||||
- { pkg: g++, exe: 'g++', version: 7 }
|
||||
- { pkg: g++, exe: 'g++', version: 8 }
|
||||
- { pkg: g++, exe: 'g++', version: 9 }
|
||||
- { pkg: g++, exe: 'g++', version: 10 }
|
||||
- { pkg: g++, exe: 'g++', version: 11 }
|
||||
- { pkg: g++, exe: 'g++', version: 12 }
|
||||
- { pkg: clang, exe: 'clang++', version: 8 }
|
||||
- { pkg: clang, exe: 'clang++', version: 9 }
|
||||
- { pkg: clang, exe: 'clang++', version: 10 }
|
||||
- { pkg: clang, exe: 'clang++', version: 11 }
|
||||
- { pkg: clang, exe: 'clang++', version: 12 }
|
||||
- { pkg: clang, exe: 'clang++', version: 13 }
|
||||
- { pkg: clang, exe: 'clang++', version: 14 }
|
||||
exclude:
|
||||
- os: ubuntu-latest
|
||||
compiler: { pkg: g++, exe: 'g++', version: 7 }
|
||||
- os: ubuntu-latest
|
||||
compiler: { pkg: g++, exe: 'g++', version: 8 }
|
||||
- os: ubuntu-latest
|
||||
compiler: { pkg: g++, exe: 'g++', version: 9 }
|
||||
- os: ubuntu-latest
|
||||
compiler: { pkg: clang, exe: 'clang++', version: 8 }
|
||||
- os: ubuntu-latest
|
||||
compiler: { pkg: clang, exe: 'clang++', version: 9 }
|
||||
- os: ubuntu-latest
|
||||
compiler: { pkg: clang, exe: 'clang++', version: 10 }
|
||||
- os: ubuntu-latest
|
||||
compiler: { pkg: clang, exe: 'clang++', version: 11 }
|
||||
- os: ubuntu-20.04
|
||||
compiler: { pkg: g++, exe: 'g++', version: 10 }
|
||||
- os: ubuntu-20.04
|
||||
compiler: { pkg: g++, exe: 'g++', version: 11 }
|
||||
- os: ubuntu-20.04
|
||||
compiler: { pkg: g++, exe: 'g++', version: 12 }
|
||||
- os: ubuntu-20.04
|
||||
compiler: { pkg: clang, exe: 'clang++', version: 12 }
|
||||
- os: ubuntu-20.04
|
||||
compiler: { pkg: clang, exe: 'clang++', version: 13 }
|
||||
- os: ubuntu-20.04
|
||||
compiler: { pkg: clang, exe: 'clang++', version: 14 }
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install compiler
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y ${{ matrix.compiler.pkg }}
|
||||
sudo apt install -y ${{ matrix.compiler.pkg }}-${{ matrix.compiler.version }}
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
env:
|
||||
CXX: ${{ matrix.compiler.exe }}
|
||||
CXX: ${{ matrix.compiler.exe }}-${{ matrix.compiler.version }}
|
||||
run: |
|
||||
cmake -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON ..
|
||||
make -j4
|
||||
@@ -48,33 +71,7 @@ jobs:
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest --timeout 30 -C Debug -j4
|
||||
|
||||
linux-extra:
|
||||
timeout-minutes: 15
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
compiler: [g++, clang++]
|
||||
id_type: ["std::uint32_t", "std::uint64_t"]
|
||||
cxx_std: [cxx_std_17, cxx_std_20]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
env:
|
||||
CXX: ${{ matrix.compiler }}
|
||||
run: |
|
||||
cmake -DENTT_BUILD_TESTING=ON -DENTT_CXX_STD=${{ matrix.cxx_std }} -DENTT_ID_TYPE=${{ matrix.id_type }} ..
|
||||
make -j4
|
||||
- name: Run tests
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest --timeout 30 -C Debug -j4
|
||||
run: ctest -C Debug -j4
|
||||
|
||||
windows:
|
||||
timeout-minutes: 15
|
||||
@@ -93,7 +90,7 @@ jobs:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
run: |
|
||||
@@ -103,37 +100,14 @@ jobs:
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest --timeout 30 -C Debug -j4
|
||||
|
||||
windows-extra:
|
||||
timeout-minutes: 15
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
id_type: ["std::uint32_t", "std::uint64_t"]
|
||||
cxx_std: [cxx_std_17, cxx_std_20]
|
||||
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
run: |
|
||||
cmake -DENTT_BUILD_TESTING=ON -DENTT_CXX_STD=${{ matrix.cxx_std }} -DENTT_ID_TYPE=${{ matrix.id_type }} ..
|
||||
cmake --build . -j 4
|
||||
- name: Run tests
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest --timeout 30 -C Debug -j4
|
||||
run: ctest -C Debug -j4
|
||||
|
||||
macos:
|
||||
timeout-minutes: 15
|
||||
runs-on: macOS-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
run: |
|
||||
@@ -143,27 +117,28 @@ jobs:
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest --timeout 30 -C Debug -j4
|
||||
run: ctest -C Debug -j4
|
||||
|
||||
macos-extra:
|
||||
extra:
|
||||
timeout-minutes: 15
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows-latest, macOS-latest, ubuntu-latest]
|
||||
id_type: ["std::uint32_t", "std::uint64_t"]
|
||||
cxx_std: [cxx_std_17, cxx_std_20]
|
||||
|
||||
runs-on: macOS-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
run: |
|
||||
cmake -DENTT_BUILD_TESTING=ON -DENTT_CXX_STD=${{ matrix.cxx_std }} -DENTT_ID_TYPE=${{ matrix.id_type }} ..
|
||||
make -j4
|
||||
cmake --build . -j 4
|
||||
- name: Run tests
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest --timeout 30 -C Debug -j4
|
||||
run: ctest -C Debug -j4
|
||||
|
||||
8
.github/workflows/coverage.yml
vendored
8
.github/workflows/coverage.yml
vendored
@@ -9,11 +9,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
env:
|
||||
CXXFLAGS: "--coverage -fno-inline"
|
||||
CXXFLAGS: "--coverage -fno-elide-constructors -fno-inline -fno-default-inline"
|
||||
CXX: g++
|
||||
run: |
|
||||
cmake -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON ..
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest --timeout 30 -C Debug -j4
|
||||
run: ctest -C Debug -j4
|
||||
- name: Collect data
|
||||
working-directory: build
|
||||
run: |
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
lcov -c -d . -o coverage.info
|
||||
lcov -l coverage.info
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v2
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: build/coverage.info
|
||||
|
||||
2
.github/workflows/deploy.yml
vendored
2
.github/workflows/deploy.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
FORMULA: entt.rb
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Clone repository
|
||||
working-directory: build
|
||||
env:
|
||||
|
||||
6
.github/workflows/sanitizer.yml
vendored
6
.github/workflows/sanitizer.yml
vendored
@@ -16,7 +16,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- 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:
|
||||
@@ -28,4 +30,4 @@ jobs:
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest --timeout 30 -C Debug -j4
|
||||
run: ctest -C Debug -j4
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -11,3 +11,5 @@ cpp.hint
|
||||
|
||||
# Bazel
|
||||
/bazel-*
|
||||
/test/bazel-*
|
||||
/user.bazelrc
|
||||
|
||||
14
BUILD.bazel
14
BUILD.bazel
@@ -1,14 +1,6 @@
|
||||
_msvc_copts = ["/std:c++17"]
|
||||
_gcc_copts = ["-std=c++17"]
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
cc_library(
|
||||
alias(
|
||||
name = "entt",
|
||||
visibility = ["//visibility:public"],
|
||||
strip_include_prefix = "src",
|
||||
hdrs = glob(["src/**/*.h", "src/**/*.hpp"]),
|
||||
copts = select({
|
||||
"@bazel_tools//src/conditions:windows": _msvc_copts,
|
||||
"@bazel_tools//src/conditions:windows_msvc": _msvc_copts,
|
||||
"//conditions:default": _gcc_copts,
|
||||
}),
|
||||
actual = "//src:entt",
|
||||
)
|
||||
|
||||
@@ -1,21 +1,15 @@
|
||||
#
|
||||
# EnTT
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.12.4)
|
||||
cmake_minimum_required(VERSION 3.15.7)
|
||||
|
||||
#
|
||||
# Read project version
|
||||
#
|
||||
|
||||
set(ENTT_VERSION_REGEX "#define ENTT_VERSION_.*[ \t]+(.+)")
|
||||
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/src/entt/config/version.h" ENTT_VERSION REGEX ${ENTT_VERSION_REGEX})
|
||||
list(TRANSFORM ENTT_VERSION REPLACE ${ENTT_VERSION_REGEX} "\\1")
|
||||
string(JOIN "." ENTT_VERSION ${ENTT_VERSION})
|
||||
|
||||
#
|
||||
# Project configuration
|
||||
#
|
||||
|
||||
project(
|
||||
EnTT
|
||||
@@ -31,21 +25,18 @@ endif()
|
||||
|
||||
message(VERBOSE "*")
|
||||
message(VERBOSE "* ${PROJECT_NAME} v${PROJECT_VERSION} (${CMAKE_BUILD_TYPE})")
|
||||
message(VERBOSE "* Copyright (c) 2017-2022 Michele Caini <michele.caini@gmail.com>")
|
||||
message(VERBOSE "* Copyright (c) 2017-2023 Michele Caini <michele.caini@gmail.com>")
|
||||
message(VERBOSE "*")
|
||||
|
||||
#
|
||||
# CMake stuff
|
||||
#
|
||||
|
||||
list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
|
||||
|
||||
#
|
||||
# Compiler stuff
|
||||
#
|
||||
|
||||
option(ENTT_USE_LIBCPP "Use libc++ by adding -stdlib=libc++ flag if available." OFF)
|
||||
option(ENTT_USE_SANITIZER "Enable sanitizers by adding -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined flags if available." OFF)
|
||||
option(ENTT_USE_CLANG_TIDY "Enable static analysis with clang-tidy" OFF)
|
||||
|
||||
if(ENTT_USE_LIBCPP)
|
||||
if(NOT WIN32)
|
||||
@@ -65,7 +56,7 @@ if(ENTT_USE_LIBCPP)
|
||||
endif()
|
||||
|
||||
if(NOT ENTT_HAS_LIBCPP)
|
||||
message(VERBOSE "The option ENTT_USE_LIBCPP is set but libc++ is not available. The flag will not be added to the target.")
|
||||
message(VERBOSE "The option ENTT_USE_LIBCPP is set but libc++ is not available.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -76,13 +67,19 @@ if(ENTT_USE_SANITIZER)
|
||||
endif()
|
||||
|
||||
if(NOT ENTT_HAS_SANITIZER)
|
||||
message(VERBOSE "The option ENTT_USE_SANITIZER is set but sanitizer support is not available. The flags will not be added to the target.")
|
||||
message(VERBOSE "The option ENTT_USE_SANITIZER is set but sanitizer support is not available.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENTT_USE_CLANG_TIDY)
|
||||
find_program(ENTT_CLANG_TIDY_EXECUTABLE "clang-tidy")
|
||||
|
||||
if(NOT ENTT_CLANG_TIDY_EXECUTABLE)
|
||||
message(VERBOSE "The option ENTT_USE_CLANG_TIDY is set but clang-tidy executable is not available.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#
|
||||
# Add EnTT target
|
||||
#
|
||||
|
||||
option(ENTT_INCLUDE_HEADERS "Add all EnTT headers to the EnTT target." OFF)
|
||||
option(ENTT_INCLUDE_NATVIS "Add EnTT natvis files to the EnTT target." OFF)
|
||||
@@ -143,6 +140,7 @@ if(ENTT_INCLUDE_HEADERS)
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/fwd.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/group.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/handle.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/mixin.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/helper.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/observer.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/organizer.hpp>
|
||||
@@ -151,7 +149,6 @@ if(ENTT_INCLUDE_HEADERS)
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/snapshot.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/sparse_set.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/storage.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/storage_mixin.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/view.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/graph/adjacency_matrix.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/graph/dot.hpp>
|
||||
@@ -216,13 +213,15 @@ if(ENTT_HAS_SANITIZER)
|
||||
target_link_libraries(EnTT INTERFACE $<$<CONFIG:Debug>:-fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined>)
|
||||
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")
|
||||
endif()
|
||||
|
||||
if(ENTT_HAS_LIBCPP)
|
||||
target_compile_options(EnTT BEFORE INTERFACE -stdlib=libc++)
|
||||
endif()
|
||||
|
||||
#
|
||||
# Install pkg-config file
|
||||
#
|
||||
|
||||
include(JoinPaths)
|
||||
|
||||
@@ -241,9 +240,7 @@ install(
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
|
||||
)
|
||||
|
||||
#
|
||||
# Install EnTT
|
||||
#
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
@@ -285,13 +282,17 @@ 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)
|
||||
|
||||
#
|
||||
# Tests
|
||||
#
|
||||
|
||||
option(ENTT_BUILD_TESTING "Enable building tests." OFF)
|
||||
|
||||
@@ -310,9 +311,15 @@ if(ENTT_BUILD_TESTING)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
#
|
||||
# Tools
|
||||
|
||||
option(ENTT_BUILD_TOOLS "Enable building tools." OFF)
|
||||
|
||||
if(ENTT_BUILD_TOOLS)
|
||||
add_subdirectory(tools)
|
||||
endif()
|
||||
|
||||
# Documentation
|
||||
#
|
||||
|
||||
option(ENTT_BUILD_DOCS "Enable building with documentation." OFF)
|
||||
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2022 Michele Caini, author of EnTT
|
||||
Copyright (c) 2017-2023 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
|
||||
|
||||
8
MODULE.bazel
Normal file
8
MODULE.bazel
Normal file
@@ -0,0 +1,8 @@
|
||||
module(
|
||||
name = "entt",
|
||||
version = "3.12.2",
|
||||
compatibility_level = 3012,
|
||||
)
|
||||
|
||||
bazel_dep(name = "rules_cc", version = "0.0.8")
|
||||
bazel_dep(name = "bazel_skylib", version = "1.4.2")
|
||||
69
README.md
69
README.md
@@ -6,10 +6,11 @@
|
||||
[](https://github.com/skypjack/entt/actions)
|
||||
[](https://codecov.io/gh/skypjack/entt)
|
||||
[](https://godbolt.org/z/zxW73f)
|
||||
[](http://entt.docsforge.com/)
|
||||
[](https://skypjack.github.io/entt/)
|
||||
[](https://vcpkg.link/ports/entt)
|
||||
[](https://conan.io/center/recipes/entt)
|
||||
[](https://gitter.im/skypjack/entt)
|
||||
[](https://discord.gg/5BjPWBd)
|
||||
[](https://www.paypal.me/skypjack)
|
||||
|
||||
> `EnTT` has been a dream so far, we haven't found a single bug to date and it's
|
||||
> super easy to work with
|
||||
@@ -38,7 +39,8 @@ Don't forget to check the
|
||||
there.
|
||||
|
||||
Do you want to support `EnTT`? Consider becoming a
|
||||
[**sponsor**](https://github.com/users/skypjack/sponsorship).
|
||||
[**sponsor**](https://github.com/users/skypjack/sponsorship) or making a
|
||||
donation via [**PayPal**](https://www.paypal.me/skypjack).<br/>
|
||||
Many thanks to [these people](https://skypjack.github.io/sponsorship/) and
|
||||
**special** thanks to:
|
||||
|
||||
@@ -50,7 +52,7 @@ Many thanks to [these people](https://skypjack.github.io/sponsorship/) and
|
||||
* [Introduction](#introduction)
|
||||
* [Code Example](#code-example)
|
||||
* [Motivation](#motivation)
|
||||
* [Performance](#performance)
|
||||
* [Benchmark](#benchmark)
|
||||
* [Integration](#integration)
|
||||
* [Requirements](#requirements)
|
||||
* [CMake](#cmake)
|
||||
@@ -174,37 +176,28 @@ Nowadays, `EnTT` is finally what I was looking for: still faster than its
|
||||
_competitors_, lower memory usage in the average case, a really good API and an
|
||||
amazing set of features. And even more, of course.
|
||||
|
||||
## Performance
|
||||
## Benchmark
|
||||
|
||||
The proposed entity-component system is incredibly fast to iterate entities and
|
||||
components, this is a fact. Some compilers make a lot of optimizations because
|
||||
of how `EnTT` works, some others aren't that good. In general, if we consider
|
||||
real world cases, `EnTT` is somewhere between a bit and much faster than many of
|
||||
the other solutions around, although I couldn't check them all for obvious
|
||||
reasons.
|
||||
For what it's worth, you'll **never** see me trying to make other projects look
|
||||
bad or offer dubious comparisons just to make this library seem cooler.<br/>
|
||||
I leave this activity to others, if they enjoy it (and it seems that some people
|
||||
actually like it). I prefer to make better use of my time.
|
||||
|
||||
If you are interested, you can compile the `benchmark` test in release mode (to
|
||||
enable compiler optimizations, otherwise it would make little sense) by setting
|
||||
the `ENTT_BUILD_BENCHMARK` option of `CMake` to `ON`, then evaluate yourself
|
||||
whether you're satisfied with the results or not.
|
||||
|
||||
Honestly I got tired of updating the README file whenever there is an
|
||||
improvement.<br/>
|
||||
There are already a lot of projects out there that use `EnTT` as a basis for
|
||||
There are also a lot of projects out there that use `EnTT` as a basis for
|
||||
comparison (this should already tell you a lot). Many of these benchmarks are
|
||||
completely wrong, many others are simply incomplete, good at omitting some
|
||||
information and using the wrong function to compare a given feature. Certainly
|
||||
there are also good ones but they age quickly if nobody updates them, especially
|
||||
when the library they are dealing with is actively developed.
|
||||
|
||||
The choice to use `EnTT` should be based on its carefully designed API, its
|
||||
set of features and the general performance, **not** because some single
|
||||
benchmark shows it to be the fastest tool available.
|
||||
|
||||
In the future I'll likely try to get even better performance while still adding
|
||||
new features, mainly for fun.<br/>
|
||||
If you want to contribute and/or have suggestions, feel free to make a PR or
|
||||
open an issue to discuss your idea.
|
||||
when the library they are dealing with is actively developed.<br/>
|
||||
Out of all of them, [this](https://github.com/abeimler/ecs_benchmark) seems like
|
||||
the most up-to-date project and also covers a certain number of libraries. I
|
||||
can't say exactly whether `EnTT` is used correctly or not. However, even if used
|
||||
poorly, it should still give the reader an idea of where it's going to operate.
|
||||
|
||||
# Integration
|
||||
|
||||
@@ -326,6 +319,18 @@ If you spot errors or have suggestions, any contribution is welcome!
|
||||
[documentation](https://build2.org/build2-toolchain/doc/build2-toolchain-intro.xhtml#guide-repositories)
|
||||
for more details.
|
||||
|
||||
* [`bzlmod`](https://bazel.build/external/overview#bzlmod), Bazel's external
|
||||
dependency management system.<br/>
|
||||
To use the [`entt`](https://registry.bazel.build/modules/entt) module in a
|
||||
`bazel` project, add the following to your `MODULE.bazel` file:
|
||||
|
||||
```starlark
|
||||
bazel_dep(name = "entt", version = "3.12.2")
|
||||
```
|
||||
|
||||
EnTT will now be available as `@entt` (short for `@entt//:entt`) to be used
|
||||
in your `cc_*` rule `deps`.
|
||||
|
||||
Consider this list a work in progress and help me to make it longer if you like.
|
||||
|
||||
## pkg-config
|
||||
@@ -343,8 +348,8 @@ The documentation is based on [doxygen](http://www.doxygen.nl/). To build it:
|
||||
$ cmake .. -DENTT_BUILD_DOCS=ON
|
||||
$ make
|
||||
|
||||
The API reference will be created in HTML format within the directory
|
||||
`build/docs/html`. To navigate it with your favorite browser:
|
||||
The API reference is created in HTML format in the `build/docs/html` directory.
|
||||
To navigate it with your favorite browser:
|
||||
|
||||
$ cd build
|
||||
$ your_favorite_browser docs/html/index.html
|
||||
@@ -353,10 +358,7 @@ The API reference will be created in HTML format within the directory
|
||||
@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. If you are looking for
|
||||
something more pleasing to the eye, consider reading the nice-looking version
|
||||
available on [docsforge](https://entt.docsforge.com/): same documentation, much
|
||||
more pleasant to read.<br/>
|
||||
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.
|
||||
<!--
|
||||
@@ -366,9 +368,8 @@ to the project where users can find all related documentation pages.
|
||||
# Tests
|
||||
|
||||
To compile and run the tests, `EnTT` requires *googletest*.<br/>
|
||||
`cmake` will download and compile the library before compiling anything else.
|
||||
In order to build the tests, set the `CMake` option `ENTT_BUILD_TESTING` to
|
||||
`ON`.
|
||||
`cmake` downloads and compiles the library before compiling anything else. In
|
||||
order to build the tests, set the `CMake` option `ENTT_BUILD_TESTING` to `ON`.
|
||||
|
||||
To build the most basic set of tests:
|
||||
|
||||
@@ -420,7 +421,7 @@ know who has participated so far.
|
||||
|
||||
# License
|
||||
|
||||
Code and documentation Copyright (c) 2017-2022 Michele Caini.<br/>
|
||||
Code and documentation Copyright (c) 2017-2023 Michele Caini.<br/>
|
||||
Colorful logo Copyright (c) 2018-2021 Richard Caseres.
|
||||
|
||||
Code released under
|
||||
|
||||
38
TODO
38
TODO
@@ -1,27 +1,31 @@
|
||||
* debugging tools (#60): the issue online already contains interesting tips on this, look at it
|
||||
* work stealing job system (see #100) + mt scheduler based on const awareness for types
|
||||
|
||||
EXAMPLES
|
||||
* filter on runtime values/variables (not only types)
|
||||
* support to polymorphic types (see #859)
|
||||
|
||||
DOC:
|
||||
* storage<void>
|
||||
* custom storage/view
|
||||
* examples (and credits) from @alanjfs :)
|
||||
* update entity doc when the storage based model is in place
|
||||
* in-place O(1) release/destroy for non-orphaned entities, out-of-sync model
|
||||
* view: single vs multi type views are no longer a thing actually
|
||||
* bump entities, reserved bits on identifiers
|
||||
|
||||
TODO (high prio):
|
||||
* remove the static storage from the const assure in the registry
|
||||
|
||||
WIP:
|
||||
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
|
||||
* add storage getter for filters to views and groups
|
||||
* exploit the tombstone mechanism to allow enabling/disabling entities (see bump, compact and clear for further details)
|
||||
* basic_storage::bind for cross-registry setups (see and remove todo from entity_copy.cpp)
|
||||
* process scheduler: reviews, use free lists internally
|
||||
* dedicated entity storage, in-place O(1) release/destroy for non-orphaned entities, out-of-sync model
|
||||
* entity-only and exclude-only views (both solved with entity storage and storage<void>)
|
||||
* custom allocators all over (registry, ...)
|
||||
* add test for maximum number of entities reached
|
||||
* deprecate non-owning groups in favor of owning views and view packs, introduce lazy owning views
|
||||
* bring nested groups back in place (see bd34e7f)
|
||||
* work stealing job system (see #100) + mt scheduler based on const awareness for types
|
||||
* view: reduce inst due to/improve perf with index-based approach in dispatch_get/pick_and_each/each (single type too, define storage ::at and ::at_as_tuple)
|
||||
* view: update natvis as needed after the last rework, merge pools/filter in the same array, drop check (?) and turn view into a position
|
||||
* view: type-only view_iterator (dyn get/excl sizes), type-only basic_common_view (dyn get/excl sizes with pointer to array from derived)
|
||||
* combine version-mask-vs-version-bits tricks with reserved bits to allow things like enabling/disabling
|
||||
* basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) uses moved-from packed in assert (same for storage)
|
||||
* maybe drop begin(v)/end(v) from sparse set and only use scoped begin/end at call sites
|
||||
* view unchecked_refresh loop is never executed if Get is 1u
|
||||
* review all // NOLINT + linter workflow + review clang-tidy file
|
||||
* self contained entity traits to avoid explicit specializations (ie enum constants)
|
||||
* review assure conflicts check, hash doesn't fit the purpose maybe
|
||||
* allow to zero-sized versions (with non-regression tests)
|
||||
* auto type info data from types if present
|
||||
|
||||
1
WORKSPACE.bazel
Normal file
1
WORKSPACE.bazel
Normal file
@@ -0,0 +1 @@
|
||||
# SEE MODULE.bazel
|
||||
0
bazel/BUILD.bazel
Normal file
0
bazel/BUILD.bazel
Normal file
13
bazel/copts.bzl
Normal file
13
bazel/copts.bzl
Normal file
@@ -0,0 +1,13 @@
|
||||
load("@bazel_skylib//lib:selects.bzl", "selects")
|
||||
|
||||
COPTS = selects.with_or({
|
||||
("//conditions:default", "@rules_cc//cc/compiler:clang", "@rules_cc//cc/compiler:gcc", "@rules_cc//cc/compiler:mingw-gcc"): [
|
||||
"-std=c++17",
|
||||
"-w",
|
||||
],
|
||||
("@rules_cc//cc/compiler:msvc-cl", "@rules_cc//cc/compiler:clang-cl"): [
|
||||
"/std:c++17",
|
||||
"/permissive-",
|
||||
"/w",
|
||||
],
|
||||
})
|
||||
@@ -1,9 +1,23 @@
|
||||
#
|
||||
# Doxygen configuration (documentation)
|
||||
#
|
||||
|
||||
set(DOXY_DEPS_DIRECTORY ${EnTT_SOURCE_DIR}/deps)
|
||||
include(FetchContent)
|
||||
|
||||
FetchContent_Declare(
|
||||
doxygen-awesome-css
|
||||
GIT_REPOSITORY https://github.com/jothepro/doxygen-awesome-css
|
||||
GIT_TAG main
|
||||
GIT_SHALLOW 1
|
||||
)
|
||||
|
||||
FetchContent_GetProperties(doxygen-awesome-css)
|
||||
|
||||
if(NOT doxygen-awesome-css_POPULATED)
|
||||
FetchContent_Populate(doxygen-awesome-css)
|
||||
set(doxygen-awesome-css_INCLUDE_DIR ${doxygen-awesome-css_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
set(DOXY_SOURCE_DIRECTORY ${EnTT_SOURCE_DIR}/src)
|
||||
set(DOXY_CSS_DIRECTORY ${doxygen-awesome-css_INCLUDE_DIR})
|
||||
set(DOXY_DOCS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set(DOXY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
|
||||
269
docs/doxy.in
269
docs/doxy.in
@@ -1,4 +1,4 @@
|
||||
# Doxyfile 1.9.4
|
||||
# Doxyfile 1.9.7
|
||||
|
||||
# This file describes the settings to be used by the documentation system
|
||||
# doxygen (www.doxygen.org) for a project.
|
||||
@@ -19,7 +19,8 @@
|
||||
# configuration file:
|
||||
# doxygen -x [configFile]
|
||||
# Use doxygen to compare the used configuration file with the template
|
||||
# configuration file without replacing the environment variables:
|
||||
# configuration file without replacing the environment variables or CMake type
|
||||
# replacement variables:
|
||||
# doxygen -x_noenv [configFile]
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -85,7 +86,7 @@ CREATE_SUBDIRS = NO
|
||||
# level increment doubles the number of directories, resulting in 4096
|
||||
# directories at level 8 which is the default and also the maximum value. The
|
||||
# sub-directories are organized in 2 levels, the first level always has a fixed
|
||||
# numer of 16 directories.
|
||||
# number of 16 directories.
|
||||
# Minimum value: 0, maximum value: 8, default value: 8.
|
||||
# This tag requires that the tag CREATE_SUBDIRS is set to YES.
|
||||
|
||||
@@ -352,6 +353,17 @@ MARKDOWN_SUPPORT = YES
|
||||
|
||||
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..
|
||||
# The default value is: DOXYGEN.
|
||||
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
|
||||
|
||||
MARKDOWN_ID_STYLE = DOXYGEN
|
||||
|
||||
# When enabled doxygen tries to link words that correspond to documented
|
||||
# classes, or namespaces to their corresponding documentation. Such a link can
|
||||
# be prevented in individual cases by putting a % sign in front of the word or
|
||||
@@ -476,6 +488,14 @@ LOOKUP_CACHE_SIZE = 0
|
||||
|
||||
NUM_PROC_THREADS = 1
|
||||
|
||||
# If the TIMESTAMP tag is set different from NO then each generated page will
|
||||
# contain the date or date and time when the page was generated. Setting this to
|
||||
# NO can help when comparing the output of multiple runs.
|
||||
# Possible values are: YES, NO, DATETIME and DATE.
|
||||
# The default value is: NO.
|
||||
|
||||
TIMESTAMP = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Build related configuration options
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -557,7 +577,8 @@ HIDE_UNDOC_MEMBERS = NO
|
||||
# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
|
||||
# undocumented classes that are normally visible in the class hierarchy. If set
|
||||
# to NO, these classes will be included in the various overviews. This option
|
||||
# has no effect if EXTRACT_ALL is enabled.
|
||||
# will also hide undocumented C++ concepts if enabled. This option has no effect
|
||||
# if EXTRACT_ALL is enabled.
|
||||
# The default value is: NO.
|
||||
|
||||
HIDE_UNDOC_CLASSES = NO
|
||||
@@ -595,7 +616,8 @@ INTERNAL_DOCS = NO
|
||||
# Windows (including Cygwin) and MacOS, users should typically set this option
|
||||
# to NO, whereas on Linux or other Unix flavors it should typically be set to
|
||||
# YES.
|
||||
# The default value is: system dependent.
|
||||
# Possible values are: SYSTEM, NO and YES.
|
||||
# The default value is: SYSTEM.
|
||||
|
||||
CASE_SENSE_NAMES = YES
|
||||
|
||||
@@ -847,11 +869,26 @@ WARN_IF_INCOMPLETE_DOC = YES
|
||||
|
||||
WARN_NO_PARAMDOC = YES
|
||||
|
||||
# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about
|
||||
# undocumented enumeration values. If set to NO, doxygen will accept
|
||||
# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag
|
||||
# will automatically be disabled.
|
||||
# The default value is: NO.
|
||||
|
||||
WARN_IF_UNDOC_ENUM_VAL = NO
|
||||
|
||||
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
|
||||
# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
|
||||
# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
|
||||
# at the end of the doxygen process doxygen will return with a non-zero status.
|
||||
# Possible values are: NO, YES and FAIL_ON_WARNINGS.
|
||||
# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves
|
||||
# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not
|
||||
# write the warning messages in between other messages but write them at the end
|
||||
# of a run, in case a WARN_LOGFILE is defined the warning messages will be
|
||||
# besides being in the defined file also be shown at the end of a run, unless
|
||||
# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case
|
||||
# the behavior will remain as with the setting FAIL_ON_WARNINGS.
|
||||
# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT.
|
||||
# The default value is: NO.
|
||||
|
||||
WARN_AS_ERROR = NO
|
||||
@@ -905,10 +942,21 @@ INPUT = @DOXY_SOURCE_DIRECTORY@ \
|
||||
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
|
||||
# documentation (see:
|
||||
# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
|
||||
# See also: INPUT_FILE_ENCODING
|
||||
# The default value is: UTF-8.
|
||||
|
||||
INPUT_ENCODING = UTF-8
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify
|
||||
# character encoding on a per file pattern basis. Doxygen will compare the file
|
||||
# name with each pattern and apply the encoding instead of the default
|
||||
# INPUT_ENCODING) if there is a match. The character encodings are a list of the
|
||||
# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding
|
||||
# "INPUT_ENCODING" for further information on supported encodings.
|
||||
|
||||
INPUT_FILE_ENCODING =
|
||||
|
||||
# If the value of the INPUT tag contains directories, you can use the
|
||||
# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
|
||||
# *.h) to filter out the source-files in the directories.
|
||||
@@ -945,7 +993,7 @@ RECURSIVE = YES
|
||||
# Note that relative paths are relative to the directory from which doxygen is
|
||||
# run.
|
||||
|
||||
EXCLUDE = @DOXY_DEPS_DIRECTORY@
|
||||
EXCLUDE =
|
||||
|
||||
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
|
||||
# directories that are symbolic links (a Unix file system feature) are excluded
|
||||
@@ -968,9 +1016,6 @@ EXCLUDE_PATTERNS =
|
||||
# output. The symbol name can be a fully qualified name, a word, or if the
|
||||
# wildcard * is used, a substring. Examples: ANamespace, AClass,
|
||||
# ANamespace::AClass, ANamespace::*Test
|
||||
#
|
||||
# Note that the wildcards are matched against the file with absolute path, so to
|
||||
# exclude all test directories use the pattern */test/*
|
||||
|
||||
EXCLUDE_SYMBOLS =
|
||||
|
||||
@@ -1015,6 +1060,11 @@ IMAGE_PATH =
|
||||
# code is scanned, but not when the output code is generated. If lines are added
|
||||
# or removed, the anchors will not be placed correctly.
|
||||
#
|
||||
# Note that doxygen will use the data processed and written to standard output
|
||||
# for further processing, therefore nothing else, like debug statements or used
|
||||
# commands (so in case of a Windows batch file always use @echo OFF), should be
|
||||
# written to standard output.
|
||||
#
|
||||
# Note that for custom extensions or not directly supported extensions you also
|
||||
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
|
||||
# properly processed by doxygen.
|
||||
@@ -1056,6 +1106,15 @@ FILTER_SOURCE_PATTERNS =
|
||||
|
||||
USE_MDFILE_AS_MAINPAGE = @PROJECT_SOURCE_DIR@/README.md
|
||||
|
||||
# The Fortran standard specifies that for fixed formatted Fortran code all
|
||||
# characters from position 72 are to be considered as comment. A common
|
||||
# extension is to allow longer lines before the automatic comment starts. The
|
||||
# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can
|
||||
# be processed before the automatic comment starts.
|
||||
# Minimum value: 7, maximum value: 10000, default value: 72.
|
||||
|
||||
FORTRAN_COMMENT_AFTER = 72
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to source browsing
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -1193,10 +1252,11 @@ CLANG_DATABASE_PATH =
|
||||
|
||||
ALPHABETICAL_INDEX = YES
|
||||
|
||||
# In case all classes in a project start with a common prefix, all classes will
|
||||
# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
|
||||
# can be used to specify a prefix (or a list of prefixes) that should be ignored
|
||||
# while generating the index headers.
|
||||
# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes)
|
||||
# that should be ignored while generating the index headers. The IGNORE_PREFIX
|
||||
# tag works for classes, function and member names. The entity will be placed in
|
||||
# the alphabetical list under the first letter of the entity name that remains
|
||||
# after removing the prefix.
|
||||
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
|
||||
|
||||
IGNORE_PREFIX =
|
||||
@@ -1265,7 +1325,7 @@ HTML_FOOTER =
|
||||
# obsolete.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_STYLESHEET =
|
||||
HTML_STYLESHEET = @DOXY_CSS_DIRECTORY@/doxygen-awesome.css
|
||||
|
||||
# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
|
||||
# cascading style sheets that are included after the standard style sheets
|
||||
@@ -1275,7 +1335,12 @@ HTML_STYLESHEET =
|
||||
# Doxygen will copy the style sheet files to the output directory.
|
||||
# Note: The order of the extra style sheet files is of importance (e.g. the last
|
||||
# style sheet in the list overrules the setting of the previous ones in the
|
||||
# list). For an example see the documentation.
|
||||
# list).
|
||||
# Note: Since the styling of scrollbars can currently not be overruled in
|
||||
# Webkit/Chromium, the styling will be left out of the default doxygen.css if
|
||||
# one or more extra stylesheets have been specified. So if scrollbar
|
||||
# customization is desired it has to be added explicitly. For an example see the
|
||||
# documentation.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_EXTRA_STYLESHEET =
|
||||
@@ -1290,6 +1355,19 @@ HTML_EXTRA_STYLESHEET =
|
||||
|
||||
HTML_EXTRA_FILES =
|
||||
|
||||
# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output
|
||||
# should be rendered with a dark or light theme.
|
||||
# Possible values are: LIGHT always generate light mode output, DARK always
|
||||
# generate dark mode output, AUTO_LIGHT automatically set the mode according to
|
||||
# the user preference, use light mode if no preference is set (the default),
|
||||
# AUTO_DARK automatically set the mode according to the user preference, use
|
||||
# dark mode if no preference is set and TOGGLE allow to user to switch between
|
||||
# light and dark mode via a button.
|
||||
# The default value is: AUTO_LIGHT.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_COLORSTYLE = LIGHT
|
||||
|
||||
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
|
||||
# will adjust the colors in the style sheet and background images according to
|
||||
# this color. Hue is specified as an angle on a color-wheel, see
|
||||
@@ -1320,15 +1398,6 @@ HTML_COLORSTYLE_SAT = 100
|
||||
|
||||
HTML_COLORSTYLE_GAMMA = 80
|
||||
|
||||
# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
|
||||
# page will contain the date and time when the page was generated. Setting this
|
||||
# to YES can help to show when doxygen was last run and thus if the
|
||||
# documentation is up to date.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_TIMESTAMP = NO
|
||||
|
||||
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
|
||||
# documentation will contain a main index with vertical navigation menus that
|
||||
# are dynamically created via JavaScript. If disabled, the navigation index will
|
||||
@@ -1478,6 +1547,16 @@ BINARY_TOC = NO
|
||||
|
||||
TOC_EXPAND = NO
|
||||
|
||||
# The SITEMAP_URL tag is used to specify the full URL of the place where the
|
||||
# generated documentation will be placed on the server by the user during the
|
||||
# deployment of the documentation. The generated sitemap is called sitemap.xml
|
||||
# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL
|
||||
# is specified no sitemap is generated. For information about the sitemap
|
||||
# protocol see https://www.sitemaps.org
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
SITEMAP_URL =
|
||||
|
||||
# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
|
||||
# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
|
||||
# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
|
||||
@@ -1653,17 +1732,6 @@ HTML_FORMULA_FORMAT = png
|
||||
|
||||
FORMULA_FONTSIZE = 10
|
||||
|
||||
# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
|
||||
# generated for formulas are transparent PNGs. Transparent PNGs are not
|
||||
# supported properly for IE 6.0, but are supported on all modern browsers.
|
||||
#
|
||||
# Note that when changing this option you need to delete any form_*.png files in
|
||||
# the HTML output directory before the changes have effect.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
FORMULA_TRANSPARENT = YES
|
||||
|
||||
# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
|
||||
# to create new LaTeX commands to be used in formulas as building blocks. See
|
||||
# the section "Including formulas" for details.
|
||||
@@ -1977,9 +2045,16 @@ PDF_HYPERLINKS = YES
|
||||
|
||||
USE_PDFLATEX = YES
|
||||
|
||||
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
|
||||
# command to the generated LaTeX files. This will instruct LaTeX to keep running
|
||||
# if errors occur, instead of asking the user for help.
|
||||
# The LATEX_BATCHMODE tag ignals 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
|
||||
# keyboard input (\read on a not open input stream) cause the job to abort,
|
||||
# NON_STOP In nonstop mode the diagnostic message will appear on the terminal,
|
||||
# but there is no possibility of user interaction just like in batch mode,
|
||||
# SCROLL In scroll mode, TeX will stop only for missing files to input or if
|
||||
# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at
|
||||
# each error, asking for user intervention.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
@@ -2000,14 +2075,6 @@ LATEX_HIDE_INDICES = NO
|
||||
|
||||
LATEX_BIB_STYLE = plain
|
||||
|
||||
# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
|
||||
# page will contain the date and time when the page was generated. Setting this
|
||||
# to NO can help when comparing the output of multiple runs.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_TIMESTAMP = NO
|
||||
|
||||
# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
|
||||
# path from which the emoji images will be read. If a relative path is entered,
|
||||
# it will be relative to the LATEX_OUTPUT directory. If left blank the
|
||||
@@ -2173,7 +2240,7 @@ DOCBOOK_OUTPUT = docbook
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
|
||||
# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
|
||||
# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures
|
||||
# the structure of the code including all documentation. Note that this feature
|
||||
# is still experimental and incomplete at the moment.
|
||||
# The default value is: NO.
|
||||
@@ -2344,16 +2411,9 @@ EXTERNAL_GROUPS = YES
|
||||
EXTERNAL_PAGES = YES
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the dot tool
|
||||
# Configuration options related to diagram generator tools
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# You can include diagrams made with dia in doxygen documentation. Doxygen will
|
||||
# then run dia to produce the diagram and insert it in the documentation. The
|
||||
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
|
||||
# If left empty dia is assumed to be found in the default search path.
|
||||
|
||||
DIA_PATH =
|
||||
|
||||
# If set to YES the inheritance and collaboration graphs will hide inheritance
|
||||
# and usage relations if the target is undocumented or is not a class.
|
||||
# The default value is: YES.
|
||||
@@ -2362,7 +2422,7 @@ HIDE_UNDOC_RELATIONS = YES
|
||||
|
||||
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
|
||||
# available from the path. This tool is part of Graphviz (see:
|
||||
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
|
||||
# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
|
||||
# Bell Labs. The other options in this section have no effect if this option is
|
||||
# set to NO
|
||||
# The default value is: NO.
|
||||
@@ -2379,37 +2439,51 @@ HAVE_DOT = YES
|
||||
|
||||
DOT_NUM_THREADS = 0
|
||||
|
||||
# When you want a differently looking font in the dot files that doxygen
|
||||
# generates you can specify the font name using DOT_FONTNAME. You need to make
|
||||
# sure dot is able to find the font, which can be done by putting it in a
|
||||
# standard location or by setting the DOTFONTPATH environment variable or by
|
||||
# setting DOT_FONTPATH to the directory containing the font.
|
||||
# The default value is: Helvetica.
|
||||
# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of
|
||||
# subgraphs. When you want a differently looking font in the dot files that
|
||||
# doxygen generates you can specify fontname, fontcolor and fontsize attributes.
|
||||
# For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node,
|
||||
# Edge and Graph Attributes specification</a> You need to make sure dot is able
|
||||
# to find the font, which can be done by putting it in a standard location or by
|
||||
# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
|
||||
# directory containing the font. Default graphviz fontsize is 14.
|
||||
# The default value is: fontname=Helvetica,fontsize=10.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_FONTNAME = Helvetica
|
||||
DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10"
|
||||
|
||||
# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
|
||||
# dot graphs.
|
||||
# Minimum value: 4, maximum value: 24, default value: 10.
|
||||
# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can
|
||||
# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a
|
||||
# href=https://graphviz.org/doc/info/arrows.html>Complete documentation about
|
||||
# arrows shapes.</a>
|
||||
# The default value is: labelfontname=Helvetica,labelfontsize=10.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_FONTSIZE = 10
|
||||
DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10"
|
||||
|
||||
# By default doxygen will tell dot to use the default font as specified with
|
||||
# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
|
||||
# the path where dot can find it using this tag.
|
||||
# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes
|
||||
# around nodes set 'shape=plain' or 'shape=plaintext' <a
|
||||
# href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a>
|
||||
# The default value is: shape=box,height=0.2,width=0.4.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4"
|
||||
|
||||
# You can set the path where dot can find font specified with fontname in
|
||||
# DOT_COMMON_ATTR and others dot attributes.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_FONTPATH =
|
||||
|
||||
# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a
|
||||
# graph for each documented class showing the direct and indirect inheritance
|
||||
# relations. In case HAVE_DOT is set as well dot will be used to draw the graph,
|
||||
# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set
|
||||
# to TEXT the direct and indirect inheritance relations will be shown as texts /
|
||||
# links.
|
||||
# Possible values are: NO, YES, TEXT and GRAPH.
|
||||
# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will
|
||||
# generate a graph for each documented class showing the direct and indirect
|
||||
# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and
|
||||
# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case
|
||||
# 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.
|
||||
# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN.
|
||||
# The default value is: YES.
|
||||
|
||||
CLASS_GRAPH = YES
|
||||
@@ -2550,7 +2624,7 @@ DIR_GRAPH_MAX_DEPTH = 1
|
||||
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
|
||||
# generated by dot. For an explanation of the image formats see the section
|
||||
# output formats in the documentation of the dot tool (Graphviz (see:
|
||||
# http://www.graphviz.org/)).
|
||||
# https://www.graphviz.org/)).
|
||||
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
|
||||
# to make the SVG files visible in IE 9+ (other browsers do not have this
|
||||
# requirement).
|
||||
@@ -2587,11 +2661,12 @@ DOT_PATH =
|
||||
|
||||
DOTFILE_DIRS =
|
||||
|
||||
# The MSCFILE_DIRS tag can be used to specify one or more directories that
|
||||
# contain msc files that are included in the documentation (see the \mscfile
|
||||
# command).
|
||||
# You can include diagrams made with dia in doxygen documentation. Doxygen will
|
||||
# then run dia to produce the diagram and insert it in the documentation. The
|
||||
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
|
||||
# If left empty dia is assumed to be found in the default search path.
|
||||
|
||||
MSCFILE_DIRS =
|
||||
DIA_PATH =
|
||||
|
||||
# The DIAFILE_DIRS tag can be used to specify one or more directories that
|
||||
# contain dia files that are included in the documentation (see the \diafile
|
||||
@@ -2641,18 +2716,6 @@ DOT_GRAPH_MAX_NODES = 50
|
||||
|
||||
MAX_DOT_GRAPH_DEPTH = 0
|
||||
|
||||
# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
|
||||
# background. This is disabled by default, because dot on Windows does not seem
|
||||
# to support this out of the box.
|
||||
#
|
||||
# Warning: Depending on the platform used, enabling this option may lead to
|
||||
# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
|
||||
# read).
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_TRANSPARENT = NO
|
||||
|
||||
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
|
||||
# files in one run (i.e. multiple -o and -T options on the command line). This
|
||||
# makes dot run faster, but since only newer versions of dot (>1.8.10) support
|
||||
@@ -2680,3 +2743,19 @@ GENERATE_LEGEND = YES
|
||||
# The default value is: YES.
|
||||
|
||||
DOT_CLEANUP = YES
|
||||
|
||||
# You can define message sequence charts within doxygen comments using the \msc
|
||||
# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will
|
||||
# use a built-in version of mscgen tool to produce the charts. Alternatively,
|
||||
# the MSCGEN_TOOL tag can also specify the name an external tool. For instance,
|
||||
# specifying prog as the value, doxygen will call the tool as prog -T
|
||||
# <outfile_format> -o <outputfile> <inputfile>. The external tool should support
|
||||
# output file formats "png", "eps", "svg", and "ismap".
|
||||
|
||||
MSCGEN_TOOL =
|
||||
|
||||
# The MSCFILE_DIRS tag can be used to specify one or more directories that
|
||||
# contain msc files that are included in the documentation (see the \mscfile
|
||||
# command).
|
||||
|
||||
MSCFILE_DIRS =
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
* [ENTT_DISABLE_ASSERT](#entt_disable_assert)
|
||||
* [ENTT_NO_ETO](#entt_no_eto)
|
||||
* [ENTT_STANDARD_CPP](#entt_standard_cpp)
|
||||
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
* [Containers](#containers)
|
||||
* [Dense map](#dense-map)
|
||||
* [Dense set](#dense-set)
|
||||
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
@@ -21,7 +20,7 @@ difficult to do better (although it's very easy to do worse, as many examples
|
||||
available online demonstrate).<br/>
|
||||
`EnTT` doesn't try in any way to replace what is offered by the standard. Quite
|
||||
the opposite, given the widespread use that is made of standard containers.<br/>
|
||||
However, the library also tries to fill a gap in features and functionality by
|
||||
However, the library also tries to fill a gap in features and functionalities by
|
||||
making available some containers initially developed for internal use.
|
||||
|
||||
This section of the library is likely to grow larger over time. However, for the
|
||||
@@ -40,7 +39,7 @@ The implementation is based on _sparse sets_ and each bucket is identified by an
|
||||
implicit list within the packed array itself.
|
||||
|
||||
The interface is very close to its counterpart in the standard library, that is,
|
||||
`std::unordered_map`.<br/>
|
||||
the `std::unordered_map` class.<br/>
|
||||
However, both local and non-local iterators returned by a dense map belong to
|
||||
the input iterator category although they respectively model the concepts of a
|
||||
_forward iterator_ type and a _random access iterator_ type.<br/>
|
||||
@@ -63,5 +62,6 @@ The implementation is based on _sparse sets_ and each bucket is identified by an
|
||||
implicit list within the packed array itself.
|
||||
|
||||
The interface is in all respects similar to its counterpart in the standard
|
||||
library, that is, `std::unordered_set`.<br/>
|
||||
Therefore, there is no need to go into the API description.
|
||||
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`).
|
||||
|
||||
279
docs/md/core.md
279
docs/md/core.md
@@ -46,25 +46,24 @@
|
||||
# Introduction
|
||||
|
||||
`EnTT` comes with a bunch of core functionalities mostly used by the other parts
|
||||
of the library itself.<br/>
|
||||
Hardly users will include these features in their code, but it's worth
|
||||
describing what `EnTT` offers so as not to reinvent the wheel in case of need.
|
||||
of the library.<br/>
|
||||
Many of these tools are also useful in everyday work. Therefore, it's worth
|
||||
describing them so as not to reinvent the wheel in case of need.
|
||||
|
||||
# Any as in any type
|
||||
|
||||
`EnTT` comes with its own `any` type. It may seem redundant considering that
|
||||
C++17 introduced `std::any`, but it is not (hopefully).<br/>
|
||||
`EnTT` offers its own `any` type. It may seem redundant considering that C++17
|
||||
introduced `std::any`, but it is not (hopefully).<br/>
|
||||
First of all, the _type_ returned by an `std::any` is a const reference to an
|
||||
`std::type_info`, an implementation defined class that's not something everyone
|
||||
wants to see in a software. Furthermore, there is no way to connect it with the
|
||||
type system of the library and therefore with its integrated RTTI support.<br/>
|
||||
Note that this class is largely used internally by the library itself.
|
||||
wants to see in a software. Furthermore, there is no way to bind it to the type
|
||||
system of the library and therefore with its integrated RTTI support.
|
||||
|
||||
The API is very similar to that of its most famous counterpart, mainly because
|
||||
this class serves the same purpose of being an opaque container for any type of
|
||||
value.<br/>
|
||||
Instances of `any` also minimize the number of allocations by relying on a well
|
||||
known technique called _small buffer optimization_ and a fake vtable.
|
||||
The `any` API is very similar to that of its most famous counterpart, mainly
|
||||
because this class serves the same purpose of being an opaque container for any
|
||||
type of value.<br/>
|
||||
Instances also minimize the number of allocations by relying on a well known
|
||||
technique called _small buffer optimization_ and a fake vtable.
|
||||
|
||||
Creating an object of the `any` type, whether empty or not, is trivial:
|
||||
|
||||
@@ -93,8 +92,8 @@ Furthermore, an instance of `any` isn't tied to an actual type. Therefore, the
|
||||
wrapper is reconfigured when it's assigned a new object of a type other than
|
||||
the one it contains.
|
||||
|
||||
There exists also a way to directly assign a value to the variable contained by
|
||||
an `entt::any`, without necessarily replacing it. This is especially useful when
|
||||
There is also a way to directly assign a value to the variable contained by an
|
||||
`entt::any`, without necessarily replacing it. This is especially useful when
|
||||
the object is used in _aliasing mode_, as described below:
|
||||
|
||||
```cpp
|
||||
@@ -108,15 +107,15 @@ any.assign(value);
|
||||
any.assign(std::move(value));
|
||||
```
|
||||
|
||||
The `any` class will also perform a check on the type information and whether or
|
||||
not the original type was copy or move assignable, as appropriate.<br/>
|
||||
In all cases, the `assign` function returns a boolean value to indicate the
|
||||
success or failure of the operation.
|
||||
The `any` class performs a check on the type information and whether or not the
|
||||
original type was copy or move assignable, as appropriate.<br/>
|
||||
In all cases, the `assign` function returns a boolean value that is true in case
|
||||
of success and false otherwise.
|
||||
|
||||
When in doubt about the type of object contained, the `type` member function of
|
||||
`any` returns a const reference to the `type_info` associated with its element,
|
||||
or `type_id<void>()` if the container is empty. The type is also used internally
|
||||
when comparing two `any` objects:
|
||||
When in doubt about the type of object contained, the `type` member function
|
||||
returns a const reference to the `type_info` associated with its element, or
|
||||
`type_id<void>()` if the container is empty.<br/>
|
||||
The type is also used internally when comparing two `any` objects:
|
||||
|
||||
```cpp
|
||||
if(any == empty) { /* ... */ }
|
||||
@@ -125,7 +124,7 @@ if(any == empty) { /* ... */ }
|
||||
In this case, before proceeding with a comparison, it's verified that the _type_
|
||||
of the two objects is actually the same.<br/>
|
||||
Refer to the `EnTT` type system documentation for more details about how
|
||||
`type_info` works and on possible risks of a comparison.
|
||||
`type_info` works and the possible risks of a comparison.
|
||||
|
||||
A particularly interesting feature of this class is that it can also be used as
|
||||
an opaque container for const and non-const references:
|
||||
@@ -153,22 +152,19 @@ entt::any ref = other.as_ref();
|
||||
```
|
||||
|
||||
In this case, it doesn't matter if the original container actually holds an
|
||||
object or acts already as a reference for unmanaged elements, the new instance
|
||||
thus created won't create copies and will only serve as a reference for the
|
||||
original item.<br/>
|
||||
This means that, starting from the example above, both `ref` and `other` will
|
||||
point to the same object, whether it's initially contained in `other` or already
|
||||
an unmanaged element.
|
||||
object or is as a reference for unmanaged elements already. The new instance
|
||||
thus created doesn't create copies and only serves as a reference for the
|
||||
original item.
|
||||
|
||||
As a side note, it's worth mentioning that, while everything works transparently
|
||||
when it comes to non-const references, there are some exceptions when it comes
|
||||
to const references.<br/>
|
||||
It's worth mentioning that, while everything works transparently when it comes
|
||||
to non-const references, there are some exceptions when it comes to const
|
||||
references.<br/>
|
||||
In particular, the `data` member function invoked on a non-const instance of
|
||||
`any` that wraps a const reference will return a null pointer in all cases.
|
||||
`any` that wraps a const reference returns a null pointer in all cases.
|
||||
|
||||
To cast an instance of `any` to a type, the library offers a set of `any_cast`
|
||||
functions in all respects similar to their most famous counterparts.<br/>
|
||||
The only difference is that, in the case of `EnTT`, these won't raise exceptions
|
||||
The only difference is that, in the case of `EnTT`, they won't raise exceptions
|
||||
but will only trigger an assert in debug mode, otherwise resulting in undefined
|
||||
behavior in case of misuse in release mode.
|
||||
|
||||
@@ -188,31 +184,23 @@ using my_any = entt::basic_any<sizeof(double[4])>;
|
||||
This feature, in addition to allowing the choice of a size that best suits the
|
||||
needs of an application, also offers the possibility of forcing dynamic creation
|
||||
of objects during construction.<br/>
|
||||
In other terms, if the size is 0, `any` avoids the use of any optimization and
|
||||
always dynamically allocates objects (except for aliasing cases).
|
||||
|
||||
Note that the size of the internal storage as well as the alignment requirements
|
||||
are directly part of the type and therefore contribute to define different types
|
||||
that won't be able to interoperate with each other.
|
||||
In other terms, if the size is 0, `any` suppresses the small buffer optimization
|
||||
and always dynamically allocates objects (except for aliasing cases).
|
||||
|
||||
## Alignment requirement
|
||||
|
||||
The alignment requirement is optional and by default the most stringent (the
|
||||
largest) for any object whose size is at most equal to the one provided.<br/>
|
||||
The `basic_any` class template inspects the alignment requirements in each case,
|
||||
even when not provided and may decide not to use the small buffer optimization
|
||||
in order to meet them.
|
||||
|
||||
The alignment requirement is provided as an optional second parameter following
|
||||
the desired size for the internal storage:
|
||||
It's provided as an optional second parameter following the desired size for the
|
||||
internal storage:
|
||||
|
||||
```cpp
|
||||
using my_any = entt::basic_any<sizeof(double[4]), alignof(double[4])>;
|
||||
```
|
||||
|
||||
Note that the alignment requirements as well as the size of the internal storage
|
||||
are directly part of the type and therefore contribute to define different types
|
||||
that won't be able to interoperate with each other.
|
||||
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.
|
||||
|
||||
# Compressed pair
|
||||
|
||||
@@ -225,8 +213,8 @@ is more important than having some cool and probably useless feature.
|
||||
|
||||
Although the API is very close to that of `std::pair` (apart from the fact that
|
||||
the template parameters are inferred from the constructor and therefore there is
|
||||
no` entt::make_compressed_pair`), the major difference is that `first` and
|
||||
`second` are functions for implementation needs:
|
||||
no `entt::make_compressed_pair`), the major difference is that `first` and
|
||||
`second` are functions for implementation requirements:
|
||||
|
||||
```cpp
|
||||
entt::compressed_pair pair{0, 3.};
|
||||
@@ -239,16 +227,15 @@ intuition. At the end of the day, it's just a pair and nothing more.
|
||||
# Enum as bitmask
|
||||
|
||||
Sometimes it's useful to be able to use enums as bitmasks. However, enum classes
|
||||
aren't really suitable for the purpose out of the box. Main problem is that they
|
||||
don't convert implicitly to their underlying type.<br/>
|
||||
All that remains is to make a choice between using old-fashioned enums (with all
|
||||
their problems that I don't want to discuss here) or writing _ugly_ code.
|
||||
aren't really suitable for the purpose. Main problem is that they don't convert
|
||||
implicitly to their underlying type.<br/>
|
||||
The choice is then between using old-fashioned enums (with all their problems
|
||||
that I don't want to discuss here) or writing _ugly_ code.
|
||||
|
||||
Fortunately, there is also a third way: adding enough operators in the global
|
||||
scope to treat enum classes as bitmask transparently.<br/>
|
||||
The ultimate goal is to be able to write code like the following (or maybe
|
||||
something more meaningful, but this should give a grasp and remain simple at the
|
||||
same time):
|
||||
scope to treat enum classes as bitmasks transparently.<br/>
|
||||
The ultimate goal is to write code like the following (or maybe something more
|
||||
meaningful, but this should give a grasp and remain simple at the same time):
|
||||
|
||||
```cpp
|
||||
enum class my_flag {
|
||||
@@ -261,11 +248,11 @@ const my_flag flags = my_flag::enabled;
|
||||
const bool is_enabled = !!(flags & my_flag::enabled);
|
||||
```
|
||||
|
||||
The problem with adding all operators to the global scope is that these will
|
||||
come into play even when not required, with the risk of introducing errors that
|
||||
are difficult to deal with.<br/>
|
||||
The problem with adding all operators to the global scope is that these come
|
||||
into play even when not required, with the risk of introducing errors that are
|
||||
difficult to deal with.<br/>
|
||||
However, C++ offers enough tools to get around this problem. In particular, the
|
||||
library requires users to register all enum classes for which bitmask support
|
||||
library requires users to register the enum classes for which bitmask support
|
||||
should be enabled:
|
||||
|
||||
```cpp
|
||||
@@ -276,7 +263,7 @@ struct entt::enum_as_bitmask<my_flag>
|
||||
```
|
||||
|
||||
This is handy when dealing with enum classes defined by third party libraries
|
||||
and over which the users have no control. However, it's also verbose and can be
|
||||
and over which the user has no control. However, it's also verbose and can be
|
||||
avoided by adding a specific value to the enum class itself:
|
||||
|
||||
```cpp
|
||||
@@ -289,23 +276,21 @@ enum class my_flag {
|
||||
```
|
||||
|
||||
In this case, there is no need to specialize the `enum_as_bitmask` traits, since
|
||||
`EnTT` will automatically detect the flag and enable the bitmask support.<br/>
|
||||
Once the enum class has been registered (in one way or the other) all the most
|
||||
common operators will be available, such as `&`, `|` but also `&=` and `|=`.
|
||||
`EnTT` automatically detects the flag and enables the bitmask support.<br/>
|
||||
Once the enum class is registered (in one way or the other), the most common
|
||||
operators such as `&`, `|` but also `&=` and `|=` are available for use.
|
||||
|
||||
Refer to the official documentation for the full list of operators.
|
||||
|
||||
# Hashed strings
|
||||
|
||||
A hashed string is a zero overhead unique identifier. Users can use
|
||||
human-readable identifiers in the codebase while using their numeric
|
||||
counterparts at runtime, thus without affecting performance.<br/>
|
||||
Hashed strings are human-readable identifiers in the codebase that turn into
|
||||
numeric values at runtime, thus without affecting performance.<br/>
|
||||
The class has an implicit `constexpr` constructor that chews a bunch of
|
||||
characters. Once created, all what one can do with it is getting back the
|
||||
original string through the `data` member function or converting the instance
|
||||
into a number.<br/>
|
||||
The good part is that a hashed string can be used wherever a constant expression
|
||||
is required and no _string-to-number_ conversion will take place at runtime if
|
||||
used carefully.
|
||||
characters. Once created, one can get the original string by means of the `data`
|
||||
member function or convert the instance into a number.<br/>
|
||||
A hashed string is well suited wherever a constant expression is required. No
|
||||
_string-to-number_ conversion will take place at runtime if used carefully.
|
||||
|
||||
Example of use:
|
||||
|
||||
@@ -318,19 +303,18 @@ auto resource = load(entt::hashed_string{"gui/background"});
|
||||
```
|
||||
|
||||
There is also a _user defined literal_ dedicated to hashed strings to make them
|
||||
more user-friendly:
|
||||
more _user-friendly_:
|
||||
|
||||
```cpp
|
||||
using namespace entt::literals;
|
||||
constexpr auto str = "text"_hs;
|
||||
```
|
||||
|
||||
To use it, remember that all user defined literals in `EnTT` are enclosed in the
|
||||
`entt::literals` namespace. Therefore, the entire namespace or selectively the
|
||||
literal of interest must be explicitly included before each use, a bit like
|
||||
`std::literals`.<br/>
|
||||
Finally, in case users need to create hashed strings at runtime, this class also
|
||||
offers the necessary functionalities:
|
||||
User defined literals in `EnTT` are enclosed in the `entt::literals` namespace.
|
||||
Therefore, the entire namespace or selectively the literal of interest must be
|
||||
explicitly included before each use, a bit like `std::literals`.<br/>
|
||||
The class also offers the necessary functionalities to create hashed strings at
|
||||
runtime:
|
||||
|
||||
```cpp
|
||||
std::string orig{"text"};
|
||||
@@ -343,16 +327,14 @@ const auto hash = entt::hashed_string::value(orig.c_str());
|
||||
```
|
||||
|
||||
This possibility shouldn't be exploited in tight loops, since the computation
|
||||
takes place at runtime and no longer at compile-time and could therefore impact
|
||||
takes place at runtime and no longer at compile-time. It could therefore affect
|
||||
performance to some degrees.
|
||||
|
||||
## Wide characters
|
||||
|
||||
The hashed string has a design that is close to that of an `std::basic_string`.
|
||||
It means that `hashed_string` is nothing more than an alias for
|
||||
`basic_hashed_string<char>`. For those who want to use the C++ type for wide
|
||||
character representation, there exists also the alias `hashed_wstring` for
|
||||
`basic_hashed_string<wchar_t>`.<br/>
|
||||
The `hashed_string` class is an alias for `basic_hashed_string<char>`. To use
|
||||
the C++ type for wide character representations, there exists also the alias
|
||||
`hashed_wstring` for `basic_hashed_string<wchar_t>`.<br/>
|
||||
In this case, the user defined literal to use to create hashed strings on the
|
||||
fly is `_hws`:
|
||||
|
||||
@@ -360,16 +342,15 @@ fly is `_hws`:
|
||||
constexpr auto str = L"text"_hws;
|
||||
```
|
||||
|
||||
Note that the hash type of the `hashed_wstring` is the same of its counterpart.
|
||||
The hash type of `hashed_wstring` is the same as its counterpart.
|
||||
|
||||
## Conflicts
|
||||
|
||||
The hashed string class uses internally FNV-1a to compute the numeric
|
||||
counterpart of a string. Because of the _pigeonhole principle_, conflicts are
|
||||
possible. This is a fact.<br/>
|
||||
The hashed string class uses FNV-1a internally to hash strings. Because of the
|
||||
_pigeonhole principle_, conflicts are possible. This is a fact.<br/>
|
||||
There is no silver bullet to solve the problem of conflicts when dealing with
|
||||
hashing functions. In this case, the best solution seemed to be to give up.
|
||||
That's all.<br/>
|
||||
hashing functions. In this case, the best solution is likely to give up. That's
|
||||
all.<br/>
|
||||
After all, human-readable unique identifiers aren't something strictly defined
|
||||
and over which users have not the control. Choosing a slightly different
|
||||
identifier is probably the best solution to make the conflict disappear in this
|
||||
@@ -377,8 +358,8 @@ case.
|
||||
|
||||
# Iterators
|
||||
|
||||
Writing and working with iterators isn't always easy and more often than not
|
||||
leads to duplicated code.<br/>
|
||||
Writing and working with iterators isn't always easy. More often than not it
|
||||
also leads to duplicated code.<br/>
|
||||
`EnTT` tries to overcome this problem by offering some utilities designed to
|
||||
make this hard work easier.
|
||||
|
||||
@@ -431,7 +412,7 @@ user.
|
||||
## Iterable adaptor
|
||||
|
||||
Typically, a container class provides `begin` and `end` member functions (with
|
||||
their const counterparts) to be iterated by the user.<br/>
|
||||
their const counterparts) for iteration.<br/>
|
||||
However, it can happen that a class offers multiple iteration methods or allows
|
||||
users to iterate different sets of _elements_.
|
||||
|
||||
@@ -452,8 +433,8 @@ by returning an iterable object for the purpose.
|
||||
There are a handful of tools within `EnTT` to interact with memory in one way or
|
||||
another.<br/>
|
||||
Some are geared towards simplifying the implementation of (internal or external)
|
||||
allocator aware containers. Others, on the other hand, are designed to help the
|
||||
developer with everyday problems.
|
||||
allocator aware containers. Others are designed to help the developer with
|
||||
everyday problems.
|
||||
|
||||
The former are very specific and for niche problems. These are tools designed to
|
||||
unwrap fancy or plain pointers (`to_address`) or to help forget the meaning of
|
||||
@@ -481,7 +462,7 @@ modulus and for this reason preferred in many areas.
|
||||
|
||||
A nasty thing in C++ (at least up to C++20) is the fact that shared pointers
|
||||
support allocators while unique pointers don't.<br/>
|
||||
There is a proposal at the moment that also shows among the other things how
|
||||
There is a proposal at the moment that also shows (among the other things) how
|
||||
this can be implemented without any compiler support.
|
||||
|
||||
The `allocate_unique` function follows this proposal, making a virtue out of
|
||||
@@ -498,13 +479,16 @@ the same feature.
|
||||
# Monostate
|
||||
|
||||
The monostate pattern is often presented as an alternative to a singleton based
|
||||
configuration system. This is exactly its purpose in `EnTT`. Moreover, this
|
||||
implementation is thread safe by design (hopefully).<br/>
|
||||
Keys are represented by hashed strings, values are basic types like `int`s or
|
||||
`bool`s. Values of different types can be associated to each key, even more than
|
||||
one at a time. Because of this, users must pay attention to use the same type
|
||||
both during an assignment and when they try to read back their data. Otherwise,
|
||||
they will probably incur in unexpected results.
|
||||
configuration system.<br/>
|
||||
This is exactly its purpose in `EnTT`. Moreover, this implementation is thread
|
||||
safe by design (hopefully).
|
||||
|
||||
Keys are integral values (easily obtained by hashed strings), values are basic
|
||||
types like `int`s or `bool`s. Values of different types can be associated with
|
||||
each key, even more than one at a time.<br/>
|
||||
Because of this, one should pay attention to use the same type both during an
|
||||
assignment and when trying to read back the data. Otherwise, there is the risk
|
||||
to incur in unexpected results.
|
||||
|
||||
Example of use:
|
||||
|
||||
@@ -542,17 +526,10 @@ Basically, the whole system relies on a handful of classes. In particular:
|
||||
auto index = entt::type_index<a_type>::value();
|
||||
```
|
||||
|
||||
The returned value isn't guaranteed to be stable across different runs.
|
||||
The returned value isn't guaranteed to be stable across different runs.<br/>
|
||||
However, it can be very useful as index in associative and unordered
|
||||
associative containers or for positional accesses in a vector or an array.
|
||||
|
||||
So as not to conflict with the other tools available, the `family` class isn't
|
||||
used to generate these indexes. Therefore, the numeric identifiers returned by
|
||||
the two tools may differ.<br/>
|
||||
On the other hand, this leaves users with full powers over the `family` class
|
||||
and therefore the generation of custom runtime sequences of indices for their
|
||||
own purposes, if necessary.
|
||||
|
||||
An external generator can also be used if needed. In fact, `type_index` can be
|
||||
specialized by type and is also _sfinae-friendly_ in order to allow more
|
||||
refined specializations such as:
|
||||
@@ -566,7 +543,7 @@ Basically, the whole system relies on a handful of classes. In particular:
|
||||
};
|
||||
```
|
||||
|
||||
Note that indexes **must** still be generated sequentially in this case.<br/>
|
||||
Indexes **must** be sequentially generated in this case.<br/>
|
||||
The tool is widely used within `EnTT`. Generating indices not sequentially
|
||||
would break an assumption and would likely lead to undesired behaviors.
|
||||
|
||||
@@ -583,14 +560,14 @@ Basically, the whole system relies on a handful of classes. In particular:
|
||||
This function **can** use non-standard features of the language for its own
|
||||
purposes. This makes it possible to provide compile-time identifiers that
|
||||
remain stable across different runs.<br/>
|
||||
In all cases, users can prevent the library from using these features by means
|
||||
of the `ENTT_STANDARD_CPP` definition. In this case, there is no guarantee
|
||||
that identifiers remain stable across executions. Moreover, they are generated
|
||||
Users can prevent the library from using these features by means of the
|
||||
`ENTT_STANDARD_CPP` definition. In this case, there is no guarantee that
|
||||
identifiers remain stable across executions. Moreover, they are generated
|
||||
at runtime and are no longer a compile-time thing.
|
||||
|
||||
As for `type_index`, also `type_hash` is a _sfinae-friendly_ class that can be
|
||||
specialized in order to customize its behavior globally or on a per-type or
|
||||
per-traits basis.
|
||||
As it happens with `type_index`, also `type_hash` is a _sfinae-friendly_ class
|
||||
that can be specialized in order to customize its behavior globally or on a
|
||||
per-type or per-traits basis.
|
||||
|
||||
* The name associated with a given type:
|
||||
|
||||
@@ -598,10 +575,9 @@ Basically, the whole system relies on a handful of classes. In particular:
|
||||
auto name = entt::type_name<a_type>::value();
|
||||
```
|
||||
|
||||
The name associated with a type is extracted from some information generally
|
||||
made available by the compiler in use. Therefore, it may differ depending on
|
||||
the compiler and may be empty in the event that this information isn't
|
||||
available.<br/>
|
||||
This value is extracted from some information generally made available by the
|
||||
compiler in use. Therefore, it may differ depending on the compiler and may be
|
||||
empty in the event that this information isn't available.<br/>
|
||||
For example, given the following class:
|
||||
|
||||
```cpp
|
||||
@@ -612,21 +588,20 @@ Basically, the whole system relies on a handful of classes. In particular:
|
||||
when MSVC is in use.<br/>
|
||||
Most of the time the name is also retrieved at compile-time and is therefore
|
||||
always returned through an `std::string_view`. Users can easily access it and
|
||||
modify it as needed, for example by removing the word `struct` to standardize
|
||||
the result. `EnTT` won't do this for obvious reasons, since it requires
|
||||
copying and creating a new string potentially at runtime.
|
||||
modify it as needed, for example by removing the word `struct` to normalize
|
||||
the result. `EnTT` doesn't do this for obvious reasons, since it would be
|
||||
creating a new string at runtime otherwise.
|
||||
|
||||
This function **can** use non-standard features of the language for its own
|
||||
purposes. Users can prevent the library from using non-standard features by
|
||||
means of the `ENTT_STANDARD_CPP` definition. In this case, the name will be
|
||||
empty by default.
|
||||
purposes. Users can prevent the library from using these features by means of
|
||||
the `ENTT_STANDARD_CPP` definition. In this case, the name is just empty.
|
||||
|
||||
As for `type_index`, also `type_name` is a _sfinae-friendly_ class that can be
|
||||
specialized in order to customize its behavior globally or on a per-type or
|
||||
per-traits basis.
|
||||
As it happens with `type_index`, also `type_name` is a _sfinae-friendly_ class
|
||||
that can be specialized in order to customize its behavior globally or on a
|
||||
per-type or per-traits basis.
|
||||
|
||||
These are then combined into utilities that aim to offer an API that is somewhat
|
||||
similar to that offered by the language.
|
||||
similar to that made available by the standard library.
|
||||
|
||||
### Type info
|
||||
|
||||
@@ -708,8 +683,8 @@ In fact, although this is quite rare, it's not entirely excluded.
|
||||
|
||||
Another case where two types are assigned the same identifier is when classes
|
||||
from different contexts (for example two or more libraries loaded at runtime)
|
||||
have the same fully qualified name. In this case, also `type_name` will return
|
||||
the same value for the two types.<br/>
|
||||
have the same fully qualified name. In this case, `type_name` returns the same
|
||||
value for the two types.<br/>
|
||||
Fortunately, there are several easy ways to deal with this:
|
||||
|
||||
* The most trivial one is to define the `ENTT_STANDARD_CPP` macro. Runtime
|
||||
@@ -739,9 +714,9 @@ offered by this module.
|
||||
|
||||
### Size of
|
||||
|
||||
The standard operator `sizeof` complains when users provide it for example with
|
||||
function or incomplete types. On the other hand, it's guaranteed that its result
|
||||
is always nonzero, even if applied to an empty class type.<br/>
|
||||
The standard operator `sizeof` complains if users provide it with functions or
|
||||
incomplete types. On the other hand, it's guaranteed that its result is always
|
||||
non-zero, even if applied to an empty class type.<br/>
|
||||
This small class combines the two and offers an alternative to `sizeof` that
|
||||
works under all circumstances, returning zero if the type isn't supported:
|
||||
|
||||
@@ -777,8 +752,8 @@ A utility to easily transfer the constness of a type to another type:
|
||||
using type = entt::constness_as_t<dst_type, const src_type>;
|
||||
```
|
||||
|
||||
The trait is subject to the rules of the language. Therefore, for example,
|
||||
transferring constness between references won't give the desired effect.
|
||||
The trait is subject to the rules of the language. For example, _transferring_
|
||||
constness between references won't give the desired effect.
|
||||
|
||||
### Member class type
|
||||
|
||||
@@ -798,7 +773,7 @@ A utility to quickly find the n-th argument of a function, member function or
|
||||
data member (for blind operations on opaque types):
|
||||
|
||||
```cpp
|
||||
using type = entt::nt_argument_t<1u, &clazz::member>;
|
||||
using type = entt::nth_argument_t<1u, decltype(&clazz::member)>;
|
||||
```
|
||||
|
||||
Disambiguation of overloaded functions is the responsibility of the user, should
|
||||
@@ -825,8 +800,8 @@ registry.emplace<enemy_tag>(entity);
|
||||
|
||||
### Tag
|
||||
|
||||
Since `id_type` is very important and widely used in `EnTT`, there is a more
|
||||
user-friendly shortcut for the creation of integral constants based on it.<br/>
|
||||
Type `id_type` is very important and widely used in `EnTT`. Therefore, there is
|
||||
a more user-friendly shortcut for the creation of constants based on it.<br/>
|
||||
This shortcut is the alias template `entt::tag`.
|
||||
|
||||
If used in combination with hashed strings, it helps to use human-readable names
|
||||
@@ -918,8 +893,8 @@ Perhaps a bit ugly to see in a codebase but it gets the job done at least.
|
||||
|
||||
## Runtime generator
|
||||
|
||||
To generate sequential numeric identifiers at runtime, `EnTT` offers the
|
||||
`family` class template:
|
||||
The `family` class template helps to generate sequential numeric identifiers for
|
||||
types at runtime:
|
||||
|
||||
```cpp
|
||||
// defines a custom generator
|
||||
@@ -936,8 +911,8 @@ numeric identifier for the given type.<br/>
|
||||
The generator is customizable, so as to get different _sequences_ for different
|
||||
purposes if needed.
|
||||
|
||||
Please, note that identifiers aren't guaranteed to be stable across different
|
||||
runs. Indeed it mostly depends on the flow of execution.
|
||||
Identifiers aren't guaranteed to be stable across different runs. Indeed it
|
||||
mostly depends on the flow of execution.
|
||||
|
||||
# Utilities
|
||||
|
||||
|
||||
1534
docs/md/entity.md
1534
docs/md/entity.md
File diff suppressed because it is too large
Load Diff
@@ -14,6 +14,7 @@
|
||||
* [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
|
||||
-->
|
||||
@@ -193,7 +194,7 @@ to mitigate the problem makes it manageable.
|
||||
|
||||
## Which functions trigger which signals
|
||||
|
||||
The `registry` class offers three signals that are emitted following specific
|
||||
Storage classes offer three _signals_ that are emitted following specific
|
||||
operations. Maybe not everyone knows what these operations are, though.<br/>
|
||||
If this isn't clear, below you can find a _vademecum_ for this purpose:
|
||||
|
||||
@@ -213,3 +214,28 @@ otherwise the latter is replaced and therefore `on_update` is triggered. As for
|
||||
the second case, components are removed from their entities and thus freed when
|
||||
they are recycled. It means that `on_destroyed` is triggered for every component
|
||||
owned by the entity that is destroyed.
|
||||
|
||||
## Duplicate storage for the same component
|
||||
|
||||
It's rare but you can see double sometimes, especially when it comes to storage.
|
||||
This can be caused by a conflict in the hash assigned to the various component
|
||||
types (one of a kind) or by bugs in your compiler
|
||||
([more common](https://github.com/skypjack/entt/issues/1063) apparently).<br/>
|
||||
Regardless of the cause, `EnTT` offers a customization point that also serves as
|
||||
a solution in this case:
|
||||
|
||||
```cpp
|
||||
template<>
|
||||
struct entt::type_hash<Type> final {
|
||||
[[nodiscard]] static constexpr id_type value() noexcept {
|
||||
return hashed_string::value("Type");
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr operator id_type() const noexcept {
|
||||
return value();
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Specializing `type_hash` directly bypasses the default implementation offered by
|
||||
`EnTT`, thus avoiding any possible conflicts or compiler bugs.
|
||||
|
||||
175
docs/md/graph.md
175
docs/md/graph.md
@@ -14,7 +14,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
|
||||
-->
|
||||
@@ -23,15 +22,15 @@
|
||||
|
||||
`EnTT` doesn't aim to offer everything one needs to work with graphs. Therefore,
|
||||
anyone looking for this in the _graph_ submodule will be disappointed.<br/>
|
||||
Quite the opposite is true. This submodule is minimal and contains only the data
|
||||
structures and algorithms strictly necessary for the development of some tools
|
||||
such as the _flow builder_.
|
||||
Quite the opposite is true though. This submodule is minimal and contains only
|
||||
the data structures and algorithms strictly necessary for the development of
|
||||
some tools such as the _flow builder_.
|
||||
|
||||
# Data structures
|
||||
|
||||
As anticipated in the introduction, the aim isn't to offer all possible data
|
||||
structures suitable for representing and working with graphs. Many will likely
|
||||
be added or refined over time, however I want to discourage anyone expecting
|
||||
be added or refined over time. However I want to discourage anyone expecting
|
||||
tight scheduling on the subject.<br/>
|
||||
The data structures presented in this section are mainly useful for the
|
||||
development and support of some tools which are also part of the same submodule.
|
||||
@@ -49,7 +48,7 @@ The `directed_tag` type _creates_ the graph as directed. There is also an
|
||||
`undirected_tag` counterpart which creates it as undirected.<br/>
|
||||
The interface deviates slightly from the typical double indexing of C and offers
|
||||
an API that is perhaps more familiar to a C++ programmer. Therefore, the access
|
||||
and modification of an element will take place via the `contains`, `insert` and
|
||||
and modification of an element takes place via the `contains`, `insert` and
|
||||
`erase` functions rather than a double call to an `operator[]`:
|
||||
|
||||
```cpp
|
||||
@@ -60,14 +59,14 @@ if(adjacency_matrix.contains(0u, 1u)) {
|
||||
}
|
||||
```
|
||||
|
||||
Both `insert` and` erase` are idempotent functions which have no effect if the
|
||||
Both `insert` and` erase` are _idempotent_ functions which have no effect if the
|
||||
element already exists or has already been deleted.<br/>
|
||||
The first one returns an `std::pair` containing the iterator to the element and
|
||||
a boolean value indicating whether the element has been inserted or was already
|
||||
present. The second one instead returns the number of deleted elements (0 or 1).
|
||||
a boolean value indicating whether the element was newly inserted or not. The
|
||||
second one returns the number of deleted elements (0 or 1).
|
||||
|
||||
An adjacency matrix must be initialized with the number of elements (vertices)
|
||||
when constructing it but can also be resized later using the `resize` function:
|
||||
An adjacency matrix is initialized with the number of elements (vertices) when
|
||||
constructing it but can also be resized later using the `resize` function:
|
||||
|
||||
```cpp
|
||||
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
|
||||
@@ -82,8 +81,8 @@ for(auto &&vertex: adjacency_matrix.vertices()) {
|
||||
}
|
||||
```
|
||||
|
||||
Note that the same result can be obtained with the following snippet, since the
|
||||
vertices are unsigned integral values:
|
||||
The same result is obtained with the following snippet, since the vertices are
|
||||
plain unsigned integral values:
|
||||
|
||||
```cpp
|
||||
for(auto last = adjacency_matrix.size(), pos = {}; pos < last; ++pos) {
|
||||
@@ -93,8 +92,8 @@ for(auto last = adjacency_matrix.size(), pos = {}; pos < last; ++pos) {
|
||||
|
||||
As for visiting the edges, a few functions are available.<br/>
|
||||
When the purpose is to visit all the edges of a given adjacency matrix, the
|
||||
`edges` function returns an iterable object that can be used to get them as
|
||||
pairs of vertices:
|
||||
`edges` function returns an iterable object that is used to get them as pairs of
|
||||
vertices:
|
||||
|
||||
```cpp
|
||||
for(auto [lhs, rhs]: adjacency_matrix.edges()) {
|
||||
@@ -102,8 +101,8 @@ for(auto [lhs, rhs]: adjacency_matrix.edges()) {
|
||||
}
|
||||
```
|
||||
|
||||
On the other hand, if the goal is to visit all the in- or out-edges of a given
|
||||
vertex, the `in_edges` and `out_edges` functions are meant for that:
|
||||
If the goal is to visit all the in- or out-edges of a given vertex instead, the
|
||||
`in_edges` and `out_edges` functions are meant for that:
|
||||
|
||||
```cpp
|
||||
for(auto [lhs, rhs]: adjacency_matrix.out_edges(3u)) {
|
||||
@@ -111,11 +110,11 @@ for(auto [lhs, rhs]: adjacency_matrix.out_edges(3u)) {
|
||||
}
|
||||
```
|
||||
|
||||
As might be expected, these functions expect the vertex to visit (that is, to
|
||||
return the in- or out-edges for) as an argument.<br/>
|
||||
Both the functions expect the vertex to visit (that is, to return the in- or
|
||||
out-edges for) as an argument.<br/>
|
||||
Finally, the adjacency matrix is an allocator-aware container and offers most of
|
||||
the functionality one would expect from this type of containers, such as `clear`
|
||||
or 'get_allocator` and so on.
|
||||
the functionalities one would expect from this type of containers, such as
|
||||
`clear` or 'get_allocator` and so on.
|
||||
|
||||
## Graphviz dot language
|
||||
|
||||
@@ -129,19 +128,19 @@ std::ostringstream output{};
|
||||
entt::dot(output, adjacency_matrix);
|
||||
```
|
||||
|
||||
However, there is also the option of providing a callback to which the vertices
|
||||
are passed and which can be used to add (`dot`) properties to the output from
|
||||
time to time:
|
||||
It's also possible to provide a callback to which the vertices are passed and
|
||||
which can be used to add (`dot`) properties to the output as needed:
|
||||
|
||||
```cpp
|
||||
std::ostringstream output{};
|
||||
|
||||
entt::dot(output, adjacency_matrix, [](auto &output, auto vertex) {
|
||||
out << "label=\"v\"" << vertex << ",shape=\"box\"";
|
||||
});
|
||||
```
|
||||
|
||||
This second mode is particularly convenient when the user wants to associate
|
||||
data managed externally to the graph being converted.
|
||||
externally managed data to the graph being converted.
|
||||
|
||||
# Flow builder
|
||||
|
||||
@@ -155,42 +154,42 @@ specified.<br/>
|
||||
Most of the functions in the API also return the flow builder itself, according
|
||||
to what is the common sense API when it comes to builder classes.
|
||||
|
||||
Once all tasks have been registered and resources assigned to them, an execution
|
||||
graph in the form of an adjacency matrix is returned to the user.<br/>
|
||||
Once all tasks are registered and resources assigned to them, an execution graph
|
||||
in the form of an adjacency matrix is returned to the user.<br/>
|
||||
This graph contains all the tasks assigned to the flow builder in the form of
|
||||
_vertices_. The _vertex_ itself can be used as an index to get the identifier
|
||||
passed during registration.
|
||||
_vertices_. The _vertex_ itself is used as an index to get the identifier passed
|
||||
during registration.
|
||||
|
||||
## Tasks and resources
|
||||
|
||||
Although these terms are used extensively in the documentation, the flow builder
|
||||
has no real concept of tasks and resources.<br/>
|
||||
This class works mainly with _identifiers_, that is, values of type `id_type`.
|
||||
That is, both tasks and resources are identified by integral values.<br/>
|
||||
In other terms, both tasks and resources are identified by integral values.<br/>
|
||||
This allows not to couple the class itself to the rest of the library or to any
|
||||
particular data structure. On the other hand, it requires the user to keep track
|
||||
of the association between identifiers and operations or actual data.
|
||||
|
||||
Once a flow builder has been created (which requires no constructor arguments),
|
||||
the first thing to do is to bind a task. This will indicate to the builder who
|
||||
intends to consume the resources that will be specified immediately after:
|
||||
Once a flow builder is created (which requires no constructor arguments), the
|
||||
first thing to do is to bind a task. This tells to the builder _who_ intends to
|
||||
consume the resources that are specified immediately after:
|
||||
|
||||
```cpp
|
||||
entt::flow builder{};
|
||||
builder.bind("task_1"_hs);
|
||||
```
|
||||
|
||||
Note that the example uses the `EnTT` hashed string to generate an identifier
|
||||
for the task.<br/>
|
||||
Indeed, the use of `id_type` as an identifier type is not by accident. In fact,
|
||||
The example uses the `EnTT` hashed string to generate an identifier for the
|
||||
task.<br/>
|
||||
Indeed, the use of `id_type` as an identifier type isn't by accident. In fact,
|
||||
it matches well with the internal hashed string class. Moreover, it's also the
|
||||
same type returned by the hash function of the internal RTTI system, in case the
|
||||
user wants to rely on that.<br/>
|
||||
However, being an integral value, it leaves the user full freedom to rely on his
|
||||
own tools if he deems it necessary.
|
||||
own tools if necessary.
|
||||
|
||||
Once a task has been associated with the flow builder, it can be assigned
|
||||
read-only or read-write resources, as appropriate:
|
||||
Once a task is associated with the flow builder, it's also assigned read-only or
|
||||
read-write resources as appropriate:
|
||||
|
||||
```cpp
|
||||
builder
|
||||
@@ -203,13 +202,87 @@ builder
|
||||
|
||||
As mentioned, many functions return the builder itself and it's therefore easy
|
||||
to concatenate the different calls.<br/>
|
||||
Also in the case of resources, these are identified by numeric values of type
|
||||
Also in the case of resources, they are identified by numeric values of type
|
||||
`id_type`. As above, the choice is not entirely random. This goes well with the
|
||||
tools offered by the library while leaving room for maximum flexibility.
|
||||
|
||||
Finally, both the `ro` and` rw` functions also offer an overload that accepts a
|
||||
pair of iterators, so that one can pass a range of resources in one go.
|
||||
|
||||
### Rebinding
|
||||
|
||||
The `flow` class is resource based rather than task based. This means that graph
|
||||
generation is driven by resources and not by the order of _appearance_ of tasks
|
||||
during flow definition.<br/>
|
||||
Although this concept is particularly important, it's almost irrelevant for the
|
||||
vast majority of cases. However, it becomes relevant when _rebinding_ resources
|
||||
or tasks.
|
||||
|
||||
In fact, nothing prevents rebinding elements to a flow.<br/>
|
||||
However, the behavior changes slightly from case to case and has some nuances
|
||||
that it's worth knowing about.
|
||||
|
||||
Directly rebinding a resource without the task being replaced trivially results
|
||||
in the task's access mode for that resource being updated:
|
||||
|
||||
```cpp
|
||||
builder.bind("task"_hs).rw("resource"_hs).ro("resource"_hs)
|
||||
```
|
||||
|
||||
In this case, the resource is accessed in read-only mode, regardless of the
|
||||
first call to `rw`.<br/>
|
||||
Behind the scenes, the call doesn't actually _replace_ the previous one but is
|
||||
queued after it instead, overwriting it when generating the graph. Thus, a large
|
||||
number of resource rebindings may even impact processing times (very difficult
|
||||
to observe but theoretically possible).
|
||||
|
||||
Rebinding resources and also combining it with changes to tasks has far more
|
||||
implications instead.<br/>
|
||||
As mentioned, graph generation takes place starting from resources and not from
|
||||
tasks. Therefore, the result may not be as expected:
|
||||
|
||||
```cpp
|
||||
builder
|
||||
.bind("task_1"_hs)
|
||||
.ro("resource"_hs)
|
||||
.bind("task_2"_hs)
|
||||
.ro("resource"_hs)
|
||||
.bind("task_1"_hs)
|
||||
.rw("resource"_hs);
|
||||
```
|
||||
|
||||
What happens here is that the resource first _sees_ a read-only access request
|
||||
from the first task, then a read-write request from the second task and finally
|
||||
a new read-only request from the first task.<br/>
|
||||
Although this definition would probably be counted as an error, the resulting
|
||||
graph may be unexpected. This in fact consists of a single edge outgoing from
|
||||
the second task and directed to the first task.<br/>
|
||||
To intuitively understand what happens, it's enough to think of the fact that a
|
||||
task never has an edge pointing to itself.
|
||||
|
||||
While not obvious, this approach has its pros and cons like any other solution.
|
||||
For example, creating loops is actually simple in the context of resource-based
|
||||
graph generations:
|
||||
|
||||
```cpp
|
||||
builder
|
||||
.bind("task_1"_hs)
|
||||
.rw("resource"_hs)
|
||||
.bind("task_2"_hs)
|
||||
.rw("resource"_hs)
|
||||
.bind("task_1"_hs)
|
||||
.rw("resource"_hs);
|
||||
```
|
||||
|
||||
As expected, this definition leads to the creation of two edges that define a
|
||||
loop between the two tasks.
|
||||
|
||||
As a general rule, rebinding resources and tasks is highly discouraged because
|
||||
it could lead to subtle bugs if users don't know what they're doing.<br/>
|
||||
However, once the mechanisms of resource-based graph generation are understood,
|
||||
it can offer to the expert user a flexibility and a range of possibilities
|
||||
otherwise inaccessible.
|
||||
|
||||
## Fake resources and order of execution
|
||||
|
||||
The flow builder doesn't offer the ability to specify when a task should execute
|
||||
@@ -217,10 +290,10 @@ before or after another task.<br/>
|
||||
In fact, the order of _registration_ on the resources also determines the order
|
||||
in which the tasks are processed during the generation of the execution graph.
|
||||
|
||||
However, there is a way to force the execution order of two processes.<br/>
|
||||
However, there is a way to _force_ the execution order of two processes.<br/>
|
||||
Briefly, since accessing a resource in opposite modes requires sequential rather
|
||||
than parallel scheduling, it's possible to make use of fake resources to force
|
||||
the order execution:
|
||||
than parallel scheduling, it's possible to make use of fake resources to rule on
|
||||
the execution order:
|
||||
|
||||
```cpp
|
||||
builder
|
||||
@@ -235,10 +308,10 @@ builder
|
||||
.ro("fake"_hs)
|
||||
```
|
||||
|
||||
This snippet forces the execution of `task_2` and `task_3` **after** `task_1`.
|
||||
This is due to the fact that the latter sets a read-write requirement on a fake
|
||||
This snippet forces the execution of `task_1` **before** `task_2` and `task_3`.
|
||||
This is due to the fact that the former sets a read-write requirement on a fake
|
||||
resource that the other tasks also want to access in read-only mode.<br/>
|
||||
Similarly, it's possible to force a task to run after a certain group:
|
||||
Similarly, it's possible to force a task to run **after** a certain group:
|
||||
|
||||
```cpp
|
||||
builder
|
||||
@@ -261,7 +334,7 @@ others tasks.
|
||||
|
||||
Sometimes it's useful to assign the role of _sync point_ to a node.<br/>
|
||||
Whether it accesses new resources or is simply a watershed, the procedure for
|
||||
assigning this role to a vertex is always the same: first it's tied to the flow
|
||||
assigning this role to a vertex is always the same. First it's tied to the flow
|
||||
builder, then the `sync` function is invoked:
|
||||
|
||||
```cpp
|
||||
@@ -283,7 +356,7 @@ all specified constraints to return the best scheduling for the vertices:
|
||||
entt::adjacency_matrix<entt::directed_tag> graph = builder.graph();
|
||||
```
|
||||
|
||||
The search for the main vertices, that is those without in-edges, is usually the
|
||||
Searching for the main vertices (that is, those without in-edges) is usually the
|
||||
first thing required:
|
||||
|
||||
```cpp
|
||||
@@ -294,6 +367,6 @@ for(auto &&vertex: graph) {
|
||||
}
|
||||
```
|
||||
|
||||
Starting from them, using the other functions appropriately (such as `out_edges`
|
||||
to retrieve the children of a given task or `edges` to access their identifiers)
|
||||
it will be possible to instantiate an execution graph.
|
||||
Then it's possible to instantiate an execution graph by means of other functions
|
||||
such as `out_edges` to retrieve the children of a given task or `edges` to get
|
||||
the identifiers.
|
||||
|
||||
@@ -19,14 +19,12 @@
|
||||
general and on GNU/Linux when default visibility was set to hidden. The
|
||||
limitation was mainly due to a custom utility used to assign unique, sequential
|
||||
identifiers with different types.<br/>
|
||||
Fortunately, nowadays using `EnTT` across boundaries is much easier.
|
||||
Fortunately, nowadays `EnTT` works smoothly across boundaries.
|
||||
|
||||
## Smooth until proven otherwise
|
||||
|
||||
Many classes in `EnTT` make extensive use of type erasure for their purposes.
|
||||
This isn't a problem on itself (in fact, it's the basis of an API so convenient
|
||||
to use). However, a way is needed to recognize the objects whose type has been
|
||||
erased on the other side of a boundary.<br/>
|
||||
This raises the need to identify objects whose type has been erased.<br/>
|
||||
The `type_hash` class template is how identifiers are generated and thus made
|
||||
available to the rest of the library. In general, this class doesn't arouse much
|
||||
interest. The only exception is when a conflict between identifiers occurs
|
||||
@@ -36,13 +34,13 @@ The section dedicated to `type_info` contains all the details to get around the
|
||||
issue in a concise and elegant way. Please refer to the specific documentation.
|
||||
|
||||
When working with linked libraries, compile definitions `ENTT_API_EXPORT` and
|
||||
`ENTT_API_IMPORT` can be used where there is a need to import or export symbols,
|
||||
so as to make everything work nicely across boundaries.<br/>
|
||||
`ENTT_API_IMPORT` are to import or export symbols, so as to make everything work
|
||||
nicely across boundaries.<br/>
|
||||
On the other hand, everything should run smoothly when working with plugins or
|
||||
shared libraries that don't export any symbols.
|
||||
|
||||
For anyone who needs more details, the test suite contains multiple examples
|
||||
covering the most common cases (see the `lib` directory for all details).<br/>
|
||||
For those who need more details, the test suite contains many examples covering
|
||||
the most common cases (see the `lib` directory for all details).<br/>
|
||||
It goes without saying that it's impossible to cover **all** possible cases.
|
||||
However, what is offered should hopefully serve as a basis for all of them.
|
||||
|
||||
@@ -70,8 +68,8 @@ entt::locator<entt::meta_ctx>::reset(handle);
|
||||
|
||||
From now on, both spaces refer to the same context and on it are attached all
|
||||
new meta types, no matter where they are created.<br/>
|
||||
Note that resetting the main context doesn't also propagate changes across
|
||||
boundaries. In other words, resetting a context results in the decoupling of the
|
||||
Note that _replacing_ the main context doesn't also propagate changes across
|
||||
boundaries. In other words, replacing a context results in the decoupling of the
|
||||
two sides and therefore a divergence in the contents.
|
||||
|
||||
## Memory Management
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
# EnTT in Action
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [EnTT in Action](#entt-in-action)
|
||||
* [Games](#games)
|
||||
* [Engines and the like](#engines-and-the-like)
|
||||
* [Articles, videos and blog posts](#articles-videos-and-blog-posts)
|
||||
* [Any Other Business](#any-other-business)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
`EnTT` is widely used in private and commercial applications. I cannot even
|
||||
mention most of them because of some signatures I put on some documents time
|
||||
ago. Fortunately, there are also people who took the time to implement open
|
||||
@@ -7,16 +24,24 @@ source projects based on `EnTT` and didn't hold back when it came to documenting
|
||||
them.
|
||||
|
||||
Below an incomplete list of games, applications and articles that can be used as
|
||||
a reference. Where I put the word _apparently_ means that the use of `EnTT` is
|
||||
documented but the authors didn't make explicit announcements or contacted me
|
||||
directly.
|
||||
a reference.<br/>
|
||||
Where I put the word _apparently_ means that the use of `EnTT` is documented but
|
||||
the authors didn't make explicit announcements or contacted me directly.
|
||||
|
||||
I hope this list can grow much more in the future:
|
||||
If you know of other resources out there that are about `EnTT`, feel free to
|
||||
open an issue or a PR and I'll be glad to add them to this page.<br/>
|
||||
I hope the following lists can grow much more in the future.
|
||||
|
||||
# EnTT in Action
|
||||
|
||||
## Games
|
||||
|
||||
* Games:
|
||||
* [Minecraft](https://minecraft.net/en-us/attribution/) by
|
||||
[Mojang](https://mojang.com/): of course, **that** Minecraft, see the
|
||||
open source attributions page for more details.
|
||||
* [Minecraft Legends](https://www.minecraft.net/it-it/about-legends) by
|
||||
[Mojang](https://mojang.com/): an action strategy game where users have to
|
||||
fight to defend the Overworld.
|
||||
* [Minecraft Earth](https://www.minecraft.net/en-us/about-earth) by
|
||||
[Mojang](https://mojang.com/): an augmented reality game for mobile, that
|
||||
lets users bring Minecraft into the real world.
|
||||
@@ -102,8 +127,19 @@ I hope this list can grow much more in the future:
|
||||
multi-player arcade shooter game prototype.
|
||||
* [Confetti Party](https://github.com/hexerei/entt-confetti): C++ sample
|
||||
application as a starting point using `EnTT` and `SDL2`.
|
||||
* [Hellbound](https://buas.itch.io/hellbound): a top-down action rogue-like
|
||||
where to fight colossal demons in procedurally generated levels of hell.
|
||||
* [Saurian Sorcery](https://github.com/cajallen/spellbook): a tower defense
|
||||
game where to assemble a tribe of lizards to defend against robot invaders.
|
||||
* [robotfindskitten](https://github.com/autogalkin/robotfindskitten): a clone
|
||||
of `robotfindskitten` inside `Notepad.exe`, powered by `EnTT`.
|
||||
* [Orion](https://github.com/alekskoloch/Orion): Outer-space Research and
|
||||
Interstellar Observation Network (a space shooter game).
|
||||
* [EnTT Boids](https://github.com/DanielEliasib/entt_boids): a simple boids
|
||||
implementation using `EnTT` and `Raylib`.
|
||||
|
||||
## Engines and the like:
|
||||
|
||||
* Engines and the like:
|
||||
* [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
|
||||
@@ -168,8 +204,26 @@ I hope this list can grow much more in the future:
|
||||
engine based on `SDL2` and `EnTT`.
|
||||
* [Ducktape](https://github.com/DucktapeEngine/Ducktape): an open source C++
|
||||
2D & 3D game engine that focuses on being fast and powerful.
|
||||
* [The Worst Engine](https://github.com/Parasik72/TWE): a game engine based on
|
||||
OpenGL.
|
||||
* [Ecsact](https://ecsact.dev/): a language aimed at describing ECS, with a
|
||||
[runtime implementation](https://github.com/ecsact-dev/ecsact_rt_entt) based
|
||||
on `EnTT`.
|
||||
* [AGE (Arc Game Engine)](https://github.com/MohitSethi99/ArcGameEngine): an
|
||||
open-source engine for building 2D & 3D real-time rendering and interactive
|
||||
contents.
|
||||
* [Kengine](https://github.com/phisko/kengine): the _Koala engine_ is a game
|
||||
engine entirely implemented as an entity-component-system.
|
||||
* [Scion2D](https://github.com/dwjclark11/Scion2D): 2D game engine with
|
||||
[YouTube series](https://www.youtube.com/playlist?list=PL3HUvSWOJR7XRDwVVQqqWO-zyyscb8L-v)
|
||||
included.
|
||||
* [EnTT Editor](https://github.com/TheDimin/EnttEditor): an editor for `EnTT`
|
||||
libary that combines its built-in reflection system with `ImGui`.
|
||||
* [Era Game Engine](https://github.com/EldarMuradov/EraGameEngine): a modern
|
||||
ECS-based game engine.
|
||||
|
||||
## Articles, videos and blog posts:
|
||||
|
||||
* Articles, videos and blog posts:
|
||||
* [Some posts](https://skypjack.github.io/tags/#entt) on my personal
|
||||
[blog](https://skypjack.github.io/) are about `EnTT`, for those who want to
|
||||
know **more** on this project.
|
||||
@@ -193,6 +247,23 @@ I hope this list can grow much more in the future:
|
||||
- ... And so on.
|
||||
[Check out](https://www.youtube.com/channel/UCQ-W1KE9EYfdxhL6S4twUNw) the
|
||||
_Game Engine Series_ by The Cherno for more videos.
|
||||
* [Game Engine series](https://www.youtube.com/@JADE-iteGames/videos) by
|
||||
[dwjclark11](https://github.com/dwjclark11) (not just `EnTT` but a lot of
|
||||
it):
|
||||
- [Getting into ECS](https://youtu.be/k9CbonLopJU?si=za3Tisyc96_92DWM)
|
||||
- [Creating ECS Wrapper Classes](https://youtu.be/yetyuMJRdbo?si=PJTkmap4Ysqbzb_M)
|
||||
- [Runtime Reflection using EnTT meta](https://youtu.be/GrXV5A07GTY?si=fKdWTj9AOhnhtiXq)
|
||||
- [Adding entt::meta and Sol2 bindings](https://youtu.be/IM55JgxOqFA?si=rsbb4AG_NVh4IUmD)
|
||||
(with [part two](https://youtu.be/-PTt-b1tzRw?si=zPJ4vEluyheMcNgO) too)
|
||||
- ... And so on.
|
||||
[Check it out](https://www.youtube.com/playlist?list=PL3HUvSWOJR7XRDwVVQqqWO-zyyscb8L-v)
|
||||
for more videos.
|
||||
* [Warmonger Dynasty devlog series](https://david-delassus.medium.com/list/warmonger-dynasty-devlogs-f64b71f556de)
|
||||
by [linkdd](https://github.com/linkdd): an interesting walkthrough of
|
||||
developing a game (also) with EnTT.
|
||||
* [Use EnTT When You Need An ECS](https://www.codingwiththomas.com/blog/use-entt-when-you-need-an-ecs)
|
||||
by [Thomas](https://www.codingwiththomas.com/): I couldn't have said it
|
||||
better.
|
||||
* [Space Battle: Huge edition](http://victor.madtriangles.com/code%20experiment/2018/06/11/post-ecs-battle-huge.html):
|
||||
huge space battle built entirely from scratch.
|
||||
* [Space Battle](https://github.com/vblanco20-1/ECS_SpaceBattle): huge space
|
||||
@@ -219,7 +290,8 @@ I hope this list can grow much more in the future:
|
||||
MMO(RPG)s and its [follow-up](https://youtu.be/yGlZeopx2hU) episode about
|
||||
player bots and full external ECS: a series definitely worth looking at.
|
||||
|
||||
* Any Other Business:
|
||||
## Any Other Business:
|
||||
|
||||
* [ArcGIS Runtime SDKs](https://developers.arcgis.com/arcgis-runtime/) by
|
||||
[Esri](https://www.esri.com/): they use `EnTT` for the internal ECS and the
|
||||
cross platform C++ rendering engine. The SDKs are utilized by a lot of
|
||||
@@ -261,6 +333,3 @@ I hope this list can grow much more in the future:
|
||||
* GitHub contains also
|
||||
[many other examples](https://github.com/search?o=desc&q=%22skypjack%2Fentt%22&s=indexed&type=Code)
|
||||
of use of `EnTT` from which to take inspiration if interested.
|
||||
|
||||
If you know of other resources out there that are about `EnTT`, feel free to
|
||||
open an issue or a PR and I'll be glad to add them to this page.
|
||||
|
||||
426
docs/md/meta.md
426
docs/md/meta.md
@@ -67,17 +67,15 @@ recommended.
|
||||
|
||||
# Reflection in a nutshell
|
||||
|
||||
Reflection always starts from real types (users cannot reflect imaginary types
|
||||
and it would not make much sense, we wouldn't be talking about reflection
|
||||
anymore).<br/>
|
||||
To create a meta node, the library provides the `meta` function that accepts a
|
||||
type to reflect as a template parameter:
|
||||
Reflection always starts from actual C++ types. Users cannot reflect _imaginary_
|
||||
types.<br/>
|
||||
The `meta` function is where it all starts:
|
||||
|
||||
```cpp
|
||||
auto factory = entt::meta<my_type>();
|
||||
```
|
||||
|
||||
The returned value is a factory object to use to continue building the meta
|
||||
The returned value is a _factory object_ to use to continue building the meta
|
||||
type.
|
||||
|
||||
By default, a meta type is associated with the identifier returned by the
|
||||
@@ -88,45 +86,42 @@ However, it's also possible to assign custom identifiers to meta types:
|
||||
auto factory = entt::meta<my_type>().type("reflected_type"_hs);
|
||||
```
|
||||
|
||||
Identifiers are important because users can retrieve meta types at runtime by
|
||||
searching for them by _name_ other than by type.<br/>
|
||||
On the other hand, there are cases in which users can be interested in adding
|
||||
features to a reflected type so that the reflection system can use it correctly
|
||||
under the hood, but they don't want to also make the type _searchable_. In this
|
||||
case, it's sufficient not to invoke `type`.
|
||||
Identifiers are used to _retrieve_ meta types at runtime by _name_ other than by
|
||||
type.<br/>
|
||||
However, users can be interested in adding features to a reflected type so that
|
||||
the reflection system can use it correctly under the hood, while they don't want
|
||||
to also make the type _searchable_. In this case, it's sufficient not to invoke
|
||||
`type`.
|
||||
|
||||
A factory is such that all its member functions return the factory itself or a
|
||||
decorated version of it. This object can be used to add the following:
|
||||
A factory is such that all its member functions return the factory itself. It's
|
||||
generally used to create the following:
|
||||
|
||||
* _Constructors_. Actual constructors can be assigned to a reflected type by
|
||||
specifying their list of arguments. Free functions (namely, factories) can be
|
||||
used as well, as long as the return type is the expected one. From a client's
|
||||
point of view, nothing changes if a constructor is a free function or an
|
||||
actual constructor.<br/>
|
||||
Use the `ctor` member function for this purpose:
|
||||
* _Constructors_. A constructors is assigned to a reflected type by specifying
|
||||
its _list of arguments_. Free functions are also accepted if the return type
|
||||
is the expected one. From a client perspective, nothing changes between a free
|
||||
function or an actual constructor:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>().ctor<int, char>().ctor<&factory>();
|
||||
```
|
||||
|
||||
* _Destructors_. Free functions and member functions can be used as destructors
|
||||
of reflected types. The purpose is to give users the ability to free up
|
||||
resources that require special treatment before an object is actually
|
||||
destroyed.<br/>
|
||||
Use the `dtor` member function for this purpose:
|
||||
Meta default constructors are implicitly generated, if possible.
|
||||
|
||||
* _Destructors_. Both free functions and member functions are valid destructors:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>().dtor<&destroy>();
|
||||
```
|
||||
|
||||
The purpose is to offer the possibility to free up resources that require
|
||||
_special treatment_ before an object is actually destroyed.<br/>
|
||||
A function should neither delete nor explicitly invoke the destructor of a
|
||||
given instance.
|
||||
|
||||
* _Data members_. Both real data members of the underlying type and static and
|
||||
global variables, as well as constants of any kind, can be attached to a meta
|
||||
type. From the point of view of the client, all the variables associated with
|
||||
the reflected type will appear as if they were part of the type itself.<br/>
|
||||
Use the `data` member function for this purpose:
|
||||
* _Data members_. Meta data members are actual data members of the underlying
|
||||
type but also static and global variables or constants of any kind. From the
|
||||
point of view of the client, all the variables associated with the reflected
|
||||
type appear as if they were part of the type itself:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>()
|
||||
@@ -135,13 +130,11 @@ decorated version of it. This object can be used to add the following:
|
||||
.data<&global_variable>("global"_hs);
|
||||
```
|
||||
|
||||
The function requires as an argument the identifier to give to the meta data
|
||||
once created. Users can then access meta data at runtime by searching for them
|
||||
by _name_.<br/>
|
||||
Data members can also be defined by means of a setter and getter pair. Setters
|
||||
and getters can be either free functions, class members or a mix of them, as
|
||||
long as they respect the required signatures. This approach is also convenient
|
||||
to create a read-only variable from a non-const data member:
|
||||
The `data` function requires the identifier to use for the meta data member.
|
||||
Users can then access it by _name_ at runtime.<br/>
|
||||
Data members are also defined by means of a setter and getter pair. These are
|
||||
either free functions, class members or a mix of them. This approach is also
|
||||
convenient to create read-only properties from a non-const data member:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>().data<nullptr, &my_type::data_member>("member"_hs);
|
||||
@@ -153,13 +146,10 @@ decorated version of it. This object can be used to add the following:
|
||||
entt::meta<my_type>().data<entt::value_list<&from_int, &from_string>, &my_type::data_member>("member"_hs);
|
||||
```
|
||||
|
||||
Refer to the inline documentation for all the details.
|
||||
|
||||
* _Member functions_. Both real member functions of the underlying type and free
|
||||
functions can be attached to a meta type. From the point of view of the
|
||||
client, all the functions associated with the reflected type will appear as if
|
||||
they were part of the type itself.<br/>
|
||||
Use the `func` member function for this purpose:
|
||||
* _Member functions_. Meta member functions are actual member functions of the
|
||||
underlying type but also plain free functions. From the point of view of the
|
||||
client, all the functions associated with the reflected type appear as if they
|
||||
were part of the type itself:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>()
|
||||
@@ -168,40 +158,31 @@ decorated version of it. This object can be used to add the following:
|
||||
.func<&free_function>("free"_hs);
|
||||
```
|
||||
|
||||
The function requires as an argument the identifier to give to the meta
|
||||
function once created. Users can then access meta functions at runtime by
|
||||
searching for them by _name_.<br/>
|
||||
The `func` function requires the identifier to use for the meta data function.
|
||||
Users can then access it by _name_ at runtime.<br/>
|
||||
Overloading of meta functions is supported. Overloaded functions are resolved
|
||||
at runtime by the reflection system according to the types of the arguments.
|
||||
|
||||
* _Base classes_. A base class is such that the underlying type is actually
|
||||
derived from it. In this case, the reflection system tracks the relationship
|
||||
and allows for implicit casts at runtime when required.<br/>
|
||||
Use the `base` member function for this purpose:
|
||||
derived from it:
|
||||
|
||||
```cpp
|
||||
entt::meta<derived_type>().base<base_type>();
|
||||
```
|
||||
|
||||
From now on, wherever a `base_type` is required, an instance of `derived_type`
|
||||
will also be accepted.
|
||||
The reflection system tracks the relationship and allows for implicit casts at
|
||||
runtime when required. In other terms, wherever a `base_type` is required, an
|
||||
instance of `derived_type` is also accepted.
|
||||
|
||||
* _Conversion functions_. Actual types can be converted, this is a fact. Just
|
||||
think of the relationship between a `double` and an `int` to see it. Similar
|
||||
to bases, conversion functions allow users to define conversions that will be
|
||||
implicitly performed by the reflection system when required.<br/>
|
||||
Use the `conv` member function for this purpose:
|
||||
* _Conversion functions_. Conversion functions allow users to define conversions
|
||||
that are implicitly performed by the reflection system when required:
|
||||
|
||||
```cpp
|
||||
entt::meta<double>().conv<int>();
|
||||
```
|
||||
|
||||
That's all, everything users need to create meta types and enjoy the reflection
|
||||
system. At first glance it may not seem that much, but users usually learn to
|
||||
appreciate it over time.<br/>
|
||||
Also, do not forget what these few lines hide under the hood: a built-in,
|
||||
non-intrusive and macro-free system for reflection in C++. Features that are
|
||||
definitely worth the price, at least for me.
|
||||
This is everything users need to create meta types. Refer to the inline
|
||||
documentation for further details.
|
||||
|
||||
## Any to the rescue
|
||||
|
||||
@@ -214,13 +195,13 @@ The API is very similar to that of the `any` type. The class `meta_any` _wraps_
|
||||
many of the feature to infer a meta node, before forwarding some or all of the
|
||||
arguments to the underlying storage.<br/>
|
||||
Among the few relevant differences, `meta_any` adds support for containers and
|
||||
pointer-like types (see the following sections for more details), while `any`
|
||||
does not.<br/>
|
||||
Similar to `any`, this class can also be used to create _aliases_ for unmanaged
|
||||
pointer-like types, while `any` doesn't.<br/>
|
||||
Similar to `any`, this class is also used to create _aliases_ for unmanaged
|
||||
objects either with `forward_as_meta` or using the `std::in_place_type<T &>`
|
||||
disambiguation tag, as well as from an existing object by means of the `as_ref`
|
||||
member function. However, unlike `any`, `meta_any` treats an empty instance and
|
||||
one initialized with `void` differently:
|
||||
member function.<br/>
|
||||
Unlike `any` instead, `meta_any` treats an empty instance and one initialized
|
||||
with `void` differently:
|
||||
|
||||
```cpp
|
||||
entt::meta_any empty{};
|
||||
@@ -229,21 +210,19 @@ entt::meta_any other{std::in_place_type<void>};
|
||||
|
||||
While `any` considers both as empty, `meta_any` treats objects initialized with
|
||||
`void` as if they were _valid_ ones. This allows to differentiate between failed
|
||||
function calls and function calls that are successful but return nothing.<br/>
|
||||
function calls and function calls that are successful but return nothing.
|
||||
|
||||
Finally, the member functions `try_cast`, `cast` and `allow_cast` are used to
|
||||
cast the underlying object to a given type (either a reference or a value type)
|
||||
or to _convert_ a `meta_any` in such a way that a cast becomes viable for the
|
||||
resulting object. There is in fact no `any_cast` equivalent for `meta_any`.
|
||||
resulting object.<br/>
|
||||
There is in fact no `any_cast` equivalent for `meta_any`.
|
||||
|
||||
## Enjoy the runtime
|
||||
|
||||
Once the web of reflected types has been constructed, it's a matter of using it
|
||||
at runtime where required.<br/>
|
||||
All this has the great merit that the reflection system stands in fact as a
|
||||
non-intrusive tool for the runtime, unlike the vast majority of the things
|
||||
offered by this library and closely linked to the compile-time.
|
||||
|
||||
To search for a reflected type there are a few options:
|
||||
Once the web of reflected types is constructed, it's a matter of using it at
|
||||
runtime where required.<br/>
|
||||
There are a few options to search for a reflected type:
|
||||
|
||||
```cpp
|
||||
// direct access to a reflected type
|
||||
@@ -257,8 +236,8 @@ auto by_type_id = entt::resolve(entt::type_id<my_type>());
|
||||
```
|
||||
|
||||
There exists also an overload of the `resolve` function to use to iterate all
|
||||
the reflected types at once. It returns an iterable object that can be used in a
|
||||
range-for loop:
|
||||
reflected types at once. It returns an iterable object to be used in a range-for
|
||||
loop:
|
||||
|
||||
```cpp
|
||||
for(auto &&[id, type]: entt::resolve()) {
|
||||
@@ -270,9 +249,7 @@ In all cases, the returned value is an instance of `meta_type` (possibly with
|
||||
its id). This kind of objects offer an API to know their _runtime identifiers_,
|
||||
to iterate all the meta objects associated with them and even to build instances
|
||||
of the underlying type.<br/>
|
||||
Refer to the inline documentation for all the details.
|
||||
|
||||
Meta data members and functions are accessed by name among the other things:
|
||||
Meta data members and functions are accessed by name:
|
||||
|
||||
* Meta data members:
|
||||
|
||||
@@ -297,11 +274,11 @@ Meta data members and functions are accessed by name among the other things:
|
||||
A meta function object offers an API to query the underlying type (for
|
||||
example, to know if it's a const or a static function), to know the number of
|
||||
arguments, the meta return type and the meta types of the parameters. In
|
||||
addition, a meta function object can be used to invoke the underlying function
|
||||
and then get the return value in the form of a `meta_any` object.
|
||||
addition, a meta function object is used to invoke the underlying function and
|
||||
then get the return value in the form of a `meta_any` object.
|
||||
|
||||
All the meta objects thus obtained as well as the meta types can be explicitly
|
||||
converted to a boolean value to check if they are valid:
|
||||
All the meta objects thus obtained as well as the meta types explicitly convert
|
||||
to a boolean value to check for validity:
|
||||
|
||||
```cpp
|
||||
if(auto func = entt::resolve<my_type>().func("member"_hs); func) {
|
||||
@@ -319,26 +296,23 @@ for(auto &&[id, type]: entt::resolve<my_type>().base()) {
|
||||
}
|
||||
```
|
||||
|
||||
A meta type can also be used to `construct` actual instances of the underlying
|
||||
Meta type are also used to `construct` actual instances of the underlying
|
||||
type.<br/>
|
||||
In particular, the `construct` member function accepts a variable number of
|
||||
arguments and searches for a match. It then returns a `meta_any` object that may
|
||||
or may not be initialized, depending on whether a suitable constructor has been
|
||||
found or not.
|
||||
or may not be initialized, depending on whether a suitable constructor was found
|
||||
or not.
|
||||
|
||||
There is no object that wraps the destructor of a meta type nor a `destroy`
|
||||
member function in its API. Destructors are invoked implicitly by `meta_any`
|
||||
behind the scenes and users have not to deal with them explicitly. Furthermore,
|
||||
they have no name, cannot be searched and wouldn't have member functions to
|
||||
expose anyway.<br/>
|
||||
Similarly, conversion functions aren't directly accessible. They are used
|
||||
they've no name, cannot be searched and wouldn't have member functions to expose
|
||||
anyway.<br/>
|
||||
Similarly, conversion functions aren't directly accessible. They're used
|
||||
internally by `meta_any` and the meta objects when needed.
|
||||
|
||||
Meta types and meta objects in general contain much more than what is said: a
|
||||
plethora of functions in addition to those listed whose purposes and uses go
|
||||
unfortunately beyond the scope of this document.<br/>
|
||||
I invite anyone interested in the subject to look at the code, experiment and
|
||||
read the inline documentation to get the best out of this powerful tool.
|
||||
Meta types and meta objects in general contain much more than what was said.
|
||||
Refer to the inline documentation for further details.
|
||||
|
||||
## Container support
|
||||
|
||||
@@ -349,7 +323,7 @@ meta system in many cases.
|
||||
|
||||
To make a container be recognized as such by the meta system, users are required
|
||||
to provide specializations for either the `meta_sequence_container_traits` class
|
||||
or the `meta_associative_container_traits` class, according to the actual type
|
||||
or the `meta_associative_container_traits` class, according to the actual _type_
|
||||
of the container.<br/>
|
||||
`EnTT` already exports the specializations for some common classes. In
|
||||
particular:
|
||||
@@ -386,11 +360,10 @@ if(any.type().is_sequence_container()) {
|
||||
|
||||
The method to use to get a proxy object for associative containers is
|
||||
`as_associative_container` instead.<br/>
|
||||
It goes without saying that it's not necessary to perform a double check.
|
||||
Instead, it's sufficient to query the meta type or verify that the proxy object
|
||||
is valid. In fact, proxies are contextually convertible to bool to know if they
|
||||
are valid. For example, invalid proxies are returned when the wrapped object
|
||||
isn't a container.<br/>
|
||||
It's not necessary to perform a double check actually. Instead, it's enough to
|
||||
query the meta type or verify that the proxy object is valid. In fact, proxies
|
||||
are contextually convertible to bool to check for validity. For example, invalid
|
||||
proxies are returned when the wrapped object isn't a container.<br/>
|
||||
In all cases, users aren't expected to _reflect_ containers explicitly. It's
|
||||
sufficient to assign a container for which a specialization of the traits
|
||||
classes exists to a `meta_any` object to be able to get its proxy object.
|
||||
@@ -402,32 +375,22 @@ to case. In particular:
|
||||
* The `value_type` member function returns the meta type of the elements.
|
||||
|
||||
* The `size` member function returns the number of elements in the container as
|
||||
an unsigned integer value:
|
||||
|
||||
```cpp
|
||||
const auto size = view.size();
|
||||
```
|
||||
an unsigned integer value.
|
||||
|
||||
* The `resize` member function allows to resize the wrapped container and
|
||||
returns true in case of success:
|
||||
|
||||
```cpp
|
||||
const bool ok = view.resize(3u);
|
||||
```
|
||||
|
||||
returns true in case of success.<br/>
|
||||
For example, it's not possible to resize fixed size containers.
|
||||
|
||||
* The `clear` member function allows to clear the wrapped container and returns
|
||||
true in case of success:
|
||||
|
||||
```cpp
|
||||
const bool ok = view.clear();
|
||||
```
|
||||
|
||||
true in case of success.<br/>
|
||||
For example, it's not possible to clear fixed size containers.
|
||||
|
||||
* The `begin` and `end` member functions return opaque iterators that can be
|
||||
used to iterate the container directly:
|
||||
* The `reserve` member function allows to increase the capacity of the wrapped
|
||||
container and returns true in case of success.<br/>
|
||||
For example, it's not possible to increase capacity of fixed size containers.
|
||||
|
||||
* The `begin` and `end` member functions return opaque iterators that is used to
|
||||
iterate the container directly:
|
||||
|
||||
```cpp
|
||||
for(entt::meta_any element: view) {
|
||||
@@ -441,7 +404,7 @@ to case. In particular:
|
||||
All meta iterators are input iterators and don't offer an indirection operator
|
||||
on purpose.
|
||||
|
||||
* The `insert` member function can be used to add elements to the container. It
|
||||
* The `insert` member function is used to add elements to the container. It
|
||||
accepts a meta iterator and the element to insert:
|
||||
|
||||
```cpp
|
||||
@@ -451,15 +414,15 @@ to case. In particular:
|
||||
```
|
||||
|
||||
This function returns a meta iterator pointing to the inserted element and a
|
||||
boolean value to indicate whether the operation was successful or not. Note
|
||||
that a call to `insert` may silently fail in case of fixed size containers or
|
||||
whether the arguments aren't at least convertible to the required types.<br/>
|
||||
Since the meta iterators are contextually convertible to bool, users can rely
|
||||
on them to know if the operation has failed on the actual container or
|
||||
upstream, for example for an argument conversion problem.
|
||||
boolean value to indicate whether the operation was successful or not. A call
|
||||
to `insert` may silently fail in case of fixed size containers or whether the
|
||||
arguments aren't at least convertible to the required types.<br/>
|
||||
Since meta iterators are contextually convertible to bool, users can rely on
|
||||
them to know if the operation failed on the actual container or upstream, for
|
||||
example due to an argument conversion problem.
|
||||
|
||||
* The `erase` member function can be used to remove elements from the container.
|
||||
It accepts a meta iterator to the element to remove:
|
||||
* The `erase` member function is used to remove elements from the container. It
|
||||
accepts a meta iterator to the element to remove:
|
||||
|
||||
```cpp
|
||||
auto first = view.begin();
|
||||
@@ -468,11 +431,11 @@ to case. In particular:
|
||||
```
|
||||
|
||||
This function returns a meta iterator following the last removed element and a
|
||||
boolean value to indicate whether the operation was successful or not. Note
|
||||
that a call to `erase` may silently fail in case of fixed size containers.
|
||||
boolean value to indicate whether the operation was successful or not. A call
|
||||
to `erase` may silently fail in case of fixed size containers.
|
||||
|
||||
* The `operator[]` can be used to access elements in a container. It accepts a
|
||||
single argument, that is the position of the element to return:
|
||||
* The `operator[]` is used to access container elements. It accepts a single
|
||||
argument, the position of the element to return:
|
||||
|
||||
```cpp
|
||||
for(std::size_t pos{}, last = view.size(); pos < last; ++pos) {
|
||||
@@ -482,8 +445,8 @@ to case. In particular:
|
||||
```
|
||||
|
||||
The function returns instances of `meta_any` that directly refer to the actual
|
||||
elements. Modifying the returned object will then directly modify the element
|
||||
inside the container.<br/>
|
||||
elements. Modifying the returned object directly modifies the element inside
|
||||
the container.<br/>
|
||||
Depending on the underlying sequence container, this operation may not be as
|
||||
efficient. For example, in the case of an `std::list`, a positional access
|
||||
translates to a linear visit of the list itself (probably not what the user
|
||||
@@ -508,21 +471,17 @@ differences in behavior in the case of key-only containers. In particular:
|
||||
`std::map<int, char>`.
|
||||
|
||||
* The `size` member function returns the number of elements in the container as
|
||||
an unsigned integer value:
|
||||
|
||||
```cpp
|
||||
const auto size = view.size();
|
||||
```
|
||||
an unsigned integer value.
|
||||
|
||||
* The `clear` member function allows to clear the wrapped container and returns
|
||||
true in case of success:
|
||||
true in case of success.
|
||||
|
||||
```cpp
|
||||
const bool ok = view.clear();
|
||||
```
|
||||
* The `reserve` member function allows to increase the capacity of the wrapped
|
||||
container and returns true in case of success.<br/>
|
||||
For example, it's not possible to increase capacity of standard maps.
|
||||
|
||||
* The `begin` and `end` member functions return opaque iterators that can be
|
||||
used to iterate the container directly:
|
||||
* The `begin` and `end` member functions return opaque iterators that are used
|
||||
to iterate the container directly:
|
||||
|
||||
```cpp
|
||||
for(std::pair<entt::meta_any, entt::meta_any> element: view) {
|
||||
@@ -539,11 +498,11 @@ differences in behavior in the case of key-only containers. In particular:
|
||||
|
||||
While the accessed key is usually constant in the associative containers and
|
||||
is therefore returned by copy, the value (if any) is wrapped by an instance of
|
||||
`meta_any` that directly refers to the actual element. Modifying it will then
|
||||
directly modify the element inside the container.
|
||||
`meta_any` that directly refers to the actual element. Modifying it directly
|
||||
modifies the element inside the container.
|
||||
|
||||
* The `insert` member function can be used to add elements to the container. It
|
||||
accepts two arguments, respectively the key and the value to be inserted:
|
||||
* The `insert` member function is used to add elements to a container. It gets
|
||||
two arguments, respectively the key and the value to insert:
|
||||
|
||||
```cpp
|
||||
auto last = view.end();
|
||||
@@ -552,39 +511,39 @@ differences in behavior in the case of key-only containers. In particular:
|
||||
```
|
||||
|
||||
This function returns a boolean value to indicate whether the operation was
|
||||
successful or not. Note that a call to `insert` may fail when the arguments
|
||||
aren't at least convertible to the required types.
|
||||
successful or not. A call to `insert` may fail when the arguments aren't at
|
||||
least convertible to the required types.
|
||||
|
||||
* The `erase` member function can be used to remove elements from the container.
|
||||
It accepts a single argument, that is the key to be removed:
|
||||
* The `erase` member function is used to remove elements from a container. It
|
||||
gets a single argument, the key to remove:
|
||||
|
||||
```cpp
|
||||
view.erase(42);
|
||||
```
|
||||
|
||||
This function returns a boolean value to indicate whether the operation was
|
||||
successful or not. Note that a call to `erase` may fail when the argument
|
||||
isn't at least convertible to the required type.
|
||||
successful or not. A call to `erase` may fail when the argument isn't at least
|
||||
convertible to the required type.
|
||||
|
||||
* The `operator[]` can be used to access elements in a container. It accepts a
|
||||
single argument, that is the key of the element to return:
|
||||
* The `operator[]` is used to access elements in a container. It gets a single
|
||||
argument, the key of the element to return:
|
||||
|
||||
```cpp
|
||||
entt::meta_any value = view[42];
|
||||
```
|
||||
|
||||
The function returns instances of `meta_any` that directly refer to the actual
|
||||
elements. Modifying the returned object will then directly modify the element
|
||||
inside the container.
|
||||
elements. Modifying the returned object directly modifies the element inside
|
||||
the container.
|
||||
|
||||
Container support is minimal but likely sufficient to satisfy all needs.
|
||||
|
||||
## Pointer-like types
|
||||
|
||||
As with containers, it's also possible to communicate to the meta system which
|
||||
types to consider _pointers_. This will allow to dereference instances of
|
||||
`meta_any`, thus obtaining light _references_ to the pointed objects that are
|
||||
also correctly associated with their meta types.<br/>
|
||||
As with containers, it's also possible to _tell_ to the meta system which types
|
||||
are _pointers_. This makes it possible to dereference instances of `meta_any`,
|
||||
thus obtaining light _references_ to pointed objects that are also correctly
|
||||
associated with their meta types.<br/>
|
||||
To make the meta system recognize a type as _pointer-like_, users can specialize
|
||||
the `is_meta_pointer_like` class. `EnTT` already exports the specializations for
|
||||
some common classes. In particular:
|
||||
@@ -614,13 +573,12 @@ if(any.type().is_pointer_like()) {
|
||||
}
|
||||
```
|
||||
|
||||
Of course, it's not necessary to perform a double check. Instead, it's enough to
|
||||
query the meta type or verify that the returned object is valid. For example,
|
||||
invalid instances are returned when the wrapped object isn't a pointer-like
|
||||
type.<br/>
|
||||
Note that dereferencing a pointer-like object returns an instance of `meta_any`
|
||||
which refers to the pointed object and allows users to modify it directly
|
||||
(unless the returned element is const, of course).
|
||||
It's not necessary to perform a double check. Instead, it's enough to query the
|
||||
meta type or verify that the returned object is valid. For example, invalid
|
||||
instances are returned when the wrapped object isn't a pointer-like type.<br/>
|
||||
Dereferencing a pointer-like object returns an instance of `meta_any` which
|
||||
_refers_ to the pointed object. Modifying it means modifying the pointed object
|
||||
directly (unless the returned element is const).
|
||||
|
||||
In general, _dereferencing_ a pointer-like type boils down to a `*ptr`. However,
|
||||
`EnTT` also supports classes that don't offer an `operator*`. In particular:
|
||||
@@ -648,12 +606,12 @@ In general, _dereferencing_ a pointer-like type boils down to a `*ptr`. However,
|
||||
};
|
||||
```
|
||||
|
||||
In all other cases, that is, when dereferencing a pointer works as expected and
|
||||
regardless of the pointed type, no user intervention is required.
|
||||
In all other cases and when dereferencing a pointer works as expected regardless
|
||||
of the pointed type, no user intervention is required.
|
||||
|
||||
## Template information
|
||||
|
||||
Meta types also provide a minimal set of information about the nature of the
|
||||
Meta types also provide a minimal set of information about the _nature_ of the
|
||||
original type in case it's a class template.<br/>
|
||||
By default, this works out of the box and requires no user action. However, it's
|
||||
important to include the header file `template.hpp` to make this information
|
||||
@@ -688,9 +646,9 @@ template<typename Ret, typename... Args>
|
||||
struct function_type<Ret(Args...)> {};
|
||||
```
|
||||
|
||||
In this case, rather than the function type, the user might want the return type
|
||||
and unpacked arguments as if they were different template parameters for the
|
||||
original class template.<br/>
|
||||
In this case, rather than the function type, it might be useful to provide the
|
||||
return type and unpacked arguments as if they were different template parameters
|
||||
for the original class template.<br/>
|
||||
To achieve this, users must enter the library internals and provide their own
|
||||
specialization for the class template `entt::meta_template_traits`, such as:
|
||||
|
||||
@@ -704,8 +662,8 @@ struct entt::meta_template_traits<function_type<Ret(Args...)>> {
|
||||
|
||||
The reflection system doesn't verify the accuracy of the information nor infer a
|
||||
correspondence between real types and meta types.<br/>
|
||||
Therefore, the specialization will be used as is and the information it contains
|
||||
will be associated with the appropriate type when required.
|
||||
Therefore, the specialization is used as is and the information it contains is
|
||||
associated with the appropriate type when required.
|
||||
|
||||
## Automatic conversions
|
||||
|
||||
@@ -752,29 +710,29 @@ any.allow_cast(type);
|
||||
int value = any.cast<int>();
|
||||
```
|
||||
|
||||
This should make working with arithmetic types and scoped or unscoped enums as
|
||||
easy as it is in C++.<br/>
|
||||
It's also worth noting that it's still possible to set up conversion functions
|
||||
manually and these will always be preferred over the automatic ones.
|
||||
This makes working with arithmetic types and scoped or unscoped enums as easy as
|
||||
it is in C++.<br/>
|
||||
It's still possible to set up conversion functions manually and these are always
|
||||
preferred over the automatic ones.
|
||||
|
||||
## Implicitly generated default constructor
|
||||
|
||||
In many cases, it's useful to be able to create objects of default constructible
|
||||
types through the reflection system, while not having to explicitly register the
|
||||
meta type or the default constructor.<br/>
|
||||
Creating objects of default constructible types through the reflection system
|
||||
while not having to explicitly register the meta type or its default constructor
|
||||
is also possible.<br/>
|
||||
For example, in the case of primitive types like `int` or `char`, but not just
|
||||
them.
|
||||
|
||||
For this reason and only for default constructible types, default constructors
|
||||
are automatically defined and associated with their meta types, whether they are
|
||||
explicitly or implicitly generated.<br/>
|
||||
For default constructible types only, default constructors are automatically
|
||||
defined and associated with their meta types, whether they are explicitly or
|
||||
implicitly generated.<br/>
|
||||
Therefore, this is all is needed to construct an integer from its meta type:
|
||||
|
||||
```cpp
|
||||
entt::resolve<int>().construct();
|
||||
```
|
||||
|
||||
Where the meta type can be for example the one returned from a meta container,
|
||||
Where the meta type is for example the one returned from a meta container,
|
||||
useful for building keys without knowing or having to register the actual types.
|
||||
|
||||
In all cases, when users register default constructors, they are preferred both
|
||||
@@ -783,8 +741,8 @@ during searches and when the `construct` member function is invoked.
|
||||
## From void to any
|
||||
|
||||
Sometimes all a user has is an opaque pointer to an object of a known meta type.
|
||||
It would be handy in this case to be able to construct a `meta_any` object from
|
||||
them.<br/>
|
||||
It would be handy in this case to be able to construct a `meta_any` element from
|
||||
it.<br/>
|
||||
For this purpose, the `meta_type` class offers a `from_void` member function
|
||||
designed to convert an opaque pointer into a `meta_any`:
|
||||
|
||||
@@ -792,9 +750,8 @@ designed to convert an opaque pointer into a `meta_any`:
|
||||
entt::meta_any any = entt::resolve(id).from_void(element);
|
||||
```
|
||||
|
||||
It goes without saying that it's not possible to do a check on the actual type.
|
||||
Therefore, this call can be considered as a _static cast_ with all the problems
|
||||
and undefined behaviors of the case following errors.<br/>
|
||||
Unfortunately, it's not possible to do a check on the actual type. Therefore,
|
||||
this call can be considered as a _static cast_ with all its _problems_.<br/>
|
||||
On the other hand, the ability to construct a `meta_any` from an opaque pointer
|
||||
opens the door to some pretty interesting uses that are worth exploring.
|
||||
|
||||
@@ -826,17 +783,17 @@ There are a few alternatives available at the moment:
|
||||
entt::meta<my_type>().func<&my_type::member_function, entt::as_void_t>("member"_hs);
|
||||
```
|
||||
|
||||
If the use with functions is obvious, it must be said that it's also possible
|
||||
to use this policy with constructors and data members. In the first case, the
|
||||
constructor will be invoked but the returned wrapper will actually be empty.
|
||||
In the second case, instead, the property will not be accessible for reading.
|
||||
If the use with functions is obvious, perhaps less so is use with constructors
|
||||
and data members. In the first case, the returned wrapper is always empty even
|
||||
though the constructor is still invoked. In the second case, the property
|
||||
isn't accessible for reading instead.
|
||||
|
||||
* The _as-ref_ and _as-cref_ policies, associated with the types
|
||||
`entt::as_ref_t` and `entt::as_cref_t`.<br/>
|
||||
They allow to build wrappers that act as references to unmanaged objects.
|
||||
Accessing the object contained in the wrapper for which the _reference_ was
|
||||
requested will make it possible to directly access the instance used to
|
||||
initialize the wrapper itself:
|
||||
requested makes it possible to directly access the instance used to initialize
|
||||
the wrapper itself:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>().data<&my_type::data_member, entt::as_ref_t>("member"_hs);
|
||||
@@ -854,21 +811,16 @@ obvious corner cases that can in turn be solved with the use of policies.
|
||||
|
||||
## Named constants and enums
|
||||
|
||||
A special mention should be made for constant values and enums. It wouldn't be
|
||||
necessary, but it will help distracted readers.
|
||||
|
||||
As mentioned, the `data` member function can be used to reflect constants of any
|
||||
type among the other things.<br/>
|
||||
This allows users to create meta types for enums that will work exactly like any
|
||||
other meta type built from a class. Similarly, arithmetic types can be enriched
|
||||
As mentioned, the `data` member function is used to reflect constants of any
|
||||
type.<br/>
|
||||
This allows users to create meta types for enums that work exactly like any
|
||||
other meta type built from a class. Similarly, arithmetic types are _enriched_
|
||||
with constants of special meaning where required.<br/>
|
||||
Personally, I find it very useful not to export what is the difference between
|
||||
enums and classes in C++ directly in the space of the reflected types.
|
||||
All values thus exported appear to users as if they were constant data members
|
||||
of the reflected types. This avoids the need to _export_ what is the difference
|
||||
between enums and classes in C++ directly in the space of the reflected types.
|
||||
|
||||
All the values thus exported will appear to users as if they were constant data
|
||||
members of the reflected types.
|
||||
|
||||
Exporting constant values or elements from an enum is as simple as ever:
|
||||
Exposing constant values or elements from an enum is quite simple:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_enum>()
|
||||
@@ -878,28 +830,22 @@ entt::meta<my_enum>()
|
||||
entt::meta<int>().data<2048>("max_int"_hs);
|
||||
```
|
||||
|
||||
It goes without saying that accessing them is trivial as well. It's a matter of
|
||||
doing the following, as with any other data member of a meta type:
|
||||
Accessing them is trivial as well. It's a matter of doing the following, as with
|
||||
any other data member of a meta type:
|
||||
|
||||
```cpp
|
||||
auto value = entt::resolve<my_enum>().data("a_value"_hs).get({}).cast<my_enum>();
|
||||
auto max = entt::resolve<int>().data("max_int"_hs).get({}).cast<int>();
|
||||
```
|
||||
|
||||
As a side note, remember that all this happens behind the scenes without any
|
||||
allocation because of the small object optimization performed by the `meta_any`
|
||||
class.
|
||||
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
|
||||
|
||||
Sometimes (for example, when it comes to creating an editor) it might be useful
|
||||
to attach properties to the meta objects created. Fortunately, this is possible
|
||||
for most of them.<br/>
|
||||
For the meta objects that support properties, the member functions of the
|
||||
factory used for registering them will return an extended version of the factory
|
||||
itself. The latter can be used to attach properties to the last created meta
|
||||
object.<br/>
|
||||
Apparently, it's more difficult to say than to do:
|
||||
for most of them:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>().type("reflected_type"_hs).prop("tooltip"_hs, "message");
|
||||
@@ -914,10 +860,10 @@ Key only properties are also supported out of the box:
|
||||
entt::meta<my_type>().type("reflected_type"_hs).prop(my_enum::key_only);
|
||||
```
|
||||
|
||||
To attach multiple properties to a meta object, it's possible to invoke `prop`
|
||||
more than once.<br/>
|
||||
It's also possible to invoke `prop` at different times, as long as the factory
|
||||
is reset to the meta object of interest.
|
||||
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/>
|
||||
@@ -940,7 +886,7 @@ form of a `meta_any` object.
|
||||
|
||||
## Unregister types
|
||||
|
||||
A type registered with the reflection system can also be unregistered. This
|
||||
A type registered with the reflection system can also be _unregistered_. This
|
||||
means unregistering all its data members, member functions, conversion functions
|
||||
and so on. However, base classes aren't unregistered as well, since they don't
|
||||
necessarily depend on it.<br/>
|
||||
@@ -969,7 +915,7 @@ A type can be re-registered later with a completely different name and form.
|
||||
## Meta context
|
||||
|
||||
All meta types and their parts are created at runtime and stored in a default
|
||||
_context_. This can be reached via a service locator as:
|
||||
_context_. This is obtained via a service locator as:
|
||||
|
||||
```cpp
|
||||
auto &&context = entt::locator<entt::meta_context>::value_or();
|
||||
@@ -984,8 +930,8 @@ auto &&context = entt::locator<entt::meta_context>::value_or();
|
||||
std::swap(context, other);
|
||||
```
|
||||
|
||||
This can be useful for testing purposes or to define multiple contexts with
|
||||
different meta objects to be used as appropriate.
|
||||
This is useful for testing purposes or to define multiple context objects with
|
||||
different meta type to use as appropriate.
|
||||
|
||||
If _replacing_ the default context isn't enough, `EnTT` also offers the ability
|
||||
to use multiple and externally managed contexts with the runtime reflection
|
||||
@@ -998,16 +944,16 @@ entt::meta_ctx context{};
|
||||
auto factory = entt::meta<my_type>(context).type("reflected_type"_hs);
|
||||
```
|
||||
|
||||
By doing so, the new meta type won't be available in the default context but
|
||||
will be usable by passing around the new context when needed, such as when
|
||||
creating a new `meta_any` object:
|
||||
By doing so, the new meta type isn't available in the default context but is
|
||||
usable by passing around the new context when needed, such as when creating a
|
||||
new `meta_any` object:
|
||||
|
||||
```cpp
|
||||
entt::meta_any any{context, std::in_place_type<my_type>};
|
||||
```
|
||||
|
||||
Similarly, to search for meta types in a context other than the default one, it
|
||||
will be necessary to pass it to the `resolve` function:
|
||||
Similarly, to search for meta types in a context other than the default one,
|
||||
it's necessary to pass it to the `resolve` function:
|
||||
|
||||
```cpp
|
||||
entt::meta_type type = entt::resolve(context, "reflected_type"_hs)
|
||||
|
||||
@@ -26,17 +26,16 @@ This module aims to make it simple and easy to use.
|
||||
|
||||
The library allows to define _concepts_ as interfaces to fulfill with concrete
|
||||
classes without having to inherit from a common base.<br/>
|
||||
This is, among others, one of the advantages of static polymorphism in general
|
||||
Among others, this is one of the advantages of static polymorphism in general
|
||||
and of a generic wrapper like that offered by the `poly` class template in
|
||||
particular.<br/>
|
||||
What users get is an object that can be passed around as such and not through a
|
||||
reference or a pointer, as happens when it comes to working with dynamic
|
||||
polymorphism.
|
||||
The result is an object to pass around as such and not through a reference or a
|
||||
pointer, as it happens when it comes to working with dynamic polymorphism.
|
||||
|
||||
Since the `poly` class template makes use of `entt::any` internally, it also
|
||||
supports most of its feature. Among the most important, the possibility to
|
||||
create aliases to existing and thus unmanaged objects. This allows users to
|
||||
exploit the static polymorphism while maintaining ownership of objects.<br/>
|
||||
supports most of its feature. For example, the possibility to create aliases to
|
||||
existing and thus unmanaged objects. This allows users to exploit the static
|
||||
polymorphism while maintaining ownership of objects.<br/>
|
||||
Likewise, the `poly` class template also benefits from the small buffer
|
||||
optimization offered by the `entt::any` class and therefore minimizes the number
|
||||
of allocations, avoiding them altogether where possible.
|
||||
@@ -44,7 +43,7 @@ of allocations, avoiding them altogether where possible.
|
||||
## Other libraries
|
||||
|
||||
There are some very interesting libraries regarding static polymorphism.<br/>
|
||||
Among all, the two that I prefer are:
|
||||
The ones that I like more are:
|
||||
|
||||
* [`dyno`](https://github.com/ldionne/dyno): runtime polymorphism done right.
|
||||
* [`Poly`](https://github.com/facebook/folly/blob/master/folly/docs/Poly.md):
|
||||
@@ -69,18 +68,18 @@ use the terminology introduced by Eric Niebler) is to define a _concept_ that
|
||||
types will have to adhere to.<br/>
|
||||
For this purpose, the library offers a single class that supports both deduced
|
||||
and fully defined interfaces. Although having interfaces deduced automatically
|
||||
is convenient and allows users to write less code in most cases, this has some
|
||||
is convenient and allows users to write less code in most cases, it has some
|
||||
limitations and it's therefore useful to be able to get around the deduction by
|
||||
providing a custom definition for the static virtual table.
|
||||
|
||||
Once the interface is defined, it will be sufficient to provide a generic
|
||||
implementation to fulfill the concept.<br/>
|
||||
Once the interface is defined, a generic implementation is needed to fulfill the
|
||||
concept itself.<br/>
|
||||
Also in this case, the library allows customizations based on types or families
|
||||
of types, so as to be able to go beyond the generic case where necessary.
|
||||
|
||||
## Deduced interface
|
||||
|
||||
This is how a concept with a deduced interface is introduced:
|
||||
This is how a concept with a deduced interface is defined:
|
||||
|
||||
```cpp
|
||||
struct Drawable: entt::type_list<> {
|
||||
@@ -108,12 +107,12 @@ struct Drawable: entt::type_list<> {
|
||||
};
|
||||
```
|
||||
|
||||
In this case, all parameters must be passed to `invoke` after the reference to
|
||||
In this case, all parameters are passed to `invoke` after the reference to
|
||||
`this` and the return value is whatever the internal call returns.<br/>
|
||||
As for `invoke`, this is a name that is injected into the _concept_ through
|
||||
`Base`, from which one must necessarily inherit. Since it's also a dependent
|
||||
name, the `this-> template` form is unfortunately necessary due to the rules of
|
||||
the language. However, there exists also an alternative that goes through an
|
||||
the language. However, there also exists an alternative that goes through an
|
||||
external call:
|
||||
|
||||
```cpp
|
||||
@@ -165,12 +164,12 @@ struct Drawable: entt::type_list<bool(int) const> {
|
||||
|
||||
Why should a user fully define a concept if the function types are the same as
|
||||
the deduced ones?<br>
|
||||
Because, in fact, this is exactly the limitation that can be worked around by
|
||||
manually defining the static virtual table.
|
||||
In fact, this is the limitation that can be worked around by manually defining
|
||||
the static virtual table.
|
||||
|
||||
When things are deduced, there is an implicit constraint.<br/>
|
||||
If the concept exposes a member function called `draw` with function type
|
||||
`void()`, a concept can be satisfied:
|
||||
`void()`, a concept is satisfied:
|
||||
|
||||
* Either by a class that exposes a member function with the same name and the
|
||||
same signature.
|
||||
@@ -179,7 +178,7 @@ If the concept exposes a member function called `draw` with function type
|
||||
interface itself.
|
||||
|
||||
In other words, it's not possible to make use of functions not belonging to the
|
||||
interface, even if they are present in the types that fulfill the concept.<br/>
|
||||
interface, even if they're part of the types that fulfill the concept.<br/>
|
||||
Similarly, it's not possible to deduce a function in the static virtual table
|
||||
with a function type different from that of the associated member function in
|
||||
the interface itself.
|
||||
@@ -200,8 +199,8 @@ struct Drawable: entt::type_list<> {
|
||||
};
|
||||
```
|
||||
|
||||
In this case, it's stated that the `draw` method of a generic type will be
|
||||
enough to satisfy the requirements of the `Drawable` concept.<br/>
|
||||
In this case, it's stated that the `draw` method of a generic type is enough to
|
||||
satisfy the requirements of the `Drawable` concept.<br/>
|
||||
Both member functions and free functions are supported to fulfill concepts:
|
||||
|
||||
```cpp
|
||||
@@ -251,15 +250,15 @@ struct DrawableAndErasable: entt::type_list<> {
|
||||
```
|
||||
|
||||
The static virtual table is empty and must remain so.<br/>
|
||||
On the other hand, `type` no longer inherits from `Base` and instead forwards
|
||||
On the other hand, `type` no longer inherits from `Base`. Instead, it forwards
|
||||
its template parameter to the type exposed by the _base class_. Internally, the
|
||||
size of the static virtual table of the base class is used as an offset for the
|
||||
local indexes.<br/>
|
||||
_size_ of the static virtual table of the base class is used as an offset for
|
||||
the local indexes.<br/>
|
||||
Finally, by means of the `value_list_cat_t` utility, the implementation consists
|
||||
in appending the new functions to the previous list.
|
||||
|
||||
As for a defined concept instead, also the list of types must be extended, in a
|
||||
similar way to what is shown for the implementation of the above concept.<br/>
|
||||
As for a defined concept instead, the list of types is _extended_ in a similar
|
||||
way to what is shown for the implementation of the above concept.<br/>
|
||||
To do this, it's useful to declare a function that allows to convert a _concept_
|
||||
into its underlying `type_list` object:
|
||||
|
||||
@@ -268,8 +267,8 @@ template<typename... Type>
|
||||
entt::type_list<Type...> as_type_list(const entt::type_list<Type...> &);
|
||||
```
|
||||
|
||||
The definition isn't strictly required, since the function will only be used
|
||||
through a `decltype` as it follows:
|
||||
The definition isn't strictly required, since the function is only used through
|
||||
a `decltype` as it follows:
|
||||
|
||||
```cpp
|
||||
struct DrawableAndErasable: entt::type_list_cat_t<
|
||||
@@ -286,9 +285,8 @@ Everything else is the same as already shown instead.
|
||||
|
||||
# Static polymorphism in the wild
|
||||
|
||||
Once the _concept_ and implementation have been introduced, it will be possible
|
||||
to use the `poly` class template to contain instances that meet the
|
||||
requirements:
|
||||
Once the _concept_ and implementation are defined, it's possible to use the
|
||||
`poly` class template to _wrap_ instances that meet the requirements:
|
||||
|
||||
```cpp
|
||||
using drawable = entt::poly<Drawable>;
|
||||
@@ -310,9 +308,9 @@ instance = square{};
|
||||
instance->draw();
|
||||
```
|
||||
|
||||
The `poly` class template offers a wide range of constructors, from the default
|
||||
one (which will return an uninitialized `poly` object) to the copy and move
|
||||
constructors, as well as the ability to create objects in-place.<br/>
|
||||
This class offers a wide range of constructors, from the default one (which
|
||||
returns an uninitialized `poly` object) to the copy and move constructors, as
|
||||
well as the ability to create objects in-place.<br/>
|
||||
Among others, there is also a constructor that allows users to wrap unmanaged
|
||||
objects in a `poly` instance (either const or non-const ones):
|
||||
|
||||
@@ -329,14 +327,14 @@ drawable other = instance.as_ref();
|
||||
```
|
||||
|
||||
In both cases, although the interface of the `poly` object doesn't change, it
|
||||
won't construct any element or take care of destroying the referenced objects.
|
||||
doesn't construct any element or take care of destroying the referenced objects.
|
||||
|
||||
Note also how the underlying concept is accessed via a call to `operator->` and
|
||||
not directly as `instance.draw()`.<br/>
|
||||
This allows users to decouple the API of the wrapper from that of the concept.
|
||||
Therefore, where `instance.data()` will invoke the `data` member function of the
|
||||
poly object, `instance->data()` will map directly to the functionality exposed
|
||||
by the underlying concept.
|
||||
Therefore, where `instance.data()` invokes the `data` member function of the
|
||||
poly object, `instance->data()` maps directly to the functionality exposed by
|
||||
the underlying concept.
|
||||
|
||||
# Storage size and alignment requirement
|
||||
|
||||
@@ -351,9 +349,9 @@ entt::basic_poly<Drawable, sizeof(double[4]), alignof(double[4])>
|
||||
|
||||
The default size is `sizeof(double[2])`, which seems like a good compromise
|
||||
between a buffer that is too large and one unable to hold anything larger than
|
||||
an integer. The alignment requirement is optional instead and by default such
|
||||
that it's the most stringent (the largest) for any object whose size is at most
|
||||
equal to the one provided.<br/>
|
||||
an integer. The alignment requirement is optional and by default such that it's
|
||||
the most stringent (the largest) for any object whose size is at most equal to
|
||||
the one provided.<br/>
|
||||
It's worth noting that providing a size of 0 (which is an accepted value in all
|
||||
respects) will force the system to dynamically allocate the contained objects in
|
||||
all cases.
|
||||
|
||||
@@ -15,18 +15,17 @@
|
||||
|
||||
# Introduction
|
||||
|
||||
Sometimes processes are a useful tool to work around the strict definition of a
|
||||
system and introduce logic in a different way, usually without resorting to the
|
||||
introduction of other components.
|
||||
|
||||
`EnTT` offers a minimal support to this paradigm by introducing a few classes
|
||||
that users can use to define and execute cooperative processes.
|
||||
Processes are a useful tool to work around the strict definition of a system and
|
||||
introduce logic in a different way, usually without resorting to other component
|
||||
types.<br/>
|
||||
`EnTT` offers minimal support to this paradigm by introducing a few classes used
|
||||
to define and execute cooperative processes.
|
||||
|
||||
# The process
|
||||
|
||||
A typical process must inherit from the `process` class template that stays true
|
||||
to the CRTP idiom. Moreover, derived classes must specify what's the intended
|
||||
type for elapsed times.
|
||||
A typical task inherits from the `process` class template that stays true to the
|
||||
CRTP idiom. Moreover, derived classes specify what the intended type for elapsed
|
||||
times is.
|
||||
|
||||
A process should expose publicly the following member functions whether needed
|
||||
(note that it isn't required to define a function unless the derived class wants
|
||||
@@ -34,39 +33,38 @@ to _override_ the default behavior):
|
||||
|
||||
* `void update(Delta, void *);`
|
||||
|
||||
It's invoked once per tick until a process is explicitly aborted or it
|
||||
terminates either with or without errors. Even though it's not mandatory to
|
||||
declare this member function, as a rule of thumb each process should at
|
||||
least define it to work properly. The `void *` parameter is an opaque pointer
|
||||
to user data (if any) forwarded directly to the process during an update.
|
||||
This is invoked once per tick until a process is explicitly aborted or ends
|
||||
either with or without errors. Even though it's not mandatory to declare this
|
||||
member function, as a rule of thumb each process should at least define it to
|
||||
work _properly_. The `void *` parameter is an opaque pointer to user data (if
|
||||
any) forwarded directly to the process during an update.
|
||||
|
||||
* `void init();`
|
||||
|
||||
It's invoked when the process joins the running queue of a scheduler. This
|
||||
happens as soon as it's attached to the scheduler if the process is a top
|
||||
level one, otherwise when it replaces its parent if the process is a
|
||||
continuation.
|
||||
This is invoked when the process joins the running queue of a scheduler. It
|
||||
happens usually as soon as the process is attached to the scheduler if it's a
|
||||
top level one, otherwise when it replaces its parent if it's a _continuation_.
|
||||
|
||||
* `void succeeded();`
|
||||
|
||||
It's invoked in case of success, immediately after an update and during the
|
||||
This is invoked in case of success, immediately after an update and during the
|
||||
same tick.
|
||||
|
||||
* `void failed();`
|
||||
|
||||
It's invoked in case of errors, immediately after an update and during the
|
||||
This is invoked in case of errors, immediately after an update and during the
|
||||
same tick.
|
||||
|
||||
* `void aborted();`
|
||||
|
||||
It's invoked only if a process is explicitly aborted. There is no guarantee
|
||||
that it executes in the same tick, this depends solely on whether the
|
||||
process is aborted immediately or not.
|
||||
This is invoked only if a process is explicitly aborted. There is no guarantee
|
||||
that it executes in the same tick, it depends solely on whether the process is
|
||||
aborted immediately or not.
|
||||
|
||||
Derived classes can also change the internal state of a process by invoking
|
||||
`succeed` and `fail`, as well as `pause` and `unpause` the process itself. All
|
||||
these are protected member functions made available to be able to manage the
|
||||
life cycle of a process from a derived class.
|
||||
`succeed` and `fail`, as well as `pause` and `unpause` the process itself.<br/>
|
||||
All these are protected member functions made available to manage the life cycle
|
||||
of a process from a derived class.
|
||||
|
||||
Here is a minimal example for the sake of curiosity:
|
||||
|
||||
@@ -95,14 +93,14 @@ private:
|
||||
|
||||
## Adaptor
|
||||
|
||||
Lambdas and functors can't be used directly with a scheduler for they are not
|
||||
Lambdas and functors can't be used directly with a scheduler because they aren't
|
||||
properly defined processes with managed life cycles.<br/>
|
||||
This class helps in filling the gap and turning lambdas and functors into
|
||||
full-featured processes usable by a scheduler.
|
||||
|
||||
The function call operator has a signature similar to the one of the `update`
|
||||
function of a process but for the fact that it receives two extra arguments to
|
||||
call whenever a process is terminated with success or with an error:
|
||||
function of a process but for the fact that it receives two extra callbacks to
|
||||
invoke whenever a process terminates with success or with an error:
|
||||
|
||||
```cpp
|
||||
void(Delta delta, void *data, auto succeed, auto fail);
|
||||
@@ -127,9 +125,9 @@ A cooperative scheduler runs different processes and helps managing their life
|
||||
cycles.
|
||||
|
||||
Each process is invoked once per tick. If it terminates, it's removed
|
||||
automatically from the scheduler and it's never invoked again. Otherwise it's
|
||||
automatically from the scheduler and it's never invoked again. Otherwise, it's
|
||||
a good candidate to run one more time the next tick.<br/>
|
||||
A process can also have a child. In this case, the parent process is replaced
|
||||
A process can also have a _child_. In this case, the parent process is replaced
|
||||
with its child when it terminates and only if it returns with success. In case
|
||||
of errors, both the parent process and its child are discarded. This way, it's
|
||||
easy to create chain of processes to run sequentially.
|
||||
@@ -138,18 +136,25 @@ Using a scheduler is straightforward. To create it, users must provide only the
|
||||
type for the elapsed times and no arguments at all:
|
||||
|
||||
```cpp
|
||||
entt::scheduler<std::uint32_t> scheduler;
|
||||
entt::basic_scheduler<std::uint64_t> scheduler;
|
||||
```
|
||||
|
||||
It has member functions to query its internal data structures, like `empty` or
|
||||
`size`, as well as a `clear` utility to reset it to a clean state:
|
||||
Otherwise, the `scheduler` alias is also available for the most common cases. It
|
||||
uses `std::uint32_t` as a default type:
|
||||
|
||||
```cpp
|
||||
entt::scheduler scheduler;
|
||||
```
|
||||
|
||||
The class has member functions to query its internal data structures, like
|
||||
`empty` or `size`, as well as a `clear` utility to reset it to a clean state:
|
||||
|
||||
```cpp
|
||||
// checks if there are processes still running
|
||||
const auto empty = scheduler.empty();
|
||||
|
||||
// gets the number of processes still running
|
||||
entt::scheduler<std::uint32_t>::size_type size = scheduler.size();
|
||||
entt::scheduler::size_type size = scheduler.size();
|
||||
|
||||
// resets the scheduler to its initial state and discards all the processes
|
||||
scheduler.clear();
|
||||
@@ -172,8 +177,8 @@ To attach a process to a scheduler there are mainly two ways:
|
||||
scheduler.attach([](auto...){ /* ... */ });
|
||||
```
|
||||
|
||||
In both cases, the return value is an opaque object that offers a `then` member
|
||||
function to use to create chains of processes to run sequentially.<br/>
|
||||
In both cases, the scheduler is returned and its `then` member function can be
|
||||
used to create chains of processes to run sequentially.<br/>
|
||||
As a minimal example of use:
|
||||
|
||||
```cpp
|
||||
@@ -201,7 +206,7 @@ scheduler.update(delta, &data);
|
||||
```
|
||||
|
||||
In addition to these functions, the scheduler offers an `abort` member function
|
||||
that can be used to discard all the running processes at once:
|
||||
that is used to discard all the running processes at once:
|
||||
|
||||
```cpp
|
||||
// aborts all the processes abruptly ...
|
||||
|
||||
@@ -1,18 +1,35 @@
|
||||
# Similar projects
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Similar projects](#similar-projects)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
There are many projects similar to `EnTT`, both open source and not.<br/>
|
||||
Some even borrowed some ideas from this library and expressed them in different
|
||||
languages.<br/>
|
||||
Others developed different architectures from scratch and therefore offer
|
||||
alternative solutions with their pros and cons.
|
||||
|
||||
Below an incomplete list of those that I've come across so far.<br/>
|
||||
If you know of other similar projects out there, feel free to open an issue or a
|
||||
PR and I'll be glad to add them to this page.<br/>
|
||||
I hope the following lists can grow much more in the future.
|
||||
|
||||
# Similar projects
|
||||
|
||||
Below an incomplete list of similar projects that I've come across so far.<br/>
|
||||
If some terms or designs aren't clear, I recommend referring to the
|
||||
[_ECS Back and Forth_](https://skypjack.github.io/tags/#ecs) series for all the
|
||||
details.
|
||||
|
||||
I hope this list can grow much more in the future:
|
||||
|
||||
* C:
|
||||
* [destral_ecs](https://github.com/roig/destral_ecs): a single-file ECS based
|
||||
on sparse sets.
|
||||
@@ -34,6 +51,8 @@ I hope this list can grow much more in the future:
|
||||
solution between an ECS and dynamic mixins.
|
||||
|
||||
* C#
|
||||
* [Arch](https://github.com/genaray/Arch): a simple, fast and _unity entities_
|
||||
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.
|
||||
* [LeoECS](https://github.com/Leopotam/ecs): simple lightweight C# Entity
|
||||
@@ -70,6 +89,3 @@ I hope this list can grow much more in the future:
|
||||
|
||||
* Zig
|
||||
* [zig-ecs](https://github.com/prime31/zig-ecs): a _zig-ification_ of `EnTT`.
|
||||
|
||||
If you know of other resources out there that can be of interest for the reader,
|
||||
feel free to open an issue or a PR and I'll be glad to add them to this page.
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
* [Delegate](#delegate)
|
||||
* [Runtime arguments](#runtime-arguments)
|
||||
* [Lambda support](#lambda-support)
|
||||
* [Raw access](#raw-access)
|
||||
* [Signals](#signals)
|
||||
* [Event dispatcher](#event-dispatcher)
|
||||
* [Named queues](#named-queues)
|
||||
@@ -38,7 +39,7 @@ lightweight classes to solve the same and many other problems.
|
||||
# Delegate
|
||||
|
||||
A delegate can be used as a general purpose invoker with no memory overhead for
|
||||
free functions and member functions provided along with an instance on which to
|
||||
free functions, lambdas and members provided along with an instance on which to
|
||||
invoke them.<br/>
|
||||
It doesn't claim to be a drop-in replacement for an `std::function`, so don't
|
||||
expect to use it whenever an `std::function` fits well. That said, it's most
|
||||
@@ -92,9 +93,9 @@ delegate.connect<&g>(c);
|
||||
delegate(42);
|
||||
```
|
||||
|
||||
The function `g` is invoked with a reference to `c` and `42`. However, the
|
||||
function type of the delegate is still `void(int)`. This is also the signature
|
||||
of its function call operator.<br/>
|
||||
Function `g` is invoked with a reference to `c` and `42`. However, the function
|
||||
type of the delegate is still `void(int)`. This is also the signature of its
|
||||
function call operator.<br/>
|
||||
Another interesting aspect of the delegate class is that it accepts functions
|
||||
with a list of parameters that is shorter than that of its function type:
|
||||
|
||||
@@ -105,9 +106,15 @@ delegate(42);
|
||||
```
|
||||
|
||||
Where the function type of the delegate is `void(int)` as above. It goes without
|
||||
saying that the extra arguments are silently discarded internally.<br/>
|
||||
This is a nice-to-have feature in a lot of cases, as an example when the
|
||||
`delegate` class is used as a building block of a signal-slot system.
|
||||
saying that the extra arguments are silently discarded internally. This is a
|
||||
nice-to-have feature in a lot of cases, as an example when the `delegate` class
|
||||
is used as a building block of a signal-slot system.<br/>
|
||||
In fact, this filtering works both ways. The class tries to pass its first
|
||||
_count_ arguments **first**, then the last _count_. Watch out for conversion
|
||||
rules if in doubt when connecting a listener!<br/>
|
||||
Arbitrary functions that pull random arguments from the delegate list aren't
|
||||
supported instead. Other feature were preferred, such as support for functions
|
||||
with compatible argument lists although not equal to those of the delegate.
|
||||
|
||||
To create and initialize a delegate at once, there are a few specialized
|
||||
constructors. Because of the rules of the language, the listener is provided by
|
||||
@@ -231,6 +238,24 @@ As above, the first parameter (`const void *`) isn't part of the function type
|
||||
of the delegate and is used to dispatch arbitrary user data back and forth. In
|
||||
other terms, the function type of the delegate above is `int(int)`.
|
||||
|
||||
## Raw access
|
||||
|
||||
While not recommended, a delegate also allows direct access to the stored
|
||||
callable function target and underlying data, if any.<br/>
|
||||
This makes it possible to bypass the behavior of the delegate itself and force
|
||||
calls on different instances:
|
||||
|
||||
```cpp
|
||||
my_struct other;
|
||||
delegate.target(&other, 42);
|
||||
```
|
||||
|
||||
It goes without saying that this type of approach is **very** risky, especially
|
||||
since there is no way of knowing whether the contained function was originally a
|
||||
member function of some class, a free function or a lambda.<br/>
|
||||
Another possible (and meaningful) use of this feature is that of identifying a
|
||||
particular delegate through its descriptive _traits_ instead.
|
||||
|
||||
# Signals
|
||||
|
||||
Signal handlers work with references to classes, function pointers and pointers
|
||||
@@ -290,7 +315,7 @@ sink.disconnect<&foo>();
|
||||
sink.disconnect<&listener::bar>(instance);
|
||||
|
||||
// disconnect all member functions of an instance, if any
|
||||
sink.disconnect(instance);
|
||||
sink.disconnect(&instance);
|
||||
|
||||
// discards all listeners at once
|
||||
sink.disconnect();
|
||||
@@ -300,15 +325,6 @@ As shown above, listeners don't have to strictly follow the signature of the
|
||||
signal. As long as a listener can be invoked with the given arguments to yield a
|
||||
result that is convertible to the given return type, everything works just
|
||||
fine.<br/>
|
||||
It's also possible to connect a listener before other elements already contained
|
||||
by the signal. The `before` function returns a `sink` object that is correctly
|
||||
initialized for the purpose and can be used to connect one or more listeners in
|
||||
order and in the desired position:
|
||||
|
||||
```cpp
|
||||
sink.before<&foo>().connect<&listener::bar>(instance);
|
||||
```
|
||||
|
||||
In all cases, the `connect` member function returns by default a `connection`
|
||||
object to be used as an alternative to break a connection by means of its
|
||||
`release` member function.<br/>
|
||||
@@ -409,7 +425,7 @@ of them at once:
|
||||
|
||||
```cpp
|
||||
dispatcher.sink<an_event>().disconnect<&listener::receive>(listener);
|
||||
dispatcher.sink<another_event>().disconnect(listener);
|
||||
dispatcher.sink<another_event>().disconnect(&listener);
|
||||
```
|
||||
|
||||
The `trigger` member function serves the purpose of sending an immediate event
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
## Enable Cpp17
|
||||
|
||||
> Skip this part if you are working with UE5, Since UE5 uses cpp17 by default.
|
||||
|
||||
As of writing (Unreal Engine v4.25), the default C++ standard of Unreal Engine
|
||||
is C++14.<br/>
|
||||
On the other hand, note that `EnTT` requires C++17 to compile. To enable it, in
|
||||
|
||||
68
entt.imp
68
entt.imp
@@ -1,34 +1,42 @@
|
||||
[
|
||||
# gtest only
|
||||
{ "include": [ "@<gtest/internal/.*>", "private", "<gtest/gtest.h>", "public" ] },
|
||||
{ "include": [ "@<gtest/gtest-.*>", "private", "<gtest/gtest.h>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/container/fwd.hpp[\">]", "private", "<entt/container/dense_map.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/container/fwd.hpp[\">]", "private", "<entt/container/dense_set.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/core/fwd.hpp[\">]", "private", "<entt/core/any.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/core/fwd.hpp[\">]", "private", "<entt/core/family.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/core/fwd.hpp[\">]", "private", "<entt/core/hashed_string.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/core/fwd.hpp[\">]", "private", "<entt/core/ident.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/core/fwd.hpp[\">]", "private", "<entt/core/monostate.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/core/fwd.hpp[\">]", "private", "<entt/core/type_info.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/core/fwd.hpp[\">]", "private", "<entt/core/type_traits.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/entity.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/group.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/handle.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/helper.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/observer.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/organizer.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/registry.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/runtime_view.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/snapshot.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/sparse_set.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/storage.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/view.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/meta/fwd.hpp[\">]", "private", "<entt/meta/meta.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/poly/fwd.hpp[\">]", "private", "<entt/poly/poly.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/resource/fwd.hpp[\">]", "private", "<entt/resource/cache.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/resource/fwd.hpp[\">]", "private", "<entt/resource/loader.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/resource/fwd.hpp[\">]", "private", "<entt/resource/resource.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/signal/fwd.hpp[\">]", "private", "<entt/signal/delegate.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/signal/fwd.hpp[\">]", "private", "<entt/signal/dispatcher.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/signal/fwd.hpp[\">]", "private", "<entt/signal/emitter.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/signal/fwd.hpp[\">]", "private", "<entt/signal/sigh.hpp>", "public" ] }
|
||||
# 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": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/any.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/family.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/hashed_string.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/ident.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/monostate.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/type_info.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/type_traits.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/component.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/entity.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/group.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/handle.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/helper.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/observer.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/organizer.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/registry.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/runtime_view.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/snapshot.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/sparse_set.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/storage.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/view.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/graph/fwd\\.hpp[\">]", "private", "<entt/graph/adjacency_matrix.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/graph/fwd\\.hpp[\">]", "private", "<entt/graph/dot.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/graph/fwd\\.hpp[\">]", "private", "<entt/graph/flow.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/meta/fwd\\.hpp[\">]", "private", "<entt/meta/meta.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/poly/fwd\\.hpp[\">]", "private", "<entt/poly/poly.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/process/fwd\\.hpp[\">]", "private", "<entt/process/process.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/process/fwd\\.hpp[\">]", "private", "<entt/process/scheduler.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/resource/fwd\\.hpp[\">]", "private", "<entt/resource/cache.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/resource/fwd\\.hpp[\">]", "private", "<entt/resource/loader.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/resource/fwd\\.hpp[\">]", "private", "<entt/resource/resource.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/signal/fwd\\.hpp[\">]", "private", "<entt/signal/delegate.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/signal/fwd\\.hpp[\">]", "private", "<entt/signal/dispatcher.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/signal/fwd\\.hpp[\">]", "private", "<entt/signal/emitter.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/signal/fwd\\.hpp[\">]", "private", "<entt/signal/sigh.hpp>", "public" ] }
|
||||
]
|
||||
|
||||
@@ -1,62 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
<Type Name="entt::basic_registry<*>">
|
||||
<Intrinsic Name="pools_size" Expression="pools.packed.first_base::value.size()"/>
|
||||
<Intrinsic Name="vars_size" Expression="vars.ctx.packed.first_base::value.size()"/>
|
||||
<Intrinsic Name="to_entity" Expression="*((entity_traits::entity_type *)&entity) & entity_traits::entity_mask">
|
||||
<Parameter Name="entity" Type="entity_traits::value_type &"/>
|
||||
</Intrinsic>
|
||||
<DisplayString>{{ size={ epool.size() } }}</DisplayString>
|
||||
<DisplayString>{{ pools={ pools.size() } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item IncludeView="simple" Name="[epool]">epool,view(simple)nr</Item>
|
||||
<Synthetic Name="[epool]" ExcludeView="simple">
|
||||
<DisplayString>{ epool.size() }</DisplayString>
|
||||
<Expand>
|
||||
<CustomListItems>
|
||||
<Variable Name="pos" InitialValue="0" />
|
||||
<Variable Name="last" InitialValue="epool.size()"/>
|
||||
<Loop>
|
||||
<Break Condition="pos == last"/>
|
||||
<If Condition="to_entity(epool[pos]) == pos">
|
||||
<Item Name="[{ pos }]">epool[pos]</Item>
|
||||
</If>
|
||||
<Exec>++pos</Exec>
|
||||
</Loop>
|
||||
</CustomListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
<Synthetic Name="[destroyed]" ExcludeView="simple">
|
||||
<DisplayString>{ to_entity(free_list) != entity_traits::entity_mask }</DisplayString>
|
||||
<Expand>
|
||||
<CustomListItems>
|
||||
<Variable Name="it" InitialValue="to_entity(free_list)" />
|
||||
<Loop>
|
||||
<Break Condition="it == entity_traits::entity_mask"/>
|
||||
<Item Name="[{ it }]">epool[it]</Item>
|
||||
<Exec>it = to_entity(epool[it])</Exec>
|
||||
</Loop>
|
||||
</CustomListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
<Item Name="[entities]">entities</Item>
|
||||
<Synthetic Name="[pools]">
|
||||
<DisplayString>{ pools_size() }</DisplayString>
|
||||
<DisplayString>{ pools.size() }</DisplayString>
|
||||
<Expand>
|
||||
<IndexListItems ExcludeView="simple">
|
||||
<Size>pools_size()</Size>
|
||||
<Size>pools.size()</Size>
|
||||
<ValueNode>*pools.packed.first_base::value[$i].element.second</ValueNode>
|
||||
</IndexListItems>
|
||||
<IndexListItems IncludeView="simple">
|
||||
<Size>pools_size()</Size>
|
||||
<Size>pools.size()</Size>
|
||||
<ValueNode>*pools.packed.first_base::value[$i].element.second,view(simple)</ValueNode>
|
||||
</IndexListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
<Item Name="[groups]" ExcludeView="simple">groups.size()</Item>
|
||||
<Synthetic Name="[vars]">
|
||||
<DisplayString>{ vars_size() }</DisplayString>
|
||||
<DisplayString>{ vars.ctx.size() }</DisplayString>
|
||||
<Expand>
|
||||
<IndexListItems>
|
||||
<Size>vars_size()</Size>
|
||||
<Size>vars.ctx.size()</Size>
|
||||
<ValueNode>vars.ctx.packed.first_base::value[$i].element.second</ValueNode>
|
||||
</IndexListItems>
|
||||
</Expand>
|
||||
@@ -64,25 +30,27 @@
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_sparse_set<*>">
|
||||
<Intrinsic Name="cap" Expression="(traits_type::version_mask << traits_type::length)"/>
|
||||
<DisplayString>{{ size={ packed.size() }, type={ info->alias,na } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[capacity]" ExcludeView="simple">packed.capacity()</Item>
|
||||
<Item Name="[policy]">mode,en</Item>
|
||||
<Item Name="[free_list]">head</Item>
|
||||
<Synthetic Name="[sparse]">
|
||||
<DisplayString>{ sparse.size() * entity_traits::page_size }</DisplayString>
|
||||
<DisplayString>{ sparse.size() * traits_type::page_size }</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem IncludeView="simple">sparse,view(simple)</ExpandedItem>
|
||||
<CustomListItems ExcludeView="simple">
|
||||
<Variable Name="pos" InitialValue="0"/>
|
||||
<Variable Name="page" InitialValue="0"/>
|
||||
<Variable Name="offset" InitialValue="0"/>
|
||||
<Variable Name="last" InitialValue="sparse.size() * entity_traits::page_size"/>
|
||||
<Variable Name="last" InitialValue="sparse.size() * traits_type::page_size"/>
|
||||
<Loop>
|
||||
<Break Condition="pos == last"/>
|
||||
<Exec>page = pos / entity_traits::page_size</Exec>
|
||||
<Exec>offset = pos & (entity_traits::page_size - 1)</Exec>
|
||||
<If Condition="sparse[page] && (*((entity_traits::entity_type *)&sparse[page][offset]) < ~entity_traits::entity_mask)">
|
||||
<Item Name="[{ pos }]">*((entity_traits::entity_type *)&sparse[page][offset]) & entity_traits::entity_mask</Item>
|
||||
<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())">
|
||||
<Item Name="[{ pos }]">*((traits_type::entity_type *)&sparse[page][offset]) & traits_type::entity_mask</Item>
|
||||
</If>
|
||||
<Exec>++pos</Exec>
|
||||
</Loop>
|
||||
@@ -98,7 +66,7 @@
|
||||
<Variable Name="last" InitialValue="packed.size()"/>
|
||||
<Loop>
|
||||
<Break Condition="pos == last"/>
|
||||
<If Condition="*((entity_traits::entity_type *)&packed[pos]) < ~entity_traits::entity_mask">
|
||||
<If Condition="*((traits_type::entity_type *)&packed[pos]) < cap()">
|
||||
<Item Name="[{ pos }]">packed[pos]</Item>
|
||||
</If>
|
||||
<Exec>++pos</Exec>
|
||||
@@ -109,20 +77,21 @@
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_storage<*>">
|
||||
<Intrinsic Name="cap" Expression="(base_type::traits_type::version_mask << base_type::traits_type::length)"/>
|
||||
<DisplayString>{{ size={ base_type::packed.size() }, type={ base_type::info->alias,na } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[capacity]" Optional="true" ExcludeView="simple">packed.first_base::value.capacity() * comp_traits::page_size</Item>
|
||||
<Item Name="[page size]" Optional="true" ExcludeView="simple">comp_traits::page_size</Item>
|
||||
<Item Name="[capacity]" Optional="true" ExcludeView="simple">payload.capacity() * traits_type::page_size</Item>
|
||||
<Item Name="[page size]" Optional="true" ExcludeView="simple">traits_type::page_size</Item>
|
||||
<Item Name="[base]" ExcludeView="simple">(base_type*)this,nand</Item>
|
||||
<Item Name="[base]" IncludeView="simple">(base_type*)this,view(simple)nand</Item>
|
||||
<!-- having SFINAE-like techniques in natvis is priceless :) -->
|
||||
<CustomListItems Condition="packed.first_base::value.size() != 0" Optional="true">
|
||||
<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::entity_traits::entity_type *)&base_type::packed[pos]) < ~base_type::entity_traits::entity_mask">
|
||||
<Item Name="[{ pos }:{ base_type::packed[pos] }]">packed.first_base::value[pos / comp_traits::page_size][pos & (comp_traits::page_size - 1)]</Item>
|
||||
<If Condition="*((base_type::traits_type::entity_type *)&base_type::packed[pos]) < cap()">
|
||||
<Item Name="[{ pos }:{ base_type::packed[pos] }]">payload[pos / traits_type::page_size][pos & (traits_type::page_size - 1)]</Item>
|
||||
</If>
|
||||
<Exec>++pos</Exec>
|
||||
</Loop>
|
||||
@@ -130,10 +99,12 @@
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_view<*>">
|
||||
<DisplayString>{{ size_hint={ view->packed.size() } }}</DisplayString>
|
||||
<DisplayString Condition="leading != nullptr">{{ size_hint={ leading->packed.size() } }}</DisplayString>
|
||||
<DisplayString>{{ size_hint=0 }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[pools]">pools,na</Item>
|
||||
<Item Name="[filter]">filter,na</Item>
|
||||
<Item Name="[pools]" Optional="true">pools,na</Item>
|
||||
<Item Name="[filter]" Optional="true">filter,na</Item>
|
||||
<Item Name="[handle]" Condition="leading != nullptr">leading,na</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_runtime_view<*>">
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_dispatcher<*>">
|
||||
<Intrinsic Name="size" Expression="pools.first_base::value.packed.first_base::value.size()"/>
|
||||
<Intrinsic Name="size" Expression="pools.first_base::value.size()"/>
|
||||
<DisplayString>{{ size={ size() } }}</DisplayString>
|
||||
<Expand>
|
||||
<Synthetic Name="[pools]">
|
||||
@@ -50,7 +50,6 @@
|
||||
<DisplayString>{{ type={ "$T1" } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[signal]">signal,na</Item>
|
||||
<Item Name="[offset]">offset</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
</AutoVisualizer>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
11
src/BUILD.bazel
Normal file
11
src/BUILD.bazel
Normal file
@@ -0,0 +1,11 @@
|
||||
load("@bazel_skylib//lib:selects.bzl", "selects")
|
||||
load("//bazel:copts.bzl", "COPTS")
|
||||
|
||||
package(default_visibility = ["//:__subpackages__"])
|
||||
|
||||
cc_library(
|
||||
name = "entt",
|
||||
includes = ["."],
|
||||
hdrs = glob(["**/*.h", "**/*.hpp"]),
|
||||
copts = COPTS,
|
||||
)
|
||||
@@ -25,6 +25,8 @@
|
||||
#ifndef ENTT_ID_TYPE
|
||||
# include <cstdint>
|
||||
# define ENTT_ID_TYPE std::uint32_t
|
||||
#else
|
||||
# include <cstdint> // provides coverage for types in the std namespace
|
||||
#endif
|
||||
|
||||
#ifndef ENTT_SPARSE_PAGE
|
||||
@@ -50,6 +52,8 @@
|
||||
# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg)
|
||||
#endif
|
||||
|
||||
#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg);
|
||||
|
||||
#ifdef ENTT_NO_ETO
|
||||
# define ENTT_ETO_TYPE(Type) void
|
||||
#else
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#include "macro.h"
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 11
|
||||
#define ENTT_VERSION_PATCH 1
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
|
||||
@@ -20,11 +20,7 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename Key, typename Type>
|
||||
@@ -69,6 +65,7 @@ public:
|
||||
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 dense_map_iterator() noexcept
|
||||
: it{} {}
|
||||
@@ -128,51 +125,51 @@ public:
|
||||
return {it->element.first, it->element.second};
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
friend constexpr std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) noexcept;
|
||||
template<typename Lhs, typename Rhs>
|
||||
friend constexpr std::ptrdiff_t operator-(const dense_map_iterator<Lhs> &, const dense_map_iterator<Rhs> &) noexcept;
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
friend constexpr bool operator==(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) noexcept;
|
||||
template<typename Lhs, typename Rhs>
|
||||
friend constexpr bool operator==(const dense_map_iterator<Lhs> &, const dense_map_iterator<Rhs> &) noexcept;
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
friend constexpr bool operator<(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) noexcept;
|
||||
template<typename Lhs, typename Rhs>
|
||||
friend constexpr bool operator<(const dense_map_iterator<Lhs> &, const dense_map_iterator<Rhs> &) noexcept;
|
||||
|
||||
private:
|
||||
It it;
|
||||
};
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
|
||||
return lhs.it - rhs.it;
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] constexpr bool operator==(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator==(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
|
||||
return lhs.it == rhs.it;
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] constexpr bool operator!=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator!=(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] constexpr bool operator<(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator<(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
|
||||
return lhs.it < rhs.it;
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] constexpr bool operator>(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator>(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
|
||||
return rhs < lhs;
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] constexpr bool operator<=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator<=(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
|
||||
return !(lhs > rhs);
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] constexpr bool operator>=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator>=(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
@@ -190,6 +187,7 @@ public:
|
||||
using reference = value_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using iterator_concept = std::forward_iterator_tag;
|
||||
|
||||
constexpr dense_map_local_iterator() noexcept
|
||||
: it{},
|
||||
@@ -230,22 +228,18 @@ private:
|
||||
std::size_t offset;
|
||||
};
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator<Lhs> &lhs, const dense_map_local_iterator<Rhs> &rhs) noexcept {
|
||||
return lhs.index() == rhs.index();
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator<Lhs> &lhs, const dense_map_local_iterator<Rhs> &rhs) noexcept {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Associative container for key-value pairs with unique keys.
|
||||
@@ -266,7 +260,7 @@ class dense_map {
|
||||
static constexpr std::size_t minimum_capacity = 8u;
|
||||
|
||||
using node_type = internal::dense_map_node<Key, Type>;
|
||||
using alloc_traits = typename std::allocator_traits<Allocator>;
|
||||
using alloc_traits = std::allocator_traits<Allocator>;
|
||||
static_assert(std::is_same_v<typename alloc_traits::value_type, std::pair<const Key, Type>>, "Invalid value type");
|
||||
using sparse_container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
|
||||
using packed_container_type = std::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
|
||||
@@ -464,7 +458,6 @@ public:
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning.
|
||||
*
|
||||
* The returned iterator points to the first instance of the internal array.
|
||||
* If the array is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @return An iterator to the first instance of the internal array.
|
||||
@@ -485,11 +478,6 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the end.
|
||||
*
|
||||
* The returned iterator points to the element following the last instance
|
||||
* of the internal array. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @return An iterator to the element following the last instance of the
|
||||
* internal array.
|
||||
*/
|
||||
@@ -838,7 +826,7 @@ public:
|
||||
}
|
||||
|
||||
/*! @copydoc equal_range */
|
||||
template<class Other>
|
||||
template<typename Other>
|
||||
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, std::pair<const_iterator, const_iterator>>>
|
||||
equal_range(const Other &key) const {
|
||||
const auto it = find(key);
|
||||
@@ -995,7 +983,7 @@ public:
|
||||
sparse.first().resize(sz);
|
||||
|
||||
for(auto &&elem: sparse.first()) {
|
||||
elem = std::numeric_limits<size_type>::max();
|
||||
elem = (std::numeric_limits<size_type>::max)();
|
||||
}
|
||||
|
||||
for(size_type pos{}, last = size(); pos < last; ++pos) {
|
||||
@@ -1039,11 +1027,7 @@ private:
|
||||
|
||||
} // namespace entt
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace std {
|
||||
|
||||
template<typename Key, typename Value, typename Allocator>
|
||||
@@ -1051,10 +1035,6 @@ struct uses_allocator<entt::internal::dense_map_node<Key, Value>, Allocator>
|
||||
: std::true_type {};
|
||||
|
||||
} // namespace std
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
|
||||
#endif
|
||||
|
||||
@@ -19,11 +19,7 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename It>
|
||||
@@ -96,51 +92,51 @@ public:
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
friend constexpr std::ptrdiff_t operator-(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) noexcept;
|
||||
template<typename Lhs, typename Rhs>
|
||||
friend constexpr std::ptrdiff_t operator-(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept;
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
friend constexpr bool operator==(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) noexcept;
|
||||
template<typename Lhs, typename Rhs>
|
||||
friend constexpr bool operator==(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept;
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
friend constexpr bool operator<(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) noexcept;
|
||||
template<typename Lhs, typename Rhs>
|
||||
friend constexpr bool operator<(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept;
|
||||
|
||||
private:
|
||||
It it;
|
||||
};
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
|
||||
return lhs.it - rhs.it;
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] constexpr bool operator==(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator==(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
|
||||
return lhs.it == rhs.it;
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] constexpr bool operator!=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator!=(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] constexpr bool operator<(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator<(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
|
||||
return lhs.it < rhs.it;
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] constexpr bool operator>(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator>(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
|
||||
return rhs < lhs;
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] constexpr bool operator<=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator<=(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
|
||||
return !(lhs > rhs);
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] constexpr bool operator>=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator>=(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
@@ -195,22 +191,18 @@ private:
|
||||
std::size_t offset;
|
||||
};
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator<ILhs> &lhs, const dense_set_local_iterator<IRhs> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator<Lhs> &lhs, const dense_set_local_iterator<Rhs> &rhs) noexcept {
|
||||
return lhs.index() == rhs.index();
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator<ILhs> &lhs, const dense_set_local_iterator<IRhs> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator<Lhs> &lhs, const dense_set_local_iterator<Rhs> &rhs) noexcept {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Associative container for unique objects of a given type.
|
||||
@@ -311,6 +303,10 @@ public:
|
||||
using iterator = internal::dense_set_iterator<typename packed_container_type::iterator>;
|
||||
/*! @brief Constant random access iterator type. */
|
||||
using const_iterator = internal::dense_set_iterator<typename packed_container_type::const_iterator>;
|
||||
/*! @brief Reverse iterator type. */
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
/*! @brief Constant reverse iterator type. */
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
/*! @brief Forward iterator type. */
|
||||
using local_iterator = internal::dense_set_local_iterator<typename packed_container_type::iterator>;
|
||||
/*! @brief Constant forward iterator type. */
|
||||
@@ -410,7 +406,6 @@ public:
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning.
|
||||
*
|
||||
* The returned iterator points to the first instance of the internal array.
|
||||
* If the array is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @return An iterator to the first instance of the internal array.
|
||||
@@ -431,11 +426,6 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the end.
|
||||
*
|
||||
* The returned iterator points to the element following the last instance
|
||||
* of the internal array. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @return An iterator to the element following the last instance of the
|
||||
* internal array.
|
||||
*/
|
||||
@@ -453,6 +443,46 @@ public:
|
||||
return packed.first().end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reverse iterator to the beginning.
|
||||
*
|
||||
* If the array is empty, the returned iterator will be equal to `rend()`.
|
||||
*
|
||||
* @return An iterator to the first instance of the reversed internal array.
|
||||
*/
|
||||
[[nodiscard]] const_reverse_iterator crbegin() const noexcept {
|
||||
return std::make_reverse_iterator(cend());
|
||||
}
|
||||
|
||||
/*! @copydoc crbegin */
|
||||
[[nodiscard]] const_reverse_iterator rbegin() const noexcept {
|
||||
return crbegin();
|
||||
}
|
||||
|
||||
/*! @copydoc rbegin */
|
||||
[[nodiscard]] reverse_iterator rbegin() noexcept {
|
||||
return std::make_reverse_iterator(end());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reverse iterator to the end.
|
||||
* @return An iterator to the element following the last instance of the
|
||||
* reversed internal array.
|
||||
*/
|
||||
[[nodiscard]] const_reverse_iterator crend() const noexcept {
|
||||
return std::make_reverse_iterator(cbegin());
|
||||
}
|
||||
|
||||
/*! @copydoc crend */
|
||||
[[nodiscard]] const_reverse_iterator rend() const noexcept {
|
||||
return crend();
|
||||
}
|
||||
|
||||
/*! @copydoc rend */
|
||||
[[nodiscard]] reverse_iterator rend() noexcept {
|
||||
return std::make_reverse_iterator(begin());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether a container is empty.
|
||||
* @return True if the container is empty, false otherwise.
|
||||
@@ -691,7 +721,7 @@ public:
|
||||
}
|
||||
|
||||
/*! @copydoc equal_range */
|
||||
template<class Other>
|
||||
template<typename Other>
|
||||
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, std::pair<const_iterator, const_iterator>>>
|
||||
equal_range(const Other &value) const {
|
||||
const auto it = find(value);
|
||||
@@ -848,7 +878,7 @@ public:
|
||||
sparse.first().resize(sz);
|
||||
|
||||
for(auto &&elem: sparse.first()) {
|
||||
elem = std::numeric_limits<size_type>::max();
|
||||
elem = (std::numeric_limits<size_type>::max)();
|
||||
}
|
||||
|
||||
for(size_type pos{}, last = size(); pos < last; ++pos) {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
@@ -95,14 +95,15 @@ struct radix_sort {
|
||||
template<typename It, typename Getter = identity>
|
||||
void operator()(It first, It last, Getter getter = Getter{}) const {
|
||||
if(first < last) {
|
||||
static constexpr auto mask = (1 << Bit) - 1;
|
||||
static constexpr auto buckets = 1 << Bit;
|
||||
static constexpr auto passes = N / Bit;
|
||||
constexpr auto passes = N / Bit;
|
||||
|
||||
using value_type = typename std::iterator_traits<It>::value_type;
|
||||
std::vector<value_type> aux(std::distance(first, last));
|
||||
|
||||
auto part = [getter = std::move(getter)](auto from, auto to, auto out, auto start) {
|
||||
constexpr auto mask = (1 << Bit) - 1;
|
||||
constexpr auto buckets = 1 << Bit;
|
||||
|
||||
std::size_t index[buckets]{};
|
||||
std::size_t count[buckets]{};
|
||||
|
||||
|
||||
@@ -13,11 +13,7 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
enum class any_operation : std::uint8_t {
|
||||
@@ -30,19 +26,19 @@ enum class any_operation : std::uint8_t {
|
||||
get
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/*! @brief Possible modes of an any object. */
|
||||
enum class any_policy : std::uint8_t {
|
||||
/*! @brief Default mode, the object owns the contained element. */
|
||||
owner,
|
||||
/*! @brief Aliasing mode, the object _points_ to a non-const element. */
|
||||
ref,
|
||||
/*! @brief Const aliasing mode, the object _points_ to a const element. */
|
||||
cref
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief A SBO friendly, type-safe container for single values of any type.
|
||||
* @tparam Len Size of the storage reserved for the small buffer optimization.
|
||||
@@ -51,7 +47,6 @@ enum class any_policy : std::uint8_t {
|
||||
template<std::size_t Len, std::size_t Align>
|
||||
class basic_any {
|
||||
using operation = internal::any_operation;
|
||||
using policy = internal::any_policy;
|
||||
using vtable_type = const void *(const operation, const basic_any &, const void *);
|
||||
|
||||
struct storage_type {
|
||||
@@ -59,15 +54,15 @@ class basic_any {
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len &&std::is_nothrow_move_constructible_v<Type>;
|
||||
static constexpr bool in_situ = Len && 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_same_v<Type, void> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
|
||||
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;
|
||||
|
||||
if constexpr(in_situ<Type>) {
|
||||
element = value.owner() ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance);
|
||||
element = (value.mode == any_policy::owner) ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance);
|
||||
} else {
|
||||
element = static_cast<const Type *>(value.instance);
|
||||
}
|
||||
@@ -80,7 +75,7 @@ class basic_any {
|
||||
break;
|
||||
case operation::move:
|
||||
if constexpr(in_situ<Type>) {
|
||||
if(value.owner()) {
|
||||
if(value.mode == any_policy::owner) {
|
||||
return new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(element))};
|
||||
}
|
||||
}
|
||||
@@ -128,17 +123,17 @@ class basic_any {
|
||||
vtable = basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>;
|
||||
|
||||
if constexpr(std::is_lvalue_reference_v<Type>) {
|
||||
static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v<Args> && ...), "Invalid arguments");
|
||||
mode = std::is_const_v<std::remove_reference_t<Type>> ? policy::cref : policy::ref;
|
||||
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(sizeof...(Args) != 0u && std::is_aggregate_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
|
||||
if constexpr(std::is_aggregate_v<std::remove_cv_t<std::remove_reference_t<Type>>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>)) {
|
||||
new(&storage) std::remove_cv_t<std::remove_reference_t<Type>>{std::forward<Args>(args)...};
|
||||
} else {
|
||||
new(&storage) std::remove_cv_t<std::remove_reference_t<Type>>(std::forward<Args>(args)...);
|
||||
}
|
||||
} else {
|
||||
if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<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>>>)) {
|
||||
instance = new std::remove_cv_t<std::remove_reference_t<Type>>{std::forward<Args>(args)...};
|
||||
} else {
|
||||
instance = new std::remove_cv_t<std::remove_reference_t<Type>>(std::forward<Args>(args)...);
|
||||
@@ -147,7 +142,7 @@ class basic_any {
|
||||
}
|
||||
}
|
||||
|
||||
basic_any(const basic_any &other, const policy pol) noexcept
|
||||
basic_any(const basic_any &other, const any_policy pol) noexcept
|
||||
: instance{other.data()},
|
||||
info{other.info},
|
||||
vtable{other.vtable},
|
||||
@@ -174,7 +169,7 @@ public:
|
||||
: instance{},
|
||||
info{},
|
||||
vtable{},
|
||||
mode{policy::owner} {
|
||||
mode{any_policy::owner} {
|
||||
initialize<Type>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -214,7 +209,7 @@ public:
|
||||
|
||||
/*! @brief Frees the internal storage, whatever it means. */
|
||||
~basic_any() {
|
||||
if(vtable && owner()) {
|
||||
if(vtable && (mode == any_policy::owner)) {
|
||||
vtable(operation::destroy, *this, nullptr);
|
||||
}
|
||||
}
|
||||
@@ -295,7 +290,7 @@ public:
|
||||
* @return An opaque pointer the contained instance, if any.
|
||||
*/
|
||||
[[nodiscard]] void *data() noexcept {
|
||||
return mode == policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data());
|
||||
return mode == any_policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -304,7 +299,7 @@ public:
|
||||
* @return An opaque pointer the contained instance, if any.
|
||||
*/
|
||||
[[nodiscard]] void *data(const type_info &req) noexcept {
|
||||
return mode == policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data(req));
|
||||
return mode == any_policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data(req));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -325,7 +320,7 @@ public:
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
bool assign(const basic_any &other) {
|
||||
if(vtable && mode != policy::cref && *info == *other.info) {
|
||||
if(vtable && mode != any_policy::cref && *info == *other.info) {
|
||||
return (vtable(operation::assign, *this, other.data()) != nullptr);
|
||||
}
|
||||
|
||||
@@ -334,7 +329,7 @@ public:
|
||||
|
||||
/*! @copydoc assign */
|
||||
bool assign(basic_any &&other) {
|
||||
if(vtable && mode != policy::cref && *info == *other.info) {
|
||||
if(vtable && mode != any_policy::cref && *info == *other.info) {
|
||||
if(auto *val = other.data(); val) {
|
||||
return (vtable(operation::transfer, *this, val) != nullptr);
|
||||
} else {
|
||||
@@ -347,7 +342,7 @@ public:
|
||||
|
||||
/*! @brief Destroys contained object */
|
||||
void reset() {
|
||||
if(vtable && owner()) {
|
||||
if(vtable && (mode == any_policy::owner)) {
|
||||
vtable(operation::destroy, *this, nullptr);
|
||||
}
|
||||
|
||||
@@ -355,7 +350,7 @@ public:
|
||||
ENTT_ASSERT((instance = nullptr) == nullptr, "");
|
||||
info = &type_id<void>();
|
||||
vtable = nullptr;
|
||||
mode = policy::owner;
|
||||
mode = any_policy::owner;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -393,20 +388,28 @@ public:
|
||||
* @return A wrapper that shares a reference to an unmanaged object.
|
||||
*/
|
||||
[[nodiscard]] basic_any as_ref() noexcept {
|
||||
return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)};
|
||||
return basic_any{*this, (mode == any_policy::cref ? any_policy::cref : any_policy::ref)};
|
||||
}
|
||||
|
||||
/*! @copydoc as_ref */
|
||||
[[nodiscard]] basic_any as_ref() const noexcept {
|
||||
return basic_any{*this, policy::cref};
|
||||
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.
|
||||
*/
|
||||
[[nodiscard]] bool owner() const noexcept {
|
||||
return (mode == policy::owner);
|
||||
[[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.
|
||||
*/
|
||||
[[nodiscard]] any_policy policy() const noexcept {
|
||||
return mode;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -416,7 +419,7 @@ private:
|
||||
};
|
||||
const type_info *info;
|
||||
vtable_type *vtable;
|
||||
policy mode;
|
||||
any_policy mode;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -428,7 +431,7 @@ private:
|
||||
* @return The element converted to the requested type.
|
||||
*/
|
||||
template<typename Type, std::size_t Len, std::size_t Align>
|
||||
Type any_cast(const basic_any<Len, Align> &data) noexcept {
|
||||
[[nodiscard]] 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);
|
||||
@@ -436,7 +439,7 @@ Type any_cast(const basic_any<Len, Align> &data) noexcept {
|
||||
|
||||
/*! @copydoc any_cast */
|
||||
template<typename Type, std::size_t Len, std::size_t Align>
|
||||
Type any_cast(basic_any<Len, Align> &data) noexcept {
|
||||
[[nodiscard]] 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");
|
||||
@@ -445,7 +448,7 @@ Type any_cast(basic_any<Len, Align> &data) noexcept {
|
||||
|
||||
/*! @copydoc any_cast */
|
||||
template<typename Type, std::size_t Len, std::size_t Align>
|
||||
Type any_cast(basic_any<Len, Align> &&data) noexcept {
|
||||
[[nodiscard]] Type any_cast(basic_any<Len, Align> &&data) noexcept {
|
||||
if constexpr(std::is_copy_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
|
||||
if(auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); instance) {
|
||||
return static_cast<Type>(std::move(*instance));
|
||||
@@ -461,14 +464,14 @@ Type any_cast(basic_any<Len, Align> &&data) noexcept {
|
||||
|
||||
/*! @copydoc any_cast */
|
||||
template<typename Type, std::size_t Len, std::size_t Align>
|
||||
const Type *any_cast(const basic_any<Len, Align> *data) noexcept {
|
||||
[[nodiscard]] const Type *any_cast(const basic_any<Len, Align> *data) noexcept {
|
||||
const auto &info = type_id<std::remove_cv_t<Type>>();
|
||||
return static_cast<const Type *>(data->data(info));
|
||||
}
|
||||
|
||||
/*! @copydoc any_cast */
|
||||
template<typename Type, std::size_t Len, std::size_t Align>
|
||||
Type *any_cast(basic_any<Len, Align> *data) noexcept {
|
||||
[[nodiscard]] Type *any_cast(basic_any<Len, Align> *data) noexcept {
|
||||
if constexpr(std::is_const_v<Type>) {
|
||||
// last attempt to make wrappers for const references return their values
|
||||
return any_cast<Type>(&std::as_const(*data));
|
||||
@@ -488,7 +491,7 @@ Type *any_cast(basic_any<Len, Align> *data) noexcept {
|
||||
* @return A properly initialized wrapper for an object of the given type.
|
||||
*/
|
||||
template<typename Type, std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename... Args>
|
||||
basic_any<Len, Align> make_any(Args &&...args) {
|
||||
[[nodiscard]] basic_any<Len, Align> make_any(Args &&...args) {
|
||||
return basic_any<Len, Align>{std::in_place_type<Type>, std::forward<Args>(args)...};
|
||||
}
|
||||
|
||||
@@ -501,7 +504,7 @@ basic_any<Len, Align> make_any(Args &&...args) {
|
||||
* @return A properly initialized and not necessarily owning wrapper.
|
||||
*/
|
||||
template<std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename Type>
|
||||
basic_any<Len, Align> forward_as_any(Type &&value) {
|
||||
[[nodiscard]] basic_any<Len, Align> forward_as_any(Type &&value) {
|
||||
return basic_any<Len, Align>{std::in_place_type<Type &&>, std::forward<Type>(value)};
|
||||
}
|
||||
|
||||
|
||||
@@ -9,11 +9,7 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename Type, std::size_t, typename = void>
|
||||
@@ -73,11 +69,7 @@ struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Ty
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief A compressed pair.
|
||||
|
||||
@@ -7,11 +7,7 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename>
|
||||
@@ -43,11 +39,7 @@ struct basic_hashed_string {
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Zero overhead unique identifier.
|
||||
@@ -67,7 +59,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 hs_traits = internal::fnv1a_traits<id_type>;
|
||||
using traits_type = internal::fnv1a_traits<id_type>;
|
||||
|
||||
struct const_wrapper {
|
||||
// non-explicit constructor on purpose
|
||||
@@ -79,10 +71,10 @@ 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, hs_traits::offset};
|
||||
base_type base{str, 0u, traits_type::offset};
|
||||
|
||||
for(; str[base.length]; ++base.length) {
|
||||
base.hash = (base.hash ^ static_cast<hs_traits::type>(str[base.length])) * hs_traits::prime;
|
||||
base.hash = (base.hash ^ static_cast<traits_type::type>(str[base.length])) * traits_type::prime;
|
||||
}
|
||||
|
||||
return base;
|
||||
@@ -90,10 +82,10 @@ 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, const std::size_t len) noexcept {
|
||||
base_type base{str, len, hs_traits::offset};
|
||||
base_type base{str, len, traits_type::offset};
|
||||
|
||||
for(size_type pos{}; pos < len; ++pos) {
|
||||
base.hash = (base.hash ^ static_cast<hs_traits::type>(str[pos])) * hs_traits::prime;
|
||||
base.hash = (base.hash ^ static_cast<traits_type::type>(str[pos])) * traits_type::prime;
|
||||
}
|
||||
|
||||
return base;
|
||||
@@ -175,7 +167,7 @@ public:
|
||||
* @return The size of the hashed string.
|
||||
*/
|
||||
[[nodiscard]] constexpr size_type size() const noexcept {
|
||||
return base_type::length;
|
||||
return base_type::length; // NOLINT
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @brief Checks whether a value is a power of two or not.
|
||||
* @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.
|
||||
*/
|
||||
@@ -21,7 +22,8 @@ namespace entt {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Computes the smallest power of two greater than or equal to a value.
|
||||
* @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.
|
||||
*/
|
||||
@@ -106,7 +108,7 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma
|
||||
|
||||
/**
|
||||
* @brief Deleter for allocator-aware unique pointers (waiting for C++20).
|
||||
* @tparam Args Types of arguments to use to construct the object.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Allocator>
|
||||
struct allocation_deleter: private Allocator {
|
||||
@@ -127,7 +129,7 @@ struct allocation_deleter: private Allocator {
|
||||
* @param ptr A valid pointer to an object of the given type.
|
||||
*/
|
||||
constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v<typename allocator_type::value_type>) {
|
||||
using alloc_traits = typename std::allocator_traits<Allocator>;
|
||||
using alloc_traits = std::allocator_traits<Allocator>;
|
||||
alloc_traits::destroy(*this, to_address(ptr));
|
||||
alloc_traits::deallocate(*this, ptr, 1u);
|
||||
}
|
||||
@@ -163,11 +165,7 @@ ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) {
|
||||
return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc};
|
||||
}
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename Type>
|
||||
@@ -223,11 +221,7 @@ struct uses_allocator_construction<std::pair<Type, Other>> {
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Uses-allocator construction utility (waiting for C++20).
|
||||
|
||||
@@ -7,11 +7,7 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename>
|
||||
@@ -21,11 +17,7 @@ template<typename... Args>
|
||||
struct is_tuple_impl<std::tuple<Args...>>: std::true_type {};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if a given type is a
|
||||
@@ -69,7 +61,7 @@ struct forward_apply: private Func {
|
||||
* @tparam Args Types of arguments to use to construct the new instance.
|
||||
* @param args Parameters to use to construct the instance.
|
||||
*/
|
||||
template<class... Args>
|
||||
template<typename... Args>
|
||||
constexpr forward_apply(Args &&...args) noexcept(std::is_nothrow_constructible_v<Func, Args...>)
|
||||
: Func{std::forward<Args>(args)...} {}
|
||||
|
||||
@@ -79,13 +71,13 @@ struct forward_apply: private Func {
|
||||
* @param args Parameters to forward to the underlying function.
|
||||
* @return Return value of the underlying function, if any.
|
||||
*/
|
||||
template<class Type>
|
||||
template<typename Type>
|
||||
constexpr decltype(auto) operator()(Type &&args) noexcept(noexcept(std::apply(std::declval<Func &>(), args))) {
|
||||
return std::apply(static_cast<Func &>(*this), std::forward<Type>(args));
|
||||
}
|
||||
|
||||
/*! @copydoc operator()() */
|
||||
template<class Type>
|
||||
template<typename Type>
|
||||
constexpr decltype(auto) operator()(Type &&args) const noexcept(noexcept(std::apply(std::declval<const Func &>(), args))) {
|
||||
return std::apply(static_cast<const Func &>(*this), std::forward<Type>(args));
|
||||
}
|
||||
|
||||
@@ -11,11 +11,7 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
struct ENTT_API type_index final {
|
||||
@@ -38,26 +34,26 @@ template<typename Type>
|
||||
}
|
||||
|
||||
template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
|
||||
[[nodiscard]] static constexpr std::string_view type_name(int) noexcept {
|
||||
[[nodiscard]] constexpr std::string_view type_name(int) noexcept {
|
||||
constexpr auto value = stripped_type_name<Type>();
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] static std::string_view type_name(char) noexcept {
|
||||
[[nodiscard]] std::string_view type_name(char) noexcept {
|
||||
static const auto value = stripped_type_name<Type>();
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
|
||||
[[nodiscard]] static constexpr id_type type_hash(int) noexcept {
|
||||
[[nodiscard]] constexpr id_type type_hash(int) noexcept {
|
||||
constexpr auto stripped = stripped_type_name<Type>();
|
||||
constexpr auto value = hashed_string::value(stripped.data(), stripped.size());
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] static id_type type_hash(char) noexcept {
|
||||
[[nodiscard]] id_type type_hash(char) noexcept {
|
||||
static const auto value = [](const auto stripped) {
|
||||
return hashed_string::value(stripped.data(), stripped.size());
|
||||
}(stripped_type_name<Type>());
|
||||
@@ -65,11 +61,7 @@ template<typename Type>
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Type sequential identifier.
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
@@ -16,7 +17,7 @@ namespace entt {
|
||||
*/
|
||||
template<std::size_t N>
|
||||
struct choice_t
|
||||
// Unfortunately, doxygen cannot parse such a construct.
|
||||
// unfortunately, doxygen cannot parse such a construct
|
||||
: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
|
||||
{};
|
||||
|
||||
@@ -55,7 +56,6 @@ using type_identity_t = typename type_identity<Type>::type;
|
||||
/**
|
||||
* @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
|
||||
* @tparam Type The type of which to return the size.
|
||||
* @tparam The size of the type if `sizeof` accepts it, 0 otherwise.
|
||||
*/
|
||||
template<typename Type, typename = void>
|
||||
struct size_of: std::integral_constant<std::size_t, 0u> {};
|
||||
@@ -250,37 +250,40 @@ struct type_list_cat<type_list<Type...>> {
|
||||
template<typename... List>
|
||||
using type_list_cat_t = typename type_list_cat<List...>::type;
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename>
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename...>
|
||||
struct type_list_unique;
|
||||
|
||||
template<typename First, typename... Other, typename... Type>
|
||||
struct type_list_unique<type_list<First, Other...>, Type...>
|
||||
: std::conditional_t<(std::is_same_v<First, Type> || ...), type_list_unique<type_list<Other...>, Type...>, type_list_unique<type_list<Other...>, Type..., First>> {};
|
||||
|
||||
template<typename... Type>
|
||||
struct type_list_unique<type_list<>, Type...> {
|
||||
using type = type_list<Type...>;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Removes duplicates types from a type list.
|
||||
* @tparam Type One of the types provided by the given type list.
|
||||
* @tparam Other The other types provided by the given type list.
|
||||
* @tparam List Type list.
|
||||
*/
|
||||
template<typename Type, typename... Other>
|
||||
struct type_list_unique<type_list<Type, Other...>> {
|
||||
template<typename List>
|
||||
struct type_list_unique {
|
||||
/*! @brief A type list without duplicate types. */
|
||||
using type = std::conditional_t<
|
||||
(std::is_same_v<Type, Other> || ...),
|
||||
typename type_list_unique<type_list<Other...>>::type,
|
||||
type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>;
|
||||
};
|
||||
|
||||
/*! @brief Removes duplicates types from a type list. */
|
||||
template<>
|
||||
struct type_list_unique<type_list<>> {
|
||||
/*! @brief A type list without duplicate types. */
|
||||
using type = type_list<>;
|
||||
using type = typename internal::type_list_unique<List>::type;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Type A type list.
|
||||
* @tparam List Type list.
|
||||
*/
|
||||
template<typename Type>
|
||||
using type_list_unique_t = typename type_list_unique<Type>::type;
|
||||
template<typename List>
|
||||
using type_list_unique_t = typename type_list_unique<List>::type;
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if a type list contains a
|
||||
@@ -297,7 +300,8 @@ struct type_list_contains;
|
||||
* @tparam Other Type to look for.
|
||||
*/
|
||||
template<typename... Type, typename Other>
|
||||
struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
|
||||
struct type_list_contains<type_list<Type...>, Other>
|
||||
: std::bool_constant<(std::is_same_v<Type, Other> || ...)> {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
@@ -385,10 +389,20 @@ struct value_list_element<Index, value_list<Value, Other...>>
|
||||
*/
|
||||
template<auto Value, auto... Other>
|
||||
struct value_list_element<0u, value_list<Value, Other...>> {
|
||||
/*! @brief Searched type. */
|
||||
using type = decltype(Value);
|
||||
/*! @brief Searched value. */
|
||||
static constexpr auto value = Value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Index Index of the type to return.
|
||||
* @tparam List Value list to search into.
|
||||
*/
|
||||
template<std::size_t Index, typename List>
|
||||
using value_list_element_t = typename value_list_element<Index, List>::type;
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Index Index of the value to return.
|
||||
@@ -397,6 +411,58 @@ struct value_list_element<0u, value_list<Value, Other...>> {
|
||||
template<std::size_t Index, typename List>
|
||||
inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<auto, typename>
|
||||
struct value_list_index;
|
||||
|
||||
/**
|
||||
* @brief Provides compile-time type access to the values of a value list.
|
||||
* @tparam Value Value to look for and for which to return the index.
|
||||
* @tparam First First value provided by the value list.
|
||||
* @tparam Other Other values provided by the value list.
|
||||
*/
|
||||
template<auto Value, auto First, auto... Other>
|
||||
struct value_list_index<Value, value_list<First, Other...>> {
|
||||
/*! @brief Unsigned integer type. */
|
||||
using value_type = std::size_t;
|
||||
/*! @brief Compile-time position of the given value in the sublist. */
|
||||
static constexpr value_type value = 1u + value_list_index<Value, value_list<Other...>>::value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Provides compile-time type access to the values of a value list.
|
||||
* @tparam Value Value to look for and for which to return the index.
|
||||
* @tparam Other Other values provided by the value list.
|
||||
*/
|
||||
template<auto Value, auto... Other>
|
||||
struct value_list_index<Value, value_list<Value, Other...>> {
|
||||
static_assert(value_list_index<Value, value_list<Other...>>::value == sizeof...(Other), "Non-unique type");
|
||||
/*! @brief Unsigned integer type. */
|
||||
using value_type = std::size_t;
|
||||
/*! @brief Compile-time position of the given value in the sublist. */
|
||||
static constexpr value_type value = 0u;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Provides compile-time type access to the values of a value list.
|
||||
* @tparam Value Value to look for and for which to return the index.
|
||||
*/
|
||||
template<auto Value>
|
||||
struct value_list_index<Value, value_list<>> {
|
||||
/*! @brief Unsigned integer type. */
|
||||
using value_type = std::size_t;
|
||||
/*! @brief Compile-time position of the given type in the sublist. */
|
||||
static constexpr value_type value = 0u;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam List Value list.
|
||||
* @tparam Value Value to look for and for which to return the index.
|
||||
*/
|
||||
template<auto Value, typename List>
|
||||
inline constexpr std::size_t value_list_index_v = value_list_index<Value, List>::value;
|
||||
|
||||
/**
|
||||
* @brief Concatenates multiple value lists.
|
||||
* @tparam Value Values provided by the first value list.
|
||||
@@ -448,6 +514,89 @@ struct value_list_cat<value_list<Value...>> {
|
||||
template<typename... List>
|
||||
using value_list_cat_t = typename value_list_cat<List...>::type;
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename>
|
||||
struct value_list_unique;
|
||||
|
||||
/**
|
||||
* @brief Removes duplicates values from a value list.
|
||||
* @tparam Value One of the values provided by the given value list.
|
||||
* @tparam Other The other values provided by the given value list.
|
||||
*/
|
||||
template<auto Value, auto... Other>
|
||||
struct value_list_unique<value_list<Value, Other...>> {
|
||||
/*! @brief A value list without duplicate types. */
|
||||
using type = std::conditional_t<
|
||||
((Value == Other) || ...),
|
||||
typename value_list_unique<value_list<Other...>>::type,
|
||||
value_list_cat_t<value_list<Value>, typename value_list_unique<value_list<Other...>>::type>>;
|
||||
};
|
||||
|
||||
/*! @brief Removes duplicates values from a value list. */
|
||||
template<>
|
||||
struct value_list_unique<value_list<>> {
|
||||
/*! @brief A value list without duplicate types. */
|
||||
using type = value_list<>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Type A value list.
|
||||
*/
|
||||
template<typename Type>
|
||||
using value_list_unique_t = typename value_list_unique<Type>::type;
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if a value list contains
|
||||
* a given value, false otherwise.
|
||||
* @tparam List Value list.
|
||||
* @tparam Value Value to look for.
|
||||
*/
|
||||
template<typename List, auto Value>
|
||||
struct value_list_contains;
|
||||
|
||||
/**
|
||||
* @copybrief value_list_contains
|
||||
* @tparam Value Values provided by the value list.
|
||||
* @tparam Other Value to look for.
|
||||
*/
|
||||
template<auto... Value, auto Other>
|
||||
struct value_list_contains<value_list<Value...>, Other>
|
||||
: std::bool_constant<((Value == Other) || ...)> {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam List Value list.
|
||||
* @tparam Value Value to look for.
|
||||
*/
|
||||
template<typename List, auto Value>
|
||||
inline constexpr bool value_list_contains_v = value_list_contains<List, Value>::value;
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename...>
|
||||
class value_list_diff;
|
||||
|
||||
/**
|
||||
* @brief Computes the difference between two value lists.
|
||||
* @tparam Value Values provided by the first value list.
|
||||
* @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:
|
||||
/*! @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>>...>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam List Value lists between which to compute the difference.
|
||||
*/
|
||||
template<typename... List>
|
||||
using value_list_diff_t = typename value_list_diff<List...>::type;
|
||||
|
||||
/*! @brief Same as std::is_invocable, but with tuples. */
|
||||
template<typename, typename>
|
||||
struct is_applicable: std::false_type {};
|
||||
@@ -529,11 +678,7 @@ inline constexpr bool is_complete_v = is_complete<Type>::value;
|
||||
template<typename Type, typename = void>
|
||||
struct is_iterator: std::false_type {};
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename, typename = void>
|
||||
@@ -543,15 +688,11 @@ template<typename Type>
|
||||
struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
|
||||
/*! @copydoc is_iterator */
|
||||
template<typename Type>
|
||||
struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_pointer_t<Type>>, void>>>
|
||||
struct is_iterator<Type, std::enable_if_t<!std::is_void_v<std::remove_cv_t<std::remove_pointer_t<Type>>>>>
|
||||
: internal::has_iterator_category<Type> {};
|
||||
|
||||
/**
|
||||
@@ -568,7 +709,7 @@ inline constexpr bool is_iterator_v = is_iterator<Type>::value;
|
||||
*/
|
||||
template<typename Type>
|
||||
struct is_ebco_eligible
|
||||
: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
|
||||
: std::bool_constant<std::is_empty_v<Type> && !std::is_final_v<Type>> {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
@@ -596,19 +737,7 @@ struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::tr
|
||||
template<typename Type>
|
||||
inline constexpr bool is_transparent_v = is_transparent<Type>::value;
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if a given type is
|
||||
* equality comparable, false otherwise.
|
||||
* @tparam Type The type to test.
|
||||
*/
|
||||
template<typename Type, typename = void>
|
||||
struct is_equality_comparable: std::false_type {};
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename, typename = void>
|
||||
@@ -617,47 +746,69 @@ struct has_tuple_size_value: std::false_type {};
|
||||
template<typename Type>
|
||||
struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
|
||||
|
||||
template<typename, typename = void>
|
||||
struct has_value_type: std::false_type {};
|
||||
|
||||
template<typename Type>
|
||||
struct has_value_type<Type, std::void_t<typename Type::value_type>>: std::true_type {};
|
||||
|
||||
template<typename>
|
||||
[[nodiscard]] constexpr bool dispatch_is_equality_comparable();
|
||||
|
||||
template<typename Type, std::size_t... Index>
|
||||
[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
|
||||
return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
|
||||
return (dispatch_is_equality_comparable<std::tuple_element_t<Index, Type>>() && ...);
|
||||
}
|
||||
|
||||
template<typename>
|
||||
[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
|
||||
[[nodiscard]] constexpr bool maybe_equality_comparable(char) {
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval<Type>() == std::declval<Type>()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
|
||||
if constexpr(is_iterator_v<Type>) {
|
||||
return true;
|
||||
} else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
|
||||
return maybe_equality_comparable<Type>(choice<0>);
|
||||
[[nodiscard]] constexpr bool dispatch_is_equality_comparable() {
|
||||
if constexpr(std::is_array_v<Type>) {
|
||||
return false;
|
||||
} else if constexpr(is_iterator_v<Type>) {
|
||||
return maybe_equality_comparable<Type>(0);
|
||||
} else if constexpr(has_value_type<Type>::value) {
|
||||
if constexpr(std::is_same_v<typename Type::value_type, Type>) {
|
||||
return maybe_equality_comparable<Type>(0);
|
||||
} else if constexpr(dispatch_is_equality_comparable<typename Type::value_type>()) {
|
||||
return maybe_equality_comparable<Type>(0);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if constexpr(is_complete_v<std::tuple_size<std::remove_cv_t<Type>>>) {
|
||||
if constexpr(has_tuple_size_value<Type>::value) {
|
||||
return maybe_equality_comparable<Type>(0) && unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
|
||||
} else {
|
||||
return maybe_equality_comparable<Type>(0);
|
||||
}
|
||||
} else {
|
||||
return is_equality_comparable<typename Type::value_type>::value;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_cv_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
|
||||
if constexpr(has_tuple_size_value<Type>::value) {
|
||||
return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
|
||||
} else {
|
||||
return maybe_equality_comparable<Type>(choice<1>);
|
||||
return maybe_equality_comparable<Type>(0);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
* @brief Provides the member constant `value` to true if a given type is
|
||||
* equality comparable, false otherwise.
|
||||
* @tparam Type The type to test.
|
||||
*/
|
||||
template<typename Type>
|
||||
struct is_equality_comparable: std::bool_constant<internal::dispatch_is_equality_comparable<Type>()> {};
|
||||
|
||||
/*! @copydoc is_equality_comparable */
|
||||
template<typename Type>
|
||||
struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
|
||||
: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
|
||||
struct is_equality_comparable<const Type>: is_equality_comparable<Type> {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
@@ -724,9 +875,9 @@ using member_class_t = typename member_class<Member>::type;
|
||||
/**
|
||||
* @brief Extracts the n-th argument of a given function or member function.
|
||||
* @tparam Index The index of the argument to extract.
|
||||
* @tparam Candidate A valid function, member function or data member.
|
||||
* @tparam Candidate A valid function, member function or data member type.
|
||||
*/
|
||||
template<std::size_t Index, auto Candidate>
|
||||
template<std::size_t Index, typename Candidate>
|
||||
class nth_argument {
|
||||
template<typename Ret, typename... Args>
|
||||
static constexpr type_list<Args...> pick_up(Ret (*)(Args...));
|
||||
@@ -742,17 +893,29 @@ class nth_argument {
|
||||
|
||||
public:
|
||||
/*! @brief N-th argument of the given function or member function. */
|
||||
using type = type_list_element_t<Index, decltype(pick_up(Candidate))>;
|
||||
using type = type_list_element_t<Index, decltype(pick_up(std::declval<Candidate>()))>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Index The index of the argument to extract.
|
||||
* @tparam Candidate A valid function, member function or data member.
|
||||
* @tparam Candidate A valid function, member function or data member type.
|
||||
*/
|
||||
template<std::size_t Index, auto Candidate>
|
||||
template<std::size_t Index, typename Candidate>
|
||||
using nth_argument_t = typename nth_argument<Index, Candidate>::type;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
template<typename... Type>
|
||||
struct std::tuple_size<entt::type_list<Type...>>: std::integral_constant<std::size_t, entt::type_list<Type...>::size> {};
|
||||
|
||||
template<std::size_t Index, typename... Type>
|
||||
struct std::tuple_element<Index, entt::type_list<Type...>>: entt::type_list_element<Index, entt::type_list<Type...>> {};
|
||||
|
||||
template<auto... Value>
|
||||
struct std::tuple_size<entt::value_list<Value...>>: std::integral_constant<std::size_t, entt::value_list<Value...>::size> {};
|
||||
|
||||
template<std::size_t Index, auto... Value>
|
||||
struct std::tuple_element<Index, entt::value_list<Value...>>: entt::value_list_element<Index, entt::value_list<Value...>> {};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -17,7 +17,7 @@ struct identity {
|
||||
* @param value The actual argument.
|
||||
* @return The submitted value as-is.
|
||||
*/
|
||||
template<class Type>
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept {
|
||||
return std::forward<Type>(value);
|
||||
}
|
||||
@@ -50,7 +50,7 @@ template<typename Func>
|
||||
* @brief Helper type for visitors.
|
||||
* @tparam Func Types of function objects.
|
||||
*/
|
||||
template<class... Func>
|
||||
template<typename... Func>
|
||||
struct overloaded: Func... {
|
||||
using Func::operator()...;
|
||||
};
|
||||
@@ -59,14 +59,14 @@ struct overloaded: Func... {
|
||||
* @brief Deduction guide.
|
||||
* @tparam Func Types of function objects.
|
||||
*/
|
||||
template<class... Func>
|
||||
template<typename... Func>
|
||||
overloaded(Func...) -> overloaded<Func...>;
|
||||
|
||||
/**
|
||||
* @brief Basic implementation of a y-combinator.
|
||||
* @tparam Func Type of a potentially recursive function.
|
||||
*/
|
||||
template<class Func>
|
||||
template<typename Func>
|
||||
struct y_combinator {
|
||||
/**
|
||||
* @brief Constructs a y-combinator from a given function.
|
||||
@@ -81,13 +81,13 @@ struct y_combinator {
|
||||
* @param args Parameters to use to invoke the underlying function.
|
||||
* @return Return value of the underlying function, if any.
|
||||
*/
|
||||
template<class... Args>
|
||||
template<typename... Args>
|
||||
constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v<Func, const y_combinator &, Args...>) {
|
||||
return func(*this, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/*! @copydoc operator()() */
|
||||
template<class... Args>
|
||||
template<typename... Args>
|
||||
constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v<Func, y_combinator &, Args...>) {
|
||||
return func(*this, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -4,19 +4,19 @@
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename Type, typename = void>
|
||||
struct in_place_delete: std::bool_constant<!(std::is_move_constructible_v<Type> && std::is_move_assignable_v<Type>)> {};
|
||||
|
||||
template<>
|
||||
struct in_place_delete<void>: std::false_type {};
|
||||
|
||||
template<typename Type>
|
||||
struct in_place_delete<Type, std::enable_if_t<Type::in_place_delete>>
|
||||
: std::true_type {};
|
||||
@@ -24,16 +24,15 @@ struct in_place_delete<Type, std::enable_if_t<Type::in_place_delete>>
|
||||
template<typename Type, typename = void>
|
||||
struct page_size: std::integral_constant<std::size_t, !std::is_empty_v<ENTT_ETO_TYPE(Type)> * ENTT_PACKED_PAGE> {};
|
||||
|
||||
template<>
|
||||
struct page_size<void>: std::integral_constant<std::size_t, 0u> {};
|
||||
|
||||
template<typename Type>
|
||||
struct page_size<Type, std::enable_if_t<std::is_convertible_v<decltype(Type::page_size), std::size_t>>>
|
||||
struct page_size<Type, std::void_t<decltype(Type::page_size)>>
|
||||
: std::integral_constant<std::size_t, Type::page_size> {};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Common way to access various properties of components.
|
||||
@@ -52,13 +51,6 @@ struct component_traits {
|
||||
static constexpr std::size_t page_size = internal::page_size<Type>::value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type Type of component.
|
||||
*/
|
||||
template<class Type>
|
||||
inline constexpr bool ignore_as_empty_v = (std::is_void_v<Type> || component_traits<Type>::page_size == 0u);
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
|
||||
@@ -9,70 +9,78 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @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;
|
||||
|
||||
template<typename Type>
|
||||
struct entt_traits<Type, std::enable_if_t<std::is_enum_v<Type>>>
|
||||
: entt_traits<std::underlying_type_t<Type>> {};
|
||||
: entt_traits<std::underlying_type_t<Type>> {
|
||||
using value_type = Type;
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
struct entt_traits<Type, std::enable_if_t<std::is_class_v<Type>>>
|
||||
: entt_traits<typename Type::entity_type> {};
|
||||
: entt_traits<typename Type::entity_type> {
|
||||
using value_type = Type;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct entt_traits<std::uint32_t> {
|
||||
using value_type = std::uint32_t;
|
||||
|
||||
using entity_type = std::uint32_t;
|
||||
using version_type = std::uint16_t;
|
||||
|
||||
static constexpr entity_type entity_mask = 0xFFFFF;
|
||||
static constexpr entity_type version_mask = 0xFFF;
|
||||
static constexpr std::size_t entity_shift = 20u;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct entt_traits<std::uint64_t> {
|
||||
using value_type = std::uint64_t;
|
||||
|
||||
using entity_type = std::uint64_t;
|
||||
using version_type = std::uint32_t;
|
||||
|
||||
static constexpr entity_type entity_mask = 0xFFFFFFFF;
|
||||
static constexpr entity_type version_mask = 0xFFFFFFFF;
|
||||
static constexpr std::size_t entity_shift = 32u;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
* @brief Common basic entity traits implementation.
|
||||
* @tparam Traits Actual entity traits to use.
|
||||
*/
|
||||
template<typename Traits>
|
||||
class basic_entt_traits {
|
||||
static constexpr auto length = internal::popcount(Traits::entity_mask);
|
||||
|
||||
/**
|
||||
* @brief Entity traits.
|
||||
* @tparam Type Type of identifier.
|
||||
*/
|
||||
template<typename Type>
|
||||
class entt_traits: internal::entt_traits<Type> {
|
||||
using base_type = internal::entt_traits<Type>;
|
||||
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");
|
||||
|
||||
public:
|
||||
/*! @brief Value type. */
|
||||
using value_type = Type;
|
||||
using value_type = typename Traits::value_type;
|
||||
/*! @brief Underlying entity type. */
|
||||
using entity_type = typename base_type::entity_type;
|
||||
using entity_type = typename Traits::entity_type;
|
||||
/*! @brief Underlying version type. */
|
||||
using version_type = typename base_type::version_type;
|
||||
/*! @brief Reserved identifier. */
|
||||
static constexpr entity_type reserved = base_type::entity_mask | (base_type::version_mask << base_type::entity_shift);
|
||||
/*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */
|
||||
static constexpr auto page_size = ENTT_SPARSE_PAGE;
|
||||
using version_type = typename Traits::version_type;
|
||||
|
||||
/*! @brief Entity mask size. */
|
||||
static constexpr entity_type entity_mask = Traits::entity_mask;
|
||||
/*! @brief Version mask size */
|
||||
static constexpr entity_type version_mask = Traits::version_mask;
|
||||
|
||||
/**
|
||||
* @brief Converts an entity to its underlying type.
|
||||
@@ -89,7 +97,7 @@ public:
|
||||
* @return The integral representation of the entity part.
|
||||
*/
|
||||
[[nodiscard]] static constexpr entity_type to_entity(const value_type value) noexcept {
|
||||
return (to_integral(value) & base_type::entity_mask);
|
||||
return (to_integral(value) & entity_mask);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,7 +106,17 @@ public:
|
||||
* @return The integral representation of the version part.
|
||||
*/
|
||||
[[nodiscard]] static constexpr version_type to_version(const value_type value) noexcept {
|
||||
return (to_integral(value) >> base_type::entity_shift);
|
||||
return (static_cast<version_type>(to_integral(value) >> length) & version_mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the successor of a given identifier.
|
||||
* @param value The identifier of which to return the successor.
|
||||
* @return The successor of the given identifier.
|
||||
*/
|
||||
[[nodiscard]] static constexpr value_type next(const value_type value) noexcept {
|
||||
const auto vers = to_version(value) + 1;
|
||||
return construct(to_integral(value), static_cast<version_type>(vers + (vers == version_mask)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,7 +130,7 @@ 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 & base_type::entity_mask) | (static_cast<entity_type>(version) << base_type::entity_shift)};
|
||||
return value_type{(entity & entity_mask) | (static_cast<entity_type>(version & version_mask) << length)};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,14 +144,27 @@ public:
|
||||
* @return A properly constructed identifier.
|
||||
*/
|
||||
[[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept {
|
||||
constexpr auto mask = (base_type::version_mask << base_type::entity_shift);
|
||||
return value_type{(lhs & base_type::entity_mask) | (rhs & mask)};
|
||||
return value_type{(lhs & entity_mask) | (rhs & (version_mask << length))};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @copydoc entt_traits<Entity>::to_integral
|
||||
* @brief Entity traits.
|
||||
* @tparam Type Type of identifier.
|
||||
*/
|
||||
template<typename Type>
|
||||
struct entt_traits: basic_entt_traits<internal::entt_traits<Type>> {
|
||||
/*! @brief Base type. */
|
||||
using base_type = basic_entt_traits<internal::entt_traits<Type>>;
|
||||
/*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */
|
||||
static constexpr std::size_t page_size = ENTT_SPARSE_PAGE;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Converts an entity to its underlying type.
|
||||
* @tparam Entity The value type.
|
||||
* @param value The value to convert.
|
||||
* @return The integral representation of the given value.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_integral(const Entity value) noexcept {
|
||||
@@ -141,8 +172,10 @@ template<typename Entity>
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc entt_traits<Entity>::to_entity
|
||||
* @brief Returns the entity part once converted to the underlying type.
|
||||
* @tparam Entity The value type.
|
||||
* @param value The value to convert.
|
||||
* @return The integral representation of the entity part.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_entity(const Entity value) noexcept {
|
||||
@@ -150,8 +183,10 @@ template<typename Entity>
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc entt_traits<Entity>::to_version
|
||||
* @brief Returns the version part once converted to the underlying type.
|
||||
* @tparam Entity The value type.
|
||||
* @param value The value to convert.
|
||||
* @return The integral representation of the version part.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr typename entt_traits<Entity>::version_type to_version(const Entity value) noexcept {
|
||||
@@ -167,8 +202,9 @@ struct null_t {
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr operator Entity() const noexcept {
|
||||
using entity_traits = entt_traits<Entity>;
|
||||
return entity_traits::combine(entity_traits::reserved, entity_traits::reserved);
|
||||
using traits_type = entt_traits<Entity>;
|
||||
constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -197,8 +233,8 @@ struct null_t {
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept {
|
||||
using entity_traits = entt_traits<Entity>;
|
||||
return entity_traits::to_entity(entity) == entity_traits::to_entity(*this);
|
||||
using traits_type = entt_traits<Entity>;
|
||||
return traits_type::to_entity(entity) == traits_type::to_entity(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -246,8 +282,9 @@ struct tombstone_t {
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr operator Entity() const noexcept {
|
||||
using entity_traits = entt_traits<Entity>;
|
||||
return entity_traits::combine(entity_traits::reserved, entity_traits::reserved);
|
||||
using traits_type = entt_traits<Entity>;
|
||||
constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -276,8 +313,8 @@ struct tombstone_t {
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept {
|
||||
using entity_traits = entt_traits<Entity>;
|
||||
return entity_traits::to_version(entity) == entity_traits::to_version(*this);
|
||||
using traits_type = entt_traits<Entity>;
|
||||
return traits_type::to_version(entity) == traits_type::to_version(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef ENTT_ENTITY_FWD_HPP
|
||||
#define ENTT_ENTITY_FWD_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include "../core/fwd.hpp"
|
||||
@@ -11,52 +12,24 @@ namespace entt {
|
||||
/*! @brief Default entity identifier. */
|
||||
enum class entity : id_type {};
|
||||
|
||||
/*! @brief Storage deletion policy. */
|
||||
enum class deletion_policy : std::uint8_t {
|
||||
/*! @brief Swap-and-pop deletion policy. */
|
||||
swap_and_pop = 0u,
|
||||
/*! @brief In-place deletion policy. */
|
||||
in_place = 1u,
|
||||
/*! @brief Swap-only deletion policy. */
|
||||
swap_only = 2u
|
||||
};
|
||||
|
||||
template<typename Entity = entity, typename = std::allocator<Entity>>
|
||||
class basic_sparse_set;
|
||||
|
||||
template<typename Type, typename = entity, typename = std::allocator<Type>, typename = void>
|
||||
class basic_storage;
|
||||
|
||||
template<typename Type>
|
||||
class sigh_storage_mixin;
|
||||
|
||||
/**
|
||||
* @brief Provides a common way to define storage types.
|
||||
* @tparam Type Storage value type.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Type, typename Entity = entity, typename Allocator = std::allocator<Type>, typename = void>
|
||||
struct storage_type {
|
||||
/*! @brief Type-to-storage conversion result. */
|
||||
using type = sigh_storage_mixin<basic_storage<Type, Entity, Allocator>>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Args Arguments to forward.
|
||||
*/
|
||||
template<typename... Args>
|
||||
using storage_type_t = typename storage_type<Args...>::type;
|
||||
|
||||
/**
|
||||
* Type-to-storage conversion utility that preserves constness.
|
||||
* @tparam Type Storage value type, eventually const.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Type, typename Entity = entity, typename Allocator = std::allocator<std::remove_const_t<Type>>>
|
||||
struct storage_for {
|
||||
/*! @brief Type-to-storage conversion result. */
|
||||
using type = constness_as_t<storage_type_t<std::remove_const_t<Type>, Entity, Allocator>, Type>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Args Arguments to forward.
|
||||
*/
|
||||
template<typename... Args>
|
||||
using storage_for_t = typename storage_for<Args...>::type;
|
||||
template<typename, typename>
|
||||
class basic_sigh_mixin;
|
||||
|
||||
template<typename Entity = entity, typename = std::allocator<Entity>>
|
||||
class basic_registry;
|
||||
@@ -70,7 +43,7 @@ class basic_runtime_view;
|
||||
template<typename, typename, typename>
|
||||
class basic_group;
|
||||
|
||||
template<typename>
|
||||
template<typename, typename Mask = std::uint32_t, typename = std::allocator<Mask>>
|
||||
class basic_observer;
|
||||
|
||||
template<typename>
|
||||
@@ -88,48 +61,6 @@ class basic_snapshot_loader;
|
||||
template<typename>
|
||||
class basic_continuous_loader;
|
||||
|
||||
/**
|
||||
* @brief Alias for exclusion lists.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
using exclude_t = type_list<Type...>;
|
||||
|
||||
/**
|
||||
* @brief Variable template for exclusion lists.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
inline constexpr exclude_t<Type...> exclude{};
|
||||
|
||||
/**
|
||||
* @brief Alias for lists of observed components.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
using get_t = type_list<Type...>;
|
||||
|
||||
/**
|
||||
* @brief Variable template for lists of observed components.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
inline constexpr get_t<Type...> get{};
|
||||
|
||||
/**
|
||||
* @brief Alias for lists of owned components.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
using owned_t = type_list<Type...>;
|
||||
|
||||
/**
|
||||
* @brief Variable template for lists of owned components.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
inline constexpr owned_t<Type...> owned{};
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using sparse_set = basic_sparse_set<>;
|
||||
|
||||
@@ -140,6 +71,13 @@ using sparse_set = basic_sparse_set<>;
|
||||
template<typename Type>
|
||||
using storage = basic_storage<Type>;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Type Underlying storage type.
|
||||
*/
|
||||
template<typename Type>
|
||||
using sigh_mixin = basic_sigh_mixin<Type, basic_registry<typename Type::entity_type, typename Type::base_type::allocator_type>>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using registry = basic_registry<>;
|
||||
|
||||
@@ -178,6 +116,134 @@ using snapshot_loader = basic_snapshot_loader<registry>;
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using continuous_loader = basic_continuous_loader<registry>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using runtime_view = basic_runtime_view<sparse_set>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using const_runtime_view = basic_runtime_view<const sparse_set>;
|
||||
|
||||
/**
|
||||
* @brief Alias for exclusion lists.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
struct exclude_t final: type_list<Type...> {
|
||||
/*! @brief Default constructor. */
|
||||
explicit constexpr exclude_t() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Variable template for exclusion lists.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
inline constexpr exclude_t<Type...> exclude{};
|
||||
|
||||
/**
|
||||
* @brief Alias for lists of observed components.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
struct get_t final: type_list<Type...> {
|
||||
/*! @brief Default constructor. */
|
||||
explicit constexpr get_t() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Variable template for lists of observed components.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
inline constexpr get_t<Type...> get{};
|
||||
|
||||
/**
|
||||
* @brief Alias for lists of owned components.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
struct owned_t final: type_list<Type...> {
|
||||
/*! @brief Default constructor. */
|
||||
explicit constexpr owned_t() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Variable template for lists of owned components.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
inline constexpr owned_t<Type...> owned{};
|
||||
|
||||
/**
|
||||
* @brief Applies a given _function_ to a get list and generate a new list.
|
||||
* @tparam Type Types provided by the get list.
|
||||
* @tparam Op Unary operation as template class with a type member named `type`.
|
||||
*/
|
||||
template<typename... Type, template<typename...> class Op>
|
||||
struct type_list_transform<get_t<Type...>, Op> {
|
||||
/*! @brief Resulting get list after applying the transform function. */
|
||||
using type = get_t<typename Op<Type>::type...>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Applies a given _function_ to an exclude list and generate a new list.
|
||||
* @tparam Type Types provided by the exclude list.
|
||||
* @tparam Op Unary operation as template class with a type member named `type`.
|
||||
*/
|
||||
template<typename... Type, template<typename...> class Op>
|
||||
struct type_list_transform<exclude_t<Type...>, Op> {
|
||||
/*! @brief Resulting exclude list after applying the transform function. */
|
||||
using type = exclude_t<typename Op<Type>::type...>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Applies a given _function_ to an owned list and generate a new list.
|
||||
* @tparam Type Types provided by the owned list.
|
||||
* @tparam Op Unary operation as template class with a type member named `type`.
|
||||
*/
|
||||
template<typename... Type, template<typename...> class Op>
|
||||
struct type_list_transform<owned_t<Type...>, Op> {
|
||||
/*! @brief Resulting owned list after applying the transform function. */
|
||||
using type = owned_t<typename Op<Type>::type...>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Provides a common way to define storage types.
|
||||
* @tparam Type Storage value type.
|
||||
* @tparam Entity A valid entity type.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Type, typename Entity = entity, typename Allocator = std::allocator<Type>, typename = void>
|
||||
struct storage_type {
|
||||
/*! @brief Type-to-storage conversion result. */
|
||||
using type = sigh_mixin<basic_storage<Type, Entity, Allocator>>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Args Arguments to forward.
|
||||
*/
|
||||
template<typename... Args>
|
||||
using storage_type_t = typename storage_type<Args...>::type;
|
||||
|
||||
/**
|
||||
* Type-to-storage conversion utility that preserves constness.
|
||||
* @tparam Type Storage value type, eventually const.
|
||||
* @tparam Entity A valid entity type.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Type, typename Entity = entity, typename Allocator = std::allocator<std::remove_const_t<Type>>>
|
||||
struct storage_for {
|
||||
/*! @brief Type-to-storage conversion result. */
|
||||
using type = constness_as_t<storage_type_t<std::remove_const_t<Type>, Entity, Allocator>, Type>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Args Arguments to forward.
|
||||
*/
|
||||
template<typename... Args>
|
||||
using storage_for_t = typename storage_for<Args...>::type;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Get Types of storage iterated by the view.
|
||||
@@ -186,12 +252,6 @@ using continuous_loader = basic_continuous_loader<registry>;
|
||||
template<typename Get, typename Exclude = exclude_t<>>
|
||||
using view = basic_view<type_list_transform_t<Get, storage_for>, type_list_transform_t<Exclude, storage_for>>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using runtime_view = basic_runtime_view<sparse_set>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using const_runtime_view = basic_runtime_view<const sparse_set>;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Owned Types of storage _owned_ by the group.
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/fwd.hpp"
|
||||
#include "../core/iterator.hpp"
|
||||
#include "../core/type_info.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "component.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "sparse_set.hpp"
|
||||
@@ -15,11 +16,7 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename, typename, typename>
|
||||
@@ -29,7 +26,7 @@ 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(ignore_as_empty_v<typename Type::value_type>) {
|
||||
if constexpr(Type::traits_type::page_size == 0u) {
|
||||
return std::make_tuple();
|
||||
} else {
|
||||
return std::forward_as_tuple(cpool.rbegin()[it.index()]);
|
||||
@@ -37,17 +34,19 @@ 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 iterator_category = std::input_iterator_tag;
|
||||
using iterator_concept = std::forward_iterator_tag;
|
||||
|
||||
constexpr extended_group_iterator()
|
||||
: it{},
|
||||
pools{} {}
|
||||
|
||||
extended_group_iterator(It from, const std::tuple<Owned *..., Get *...> &cpools)
|
||||
extended_group_iterator(iterator_type from, const std::tuple<Owned *..., Get *...> &cpools)
|
||||
: it{from},
|
||||
pools{cpools} {}
|
||||
|
||||
@@ -68,6 +67,10 @@ public:
|
||||
return operator*();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr iterator_type base() const noexcept {
|
||||
return it;
|
||||
}
|
||||
|
||||
template<typename... Lhs, typename... Rhs>
|
||||
friend constexpr bool operator==(const extended_group_iterator<Lhs...> &, const extended_group_iterator<Rhs...> &) noexcept;
|
||||
|
||||
@@ -86,12 +89,163 @@ template<typename... Lhs, typename... Rhs>
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
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<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, 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, 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, std::index_sequence_for<Owned...>{});
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// 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) {
|
||||
push_on_construct(*first);
|
||||
}
|
||||
}
|
||||
|
||||
size_type owned(const id_type *elem, const size_type length) const noexcept final {
|
||||
size_type cnt = 0u;
|
||||
|
||||
for(auto pos = 0u; pos < length; ++pos) {
|
||||
cnt += ((elem[pos] == entt::type_hash<typename Owned::value_type>::value()) || ...);
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
[[nodiscard]] size_type length() const noexcept {
|
||||
return len;
|
||||
}
|
||||
|
||||
auto pools_as_tuple() const noexcept {
|
||||
return pools;
|
||||
}
|
||||
|
||||
auto filter_as_tuple() const noexcept {
|
||||
return filter;
|
||||
}
|
||||
|
||||
private:
|
||||
std::tuple<Owned *..., Get *...> pools;
|
||||
std::tuple<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;
|
||||
|
||||
void push_on_construct(const entity_type entt) {
|
||||
if(!elem.contains(entt)
|
||||
&& std::apply([entt](auto *...cpool) { return (cpool->contains(entt) && ...); }, pools)
|
||||
&& std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) {
|
||||
elem.push(entt);
|
||||
}
|
||||
}
|
||||
|
||||
void push_on_destroy(const entity_type entt) {
|
||||
if(!elem.contains(entt)
|
||||
&& std::apply([entt](auto *...cpool) { return (cpool->contains(entt) && ...); }, pools)
|
||||
&& std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) {
|
||||
elem.push(entt);
|
||||
}
|
||||
}
|
||||
|
||||
void remove_if(const entity_type entt) {
|
||||
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))) {
|
||||
push_on_construct(entity);
|
||||
}
|
||||
}
|
||||
|
||||
common_type &handle() noexcept {
|
||||
return elem;
|
||||
}
|
||||
|
||||
const common_type &handle() const noexcept {
|
||||
return elem;
|
||||
}
|
||||
|
||||
auto pools_as_tuple() const noexcept {
|
||||
return pools;
|
||||
}
|
||||
|
||||
auto filter_as_tuple() const noexcept {
|
||||
return filter;
|
||||
}
|
||||
|
||||
private:
|
||||
std::tuple<Get *...> pools;
|
||||
std::tuple<Exclude *...> filter;
|
||||
base_type elem;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Group.
|
||||
@@ -119,18 +273,28 @@ class basic_group;
|
||||
* * The entity currently pointed is destroyed.
|
||||
*
|
||||
* In all other cases, modifying the pools iterated by the group in any way
|
||||
* invalidates all the iterators and using them results in undefined behavior.
|
||||
* invalidates all the iterators.
|
||||
*
|
||||
* @tparam Get Types of storage _observed_ by the group.
|
||||
* @tparam Exclude Types of storage used to filter the group.
|
||||
*/
|
||||
template<typename... Get, typename... Exclude>
|
||||
class basic_group<owned_t<>, get_t<Get...>, exclude_t<Exclude...>> {
|
||||
using underlying_type = std::common_type_t<typename Get::entity_type..., typename Exclude::entity_type...>;
|
||||
using basic_common_type = std::common_type_t<typename Get::base_type..., typename Exclude::base_type...>;
|
||||
using base_type = std::common_type_t<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 Get::value_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...>>;
|
||||
|
||||
auto pools() const noexcept {
|
||||
using return_type = std::tuple<Get *...>;
|
||||
return descriptor ? descriptor->pools_as_tuple() : return_type{};
|
||||
}
|
||||
|
||||
auto filter() const noexcept {
|
||||
using return_type = std::tuple<Exclude *...>;
|
||||
return descriptor ? descriptor->filter_as_tuple() : return_type{};
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
@@ -138,53 +302,59 @@ public:
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Common type among all storage types. */
|
||||
using base_type = basic_common_type;
|
||||
using common_type = base_type;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator = typename base_type::iterator;
|
||||
using iterator = typename common_type::iterator;
|
||||
/*! @brief Reversed iterator type. */
|
||||
using reverse_iterator = typename base_type::reverse_iterator;
|
||||
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>...>>;
|
||||
|
||||
/*! @brief Default constructor to use to create empty, invalid groups. */
|
||||
basic_group() noexcept
|
||||
: handler{} {}
|
||||
: descriptor{} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a group from a set of storage classes.
|
||||
* @param ref The actual entities to iterate.
|
||||
* @param gpool Storage types to iterate _observed_ by the group.
|
||||
* @param ref A reference to a group handler.
|
||||
*/
|
||||
basic_group(basic_common_type &ref, Get &...gpool) noexcept
|
||||
: handler{&ref},
|
||||
pools{&gpool...} {}
|
||||
basic_group(handler &ref) noexcept
|
||||
: descriptor{&ref} {}
|
||||
|
||||
/**
|
||||
* @brief Returns a const reference to the underlying handler.
|
||||
* @return A const reference to the underlying handler.
|
||||
* @brief Returns the leading storage of a group.
|
||||
* @return The leading storage of the group.
|
||||
*/
|
||||
[[nodiscard]] const base_type &handle() const noexcept {
|
||||
return *handler;
|
||||
[[nodiscard]] const common_type &handle() const noexcept {
|
||||
return descriptor->handle();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the storage for a given component type.
|
||||
* @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.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] decltype(auto) storage() const noexcept {
|
||||
[[nodiscard]] auto *storage() const noexcept {
|
||||
return storage<index_of<Type>>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the storage for a given index.
|
||||
* @brief Returns the storage for a given index, if any.
|
||||
* @tparam Index Index of the storage to return.
|
||||
* @return The storage for the given index.
|
||||
*/
|
||||
template<std::size_t Index>
|
||||
[[nodiscard]] decltype(auto) storage() const noexcept {
|
||||
return *std::get<Index>(pools);
|
||||
[[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());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,7 +362,7 @@ public:
|
||||
* @return Number of entities that are part of the group.
|
||||
*/
|
||||
[[nodiscard]] size_type size() const noexcept {
|
||||
return *this ? handler->size() : size_type{};
|
||||
return *this ? handle().size() : size_type{};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -201,13 +371,13 @@ public:
|
||||
* @return Capacity of the group.
|
||||
*/
|
||||
[[nodiscard]] size_type capacity() const noexcept {
|
||||
return *this ? handler->capacity() : size_type{};
|
||||
return *this ? handle().capacity() : size_type{};
|
||||
}
|
||||
|
||||
/*! @brief Requests the removal of unused capacity. */
|
||||
void shrink_to_fit() {
|
||||
if(*this) {
|
||||
handler->shrink_to_fit();
|
||||
descriptor->handle().shrink_to_fit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,60 +386,48 @@ public:
|
||||
* @return True if the group is empty, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool empty() const noexcept {
|
||||
return !*this || handler->empty();
|
||||
return !*this || handle().empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity of the group.
|
||||
*
|
||||
* The returned iterator points to the first entity of the group. If the
|
||||
* group is empty, the returned iterator will be equal to `end()`.
|
||||
* If the group is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @return An iterator to the first entity of the group.
|
||||
*/
|
||||
[[nodiscard]] iterator begin() const noexcept {
|
||||
return *this ? handler->begin() : iterator{};
|
||||
return *this ? handle().begin() : iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity of the group.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity of
|
||||
* the group. Attempting to dereference the returned iterator results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity of the
|
||||
* group.
|
||||
*/
|
||||
[[nodiscard]] iterator end() const noexcept {
|
||||
return *this ? handler->end() : iterator{};
|
||||
return *this ? handle().end() : iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity of the reversed group.
|
||||
*
|
||||
* The returned iterator points to the first entity of the reversed group.
|
||||
* If the group is empty, the returned iterator will be equal to `rend()`.
|
||||
*
|
||||
* @return An iterator to the first entity of the reversed group.
|
||||
*/
|
||||
[[nodiscard]] reverse_iterator rbegin() const noexcept {
|
||||
return *this ? handler->rbegin() : reverse_iterator{};
|
||||
return *this ? handle().rbegin() : reverse_iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity of the reversed
|
||||
* group.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity of
|
||||
* the reversed group. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity of the
|
||||
* reversed group.
|
||||
*/
|
||||
[[nodiscard]] reverse_iterator rend() const noexcept {
|
||||
return *this ? handler->rend() : reverse_iterator{};
|
||||
return *this ? handle().rend() : reverse_iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -299,8 +457,7 @@ public:
|
||||
* iterator otherwise.
|
||||
*/
|
||||
[[nodiscard]] iterator find(const entity_type entt) const noexcept {
|
||||
const auto it = *this ? handler->find(entt) : iterator{};
|
||||
return it != end() && *it == entt ? it : end();
|
||||
return *this ? handle().find(entt) : iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -317,7 +474,7 @@ public:
|
||||
* @return True if the group is properly initialized, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] explicit operator bool() const noexcept {
|
||||
return handler != nullptr;
|
||||
return descriptor != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -326,32 +483,37 @@ public:
|
||||
* @return True if the group contains the given entity, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool contains(const entity_type entt) const noexcept {
|
||||
return *this && handler->contains(entt);
|
||||
return *this && handle().contains(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the components assigned to the given entity.
|
||||
*
|
||||
* Prefer this function instead of `registry::get` during iterations. It has
|
||||
* far better performance than its counterpart.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to use an invalid component type results in a compilation
|
||||
* error. Attempting to use an entity that doesn't belong to the group
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @tparam Type Types of components to get.
|
||||
* @tparam Type Type of the component to get.
|
||||
* @tparam Other Other types of components to get.
|
||||
* @param entt A valid identifier.
|
||||
* @return The components assigned to the entity.
|
||||
*/
|
||||
template<typename... Type>
|
||||
template<typename Type, typename... Other>
|
||||
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
|
||||
if constexpr(sizeof...(Type) == 0) {
|
||||
return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools);
|
||||
} else if constexpr(sizeof...(Type) == 1) {
|
||||
return (std::get<index_of<Type>>(pools)->get(entt), ...);
|
||||
return get<index_of<Type>, index_of<Other>...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the components assigned to the given entity.
|
||||
* @tparam Index Indexes of the components to get.
|
||||
* @param entt A valid identifier.
|
||||
* @return The components assigned to the entity.
|
||||
*/
|
||||
template<std::size_t... Index>
|
||||
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
|
||||
const auto cpools = pools();
|
||||
|
||||
if constexpr(sizeof...(Index) == 0) {
|
||||
return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools);
|
||||
} else if constexpr(sizeof...(Index) == 1) {
|
||||
return (std::get<Index>(cpools)->get(entt), ...);
|
||||
} else {
|
||||
return std::tuple_cat(std::get<index_of<Type>>(pools)->get_as_tuple(entt)...);
|
||||
return std::tuple_cat(std::get<Index>(cpools)->get_as_tuple(entt)...);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,16 +564,13 @@ public:
|
||||
* @return An iterable object to use to _visit_ the group.
|
||||
*/
|
||||
[[nodiscard]] iterable each() const noexcept {
|
||||
return iterable{{begin(), pools}, {end(), pools}};
|
||||
const auto cpools = pools();
|
||||
return iterable{{begin(), cpools}, {end(), cpools}};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sort a group according to the given comparison function.
|
||||
*
|
||||
* Sort the group so that iterating it with a couple of iterators returns
|
||||
* entities and components in the expected order. See `begin` and `end` for
|
||||
* more details.
|
||||
*
|
||||
* The comparison function object must return `true` if the first element
|
||||
* is _less_ than the second one, `false` otherwise. The signature of the
|
||||
* comparison function should be equivalent to one of the following:
|
||||
@@ -433,7 +592,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 types of components to compare.
|
||||
* @tparam Type Optional type of component to compare.
|
||||
* @tparam Other Other optional types of components 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.
|
||||
@@ -441,52 +601,71 @@ public:
|
||||
* @param algo A valid sort function object.
|
||||
* @param args Arguments to forward to the sort function object, if any.
|
||||
*/
|
||||
template<typename... Type, typename Compare, typename Sort = std_sort, typename... Args>
|
||||
template<typename Type, typename... Other, typename Compare, typename Sort = std_sort, typename... Args>
|
||||
void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
|
||||
sort<index_of<Type>, index_of<Other>...>(std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sort a group according to the given comparison function.
|
||||
*
|
||||
* @sa sort
|
||||
*
|
||||
* @tparam Index Optional indexes of components 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.
|
||||
* @param compare A valid comparison function object.
|
||||
* @param algo A valid sort function object.
|
||||
* @param args Arguments to forward to the sort function object, if any.
|
||||
*/
|
||||
template<std::size_t... Index, typename Compare, typename Sort = std_sort, typename... Args>
|
||||
void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
|
||||
if(*this) {
|
||||
if constexpr(sizeof...(Type) == 0) {
|
||||
if constexpr(sizeof...(Index) == 0) {
|
||||
static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function");
|
||||
handler->sort(std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
descriptor->handle().sort(std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
} else {
|
||||
auto comp = [this, &compare](const entity_type lhs, const entity_type rhs) {
|
||||
if constexpr(sizeof...(Type) == 1) {
|
||||
return compare((std::get<index_of<Type>>(pools)->get(lhs), ...), (std::get<index_of<Type>>(pools)->get(rhs), ...));
|
||||
auto comp = [&compare, cpools = pools()](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 {
|
||||
return compare(std::forward_as_tuple(std::get<index_of<Type>>(pools)->get(lhs)...), std::forward_as_tuple(std::get<index_of<Type>>(pools)->get(rhs)...));
|
||||
return compare(std::forward_as_tuple(std::get<Index>(cpools)->get(lhs)...), std::forward_as_tuple(std::get<Index>(cpools)->get(rhs)...));
|
||||
}
|
||||
};
|
||||
|
||||
handler->sort(std::move(comp), std::move(algo), std::forward<Args>(args)...);
|
||||
descriptor->handle().sort(std::move(comp), std::move(algo), std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sort the shared pool of entities according to the given component.
|
||||
* @brief Sort entities according to their order in a range.
|
||||
*
|
||||
* Non-owning groups of the same type share with the registry a pool of
|
||||
* entities with its own order that doesn't depend on the order of any pool
|
||||
* of components. Users can order the underlying data structure so that it
|
||||
* respects the order of the pool of the given component.
|
||||
*
|
||||
* @note
|
||||
* The shared pool of entities and thus its order is affected by the changes
|
||||
* to each and every pool that it tracks. Therefore changes to those pools
|
||||
* can quickly ruin the order imposed to the pool of entities shared between
|
||||
* the non-owning groups.
|
||||
* to each and every pool that it tracks.
|
||||
*
|
||||
* @tparam Type Type of component to use to impose the order.
|
||||
* @tparam It Type of input iterator.
|
||||
* @param first An iterator to the first element of the range of entities.
|
||||
* @param last An iterator past the last element of the range of entities.
|
||||
*/
|
||||
template<typename Type>
|
||||
void sort() const {
|
||||
template<typename It>
|
||||
void sort_as(It first, It last) const {
|
||||
if(*this) {
|
||||
handler->respect(*std::get<index_of<Type>>(pools));
|
||||
descriptor->handle().sort_as(first, last);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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:
|
||||
base_type *const handler;
|
||||
const std::tuple<Get *...> pools;
|
||||
handler *descriptor;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -514,7 +693,7 @@ private:
|
||||
* * The entity currently pointed is destroyed.
|
||||
*
|
||||
* In all other cases, modifying the pools iterated by the group in any way
|
||||
* invalidates all the iterators and using them results in undefined behavior.
|
||||
* invalidates all the iterators.
|
||||
*
|
||||
* @tparam Owned Types of storage _owned_ by the group.
|
||||
* @tparam Get Types of storage _observed_ by the group.
|
||||
@@ -522,11 +701,21 @@ private:
|
||||
*/
|
||||
template<typename... Owned, typename... Get, typename... Exclude>
|
||||
class basic_group<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> {
|
||||
using underlying_type = std::common_type_t<typename Owned::entity_type..., typename Get::entity_type..., typename Exclude::entity_type...>;
|
||||
using basic_common_type = std::common_type_t<typename Owned::base_type..., typename Get::base_type..., typename Exclude::base_type...>;
|
||||
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...>>;
|
||||
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...>>;
|
||||
|
||||
auto pools() const noexcept {
|
||||
using return_type = std::tuple<Owned *..., Get *...>;
|
||||
return descriptor ? descriptor->pools_as_tuple() : return_type{};
|
||||
}
|
||||
|
||||
auto filter() const noexcept {
|
||||
using return_type = std::tuple<Exclude *...>;
|
||||
return descriptor ? descriptor->filter_as_tuple() : return_type{};
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
@@ -534,46 +723,59 @@ public:
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Common type among all storage types. */
|
||||
using base_type = basic_common_type;
|
||||
using common_type = base_type;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator = typename base_type::iterator;
|
||||
using iterator = typename common_type::iterator;
|
||||
/*! @brief Reversed iterator type. */
|
||||
using reverse_iterator = typename base_type::reverse_iterator;
|
||||
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>...>>;
|
||||
|
||||
/*! @brief Default constructor to use to create empty, invalid groups. */
|
||||
basic_group() noexcept
|
||||
: length{} {}
|
||||
: descriptor{} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a group from a set of storage classes.
|
||||
* @param extent The actual number of entities to iterate.
|
||||
* @param opool Storage types to iterate _owned_ by the group.
|
||||
* @param gpool Storage types to iterate _observed_ by the group.
|
||||
* @param ref A reference to a group handler.
|
||||
*/
|
||||
basic_group(const std::size_t &extent, Owned &...opool, Get &...gpool) noexcept
|
||||
: pools{&opool..., &gpool...},
|
||||
length{&extent} {}
|
||||
basic_group(handler &ref) noexcept
|
||||
: descriptor{&ref} {}
|
||||
|
||||
/**
|
||||
* @brief Returns the storage for a given component type.
|
||||
* @brief Returns the leading storage of a group.
|
||||
* @return The leading storage of the group.
|
||||
*/
|
||||
[[nodiscard]] const common_type &handle() const noexcept {
|
||||
return *storage<0>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] decltype(auto) storage() const noexcept {
|
||||
[[nodiscard]] auto *storage() const noexcept {
|
||||
return storage<index_of<Type>>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the storage for a given index.
|
||||
* @brief Returns the storage for a given index, if any.
|
||||
* @tparam Index Index of the storage to return.
|
||||
* @return The storage for the given index.
|
||||
*/
|
||||
template<std::size_t Index>
|
||||
[[nodiscard]] decltype(auto) storage() const noexcept {
|
||||
return *std::get<Index>(pools);
|
||||
[[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());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -581,7 +783,7 @@ public:
|
||||
* @return Number of entities that that are part of the group.
|
||||
*/
|
||||
[[nodiscard]] size_type size() const noexcept {
|
||||
return *this ? *length : size_type{};
|
||||
return *this ? descriptor->length() : size_type{};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -589,60 +791,48 @@ public:
|
||||
* @return True if the group is empty, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool empty() const noexcept {
|
||||
return !*this || !*length;
|
||||
return !*this || !descriptor->length();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity of the group.
|
||||
*
|
||||
* The returned iterator points to the first entity of the group. If the
|
||||
* group is empty, the returned iterator will be equal to `end()`.
|
||||
* If the group is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @return An iterator to the first entity of the group.
|
||||
*/
|
||||
[[nodiscard]] iterator begin() const noexcept {
|
||||
return *this ? (std::get<0>(pools)->base_type::end() - *length) : iterator{};
|
||||
return *this ? (handle().end() - descriptor->length()) : iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity of the group.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity of
|
||||
* the group. Attempting to dereference the returned iterator results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity of the
|
||||
* group.
|
||||
*/
|
||||
[[nodiscard]] iterator end() const noexcept {
|
||||
return *this ? std::get<0>(pools)->base_type::end() : iterator{};
|
||||
return *this ? handle().end() : iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity of the reversed group.
|
||||
*
|
||||
* The returned iterator points to the first entity of the reversed group.
|
||||
* If the group is empty, the returned iterator will be equal to `rend()`.
|
||||
*
|
||||
* @return An iterator to the first entity of the reversed group.
|
||||
*/
|
||||
[[nodiscard]] reverse_iterator rbegin() const noexcept {
|
||||
return *this ? std::get<0>(pools)->base_type::rbegin() : reverse_iterator{};
|
||||
return *this ? handle().rbegin() : reverse_iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity of the reversed
|
||||
* group.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity of
|
||||
* the reversed group. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity of the
|
||||
* reversed group.
|
||||
*/
|
||||
[[nodiscard]] reverse_iterator rend() const noexcept {
|
||||
return *this ? (std::get<0>(pools)->base_type::rbegin() + *length) : reverse_iterator{};
|
||||
return *this ? (handle().rbegin() + descriptor->length()) : reverse_iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -672,8 +862,8 @@ public:
|
||||
* iterator otherwise.
|
||||
*/
|
||||
[[nodiscard]] iterator find(const entity_type entt) const noexcept {
|
||||
const auto it = *this ? std::get<0>(pools)->find(entt) : iterator{};
|
||||
return it != end() && it >= begin() && *it == entt ? it : end();
|
||||
const auto it = *this ? handle().find(entt) : iterator{};
|
||||
return it >= begin() ? it : iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -690,7 +880,7 @@ public:
|
||||
* @return True if the group is properly initialized, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] explicit operator bool() const noexcept {
|
||||
return length != nullptr;
|
||||
return descriptor != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -699,32 +889,37 @@ public:
|
||||
* @return True if the group contains the given entity, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool contains(const entity_type entt) const noexcept {
|
||||
return *this && std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < (*length));
|
||||
return *this && handle().contains(entt) && (handle().index(entt) < (descriptor->length()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the components assigned to the given entity.
|
||||
*
|
||||
* Prefer this function instead of `registry::get` during iterations. It has
|
||||
* far better performance than its counterpart.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to use an invalid component type results in a compilation
|
||||
* error. Attempting to use an entity that doesn't belong to the group
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @tparam Type Types of components to get.
|
||||
* @tparam Type Type of the component to get.
|
||||
* @tparam Other Other types of components to get.
|
||||
* @param entt A valid identifier.
|
||||
* @return The components assigned to the entity.
|
||||
*/
|
||||
template<typename... Type>
|
||||
template<typename Type, typename... Other>
|
||||
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
|
||||
if constexpr(sizeof...(Type) == 0) {
|
||||
return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools);
|
||||
} else if constexpr(sizeof...(Type) == 1) {
|
||||
return (std::get<index_of<Type>>(pools)->get(entt), ...);
|
||||
return get<index_of<Type>, index_of<Other>...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the components assigned to the given entity.
|
||||
* @tparam Index Indexes of the components to get.
|
||||
* @param entt A valid identifier.
|
||||
* @return The components assigned to the entity.
|
||||
*/
|
||||
template<std::size_t... Index>
|
||||
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
|
||||
const auto cpools = pools();
|
||||
|
||||
if constexpr(sizeof...(Index) == 0) {
|
||||
return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools);
|
||||
} else if constexpr(sizeof...(Index) == 1) {
|
||||
return (std::get<Index>(cpools)->get(entt), ...);
|
||||
} else {
|
||||
return std::tuple_cat(std::get<index_of<Type>>(pools)->get_as_tuple(entt)...);
|
||||
return std::tuple_cat(std::get<Index>(cpools)->get_as_tuple(entt)...);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -775,16 +970,13 @@ public:
|
||||
* @return An iterable object to use to _visit_ the group.
|
||||
*/
|
||||
[[nodiscard]] iterable each() const noexcept {
|
||||
return {{begin(), pools}, {end(), pools}};
|
||||
const auto cpools = pools();
|
||||
return {{begin(), cpools}, {end(), cpools}};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sort a group according to the given comparison function.
|
||||
*
|
||||
* Sort the group so that iterating it with a couple of iterators returns
|
||||
* entities and components in the expected order. See `begin` and `end` for
|
||||
* more details.
|
||||
*
|
||||
* The comparison function object must return `true` if the first element
|
||||
* is _less_ than the second one, `false` otherwise. The signature of the
|
||||
* comparison function should be equivalent to one of the following:
|
||||
@@ -807,7 +999,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 types of components to compare.
|
||||
* @tparam Type Optional type of component to compare.
|
||||
* @tparam Other Other optional types of components 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.
|
||||
@@ -815,36 +1008,56 @@ public:
|
||||
* @param algo A valid sort function object.
|
||||
* @param args Arguments to forward to the sort function object, if any.
|
||||
*/
|
||||
template<typename... Type, typename Compare, typename Sort = std_sort, typename... Args>
|
||||
template<typename Type, typename... Other, typename Compare, typename Sort = std_sort, typename... Args>
|
||||
void sort(Compare compare, Sort algo = Sort{}, Args &&...args) const {
|
||||
if constexpr(sizeof...(Type) == 0) {
|
||||
sort<index_of<Type>, index_of<Other>...>(std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sort a group according to the given comparison function.
|
||||
*
|
||||
* @sa sort
|
||||
*
|
||||
* @tparam Index Optional indexes of components 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.
|
||||
* @param compare A valid comparison function object.
|
||||
* @param algo A valid sort function object.
|
||||
* @param args Arguments to forward to the sort function object, if any.
|
||||
*/
|
||||
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();
|
||||
|
||||
if constexpr(sizeof...(Index) == 0) {
|
||||
static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function");
|
||||
std::get<0>(pools)->sort_n(*length, std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
storage<0>()->sort_n(descriptor->length(), std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
} else {
|
||||
auto comp = [this, &compare](const entity_type lhs, const entity_type rhs) {
|
||||
if constexpr(sizeof...(Type) == 1) {
|
||||
return compare((std::get<index_of<Type>>(pools)->get(lhs), ...), (std::get<index_of<Type>>(pools)->get(rhs), ...));
|
||||
auto comp = [&compare, &cpools](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 {
|
||||
return compare(std::forward_as_tuple(std::get<index_of<Type>>(pools)->get(lhs)...), std::forward_as_tuple(std::get<index_of<Type>>(pools)->get(rhs)...));
|
||||
return compare(std::forward_as_tuple(std::get<Index>(cpools)->get(lhs)...), std::forward_as_tuple(std::get<Index>(cpools)->get(rhs)...));
|
||||
}
|
||||
};
|
||||
|
||||
std::get<0>(pools)->sort_n(*length, std::move(comp), std::move(algo), std::forward<Args>(args)...);
|
||||
storage<0>()->sort_n(descriptor->length(), std::move(comp), std::move(algo), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
std::apply([this](auto *head, auto *...other) {
|
||||
for(auto next = *length; next; --next) {
|
||||
auto cb = [this](auto *head, auto *...other) {
|
||||
for(auto next = descriptor->length(); next; --next) {
|
||||
const auto pos = next - 1;
|
||||
[[maybe_unused]] const auto entt = head->data()[pos];
|
||||
(other->swap_elements(other->data()[pos], entt), ...);
|
||||
}
|
||||
},
|
||||
pools);
|
||||
};
|
||||
|
||||
std::apply(cb, cpools);
|
||||
}
|
||||
|
||||
private:
|
||||
const std::tuple<Owned *..., Get *...> pools;
|
||||
const size_type *const length;
|
||||
handler *descriptor;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -12,11 +12,7 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename It>
|
||||
@@ -33,6 +29,7 @@ public:
|
||||
using reference = value_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using iterator_concept = std::forward_iterator_tag;
|
||||
|
||||
constexpr handle_storage_iterator() noexcept
|
||||
: entt{null},
|
||||
@@ -43,7 +40,9 @@ public:
|
||||
: entt{value},
|
||||
it{from},
|
||||
last{to} {
|
||||
while(it != last && !it->second.contains(entt)) { ++it; }
|
||||
while(it != last && !it->second.contains(entt)) {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr handle_storage_iterator &operator++() noexcept {
|
||||
@@ -84,11 +83,7 @@ template<typename ILhs, typename IRhs>
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Non-owning handle to an entity.
|
||||
@@ -141,7 +136,7 @@ struct basic_handle {
|
||||
|
||||
/**
|
||||
* @brief Constructs a const handle from a non-const one.
|
||||
* @tparam Other A valid entity type (see entt_traits for more details).
|
||||
* @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.
|
||||
@@ -196,7 +191,7 @@ struct basic_handle {
|
||||
|
||||
/*! @brief Destroys the entity associated with a handle. */
|
||||
void destroy() {
|
||||
reg->destroy(entt);
|
||||
reg->destroy(std::exchange(entt, null));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,7 +199,7 @@ struct basic_handle {
|
||||
* @param version A desired version upon destruction.
|
||||
*/
|
||||
void destroy(const version_type version) {
|
||||
reg->destroy(entt, version);
|
||||
reg->destroy(std::exchange(entt, null), version);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#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"
|
||||
#include "view.hpp"
|
||||
|
||||
namespace entt {
|
||||
@@ -28,7 +29,7 @@ public:
|
||||
/*! @brief Type of registry to convert. */
|
||||
using registry_type = Registry;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = std::remove_const_t<typename registry_type::entity_type>;
|
||||
using entity_type = typename registry_type::entity_type;
|
||||
|
||||
/**
|
||||
* @brief Constructs a converter for a given registry.
|
||||
@@ -71,7 +72,7 @@ public:
|
||||
/*! @brief Type of registry to convert. */
|
||||
using registry_type = Registry;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = std::remove_const_t<typename registry_type::entity_type>;
|
||||
using entity_type = typename registry_type::entity_type;
|
||||
|
||||
/**
|
||||
* @brief Constructs a converter for a given registry.
|
||||
@@ -103,7 +104,7 @@ private:
|
||||
* @param reg A registry that contains the given entity and its components.
|
||||
* @param entt Entity from which to get the component.
|
||||
*/
|
||||
template<auto Member, typename Registry = std::decay_t<nth_argument_t<0u, Member>>>
|
||||
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;
|
||||
@@ -115,23 +116,22 @@ void invoke(Registry ®, const typename Registry::entity_type entt) {
|
||||
* @brief Returns the entity associated with a given component.
|
||||
*
|
||||
* @warning
|
||||
* Currently, this function only works correctly with the default pool as it
|
||||
* Currently, this function only works correctly with the default storage as it
|
||||
* makes assumptions about how the components are laid out.
|
||||
*
|
||||
* @tparam Registry Basic registry type.
|
||||
* @tparam Component Type of component.
|
||||
* @param reg A registry that contains the given entity and its components.
|
||||
* @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.
|
||||
*/
|
||||
template<typename Registry, typename Component>
|
||||
typename Registry::entity_type to_entity(const Registry ®, const Component &instance) {
|
||||
const auto &storage = reg.template storage<Component>();
|
||||
const typename Registry::base_type &base = storage;
|
||||
template<typename... Args>
|
||||
auto to_entity(const basic_storage<Args...> &storage, const typename basic_storage<Args...>::value_type &instance) -> typename basic_storage<Args...>::entity_type {
|
||||
constexpr auto page_size = basic_storage<Args...>::traits_type::page_size;
|
||||
const typename basic_storage<Args...>::base_type &base = storage;
|
||||
const auto *addr = std::addressof(instance);
|
||||
|
||||
for(auto it = base.rbegin(), last = base.rend(); it < last; it += component_traits<Component>::page_size) {
|
||||
if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < static_cast<decltype(dist)>(component_traits<Component>::page_size)) {
|
||||
for(auto it = base.rbegin(), last = base.rend(); it < last; it += page_size) {
|
||||
if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < static_cast<decltype(dist)>(page_size)) {
|
||||
return *(it + dist);
|
||||
}
|
||||
}
|
||||
@@ -139,6 +139,135 @@ typename Registry::entity_type to_entity(const Registry ®, const Component &i
|
||||
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;
|
||||
|
||||
/**
|
||||
* @brief Signal connection helper for registries.
|
||||
* @tparam Registry Basic registry type.
|
||||
*/
|
||||
template<typename Registry>
|
||||
struct sigh_helper<Registry> {
|
||||
/*! @brief Registry type. */
|
||||
using registry_type = Registry;
|
||||
|
||||
/**
|
||||
* @brief Constructs a helper for a given registry.
|
||||
* @param ref A valid reference to a registry.
|
||||
*/
|
||||
sigh_helper(registry_type &ref)
|
||||
: bucket{&ref} {}
|
||||
|
||||
/**
|
||||
* @brief Binds a properly initialized helper to a given signal type.
|
||||
* @tparam Type Type of signal to bind the helper to.
|
||||
* @param id Optional name for the underlying storage to use.
|
||||
* @return A helper for a given registry and signal type.
|
||||
*/
|
||||
template<typename Type>
|
||||
auto with(const id_type id = type_hash<Type>::value()) noexcept {
|
||||
return sigh_helper<registry_type, Type>{*bucket, id};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the underlying registry.
|
||||
* @return A reference to the underlying registry.
|
||||
*/
|
||||
[[nodiscard]] registry_type ®istry() noexcept {
|
||||
return *bucket;
|
||||
}
|
||||
|
||||
private:
|
||||
registry_type *bucket;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Signal connection helper for registries.
|
||||
* @tparam Registry Basic registry type.
|
||||
* @tparam Type Type of signal to connect listeners to.
|
||||
*/
|
||||
template<typename Registry, typename Type>
|
||||
struct sigh_helper<Registry, Type> final: sigh_helper<Registry> {
|
||||
/*! @brief Registry type. */
|
||||
using registry_type = Registry;
|
||||
|
||||
/**
|
||||
* @brief Constructs a helper for a given registry.
|
||||
* @param ref A valid reference to a registry.
|
||||
* @param id Optional name for the underlying storage to use.
|
||||
*/
|
||||
sigh_helper(registry_type &ref, const id_type id = type_hash<Type>::value())
|
||||
: sigh_helper<Registry>{ref},
|
||||
name{id} {}
|
||||
|
||||
/**
|
||||
* @brief Forwards the call to `on_construct` on the underlying storage.
|
||||
* @tparam Candidate Function or member to connect.
|
||||
* @tparam Args Type of class or type of payload, if any.
|
||||
* @param args A valid object that fits the purpose, if any.
|
||||
* @return This helper.
|
||||
*/
|
||||
template<auto Candidate, typename... Args>
|
||||
auto on_construct(Args &&...args) {
|
||||
this->registry().template on_construct<Type>(name).template connect<Candidate>(std::forward<Args>(args)...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Forwards the call to `on_update` on the underlying storage.
|
||||
* @tparam Candidate Function or member to connect.
|
||||
* @tparam Args Type of class or type of payload, if any.
|
||||
* @param args A valid object that fits the purpose, if any.
|
||||
* @return This helper.
|
||||
*/
|
||||
template<auto Candidate, typename... Args>
|
||||
auto on_update(Args &&...args) {
|
||||
this->registry().template on_update<Type>(name).template connect<Candidate>(std::forward<Args>(args)...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Forwards the call to `on_destroy` on the underlying storage.
|
||||
* @tparam Candidate Function or member to connect.
|
||||
* @tparam Args Type of class or type of payload, if any.
|
||||
* @param args A valid object that fits the purpose, if any.
|
||||
* @return This helper.
|
||||
*/
|
||||
template<auto Candidate, typename... Args>
|
||||
auto on_destroy(Args &&...args) {
|
||||
this->registry().template on_destroy<Type>(name).template connect<Candidate>(std::forward<Args>(args)...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
id_type name;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
* @tparam Registry Basic registry type.
|
||||
*/
|
||||
template<typename Registry>
|
||||
sigh_helper(Registry &) -> sigh_helper<Registry>;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
|
||||
303
src/entt/entity/mixin.hpp
Normal file
303
src/entt/entity/mixin.hpp
Normal file
@@ -0,0 +1,303 @@
|
||||
#ifndef ENTT_ENTITY_MIXIN_HPP
|
||||
#define ENTT_ENTITY_MIXIN_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/any.hpp"
|
||||
#include "../signal/sigh.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @brief Mixin type used to add signal support to storage types.
|
||||
*
|
||||
* The function type of a listener is equivalent to:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(basic_registry<entity_type> &, entity_type);
|
||||
* @endcode
|
||||
*
|
||||
* This applies to all signals made available.
|
||||
*
|
||||
* @tparam Type Underlying storage type.
|
||||
* @tparam Registry Basic registry type.
|
||||
*/
|
||||
template<typename Type, typename Registry>
|
||||
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 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 {
|
||||
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
|
||||
return static_cast<owner_type &>(*owner);
|
||||
}
|
||||
|
||||
void pop(underlying_iterator first, underlying_iterator last) final {
|
||||
if(auto ® = owner_or_assert(); destruction.empty()) {
|
||||
underlying_type::pop(first, last);
|
||||
} else {
|
||||
for(; first != last; ++first) {
|
||||
const auto entt = *first;
|
||||
destruction.publish(reg, entt);
|
||||
const auto it = underlying_type::find(entt);
|
||||
underlying_type::pop(it, it + 1u);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
destruction.publish(reg, entt);
|
||||
}
|
||||
} else {
|
||||
destruction.publish(reg, *it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
underlying_type::pop_all();
|
||||
}
|
||||
|
||||
underlying_iterator try_emplace(const typename underlying_type::entity_type entt, const bool force_back, const void *value) final {
|
||||
const auto it = underlying_type::try_emplace(entt, force_back, value);
|
||||
|
||||
if(auto ® = owner_or_assert(); it != underlying_type::base_type::end()) {
|
||||
construction.publish(reg, *it);
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
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_sigh_mixin()
|
||||
: basic_sigh_mixin{allocator_type{}} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an empty storage with a given allocator.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit basic_sigh_mixin(const allocator_type &allocator)
|
||||
: underlying_type{allocator},
|
||||
owner{},
|
||||
construction{allocator},
|
||||
destruction{allocator},
|
||||
update{allocator} {}
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
basic_sigh_mixin(basic_sigh_mixin &&other) noexcept
|
||||
: underlying_type{std::move(other)},
|
||||
owner{other.owner},
|
||||
construction{std::move(other.construction)},
|
||||
destruction{std::move(other.destruction)},
|
||||
update{std::move(other.update)} {}
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
basic_sigh_mixin(basic_sigh_mixin &&other, const allocator_type &allocator) noexcept
|
||||
: 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 Move assignment operator.
|
||||
* @param other The instance to move from.
|
||||
* @return This storage.
|
||||
*/
|
||||
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);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exchanges the contents with those of a given storage.
|
||||
* @param other Storage to exchange the content with.
|
||||
*/
|
||||
void swap(basic_sigh_mixin &other) {
|
||||
using std::swap;
|
||||
underlying_type::swap(other);
|
||||
swap(owner, other.owner);
|
||||
swap(construction, other.construction);
|
||||
swap(destruction, other.destruction);
|
||||
swap(update, other.update);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a sink object.
|
||||
*
|
||||
* The sink returned by this function can be used to receive notifications
|
||||
* whenever a new instance is created and assigned to an entity.<br/>
|
||||
* Listeners are invoked after the object has been assigned to the entity.
|
||||
*
|
||||
* @sa sink
|
||||
*
|
||||
* @return A temporary sink object.
|
||||
*/
|
||||
[[nodiscard]] auto on_construct() noexcept {
|
||||
return sink{construction};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a sink object.
|
||||
*
|
||||
* The sink returned by this function can be used to receive notifications
|
||||
* whenever an instance is explicitly updated.<br/>
|
||||
* Listeners are invoked after the object has been updated.
|
||||
*
|
||||
* @sa sink
|
||||
*
|
||||
* @return A temporary sink object.
|
||||
*/
|
||||
[[nodiscard]] auto on_update() noexcept {
|
||||
return sink{update};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a sink object.
|
||||
*
|
||||
* The sink returned by this function can be used to receive notifications
|
||||
* whenever an instance is removed from an entity and thus destroyed.<br/>
|
||||
* Listeners are invoked before the object has been removed from the entity.
|
||||
*
|
||||
* @sa sink
|
||||
*
|
||||
* @return A temporary sink object.
|
||||
*/
|
||||
[[nodiscard]] auto on_destroy() noexcept {
|
||||
return sink{destruction};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Emplace elements into a storage.
|
||||
*
|
||||
* The behavior of this operation depends on the underlying storage type
|
||||
* (for example, components vs entities).<br/>
|
||||
* Refer to the specific documentation for more details.
|
||||
*
|
||||
* @return A return value as returned by the underlying storage.
|
||||
*/
|
||||
auto emplace() {
|
||||
const auto entt = underlying_type::emplace();
|
||||
construction.publish(owner_or_assert(), entt);
|
||||
return entt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Emplace elements into a storage.
|
||||
*
|
||||
* The behavior of this operation depends on the underlying storage type
|
||||
* (for example, components vs entities).<br/>
|
||||
* Refer to the specific documentation for more details.
|
||||
*
|
||||
* @tparam Args Types of arguments to forward to the underlying storage.
|
||||
* @param hint A valid identifier.
|
||||
* @param args Parameters to forward to the underlying storage.
|
||||
* @return A return value as returned by the underlying storage.
|
||||
*/
|
||||
template<typename... Args>
|
||||
decltype(auto) emplace(const entity_type hint, Args &&...args) {
|
||||
if constexpr(std::is_same_v<typename underlying_type::value_type, typename underlying_type::entity_type>) {
|
||||
const auto entt = underlying_type::emplace(hint, std::forward<Args>(args)...);
|
||||
construction.publish(owner_or_assert(), entt);
|
||||
return entt;
|
||||
} else {
|
||||
underlying_type::emplace(hint, std::forward<Args>(args)...);
|
||||
construction.publish(owner_or_assert(), hint);
|
||||
return this->get(hint);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Patches the given instance for an entity.
|
||||
* @tparam Func Types of the function objects to invoke.
|
||||
* @param entt A valid identifier.
|
||||
* @param func Valid function objects.
|
||||
* @return A reference to the patched instance.
|
||||
*/
|
||||
template<typename... Func>
|
||||
decltype(auto) patch(const entity_type entt, Func &&...func) {
|
||||
underlying_type::patch(entt, std::forward<Func>(func)...);
|
||||
update.publish(owner_or_assert(), entt);
|
||||
return this->get(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Emplace elements into a storage.
|
||||
*
|
||||
* The behavior of this operation depends on the underlying storage type
|
||||
* (for example, components vs entities).<br/>
|
||||
* Refer to the specific documentation for more details.
|
||||
*
|
||||
* @tparam It Iterator type (as required by the underlying storage type).
|
||||
* @tparam Args Types of arguments to forward to the underlying storage.
|
||||
* @param first An iterator to the first element of the range.
|
||||
* @param last An iterator past the last element of the range.
|
||||
* @param args Parameters to use to forward to the underlying storage.
|
||||
*/
|
||||
template<typename It, typename... Args>
|
||||
void insert(It first, It last, Args &&...args) {
|
||||
auto from = underlying_type::size();
|
||||
underlying_type::insert(first, last, std::forward<Args>(args)...);
|
||||
|
||||
if(auto ® = owner_or_assert(); !construction.empty()) {
|
||||
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;
|
||||
sigh_type destruction;
|
||||
sigh_type update;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
@@ -43,7 +43,7 @@ struct basic_collector<> {
|
||||
* @return The updated collector.
|
||||
*/
|
||||
template<typename... AllOf, typename... NoneOf>
|
||||
static constexpr auto group(exclude_t<NoneOf...> = {}) noexcept {
|
||||
static constexpr auto group(exclude_t<NoneOf...> = exclude_t{}) noexcept {
|
||||
return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>>{};
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule
|
||||
* @return The updated collector.
|
||||
*/
|
||||
template<typename... AllOf, typename... NoneOf>
|
||||
static constexpr auto group(exclude_t<NoneOf...> = {}) noexcept {
|
||||
static constexpr auto group(exclude_t<NoneOf...> = exclude_t{}) noexcept {
|
||||
return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>, current_type, Other...>{};
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule
|
||||
* @return The updated collector.
|
||||
*/
|
||||
template<typename... AllOf, typename... NoneOf>
|
||||
static constexpr auto where(exclude_t<NoneOf...> = {}) noexcept {
|
||||
static constexpr auto where(exclude_t<NoneOf...> = exclude_t{}) noexcept {
|
||||
using extended_type = matcher<type_list<Reject..., NoneOf...>, type_list<Require..., AllOf...>, Rule...>;
|
||||
return basic_collector<extended_type, Other...>{};
|
||||
}
|
||||
@@ -146,8 +146,7 @@ inline constexpr basic_collector<> collector{};
|
||||
* * The entity currently pointed is destroyed.
|
||||
*
|
||||
* In all the other cases, modifying the pools of the given components in any
|
||||
* way invalidates all the iterators and using them results in undefined
|
||||
* behavior.
|
||||
* way invalidates all the iterators.
|
||||
*
|
||||
* @warning
|
||||
* Lifetime of an observer doesn't necessarily have to overcome that of the
|
||||
@@ -156,10 +155,12 @@ 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>
|
||||
class basic_observer: private basic_storage<std::uint32_t, typename Registry::entity_type> {
|
||||
using base_type = basic_storage<std::uint32_t, typename Registry::entity_type>;
|
||||
template<typename Registry, typename Mask, typename Allocator>
|
||||
class basic_observer: private basic_storage<Mask, typename Registry::entity_type, Allocator> {
|
||||
using base_type = basic_storage<Mask, typename Registry::entity_type, Allocator>;
|
||||
|
||||
template<typename>
|
||||
struct matcher_handler;
|
||||
@@ -193,10 +194,10 @@ class basic_observer: private basic_storage<std::uint32_t, typename Registry::en
|
||||
}
|
||||
|
||||
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);
|
||||
(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);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -239,12 +240,12 @@ class basic_observer: private basic_storage<std::uint32_t, typename Registry::en
|
||||
}
|
||||
|
||||
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), ...);
|
||||
(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), ...);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -267,15 +268,26 @@ public:
|
||||
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::base_type::iterator;
|
||||
using iterator = typename registry_type::common_type::iterator;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
basic_observer()
|
||||
: release{} {}
|
||||
: basic_observer{allocator_type{}} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an empty storage with a given allocator.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit basic_observer(const allocator_type &allocator)
|
||||
: base_type{allocator},
|
||||
release{} {}
|
||||
|
||||
/*! @brief Default copy constructor, deleted on purpose. */
|
||||
basic_observer(const basic_observer &) = delete;
|
||||
|
||||
/*! @brief Default move constructor, deleted on purpose. */
|
||||
basic_observer(basic_observer &&) = delete;
|
||||
|
||||
@@ -283,16 +295,14 @@ public:
|
||||
* @brief Creates an observer and connects it to a given registry.
|
||||
* @tparam Matcher Types of matchers to use to initialize the observer.
|
||||
* @param reg A valid reference to a registry.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
template<typename... Matcher>
|
||||
basic_observer(registry_type ®, basic_collector<Matcher...>)
|
||||
: basic_observer{} {
|
||||
basic_observer(registry_type ®, basic_collector<Matcher...>, const allocator_type &allocator = allocator_type{})
|
||||
: basic_observer{allocator} {
|
||||
connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{});
|
||||
}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~basic_observer() = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator, deleted on purpose.
|
||||
* @return This observer.
|
||||
@@ -360,8 +370,7 @@ public:
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity of the observer.
|
||||
*
|
||||
* The returned iterator points to the first entity of the observer. If the
|
||||
* container is empty, the returned iterator will be equal to `end()`.
|
||||
* If the observer is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @return An iterator to the first entity of the observer.
|
||||
*/
|
||||
@@ -371,11 +380,6 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity of the observer.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity of
|
||||
* the observer. Attempting to dereference the returned iterator results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity of the
|
||||
* observer.
|
||||
*/
|
||||
|
||||
@@ -15,11 +15,7 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename>
|
||||
@@ -87,11 +83,7 @@ template<typename... Req, typename Ret, typename Class, typename... Args>
|
||||
resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...) const);
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Utility class for creating a static task graph.
|
||||
@@ -126,7 +118,7 @@ class basic_organizer final {
|
||||
if constexpr(std::is_same_v<Type, Registry>) {
|
||||
return reg;
|
||||
} else if constexpr(internal::is_view_v<Type>) {
|
||||
return as_view{reg};
|
||||
return static_cast<Type>(as_view{reg});
|
||||
} else {
|
||||
return reg.ctx().template emplace<std::remove_reference_t<Type>>();
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,11 +11,7 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename Set>
|
||||
@@ -95,47 +91,27 @@ private:
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Generic runtime view.
|
||||
*
|
||||
* Runtime views iterate over those entities that have at least all the given
|
||||
* components in their bags. During initialization, a runtime view looks at the
|
||||
* number of entities available for each component and picks up a reference to
|
||||
* the smallest set of candidate entities in order to get a performance boost
|
||||
* when iterate.<br/>
|
||||
* Order of elements during iterations are highly dependent on the order of the
|
||||
* underlying data structures. See sparse_set and its specializations for more
|
||||
* details.
|
||||
* 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.
|
||||
*
|
||||
* @b Important
|
||||
*
|
||||
* Iterators aren't invalidated if:
|
||||
*
|
||||
* * New instances of the given components are created and assigned to entities.
|
||||
* * The entity currently pointed is modified (as an example, if one of the
|
||||
* given components is removed from the entity to which the iterator points).
|
||||
* * New elements are added to the storage.
|
||||
* * The entity currently pointed is modified (for example, components are added
|
||||
* or removed from it).
|
||||
* * The entity currently pointed is destroyed.
|
||||
*
|
||||
* In all the other cases, modifying the pools of the given components in any
|
||||
* way invalidates all the iterators and using them results in undefined
|
||||
* behavior.
|
||||
*
|
||||
* @note
|
||||
* Views share references to the underlying data structures of the registry that
|
||||
* generated them. Therefore any change to the entities and to the components
|
||||
* made by means of the registry are immediately reflected by the views, unless
|
||||
* a pool was missing when the view was built (in this case, the view won't
|
||||
* have a valid reference and won't be updated accordingly).
|
||||
*
|
||||
* @warning
|
||||
* Lifetime of a view must not overcome that of the registry that generated it.
|
||||
* In any other case, attempting to use a view results in undefined behavior.
|
||||
* In all other cases, modifying the storage iterated by the view in any way
|
||||
* invalidates all the iterators.
|
||||
*
|
||||
* @tparam Type Common base type.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
@@ -154,9 +130,9 @@ public:
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Common type among all storage types. */
|
||||
using base_type = Type;
|
||||
using common_type = Type;
|
||||
/*! @brief Bidirectional iterator type. */
|
||||
using iterator = internal::runtime_view_iterator<base_type>;
|
||||
using iterator = internal::runtime_view_iterator<common_type>;
|
||||
|
||||
/*! @brief Default constructor to use to create empty, invalid views. */
|
||||
basic_runtime_view() noexcept
|
||||
@@ -235,7 +211,7 @@ public:
|
||||
* @param base An opaque reference to a storage object.
|
||||
* @return This runtime view.
|
||||
*/
|
||||
basic_runtime_view &iterate(base_type &base) {
|
||||
basic_runtime_view &iterate(common_type &base) {
|
||||
if(pools.empty() || !(base.size() < pools[0u]->size())) {
|
||||
pools.push_back(&base);
|
||||
} else {
|
||||
@@ -250,7 +226,7 @@ public:
|
||||
* @param base An opaque reference to a storage object.
|
||||
* @return This runtime view.
|
||||
*/
|
||||
basic_runtime_view &exclude(base_type &base) {
|
||||
basic_runtime_view &exclude(common_type &base) {
|
||||
filter.push_back(&base);
|
||||
return *this;
|
||||
}
|
||||
@@ -267,9 +243,7 @@ public:
|
||||
* @brief Returns an iterator to the first entity that has the given
|
||||
* components.
|
||||
*
|
||||
* The returned iterator points to the first entity that has the given
|
||||
* components. If the view is empty, the returned iterator will be equal to
|
||||
* `end()`.
|
||||
* 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.
|
||||
*/
|
||||
@@ -280,11 +254,6 @@ public:
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity that has the
|
||||
* given components.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity that
|
||||
* has the given components. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity that has the
|
||||
* given components.
|
||||
*/
|
||||
@@ -307,8 +276,7 @@ public:
|
||||
* @brief Iterates entities and applies the given function object to them.
|
||||
*
|
||||
* The function object is invoked for each entity. It is provided only with
|
||||
* the entity itself. To get the components, users can use the registry with
|
||||
* which the view was built.<br/>
|
||||
* the entity itself.<br/>
|
||||
* The signature of the function should be equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#ifndef ENTT_ENTITY_SNAPSHOT_HPP
|
||||
#define ENTT_ENTITY_SNAPSHOT_HPP
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
@@ -11,13 +10,29 @@
|
||||
#include "../config/config.h"
|
||||
#include "../container/dense_map.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "component.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "view.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename Registry>
|
||||
void orphans(Registry ®istry) {
|
||||
auto &storage = registry.template storage<typename Registry::entity_type>();
|
||||
|
||||
for(auto entt: storage) {
|
||||
if(registry.orphan(entt)) {
|
||||
storage.erase(entt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Utility class to create snapshots from a registry.
|
||||
*
|
||||
@@ -30,34 +45,8 @@ namespace entt {
|
||||
*/
|
||||
template<typename Registry>
|
||||
class basic_snapshot {
|
||||
using entity_traits = entt_traits<typename Registry::entity_type>;
|
||||
|
||||
template<typename Component, typename Archive, typename It>
|
||||
void get(Archive &archive, std::size_t sz, It first, It last) const {
|
||||
const auto view = reg->template view<const Component>();
|
||||
archive(typename entity_traits::entity_type(sz));
|
||||
|
||||
while(first != last) {
|
||||
const auto entt = *(first++);
|
||||
|
||||
if(reg->template all_of<Component>(entt)) {
|
||||
std::apply(archive, std::tuple_cat(std::make_tuple(entt), view.get(entt)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... Component, typename Archive, typename It, std::size_t... Index>
|
||||
void component(Archive &archive, It first, It last, std::index_sequence<Index...>) const {
|
||||
std::array<std::size_t, sizeof...(Index)> size{};
|
||||
auto begin = first;
|
||||
|
||||
while(begin != last) {
|
||||
const auto entt = *(begin++);
|
||||
((reg->template all_of<Component>(entt) ? ++size[Index] : 0u), ...);
|
||||
}
|
||||
|
||||
(get<Component>(archive, size[Index], first, last), ...);
|
||||
}
|
||||
static_assert(!std::is_const_v<Registry>, "Non-const registry type required");
|
||||
using traits_type = typename Registry::traits_type;
|
||||
|
||||
public:
|
||||
/*! Basic registry type. */
|
||||
@@ -79,69 +68,78 @@ public:
|
||||
basic_snapshot &operator=(basic_snapshot &&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Puts aside all the entities from the underlying registry.
|
||||
*
|
||||
* Entities are serialized along with their versions. Destroyed entities are
|
||||
* taken in consideration as well by this function.
|
||||
*
|
||||
* @brief Serializes all elements of a type with associated identifiers.
|
||||
* @tparam Type Type of elements to serialize.
|
||||
* @tparam Archive Type of output archive.
|
||||
* @param archive A valid reference to an output archive.
|
||||
* @param id Optional name used to map the storage within the registry.
|
||||
* @return An object of this type to continue creating the snapshot.
|
||||
*/
|
||||
template<typename Archive>
|
||||
const basic_snapshot &entities(Archive &archive) const {
|
||||
const auto sz = reg->size();
|
||||
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) {
|
||||
archive(static_cast<typename traits_type::entity_type>(storage->size()));
|
||||
|
||||
archive(typename entity_traits::entity_type(sz + 1u));
|
||||
archive(reg->released());
|
||||
if constexpr(std::is_same_v<Type, entity_type>) {
|
||||
archive(static_cast<typename traits_type::entity_type>(storage->free_list()));
|
||||
|
||||
for(auto first = reg->data(), last = first + sz; first != last; ++first) {
|
||||
archive(*first);
|
||||
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);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
archive(typename traits_type::entity_type{});
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Puts aside the given components.
|
||||
*
|
||||
* Each instance is serialized together with the entity to which it belongs.
|
||||
* Entities are serialized along with their versions.
|
||||
*
|
||||
* @tparam Component Types of components to serialize.
|
||||
* @tparam Archive Type of output archive.
|
||||
* @param archive A valid reference to an output archive.
|
||||
* @return An object of this type to continue creating the snapshot.
|
||||
*/
|
||||
template<typename... Component, typename Archive>
|
||||
const basic_snapshot &component(Archive &archive) const {
|
||||
if constexpr(sizeof...(Component) == 1u) {
|
||||
const auto view = reg->template view<const Component...>();
|
||||
(component<Component>(archive, view.rbegin(), view.rend()), ...);
|
||||
return *this;
|
||||
} else {
|
||||
(component<Component>(archive), ...);
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Puts aside the given components for the entities in a range.
|
||||
*
|
||||
* Each instance is serialized together with the entity to which it belongs.
|
||||
* Entities are serialized along with their versions.
|
||||
*
|
||||
* @tparam Component Types of components to serialize.
|
||||
* @brief Serializes all elements of a type with associated identifiers for
|
||||
* the entities in a range.
|
||||
* @tparam Type Type of elements to serialize.
|
||||
* @tparam Archive Type of output archive.
|
||||
* @tparam It Type of input iterator.
|
||||
* @param archive A valid reference to an output archive.
|
||||
* @param first An iterator to the first element of the range to serialize.
|
||||
* @param last An iterator past the last element of the range to serialize.
|
||||
* @param id Optional name used to map the storage within the registry.
|
||||
* @return An object of this type to continue creating the snapshot.
|
||||
*/
|
||||
template<typename... Component, typename Archive, typename It>
|
||||
const basic_snapshot &component(Archive &archive, It first, It last) const {
|
||||
component<Component...>(archive, first, last, std::index_sequence_for<Component...>{});
|
||||
template<typename Type, typename Archive, typename It>
|
||||
const basic_snapshot &get(Archive &archive, It first, It last, const id_type id = type_hash<Type>::value()) const {
|
||||
static_assert(!std::is_same_v<Type, entity_type>, "Entity types not supported");
|
||||
|
||||
if(const auto *storage = reg->template storage<Type>(id); storage && !storage->empty()) {
|
||||
archive(static_cast<typename traits_type::entity_type>(std::distance(first, last)));
|
||||
|
||||
for(; first != last; ++first) {
|
||||
if(const auto entt = *first; storage->contains(entt)) {
|
||||
archive(entt);
|
||||
std::apply([&archive](auto &&...args) { (archive(std::forward<decltype(args)>(args)), ...); }, storage->get_as_tuple(entt));
|
||||
} else {
|
||||
archive(static_cast<entity_type>(null));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
archive(typename traits_type::entity_type{});
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -161,33 +159,8 @@ private:
|
||||
*/
|
||||
template<typename Registry>
|
||||
class basic_snapshot_loader {
|
||||
using entity_traits = entt_traits<typename Registry::entity_type>;
|
||||
|
||||
template<typename Component, typename Archive>
|
||||
void assign(Archive &archive) const {
|
||||
typename entity_traits::entity_type length{};
|
||||
entity_type entt;
|
||||
|
||||
archive(length);
|
||||
|
||||
if constexpr(ignore_as_empty_v<Component>) {
|
||||
while(length--) {
|
||||
archive(entt);
|
||||
const auto entity = reg->valid(entt) ? entt : reg->create(entt);
|
||||
ENTT_ASSERT(entity == entt, "Entity not available for use");
|
||||
reg->template emplace<Component>(entt);
|
||||
}
|
||||
} else {
|
||||
Component instance;
|
||||
|
||||
while(length--) {
|
||||
archive(entt, instance);
|
||||
const auto entity = reg->valid(entt) ? entt : reg->create(entt);
|
||||
ENTT_ASSERT(entity == entt, "Entity not available for use");
|
||||
reg->template emplace<Component>(entt, std::move(instance));
|
||||
}
|
||||
}
|
||||
}
|
||||
static_assert(!std::is_const_v<Registry>, "Non-const registry type required");
|
||||
using traits_type = typename Registry::traits_type;
|
||||
|
||||
public:
|
||||
/*! Basic registry type. */
|
||||
@@ -202,7 +175,7 @@ public:
|
||||
basic_snapshot_loader(registry_type &source) noexcept
|
||||
: reg{&source} {
|
||||
// restoring a snapshot as a whole requires a clean registry
|
||||
ENTT_ASSERT(reg->empty(), "Registry must be empty");
|
||||
ENTT_ASSERT(reg->template storage<entity_type>().free_list() == 0u, "Registry must be empty");
|
||||
}
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
@@ -212,47 +185,52 @@ public:
|
||||
basic_snapshot_loader &operator=(basic_snapshot_loader &&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Restores entities that were in use during serialization.
|
||||
*
|
||||
* This function restores the entities that were in use during serialization
|
||||
* and gives them the versions they originally had.
|
||||
*
|
||||
* @brief Restores all elements of a type with associated identifiers.
|
||||
* @tparam Type Type of elements to restore.
|
||||
* @tparam Archive Type of input archive.
|
||||
* @param archive A valid reference to an input archive.
|
||||
* @param id Optional name used to map the storage within the registry.
|
||||
* @return A valid loader to continue restoring data.
|
||||
*/
|
||||
template<typename Archive>
|
||||
const basic_snapshot_loader &entities(Archive &archive) const {
|
||||
typename entity_traits::entity_type length{};
|
||||
template<typename Type, typename Archive>
|
||||
basic_snapshot_loader &get(Archive &archive, const id_type id = type_hash<Type>::value()) {
|
||||
auto &storage = reg->template storage<Type>(id);
|
||||
typename traits_type::entity_type length{};
|
||||
|
||||
archive(length);
|
||||
std::vector<entity_type> all(length);
|
||||
|
||||
for(std::size_t pos{}; pos < length; ++pos) {
|
||||
archive(all[pos]);
|
||||
if constexpr(std::is_same_v<Type, entity_type>) {
|
||||
typename traits_type::entity_type count{};
|
||||
|
||||
storage.reserve(length);
|
||||
archive(count);
|
||||
|
||||
for(entity_type entity = null; length; --length) {
|
||||
archive(entity);
|
||||
storage.emplace(entity);
|
||||
}
|
||||
|
||||
storage.free_list(count);
|
||||
} else {
|
||||
auto &other = reg->template storage<entity_type>();
|
||||
entity_type entt{null};
|
||||
|
||||
while(length--) {
|
||||
if(archive(entt); entt != null) {
|
||||
const auto entity = other.contains(entt) ? entt : other.emplace(entt);
|
||||
ENTT_ASSERT(entity == entt, "Entity not available for use");
|
||||
|
||||
if constexpr(std::tuple_size_v<decltype(storage.get_as_tuple({}))> == 0u) {
|
||||
storage.emplace(entity);
|
||||
} else {
|
||||
Type elem{};
|
||||
archive(elem);
|
||||
storage.emplace(entity, std::move(elem));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reg->assign(++all.cbegin(), all.cend(), all[0u]);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Restores components and assigns them to the right entities.
|
||||
*
|
||||
* The template parameter list must be exactly the same used during
|
||||
* serialization. In the event that the entity to which the component is
|
||||
* assigned doesn't exist yet, the loader will take care to create it with
|
||||
* the version it originally had.
|
||||
*
|
||||
* @tparam Component Types of components to restore.
|
||||
* @tparam Archive Type of input archive.
|
||||
* @param archive A valid reference to an input archive.
|
||||
* @return A valid loader to continue restoring data.
|
||||
*/
|
||||
template<typename... Component, typename Archive>
|
||||
const basic_snapshot_loader &component(Archive &archive) const {
|
||||
(assign<Component>(archive), ...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -262,17 +240,12 @@ public:
|
||||
* 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
|
||||
* once restored.<br/>
|
||||
* This functions helps to identify and destroy those entities.
|
||||
* This function helps to identify and destroy those entities.
|
||||
*
|
||||
* @return A valid loader to continue restoring data.
|
||||
*/
|
||||
const basic_snapshot_loader &orphans() const {
|
||||
reg->each([this](const auto entt) {
|
||||
if(reg->orphan(entt)) {
|
||||
reg->release(entt);
|
||||
}
|
||||
});
|
||||
|
||||
basic_snapshot_loader &orphans() {
|
||||
internal::orphans(*reg);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -290,7 +263,7 @@ private:
|
||||
* Identifiers that entities originally had are not transferred to the target.
|
||||
* Instead, the loader maps remote identifiers to local ones while restoring a
|
||||
* snapshot.<br/>
|
||||
* An example of use is the implementation of a client-server applications with
|
||||
* An example of use is the implementation of a client-server application with
|
||||
* the requirement of transferring somehow parts of the representation side to
|
||||
* side.
|
||||
*
|
||||
@@ -298,29 +271,16 @@ private:
|
||||
*/
|
||||
template<typename Registry>
|
||||
class basic_continuous_loader {
|
||||
using entity_traits = entt_traits<typename Registry::entity_type>;
|
||||
|
||||
void destroy(typename Registry::entity_type entt) {
|
||||
if(const auto it = remloc.find(entt); it == remloc.cend()) {
|
||||
const auto local = reg->create();
|
||||
remloc.emplace(entt, std::make_pair(local, true));
|
||||
reg->destroy(local);
|
||||
}
|
||||
}
|
||||
static_assert(!std::is_const_v<Registry>, "Non-const registry type required");
|
||||
using traits_type = typename Registry::traits_type;
|
||||
|
||||
void restore(typename Registry::entity_type entt) {
|
||||
const auto it = remloc.find(entt);
|
||||
|
||||
if(it == remloc.cend()) {
|
||||
const auto local = reg->create();
|
||||
remloc.emplace(entt, std::make_pair(local, true));
|
||||
} else {
|
||||
if(!reg->valid(remloc[entt].first)) {
|
||||
remloc[entt].first = reg->create();
|
||||
if(const auto entity = to_entity(entt); remloc.contains(entity) && remloc[entity].first == entt) {
|
||||
if(!reg->valid(remloc[entity].second)) {
|
||||
remloc[entity].second = reg->create();
|
||||
}
|
||||
|
||||
// set the dirty flag
|
||||
remloc[entt].second = true;
|
||||
} else {
|
||||
remloc.insert_or_assign(entity, std::make_pair(entt, reg->create()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,42 +329,6 @@ class basic_continuous_loader {
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Component>
|
||||
void remove_if_exists() {
|
||||
for(auto &&ref: remloc) {
|
||||
const auto local = ref.second.first;
|
||||
|
||||
if(reg->valid(local)) {
|
||||
reg->template remove<Component>(local);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Component, typename Archive, typename... Other, typename... Member>
|
||||
void assign(Archive &archive, [[maybe_unused]] Member Other::*...member) {
|
||||
typename entity_traits::entity_type length{};
|
||||
entity_type entt;
|
||||
|
||||
archive(length);
|
||||
|
||||
if constexpr(ignore_as_empty_v<Component>) {
|
||||
while(length--) {
|
||||
archive(entt);
|
||||
restore(entt);
|
||||
reg->template emplace_or_replace<Component>(map(entt));
|
||||
}
|
||||
} else {
|
||||
Component instance;
|
||||
|
||||
while(length--) {
|
||||
archive(entt, instance);
|
||||
(update(instance, member), ...);
|
||||
restore(entt);
|
||||
reg->template emplace_or_replace<Component>(map(entt), std::move(instance));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/*! Basic registry type. */
|
||||
using registry_type = Registry;
|
||||
@@ -416,7 +340,8 @@ public:
|
||||
* @param source A valid reference to a registry.
|
||||
*/
|
||||
basic_continuous_loader(registry_type &source) noexcept
|
||||
: reg{&source} {}
|
||||
: remloc{source.get_allocator()},
|
||||
reg{&source} {}
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
basic_continuous_loader(basic_continuous_loader &&) = default;
|
||||
@@ -425,87 +350,66 @@ public:
|
||||
basic_continuous_loader &operator=(basic_continuous_loader &&) = default;
|
||||
|
||||
/**
|
||||
* @brief Restores entities that were in use during serialization.
|
||||
* @brief Restores all elements of a type with associated identifiers.
|
||||
*
|
||||
* This function restores the entities that were in use during serialization
|
||||
* and creates local counterparts for them if required.
|
||||
* It creates local counterparts for remote elements as needed.<br/>
|
||||
* Members are either data members of type entity_type or containers of
|
||||
* entities. In both cases, a loader visits them and replaces entities with
|
||||
* their local counterpart.
|
||||
*
|
||||
* @tparam Type Type of elements to restore.
|
||||
* @tparam Archive Type of input archive.
|
||||
* @param archive A valid reference to an input archive.
|
||||
* @return A non-const reference to this loader.
|
||||
* @param id Optional name used to map the storage within the registry.
|
||||
* @return A valid loader to continue restoring data.
|
||||
*/
|
||||
template<typename Archive>
|
||||
basic_continuous_loader &entities(Archive &archive) {
|
||||
typename entity_traits::entity_type length{};
|
||||
entity_type entt{};
|
||||
template<typename Type, typename Archive>
|
||||
basic_continuous_loader &get(Archive &archive, const id_type id = type_hash<Type>::value()) {
|
||||
auto &storage = reg->template storage<Type>(id);
|
||||
typename traits_type::entity_type length{};
|
||||
entity_type entt{null};
|
||||
|
||||
archive(length);
|
||||
// discards the head of the list of destroyed entities
|
||||
archive(entt);
|
||||
|
||||
for(std::size_t pos{}, last = length - 1u; pos < last; ++pos) {
|
||||
archive(entt);
|
||||
if constexpr(std::is_same_v<Type, entity_type>) {
|
||||
typename traits_type::entity_type in_use{};
|
||||
|
||||
if(const auto entity = entity_traits::to_entity(entt); entity == pos) {
|
||||
storage.reserve(length);
|
||||
archive(in_use);
|
||||
|
||||
for(std::size_t pos{}; pos < in_use; ++pos) {
|
||||
archive(entt);
|
||||
restore(entt);
|
||||
} else {
|
||||
destroy(entt);
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
for(std::size_t pos = in_use; pos < length; ++pos) {
|
||||
archive(entt);
|
||||
|
||||
/**
|
||||
* @brief Restores components and assigns them to the right entities.
|
||||
*
|
||||
* The template parameter list must be exactly the same used during
|
||||
* serialization. In the event that the entity to which the component is
|
||||
* assigned doesn't exist yet, the loader will take care to create a local
|
||||
* counterpart for it.<br/>
|
||||
* Members can be either data members of type entity_type or containers of
|
||||
* entities. In both cases, the loader will visit them and update the
|
||||
* entities by replacing each one with its local counterpart.
|
||||
*
|
||||
* @tparam Component Type of component to restore.
|
||||
* @tparam Archive Type of input archive.
|
||||
* @tparam Other Types of components to update with local counterparts.
|
||||
* @tparam Member Types of members to update with their local counterparts.
|
||||
* @param archive A valid reference to an input archive.
|
||||
* @param member Members to update with their local counterparts.
|
||||
* @return A non-const reference to this loader.
|
||||
*/
|
||||
template<typename... Component, typename Archive, typename... Other, typename... Member>
|
||||
basic_continuous_loader &component(Archive &archive, Member Other::*...member) {
|
||||
(remove_if_exists<Component>(), ...);
|
||||
(assign<Component>(archive, member...), ...);
|
||||
return *this;
|
||||
}
|
||||
if(const auto entity = to_entity(entt); remloc.contains(entity)) {
|
||||
if(reg->valid(remloc[entity].second)) {
|
||||
reg->destroy(remloc[entity].second);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helps to purge entities that no longer have a conterpart.
|
||||
*
|
||||
* Users should invoke this member function after restoring each snapshot,
|
||||
* unless they know exactly what they are doing.
|
||||
*
|
||||
* @return A non-const reference to this loader.
|
||||
*/
|
||||
basic_continuous_loader &shrink() {
|
||||
auto it = remloc.begin();
|
||||
|
||||
while(it != remloc.cend()) {
|
||||
const auto local = it->second.first;
|
||||
bool &dirty = it->second.second;
|
||||
|
||||
if(dirty) {
|
||||
dirty = false;
|
||||
++it;
|
||||
} else {
|
||||
if(reg->valid(local)) {
|
||||
reg->destroy(local);
|
||||
remloc.erase(entity);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(auto &&ref: remloc) {
|
||||
storage.remove(ref.second.second);
|
||||
}
|
||||
|
||||
it = remloc.erase(it);
|
||||
while(length--) {
|
||||
if(archive(entt); entt != null) {
|
||||
restore(entt);
|
||||
|
||||
if constexpr(std::tuple_size_v<decltype(storage.get_as_tuple({}))> == 0u) {
|
||||
storage.emplace(map(entt));
|
||||
} else {
|
||||
Type elem{};
|
||||
archive(elem);
|
||||
storage.emplace(map(entt), std::move(elem));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -518,17 +422,12 @@ public:
|
||||
* 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
|
||||
* once restored.<br/>
|
||||
* This functions helps to identify and destroy those entities.
|
||||
* This function helps to identify and destroy those entities.
|
||||
*
|
||||
* @return A non-const reference to this loader.
|
||||
*/
|
||||
basic_continuous_loader &orphans() {
|
||||
reg->each([this](const auto entt) {
|
||||
if(reg->orphan(entt)) {
|
||||
reg->release(entt);
|
||||
}
|
||||
});
|
||||
|
||||
internal::orphans(*reg);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -538,7 +437,8 @@ public:
|
||||
* @return True if `entity` is managed by the loader, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool contains(entity_type entt) const noexcept {
|
||||
return (remloc.find(entt) != remloc.cend());
|
||||
const auto it = remloc.find(to_entity(entt));
|
||||
return it != remloc.cend() && it->second.first == entt;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -547,18 +447,15 @@ public:
|
||||
* @return The local identifier if any, the null entity otherwise.
|
||||
*/
|
||||
[[nodiscard]] entity_type map(entity_type entt) const noexcept {
|
||||
const auto it = remloc.find(entt);
|
||||
entity_type other = null;
|
||||
|
||||
if(it != remloc.cend()) {
|
||||
other = it->second.first;
|
||||
if(const auto it = remloc.find(to_entity(entt)); it != remloc.cend() && it->second.first == entt) {
|
||||
return it->second.second;
|
||||
}
|
||||
|
||||
return other;
|
||||
return null;
|
||||
}
|
||||
|
||||
private:
|
||||
dense_map<entity_type, std::pair<entity_type, bool>> remloc;
|
||||
dense_map<typename traits_type::entity_type, std::pair<entity_type, entity_type>> remloc;
|
||||
registry_type *reg;
|
||||
};
|
||||
|
||||
|
||||
@@ -17,11 +17,7 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename Container>
|
||||
@@ -88,6 +84,10 @@ struct sparse_set_iterator final {
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr pointer data() const noexcept {
|
||||
return packed ? packed->data() : nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr difference_type index() const noexcept {
|
||||
return offset - 1;
|
||||
}
|
||||
@@ -97,55 +97,43 @@ private:
|
||||
difference_type offset;
|
||||
};
|
||||
|
||||
template<typename Type, typename Other>
|
||||
[[nodiscard]] constexpr std::ptrdiff_t operator-(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) noexcept {
|
||||
template<typename Container>
|
||||
[[nodiscard]] constexpr std::ptrdiff_t operator-(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
|
||||
return rhs.index() - lhs.index();
|
||||
}
|
||||
|
||||
template<typename Type, typename Other>
|
||||
[[nodiscard]] constexpr bool operator==(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) noexcept {
|
||||
template<typename Container>
|
||||
[[nodiscard]] constexpr bool operator==(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
|
||||
return lhs.index() == rhs.index();
|
||||
}
|
||||
|
||||
template<typename Type, typename Other>
|
||||
[[nodiscard]] constexpr bool operator!=(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) noexcept {
|
||||
template<typename Container>
|
||||
[[nodiscard]] constexpr bool operator!=(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template<typename Type, typename Other>
|
||||
[[nodiscard]] constexpr bool operator<(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) noexcept {
|
||||
template<typename Container>
|
||||
[[nodiscard]] constexpr bool operator<(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
|
||||
return lhs.index() > rhs.index();
|
||||
}
|
||||
|
||||
template<typename Type, typename Other>
|
||||
[[nodiscard]] constexpr bool operator>(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) noexcept {
|
||||
return lhs.index() < rhs.index();
|
||||
template<typename Container>
|
||||
[[nodiscard]] constexpr bool operator>(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
|
||||
return rhs < lhs;
|
||||
}
|
||||
|
||||
template<typename Type, typename Other>
|
||||
[[nodiscard]] constexpr bool operator<=(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) noexcept {
|
||||
template<typename Container>
|
||||
[[nodiscard]] constexpr bool operator<=(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
|
||||
return !(lhs > rhs);
|
||||
}
|
||||
|
||||
template<typename Type, typename Other>
|
||||
[[nodiscard]] constexpr bool operator>=(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) noexcept {
|
||||
template<typename Container>
|
||||
[[nodiscard]] constexpr bool operator>=(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/*! @brief Sparse set deletion policy. */
|
||||
enum class deletion_policy : std::uint8_t {
|
||||
/*! @brief Swap-and-pop deletion policy. */
|
||||
swap_and_pop = 0u,
|
||||
/*! @brief In-place deletion policy. */
|
||||
in_place = 1u
|
||||
};
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Basic sparse set implementation.
|
||||
@@ -154,10 +142,6 @@ enum class deletion_policy : std::uint8_t {
|
||||
* Two arrays: an _external_ one and an _internal_ one; a _sparse_ one and a
|
||||
* _packed_ one; one used for direct access through contiguous memory, the other
|
||||
* one used to get the data through an extra level of indirection.<br/>
|
||||
* This is largely used by the registry to offer users the fastest access ever
|
||||
* to the components. Views and groups in general are almost entirely designed
|
||||
* around sparse sets.
|
||||
*
|
||||
* This type of data structure is widely documented in the literature and on the
|
||||
* web. This is nothing more than a customized implementation suitable for the
|
||||
* purpose of the framework.
|
||||
@@ -167,7 +151,7 @@ enum class deletion_policy : std::uint8_t {
|
||||
* no guarantees that entities are returned in the insertion order when iterate
|
||||
* a sparse set. Do not make assumption on the order in any case.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Entity A valid entity type.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Entity, typename Allocator>
|
||||
@@ -176,37 +160,40 @@ 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 entity_traits = entt_traits<Entity>;
|
||||
using underlying_type = typename entt_traits<Entity>::entity_type;
|
||||
|
||||
[[nodiscard]] auto sparse_ptr(const Entity entt) const {
|
||||
const auto pos = static_cast<size_type>(entity_traits::to_entity(entt));
|
||||
const auto page = pos / entity_traits::page_size;
|
||||
return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, entity_traits::page_size)) : nullptr;
|
||||
const auto pos = static_cast<size_type>(traits_type::to_entity(entt));
|
||||
const auto page = pos / traits_type::page_size;
|
||||
return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, traits_type::page_size)) : nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto &sparse_ref(const Entity entt) const {
|
||||
ENTT_ASSERT(sparse_ptr(entt), "Invalid element");
|
||||
const auto pos = static_cast<size_type>(entity_traits::to_entity(entt));
|
||||
return sparse[pos / entity_traits::page_size][fast_mod(pos, entity_traits::page_size)];
|
||||
const auto pos = static_cast<size_type>(traits_type::to_entity(entt));
|
||||
return sparse[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)];
|
||||
}
|
||||
|
||||
[[nodiscard]] auto to_iterator(const Entity entt) const {
|
||||
return --(end() - index(entt));
|
||||
}
|
||||
|
||||
[[nodiscard]] auto &assure_at_least(const Entity entt) {
|
||||
const auto pos = static_cast<size_type>(entity_traits::to_entity(entt));
|
||||
const auto page = pos / entity_traits::page_size;
|
||||
const auto pos = static_cast<size_type>(traits_type::to_entity(entt));
|
||||
const auto page = pos / traits_type::page_size;
|
||||
|
||||
if(!(page < sparse.size())) {
|
||||
sparse.resize(page + 1u, nullptr);
|
||||
}
|
||||
|
||||
if(!sparse[page]) {
|
||||
constexpr entity_type init = null;
|
||||
auto page_allocator{packed.get_allocator()};
|
||||
sparse[page] = alloc_traits::allocate(page_allocator, entity_traits::page_size);
|
||||
std::uninitialized_fill(sparse[page], sparse[page] + entity_traits::page_size, null);
|
||||
sparse[page] = alloc_traits::allocate(page_allocator, traits_type::page_size);
|
||||
std::uninitialized_fill(sparse[page], sparse[page] + traits_type::page_size, init);
|
||||
}
|
||||
|
||||
auto &elem = sparse[page][fast_mod(pos, entity_traits::page_size)];
|
||||
ENTT_ASSERT(elem == null, "Slot not available");
|
||||
return elem;
|
||||
return sparse[page][fast_mod(pos, traits_type::page_size)];
|
||||
}
|
||||
|
||||
void release_sparse_pages() {
|
||||
@@ -214,34 +201,60 @@ class basic_sparse_set {
|
||||
|
||||
for(auto &&page: sparse) {
|
||||
if(page != nullptr) {
|
||||
std::destroy(page, page + entity_traits::page_size);
|
||||
alloc_traits::deallocate(page_allocator, page, entity_traits::page_size);
|
||||
std::destroy(page, page + traits_type::page_size);
|
||||
alloc_traits::deallocate(page_allocator, page, traits_type::page_size);
|
||||
page = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void swap_at(const std::size_t from, const std::size_t to) {
|
||||
auto &lhs = packed[from];
|
||||
auto &rhs = packed[to];
|
||||
|
||||
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));
|
||||
|
||||
std::swap(lhs, rhs);
|
||||
}
|
||||
|
||||
underlying_type policy_to_head() {
|
||||
return traits_type::entity_mask * (mode != deletion_policy::swap_only);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual const void *get_at(const std::size_t) const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual void swap_at(const std::size_t, const std::size_t) {}
|
||||
virtual void move_element(const std::size_t, const std::size_t) {}
|
||||
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");
|
||||
}
|
||||
|
||||
protected:
|
||||
/*! @brief Random access iterator type. */
|
||||
using basic_iterator = internal::sparse_set_iterator<packed_container_type>;
|
||||
|
||||
/**
|
||||
* @brief Erases an entity from a sparse set.
|
||||
* @param it An iterator to the element to pop.
|
||||
*/
|
||||
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));
|
||||
bump(traits_type::next(*it));
|
||||
swap_at(pos, static_cast<size_type>(head -= (pos < head)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erases an entity from a sparse set.
|
||||
* @param it An iterator to the element to pop.
|
||||
*/
|
||||
void swap_and_pop(const basic_iterator it) {
|
||||
ENTT_ASSERT(mode == deletion_policy::swap_and_pop, "Deletion policy mismatched");
|
||||
ENTT_ASSERT(mode == deletion_policy::swap_and_pop, "Deletion policy mismatch");
|
||||
auto &self = sparse_ref(*it);
|
||||
const auto entt = entity_traits::to_entity(self);
|
||||
sparse_ref(packed.back()) = entity_traits::combine(entt, entity_traits::to_integral(packed.back()));
|
||||
const auto entt = traits_type::to_entity(self);
|
||||
sparse_ref(packed.back()) = traits_type::combine(entt, traits_type::to_integral(packed.back()));
|
||||
packed[static_cast<size_type>(entt)] = packed.back();
|
||||
// unnecessary but it helps to detect nasty bugs
|
||||
ENTT_ASSERT((packed.back() = null, true), "");
|
||||
@@ -255,9 +268,9 @@ protected:
|
||||
* @param it An iterator to the element to pop.
|
||||
*/
|
||||
void in_place_pop(const basic_iterator it) {
|
||||
ENTT_ASSERT(mode == deletion_policy::in_place, "Deletion policy mismatched");
|
||||
const auto entt = entity_traits::to_entity(std::exchange(sparse_ref(*it), null));
|
||||
packed[static_cast<size_type>(entt)] = std::exchange(free_list, entity_traits::combine(entt, entity_traits::reserved));
|
||||
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);
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -267,17 +280,50 @@ protected:
|
||||
* @param last An iterator past the last element of the range of entities.
|
||||
*/
|
||||
virtual void pop(basic_iterator first, basic_iterator last) {
|
||||
if(mode == deletion_policy::swap_and_pop) {
|
||||
switch(mode) {
|
||||
case deletion_policy::swap_and_pop:
|
||||
for(; first != last; ++first) {
|
||||
swap_and_pop(first);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
case deletion_policy::in_place:
|
||||
for(; first != last; ++first) {
|
||||
in_place_pop(first);
|
||||
}
|
||||
break;
|
||||
case deletion_policy::swap_only:
|
||||
for(; first != last; ++first) {
|
||||
swap_only(first);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*! @brief Erases all entities of a sparse set. */
|
||||
virtual void pop_all() {
|
||||
switch(mode) {
|
||||
case deletion_policy::in_place:
|
||||
if(head != traits_type::to_entity(null)) {
|
||||
for(auto first = begin(); !(first.index() < 0); ++first) {
|
||||
if(*first != tombstone) {
|
||||
sparse_ref(*first) = null;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
case deletion_policy::swap_only:
|
||||
case deletion_policy::swap_and_pop:
|
||||
for(auto first = begin(); !(first.index() < 0); ++first) {
|
||||
sparse_ref(*first) = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
head = policy_to_head();
|
||||
packed.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns an entity to a sparse set.
|
||||
* @param entt A valid identifier.
|
||||
@@ -285,29 +331,55 @@ 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(!contains(entt), "Set already contains entity");
|
||||
auto &elem = assure_at_least(entt);
|
||||
auto pos = size();
|
||||
|
||||
if(auto &elem = assure_at_least(entt); free_list == null || force_back) {
|
||||
switch(mode) {
|
||||
case deletion_policy::in_place:
|
||||
if(head != traits_type::to_entity(null) && !force_back) {
|
||||
pos = static_cast<size_type>(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));
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
case deletion_policy::swap_and_pop:
|
||||
packed.push_back(entt);
|
||||
elem = entity_traits::combine(static_cast<typename entity_traits::entity_type>(packed.size() - 1u), entity_traits::to_integral(entt));
|
||||
return begin();
|
||||
} else {
|
||||
const auto pos = static_cast<size_type>(entity_traits::to_entity(free_list));
|
||||
elem = entity_traits::combine(entity_traits::to_integral(free_list), entity_traits::to_integral(entt));
|
||||
free_list = std::exchange(packed[pos], entt);
|
||||
return --(end() - pos);
|
||||
ENTT_ASSERT(elem == null, "Slot not available");
|
||||
elem = traits_type::combine(static_cast<typename traits_type::entity_type>(packed.size() - 1u), traits_type::to_integral(entt));
|
||||
break;
|
||||
case deletion_policy::swap_only:
|
||||
if(elem == null) {
|
||||
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");
|
||||
bump(entt);
|
||||
}
|
||||
|
||||
if(force_back) {
|
||||
pos = static_cast<size_type>(head++);
|
||||
swap_at(static_cast<size_type>(traits_type::to_entity(elem)), pos);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return --(end() - pos);
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Entity traits. */
|
||||
using traits_type = entt_traits<Entity>;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename entity_traits::value_type;
|
||||
using entity_type = typename traits_type::value_type;
|
||||
/*! @brief Underlying version type. */
|
||||
using version_type = typename entity_traits::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. */
|
||||
@@ -317,7 +389,7 @@ public:
|
||||
/*! @brief Reverse iterator type. */
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
/*! @brief Constant reverse iterator type. */
|
||||
using const_reverse_iterator = reverse_iterator;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
basic_sparse_set()
|
||||
@@ -341,16 +413,16 @@ public:
|
||||
/**
|
||||
* @brief Constructs an empty container with the given value type, policy
|
||||
* and allocator.
|
||||
* @param value Returned value type, if any.
|
||||
* @param elem Returned value type, if any.
|
||||
* @param pol Type of deletion policy.
|
||||
* @param allocator The allocator to use (possibly default-constructed).
|
||||
*/
|
||||
explicit basic_sparse_set(const type_info &value, deletion_policy pol = deletion_policy::swap_and_pop, const allocator_type &allocator = {})
|
||||
explicit basic_sparse_set(const type_info &elem, deletion_policy pol = deletion_policy::swap_and_pop, const allocator_type &allocator = {})
|
||||
: sparse{allocator},
|
||||
packed{allocator},
|
||||
info{&value},
|
||||
free_list{tombstone},
|
||||
mode{pol} {}
|
||||
info{&elem},
|
||||
mode{pol},
|
||||
head{policy_to_head()} {}
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
@@ -360,8 +432,8 @@ public:
|
||||
: sparse{std::move(other.sparse)},
|
||||
packed{std::move(other.packed)},
|
||||
info{other.info},
|
||||
free_list{std::exchange(other.free_list, tombstone)},
|
||||
mode{other.mode} {}
|
||||
mode{other.mode},
|
||||
head{std::exchange(other.head, policy_to_head())} {}
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
@@ -372,8 +444,8 @@ public:
|
||||
: sparse{std::move(other.sparse), allocator},
|
||||
packed{std::move(other.packed), allocator},
|
||||
info{other.info},
|
||||
free_list{std::exchange(other.free_list, tombstone)},
|
||||
mode{other.mode} {
|
||||
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");
|
||||
}
|
||||
|
||||
@@ -394,8 +466,8 @@ public:
|
||||
sparse = std::move(other.sparse);
|
||||
packed = std::move(other.packed);
|
||||
info = other.info;
|
||||
free_list = std::exchange(other.free_list, tombstone);
|
||||
mode = other.mode;
|
||||
head = std::exchange(other.head, policy_to_head());
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -408,8 +480,8 @@ public:
|
||||
swap(sparse, other.sparse);
|
||||
swap(packed, other.packed);
|
||||
swap(info, other.info);
|
||||
swap(free_list, other.free_list);
|
||||
swap(mode, other.mode);
|
||||
swap(head, other.head);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -428,6 +500,23 @@ public:
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the head of the free list, if any.
|
||||
* @return The head of the free list.
|
||||
*/
|
||||
[[nodiscard]] size_type free_list() const noexcept {
|
||||
return static_cast<size_type>(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.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Increases the capacity of a sparse set.
|
||||
*
|
||||
@@ -465,7 +554,7 @@ public:
|
||||
* @return Extent of the sparse set.
|
||||
*/
|
||||
[[nodiscard]] size_type extent() const noexcept {
|
||||
return sparse.size() * entity_traits::page_size;
|
||||
return sparse.size() * traits_type::page_size;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -490,6 +579,14 @@ public:
|
||||
return packed.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether a sparse set is fully packed.
|
||||
* @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));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Direct access to the internal packed array.
|
||||
* @return A pointer to the internal packed array.
|
||||
@@ -501,13 +598,12 @@ public:
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning.
|
||||
*
|
||||
* The returned iterator points to the first entity of the internal packed
|
||||
* array. If the sparse set is empty, the returned iterator will be equal to
|
||||
* If the sparse set is empty, the returned iterator will be equal to
|
||||
* `end()`.
|
||||
*
|
||||
* @return An iterator to the first entity of the sparse set.
|
||||
*/
|
||||
[[nodiscard]] const_iterator begin() const noexcept {
|
||||
[[nodiscard]] iterator begin() const noexcept {
|
||||
const auto pos = static_cast<typename iterator::difference_type>(packed.size());
|
||||
return iterator{packed, pos};
|
||||
}
|
||||
@@ -519,11 +615,6 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the end.
|
||||
*
|
||||
* The returned iterator points to the element following the last entity in
|
||||
* a sparse set. Attempting to dereference the returned iterator results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @return An iterator to the element following the last entity of a sparse
|
||||
* set.
|
||||
*/
|
||||
@@ -539,14 +630,13 @@ public:
|
||||
/**
|
||||
* @brief Returns a reverse iterator to the beginning.
|
||||
*
|
||||
* The returned iterator points to the first entity of the reversed internal
|
||||
* packed array. If the sparse set is empty, the returned iterator will be
|
||||
* equal to `rend()`.
|
||||
* If the sparse set is empty, the returned iterator will be equal to
|
||||
* `rend()`.
|
||||
*
|
||||
* @return An iterator to the first entity of the reversed internal packed
|
||||
* array.
|
||||
*/
|
||||
[[nodiscard]] const_reverse_iterator rbegin() const noexcept {
|
||||
[[nodiscard]] reverse_iterator rbegin() const noexcept {
|
||||
return std::make_reverse_iterator(end());
|
||||
}
|
||||
|
||||
@@ -557,11 +647,6 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Returns a reverse iterator to the end.
|
||||
*
|
||||
* The returned iterator points to the element following the last entity in
|
||||
* the reversed sparse set. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @return An iterator to the element following the last entity of the
|
||||
* reversed sparse set.
|
||||
*/
|
||||
@@ -574,14 +659,54 @@ 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.
|
||||
* @return An iterator to the given entity if it's found, past the end
|
||||
* iterator otherwise.
|
||||
*/
|
||||
[[nodiscard]] iterator find(const entity_type entt) const noexcept {
|
||||
return contains(entt) ? --(end() - index(entt)) : end();
|
||||
[[nodiscard]] const_iterator find(const entity_type entt) const noexcept {
|
||||
return contains(entt) ? to_iterator(entt) : end();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -591,9 +716,10 @@ public:
|
||||
*/
|
||||
[[nodiscard]] bool contains(const entity_type entt) const noexcept {
|
||||
const auto elem = sparse_ptr(entt);
|
||||
constexpr auto cap = entity_traits::to_entity(null);
|
||||
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
|
||||
return elem && (((~cap & entity_traits::to_integral(entt)) ^ entity_traits::to_integral(*elem)) < cap);
|
||||
return elem && (((mask & traits_type::to_integral(entt)) ^ traits_type::to_integral(*elem)) < cap);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -604,8 +730,8 @@ public:
|
||||
*/
|
||||
[[nodiscard]] version_type current(const entity_type entt) const noexcept {
|
||||
const auto elem = sparse_ptr(entt);
|
||||
constexpr auto fallback = entity_traits::to_version(tombstone);
|
||||
return elem ? entity_traits::to_version(*elem) : fallback;
|
||||
constexpr auto fallback = traits_type::to_version(tombstone);
|
||||
return elem ? traits_type::to_version(*elem) : fallback;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -620,7 +746,7 @@ public:
|
||||
*/
|
||||
[[nodiscard]] size_type index(const entity_type entt) const noexcept {
|
||||
ENTT_ASSERT(contains(entt), "Set does not contain entity");
|
||||
return static_cast<size_type>(entity_traits::to_entity(sparse_ref(entt)));
|
||||
return static_cast<size_type>(traits_type::to_entity(sparse_ref(entt)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -628,7 +754,7 @@ public:
|
||||
* @param pos The position for which to return the entity.
|
||||
* @return The entity at specified location if any, a null entity otherwise.
|
||||
*/
|
||||
[[nodiscard]] entity_type at(const size_type pos) const noexcept {
|
||||
[[deprecated("use .begin()[pos] instead")]] [[nodiscard]] entity_type at(const size_type pos) const noexcept {
|
||||
return pos < packed.size() ? packed[pos] : null;
|
||||
}
|
||||
|
||||
@@ -652,13 +778,13 @@ public:
|
||||
* @param entt A valid identifier.
|
||||
* @return An opaque pointer to the element assigned to the entity, if any.
|
||||
*/
|
||||
[[nodiscard]] const void *get(const entity_type entt) const noexcept {
|
||||
[[nodiscard]] const void *value(const entity_type entt) const noexcept {
|
||||
return get_at(index(entt));
|
||||
}
|
||||
|
||||
/*! @copydoc get */
|
||||
[[nodiscard]] void *get(const entity_type entt) noexcept {
|
||||
return const_cast<void *>(std::as_const(*this).get(entt));
|
||||
/*! @copydoc value */
|
||||
[[nodiscard]] void *value(const entity_type entt) noexcept {
|
||||
return const_cast<void *>(std::as_const(*this).value(entt));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -669,28 +795,12 @@ public:
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @param entt A valid identifier.
|
||||
* @param value Optional opaque value to forward to mixins, if any.
|
||||
* @param elem Optional opaque element to forward to mixins, if any.
|
||||
* @return Iterator pointing to the emplaced element in case of success, the
|
||||
* `end()` iterator otherwise.
|
||||
*/
|
||||
iterator emplace(const entity_type entt, const void *value = nullptr) {
|
||||
return try_emplace(entt, false, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Bump the version number of an entity.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to bump the version of an entity that doesn't belong to the
|
||||
* sparse set results in undefined behavior.
|
||||
*
|
||||
* @param entt A valid identifier.
|
||||
*/
|
||||
void bump(const entity_type entt) {
|
||||
auto &entity = sparse_ref(entt);
|
||||
ENTT_ASSERT(entt != tombstone && entity != null, "Cannot set the required version");
|
||||
entity = entity_traits::combine(entity_traits::to_integral(entity), entity_traits::to_integral(entt));
|
||||
packed[static_cast<size_type>(entity_traits::to_entity(entity))] = entt;
|
||||
iterator push(const entity_type entt, const void *elem = nullptr) {
|
||||
return try_emplace(entt, (mode == deletion_policy::swap_only), elem);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -707,7 +817,7 @@ public:
|
||||
* success, the `end()` iterator otherwise.
|
||||
*/
|
||||
template<typename It>
|
||||
iterator insert(It first, It last) {
|
||||
iterator push(It first, It last) {
|
||||
for(auto it = first; it != last; ++it) {
|
||||
try_emplace(*it, true);
|
||||
}
|
||||
@@ -715,6 +825,24 @@ public:
|
||||
return first == last ? end() : find(*first);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Bump the version number of an entity.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to bump the version of an entity that doesn't belong to the
|
||||
* sparse set results in undefined behavior.
|
||||
*
|
||||
* @param entt A valid identifier.
|
||||
* @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;
|
||||
return traits_type::to_version(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erases an entity from a sparse set.
|
||||
*
|
||||
@@ -725,7 +853,7 @@ public:
|
||||
* @param entt A valid identifier.
|
||||
*/
|
||||
void erase(const entity_type entt) {
|
||||
const auto it = --(end() - index(entt));
|
||||
const auto it = to_iterator(entt);
|
||||
pop(it, it + 1u);
|
||||
}
|
||||
|
||||
@@ -769,35 +897,52 @@ public:
|
||||
size_type remove(It first, It last) {
|
||||
size_type count{};
|
||||
|
||||
for(; first != last; ++first) {
|
||||
count += remove(*first);
|
||||
if constexpr(std::is_same_v<It, basic_iterator>) {
|
||||
while(first != last) {
|
||||
while(first != last && !contains(*first)) {
|
||||
++first;
|
||||
}
|
||||
|
||||
const auto it = first;
|
||||
|
||||
while(first != last && contains(*first)) {
|
||||
++first;
|
||||
}
|
||||
|
||||
count += std::distance(it, first);
|
||||
erase(it, first);
|
||||
}
|
||||
} else {
|
||||
for(; first != last; ++first) {
|
||||
count += remove(*first);
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*! @brief Removes all tombstones from the packed array of a sparse set. */
|
||||
/*! @brief Removes all tombstones from a sparse set. */
|
||||
void compact() {
|
||||
size_type from = packed.size();
|
||||
for(; from && packed[from - 1u] == tombstone; --from) {}
|
||||
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);
|
||||
|
||||
for(auto *it = &free_list; *it != null && from; it = std::addressof(packed[entity_traits::to_entity(*it)])) {
|
||||
if(const size_type to = entity_traits::to_entity(*it); to < from) {
|
||||
--from;
|
||||
move_element(from, to);
|
||||
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) {
|
||||
--from;
|
||||
swap_or_move(from, to);
|
||||
|
||||
using std::swap;
|
||||
swap(packed[from], packed[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 entity = static_cast<typename entity_traits::entity_type>(to);
|
||||
sparse_ref(packed[to]) = entity_traits::combine(entity, entity_traits::to_integral(packed[to]));
|
||||
*it = entity_traits::combine(static_cast<typename entity_traits::entity_type>(from), entity_traits::reserved);
|
||||
for(; from && packed[from - 1u] == tombstone; --from) {}
|
||||
for(; from && packed[from - 1u] == tombstone; --from) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free_list = tombstone;
|
||||
packed.resize(from);
|
||||
packed.erase(packed.begin() + from, packed.end());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -814,21 +959,12 @@ public:
|
||||
* @param rhs A valid identifier.
|
||||
*/
|
||||
void swap_elements(const entity_type lhs, const entity_type rhs) {
|
||||
ENTT_ASSERT(contains(lhs) && contains(rhs), "Set does not contain entities");
|
||||
const auto from = index(lhs);
|
||||
const auto to = index(rhs);
|
||||
|
||||
auto &entt = sparse_ref(lhs);
|
||||
auto &other = sparse_ref(rhs);
|
||||
|
||||
const auto from = entity_traits::to_entity(entt);
|
||||
const auto to = entity_traits::to_entity(other);
|
||||
|
||||
// basic no-leak guarantee (with invalid state) if swapping throws
|
||||
swap_at(static_cast<size_type>(from), static_cast<size_type>(to));
|
||||
entt = entity_traits::combine(to, entity_traits::to_integral(packed[from]));
|
||||
other = entity_traits::combine(from, entity_traits::to_integral(packed[to]));
|
||||
|
||||
using std::swap;
|
||||
swap(packed[from], packed[to]);
|
||||
// basic no-leak guarantee if swapping throws
|
||||
swap_or_move(from, to);
|
||||
swap_at(from, to);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -863,8 +999,8 @@ 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(!(length > packed.size()), "Length exceeds the number of elements");
|
||||
ENTT_ASSERT(free_list == null, "Partial sorting with tombstones is not supported");
|
||||
|
||||
algo(packed.rend() - length, packed.rend(), std::move(compare), std::forward<Args>(args)...);
|
||||
|
||||
@@ -876,9 +1012,9 @@ public:
|
||||
const auto idx = index(packed[next]);
|
||||
const auto entt = packed[curr];
|
||||
|
||||
swap_at(next, idx);
|
||||
const auto entity = static_cast<typename entity_traits::entity_type>(curr);
|
||||
sparse_ref(entt) = entity_traits::combine(entity, entity_traits::to_integral(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]));
|
||||
curr = std::exchange(next, idx);
|
||||
}
|
||||
}
|
||||
@@ -898,57 +1034,51 @@ public:
|
||||
*/
|
||||
template<typename Compare, typename Sort = std_sort, typename... Args>
|
||||
void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
|
||||
compact();
|
||||
sort_n(packed.size(), std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
sort_n(static_cast<size_type>(end(0) - begin(0)), std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sort entities according to their order in another sparse set.
|
||||
* @brief Sort entities according to their order in a range.
|
||||
*
|
||||
* Entities that are part of both the sparse sets are ordered internally
|
||||
* according to the order they have in `other`. All the other entities goes
|
||||
* to the end of the list and there are no guarantees on their order.<br/>
|
||||
* In other terms, this function can be used to impose the same order on two
|
||||
* sets by using one of them as a master and the other one as a slave.
|
||||
* Entities that are part of both the sparse set and the range are ordered
|
||||
* internally according to the order they have in the range.<br/>
|
||||
* All other entities goes to the end of the sparse set and there are no
|
||||
* guarantees on their order.
|
||||
*
|
||||
* Iterating the sparse set with a couple of iterators returns elements in
|
||||
* the expected order after a call to `respect`. See `begin` and `end` for
|
||||
* more details.
|
||||
*
|
||||
* @param other The sparse sets that imposes the order of the entities.
|
||||
* @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.
|
||||
*/
|
||||
void respect(const basic_sparse_set &other) {
|
||||
compact();
|
||||
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");
|
||||
|
||||
const auto to = other.end();
|
||||
auto from = other.begin();
|
||||
|
||||
for(size_type pos = packed.size() - 1; pos && from != to; ++from) {
|
||||
if(contains(*from)) {
|
||||
if(*from != packed[pos]) {
|
||||
for(auto it = begin(0); it.index() && 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
|
||||
swap_elements(packed[pos], *from);
|
||||
swap_elements(entt, curr);
|
||||
}
|
||||
|
||||
--pos;
|
||||
++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());
|
||||
}
|
||||
|
||||
/*! @brief Clears a sparse set. */
|
||||
void clear() {
|
||||
if(const auto last = end(); free_list == null) {
|
||||
pop(begin(), last);
|
||||
} else {
|
||||
for(auto &&entity: *this) {
|
||||
// tombstone filter on itself
|
||||
if(const auto it = find(entity); it != last) {
|
||||
pop(it, it + 1u);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free_list = tombstone;
|
||||
pop_all();
|
||||
// sanity check to avoid subtle issues due to storage classes
|
||||
ENTT_ASSERT((compact(), size()) == 0u, "Non-empty set");
|
||||
head = policy_to_head();
|
||||
packed.clear();
|
||||
}
|
||||
|
||||
@@ -967,8 +1097,8 @@ private:
|
||||
sparse_container_type sparse;
|
||||
packed_container_type packed;
|
||||
const type_info *info;
|
||||
entity_type free_list;
|
||||
deletion_policy mode;
|
||||
underlying_type head;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../config/config.h"
|
||||
#include "../core/compressed_pair.hpp"
|
||||
#include "../core/iterator.hpp"
|
||||
#include "../core/memory.hpp"
|
||||
#include "../core/type_info.hpp"
|
||||
@@ -17,24 +16,18 @@
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "sparse_set.hpp"
|
||||
#include "storage_mixin.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename Container>
|
||||
template<typename Container, std::size_t Size>
|
||||
class storage_iterator final {
|
||||
friend storage_iterator<const Container>;
|
||||
friend storage_iterator<const Container, Size>;
|
||||
|
||||
using container_type = std::remove_const_t<Container>;
|
||||
using alloc_traits = std::allocator_traits<typename container_type::allocator_type>;
|
||||
using comp_traits = component_traits<std::remove_pointer_t<typename container_type::value_type>>;
|
||||
|
||||
using iterator_traits = std::iterator_traits<std::conditional_t<
|
||||
std::is_const_v<Container>,
|
||||
@@ -51,12 +44,12 @@ public:
|
||||
constexpr storage_iterator() noexcept = default;
|
||||
|
||||
constexpr storage_iterator(Container *ref, const difference_type idx) noexcept
|
||||
: packed{ref},
|
||||
: payload{ref},
|
||||
offset{idx} {}
|
||||
|
||||
template<bool Const = std::is_const_v<Container>, typename = std::enable_if_t<Const>>
|
||||
constexpr storage_iterator(const storage_iterator<std::remove_const_t<Container>> &other) noexcept
|
||||
: storage_iterator{other.packed, other.offset} {}
|
||||
constexpr storage_iterator(const storage_iterator<std::remove_const_t<Container>, Size> &other) noexcept
|
||||
: storage_iterator{other.payload, other.offset} {}
|
||||
|
||||
constexpr storage_iterator &operator++() noexcept {
|
||||
return --offset, *this;
|
||||
@@ -96,12 +89,12 @@ public:
|
||||
|
||||
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
|
||||
const auto pos = index() - value;
|
||||
return (*packed)[pos / comp_traits::page_size][fast_mod(pos, comp_traits::page_size)];
|
||||
return (*payload)[pos / Size][fast_mod(pos, Size)];
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr pointer operator->() const noexcept {
|
||||
const auto pos = index();
|
||||
return (*packed)[pos / comp_traits::page_size] + fast_mod(pos, comp_traits::page_size);
|
||||
return (*payload)[pos / Size] + fast_mod(pos, Size);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr reference operator*() const noexcept {
|
||||
@@ -113,42 +106,42 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
Container *packed;
|
||||
Container *payload;
|
||||
difference_type offset;
|
||||
};
|
||||
|
||||
template<typename CLhs, typename CRhs>
|
||||
[[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) noexcept {
|
||||
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 {
|
||||
return rhs.index() - lhs.index();
|
||||
}
|
||||
|
||||
template<typename CLhs, typename CRhs>
|
||||
[[nodiscard]] constexpr bool operator==(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) noexcept {
|
||||
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 {
|
||||
return lhs.index() == rhs.index();
|
||||
}
|
||||
|
||||
template<typename CLhs, typename CRhs>
|
||||
[[nodiscard]] constexpr bool operator!=(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) noexcept {
|
||||
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 {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template<typename CLhs, typename CRhs>
|
||||
[[nodiscard]] constexpr bool operator<(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) noexcept {
|
||||
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 {
|
||||
return lhs.index() > rhs.index();
|
||||
}
|
||||
|
||||
template<typename CLhs, typename CRhs>
|
||||
[[nodiscard]] constexpr bool operator>(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &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 {
|
||||
return rhs < lhs;
|
||||
}
|
||||
|
||||
template<typename CLhs, typename CRhs>
|
||||
[[nodiscard]] constexpr bool operator<=(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) noexcept {
|
||||
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 {
|
||||
return !(lhs > rhs);
|
||||
}
|
||||
|
||||
template<typename CLhs, typename CRhs>
|
||||
[[nodiscard]] constexpr bool operator>=(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) noexcept {
|
||||
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 {
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
@@ -158,16 +151,18 @@ class extended_storage_iterator final {
|
||||
friend class extended_storage_iterator;
|
||||
|
||||
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;
|
||||
|
||||
constexpr extended_storage_iterator()
|
||||
: it{} {}
|
||||
|
||||
constexpr extended_storage_iterator(It base, Other... other)
|
||||
constexpr extended_storage_iterator(iterator_type base, Other... other)
|
||||
: it{base, other...} {}
|
||||
|
||||
template<typename... Args, typename = std::enable_if_t<(!std::is_same_v<Other, Args> && ...) && (std::is_constructible_v<Other, Args> && ...)>>
|
||||
@@ -191,29 +186,29 @@ public:
|
||||
return {*std::get<It>(it), *std::get<Other>(it)...};
|
||||
}
|
||||
|
||||
template<typename... CLhs, typename... CRhs>
|
||||
friend constexpr bool operator==(const extended_storage_iterator<CLhs...> &, const extended_storage_iterator<CRhs...> &) noexcept;
|
||||
[[nodiscard]] constexpr iterator_type base() const noexcept {
|
||||
return std::get<It>(it);
|
||||
}
|
||||
|
||||
template<typename... Lhs, typename... Rhs>
|
||||
friend constexpr bool operator==(const extended_storage_iterator<Lhs...> &, const extended_storage_iterator<Rhs...> &) noexcept;
|
||||
|
||||
private:
|
||||
std::tuple<It, Other...> it;
|
||||
};
|
||||
|
||||
template<typename... CLhs, typename... CRhs>
|
||||
[[nodiscard]] constexpr bool operator==(const extended_storage_iterator<CLhs...> &lhs, const extended_storage_iterator<CRhs...> &rhs) noexcept {
|
||||
template<typename... Lhs, typename... Rhs>
|
||||
[[nodiscard]] constexpr bool operator==(const extended_storage_iterator<Lhs...> &lhs, const extended_storage_iterator<Rhs...> &rhs) noexcept {
|
||||
return std::get<0>(lhs.it) == std::get<0>(rhs.it);
|
||||
}
|
||||
|
||||
template<typename... CLhs, typename... CRhs>
|
||||
[[nodiscard]] constexpr bool operator!=(const extended_storage_iterator<CLhs...> &lhs, const extended_storage_iterator<CRhs...> &rhs) noexcept {
|
||||
template<typename... Lhs, typename... Rhs>
|
||||
[[nodiscard]] constexpr bool operator!=(const extended_storage_iterator<Lhs...> &lhs, const extended_storage_iterator<Rhs...> &rhs) noexcept {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Basic storage implementation.
|
||||
@@ -227,43 +222,41 @@ template<typename... CLhs, typename... CRhs>
|
||||
* normally available for non-empty types will not be available for empty ones.
|
||||
*
|
||||
* @tparam Type Type of objects assigned to the entities.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Entity A valid entity type.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Type, typename Entity, typename Allocator, typename>
|
||||
class basic_storage: 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 underlying_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
|
||||
using container_type = std::vector<typename alloc_traits::pointer, typename alloc_traits::template rebind_alloc<typename alloc_traits::pointer>>;
|
||||
using comp_traits = component_traits<Type>;
|
||||
|
||||
static constexpr bool is_pinned_type_v = !(std::is_move_constructible_v<Type> && std::is_move_assignable_v<Type>);
|
||||
using underlying_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
|
||||
using underlying_iterator = typename underlying_type::basic_iterator;
|
||||
|
||||
[[nodiscard]] auto &element_at(const std::size_t pos) const {
|
||||
return packed.first()[pos / comp_traits::page_size][fast_mod(pos, comp_traits::page_size)];
|
||||
return payload[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)];
|
||||
}
|
||||
|
||||
auto assure_at_least(const std::size_t pos) {
|
||||
auto &&container = packed.first();
|
||||
const auto idx = pos / comp_traits::page_size;
|
||||
const auto idx = pos / traits_type::page_size;
|
||||
|
||||
if(!(idx < container.size())) {
|
||||
auto curr = container.size();
|
||||
container.resize(idx + 1u, nullptr);
|
||||
if(!(idx < payload.size())) {
|
||||
auto curr = payload.size();
|
||||
allocator_type allocator{get_allocator()};
|
||||
payload.resize(idx + 1u, nullptr);
|
||||
|
||||
ENTT_TRY {
|
||||
for(const auto last = container.size(); curr < last; ++curr) {
|
||||
container[curr] = alloc_traits::allocate(packed.second(), comp_traits::page_size);
|
||||
for(const auto last = payload.size(); curr < last; ++curr) {
|
||||
payload[curr] = alloc_traits::allocate(allocator, traits_type::page_size);
|
||||
}
|
||||
}
|
||||
ENTT_CATCH {
|
||||
container.resize(curr);
|
||||
payload.resize(curr);
|
||||
ENTT_THROW;
|
||||
}
|
||||
}
|
||||
|
||||
return container[idx] + fast_mod(pos, comp_traits::page_size);
|
||||
return payload[idx] + fast_mod(pos, traits_type::page_size);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
@@ -272,7 +265,7 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
|
||||
|
||||
ENTT_TRY {
|
||||
auto elem = assure_at_least(static_cast<size_type>(it.index()));
|
||||
entt::uninitialized_construct_using_allocator(to_address(elem), packed.second(), std::forward<Args>(args)...);
|
||||
entt::uninitialized_construct_using_allocator(to_address(elem), get_allocator(), std::forward<Args>(args)...);
|
||||
}
|
||||
ENTT_CATCH {
|
||||
base_type::pop(it, it + 1u);
|
||||
@@ -283,25 +276,24 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
|
||||
}
|
||||
|
||||
void shrink_to_size(const std::size_t sz) {
|
||||
const auto from = (sz + traits_type::page_size - 1u) / traits_type::page_size;
|
||||
allocator_type allocator{get_allocator()};
|
||||
|
||||
for(auto pos = sz, length = base_type::size(); pos < length; ++pos) {
|
||||
if constexpr(comp_traits::in_place_delete) {
|
||||
if(base_type::at(pos) != tombstone) {
|
||||
std::destroy_at(std::addressof(element_at(pos)));
|
||||
if constexpr(traits_type::in_place_delete) {
|
||||
if(base_type::data()[pos] != tombstone) {
|
||||
alloc_traits::destroy(allocator, std::addressof(element_at(pos)));
|
||||
}
|
||||
} else {
|
||||
std::destroy_at(std::addressof(element_at(pos)));
|
||||
alloc_traits::destroy(allocator, std::addressof(element_at(pos)));
|
||||
}
|
||||
}
|
||||
|
||||
auto &&container = packed.first();
|
||||
auto page_allocator{packed.second()};
|
||||
const auto from = (sz + comp_traits::page_size - 1u) / comp_traits::page_size;
|
||||
|
||||
for(auto pos = from, last = container.size(); pos < last; ++pos) {
|
||||
alloc_traits::deallocate(page_allocator, container[pos], comp_traits::page_size);
|
||||
for(auto pos = from, last = payload.size(); pos < last; ++pos) {
|
||||
alloc_traits::deallocate(allocator, payload[pos], traits_type::page_size);
|
||||
}
|
||||
|
||||
container.resize(from);
|
||||
payload.resize(from);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -309,54 +301,69 @@ private:
|
||||
return std::addressof(element_at(pos));
|
||||
}
|
||||
|
||||
void swap_at([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) final {
|
||||
// use a runtime value to avoid compile-time suppression that drives the code coverage tool crazy
|
||||
ENTT_ASSERT((lhs + 1u) && !is_pinned_type_v, "Pinned type");
|
||||
|
||||
if constexpr(!is_pinned_type_v) {
|
||||
using std::swap;
|
||||
swap(element_at(lhs), element_at(rhs));
|
||||
}
|
||||
}
|
||||
|
||||
void move_element([[maybe_unused]] const std::size_t from, [[maybe_unused]] const std::size_t to) final {
|
||||
void swap_or_move([[maybe_unused]] const std::size_t from, [[maybe_unused]] const std::size_t to) override {
|
||||
static constexpr bool is_pinned_type_v = !(std::is_move_constructible_v<Type> && std::is_move_assignable_v<Type>);
|
||||
// use a runtime value to avoid compile-time suppression that drives the code coverage tool crazy
|
||||
ENTT_ASSERT((from + 1u) && !is_pinned_type_v, "Pinned type");
|
||||
|
||||
if constexpr(!is_pinned_type_v) {
|
||||
auto &elem = element_at(from);
|
||||
entt::uninitialized_construct_using_allocator(to_address(assure_at_least(to)), packed.second(), std::move(elem));
|
||||
std::destroy_at(std::addressof(elem));
|
||||
|
||||
if constexpr(traits_type::in_place_delete) {
|
||||
if(base_type::operator[](to) == tombstone) {
|
||||
allocator_type allocator{get_allocator()};
|
||||
entt::uninitialized_construct_using_allocator(to_address(assure_at_least(to)), allocator, std::move(elem));
|
||||
alloc_traits::destroy(allocator, std::addressof(elem));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
using std::swap;
|
||||
swap(elem, element_at(to));
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
/*! @brief Random access iterator type. */
|
||||
using basic_iterator = typename underlying_type::basic_iterator;
|
||||
|
||||
/**
|
||||
* @brief Erases entities from a sparse set.
|
||||
* @brief Erases entities from a storage.
|
||||
* @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.
|
||||
*/
|
||||
void pop(basic_iterator first, basic_iterator last) override {
|
||||
for(; first != last; ++first) {
|
||||
void pop(underlying_iterator first, underlying_iterator last) override {
|
||||
for(allocator_type allocator{get_allocator()}; first != last; ++first) {
|
||||
// cannot use first.index() because it would break with cross iterators
|
||||
auto &elem = element_at(base_type::index(*first));
|
||||
|
||||
if constexpr(comp_traits::in_place_delete) {
|
||||
if constexpr(traits_type::in_place_delete) {
|
||||
base_type::in_place_pop(first);
|
||||
std::destroy_at(std::addressof(elem));
|
||||
alloc_traits::destroy(allocator, std::addressof(elem));
|
||||
} else {
|
||||
auto &other = element_at(base_type::size() - 1u);
|
||||
// destroying on exit allows reentrant destructors
|
||||
[[maybe_unused]] auto unused = std::exchange(elem, std::move(other));
|
||||
std::destroy_at(std::addressof(other));
|
||||
alloc_traits::destroy(allocator, std::addressof(other));
|
||||
base_type::swap_and_pop(first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*! @brief Erases all entities of a storage. */
|
||||
void pop_all() override {
|
||||
allocator_type allocator{get_allocator()};
|
||||
|
||||
for(auto first = base_type::begin(); !(first.index() < 0); ++first) {
|
||||
if constexpr(traits_type::in_place_delete) {
|
||||
if(*first != tombstone) {
|
||||
base_type::in_place_pop(first);
|
||||
alloc_traits::destroy(allocator, std::addressof(element_at(static_cast<size_type>(first.index()))));
|
||||
}
|
||||
} else {
|
||||
base_type::swap_and_pop(first);
|
||||
alloc_traits::destroy(allocator, std::addressof(element_at(static_cast<size_type>(first.index()))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns an entity to a storage.
|
||||
* @param entt A valid identifier.
|
||||
@@ -364,7 +371,7 @@ protected:
|
||||
* @param force_back Force back insertion.
|
||||
* @return Iterator pointing to the emplaced element.
|
||||
*/
|
||||
basic_iterator try_emplace([[maybe_unused]] const Entity entt, [[maybe_unused]] const bool force_back, const void *value) override {
|
||||
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));
|
||||
@@ -383,22 +390,24 @@ protected:
|
||||
public:
|
||||
/*! @brief Base type. */
|
||||
using base_type = underlying_type;
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Type of the objects assigned to entities. */
|
||||
using value_type = Type;
|
||||
/*! @brief Component traits. */
|
||||
using traits_type = component_traits<value_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>;
|
||||
using iterator = internal::storage_iterator<container_type, traits_type::page_size>;
|
||||
/*! @brief Constant random access iterator type. */
|
||||
using const_iterator = internal::storage_iterator<const container_type>;
|
||||
using const_iterator = internal::storage_iterator<const container_type, traits_type::page_size>;
|
||||
/*! @brief Reverse iterator type. */
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
/*! @brief Constant reverse iterator type. */
|
||||
@@ -407,6 +416,10 @@ public:
|
||||
using iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::iterator, iterator>>;
|
||||
/*! @brief Constant extended iterable storage proxy. */
|
||||
using const_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_iterator, const_iterator>>;
|
||||
/*! @brief Extended reverse iterable storage proxy. */
|
||||
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 Default constructor. */
|
||||
basic_storage()
|
||||
@@ -417,8 +430,8 @@ public:
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit basic_storage(const allocator_type &allocator)
|
||||
: base_type{type_id<value_type>(), deletion_policy{comp_traits::in_place_delete}, allocator},
|
||||
packed{container_type{allocator}, allocator} {}
|
||||
: base_type{type_id<value_type>(), deletion_policy{traits_type::in_place_delete}, allocator},
|
||||
payload{allocator} {}
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
@@ -426,7 +439,7 @@ public:
|
||||
*/
|
||||
basic_storage(basic_storage &&other) noexcept
|
||||
: base_type{std::move(other)},
|
||||
packed{std::move(other.packed)} {}
|
||||
payload{std::move(other.payload)} {}
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
@@ -435,8 +448,8 @@ public:
|
||||
*/
|
||||
basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept
|
||||
: base_type{std::move(other), allocator},
|
||||
packed{container_type{std::move(other.packed.first()), allocator}, allocator} {
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.second() == other.packed.second(), "Copying a storage is not allowed");
|
||||
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");
|
||||
}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
@@ -450,12 +463,11 @@ public:
|
||||
* @return This storage.
|
||||
*/
|
||||
basic_storage &operator=(basic_storage &&other) noexcept {
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.second() == other.packed.second(), "Copying a storage is not allowed");
|
||||
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));
|
||||
packed.first() = std::move(other.packed.first());
|
||||
propagate_on_container_move_assignment(packed.second(), other.packed.second());
|
||||
payload = std::move(other.payload);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -465,9 +477,8 @@ public:
|
||||
*/
|
||||
void swap(basic_storage &other) {
|
||||
using std::swap;
|
||||
underlying_type::swap(other);
|
||||
propagate_on_container_swap(packed.second(), other.packed.second());
|
||||
swap(packed.first(), other.packed.first());
|
||||
base_type::swap(other);
|
||||
swap(payload, other.payload);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -475,7 +486,7 @@ public:
|
||||
* @return The associated allocator.
|
||||
*/
|
||||
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
|
||||
return allocator_type{packed.second()};
|
||||
return payload.get_allocator();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -499,7 +510,7 @@ public:
|
||||
* @return Capacity of the storage.
|
||||
*/
|
||||
[[nodiscard]] size_type capacity() const noexcept override {
|
||||
return packed.first().size() * comp_traits::page_size;
|
||||
return payload.size() * traits_type::page_size;
|
||||
}
|
||||
|
||||
/*! @brief Requests the removal of unused capacity. */
|
||||
@@ -513,25 +524,24 @@ public:
|
||||
* @return A pointer to the array of objects.
|
||||
*/
|
||||
[[nodiscard]] const_pointer raw() const noexcept {
|
||||
return packed.first().data();
|
||||
return payload.data();
|
||||
}
|
||||
|
||||
/*! @copydoc raw */
|
||||
[[nodiscard]] pointer raw() noexcept {
|
||||
return packed.first().data();
|
||||
return payload.data();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning.
|
||||
*
|
||||
* The returned iterator points to the first instance of the internal array.
|
||||
* If the storage is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @return An iterator to the first instance of the internal array.
|
||||
*/
|
||||
[[nodiscard]] const_iterator cbegin() const noexcept {
|
||||
const auto pos = static_cast<typename iterator::difference_type>(base_type::size());
|
||||
return const_iterator{&packed.first(), pos};
|
||||
return const_iterator{&payload, pos};
|
||||
}
|
||||
|
||||
/*! @copydoc cbegin */
|
||||
@@ -542,21 +552,16 @@ public:
|
||||
/*! @copydoc begin */
|
||||
[[nodiscard]] iterator begin() noexcept {
|
||||
const auto pos = static_cast<typename iterator::difference_type>(base_type::size());
|
||||
return iterator{&packed.first(), pos};
|
||||
return iterator{&payload, pos};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the end.
|
||||
*
|
||||
* The returned iterator points to the element following the last instance
|
||||
* of the internal array. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @return An iterator to the element following the last instance of the
|
||||
* internal array.
|
||||
*/
|
||||
[[nodiscard]] const_iterator cend() const noexcept {
|
||||
return const_iterator{&packed.first(), {}};
|
||||
return const_iterator{&payload, {}};
|
||||
}
|
||||
|
||||
/*! @copydoc cend */
|
||||
@@ -566,15 +571,13 @@ public:
|
||||
|
||||
/*! @copydoc end */
|
||||
[[nodiscard]] iterator end() noexcept {
|
||||
return iterator{&packed.first(), {}};
|
||||
return iterator{&payload, {}};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reverse iterator to the beginning.
|
||||
*
|
||||
* The returned iterator points to the first instance of the reversed
|
||||
* internal array. If the storage is empty, the returned iterator will be
|
||||
* equal to `rend()`.
|
||||
* If the storage is empty, the returned iterator will be equal to `rend()`.
|
||||
*
|
||||
* @return An iterator to the first instance of the reversed internal array.
|
||||
*/
|
||||
@@ -594,11 +597,6 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Returns a reverse iterator to the end.
|
||||
*
|
||||
* The returned iterator points to the element following the last instance
|
||||
* of the reversed internal array. Attempting to dereference the returned
|
||||
* iterator results in undefined behavior.
|
||||
*
|
||||
* @return An iterator to the element following the last instance of the
|
||||
* reversed internal array.
|
||||
*/
|
||||
@@ -663,7 +661,7 @@ public:
|
||||
*/
|
||||
template<typename... Args>
|
||||
value_type &emplace(const entity_type entt, Args &&...args) {
|
||||
if constexpr(std::is_aggregate_v<value_type>) {
|
||||
if constexpr(std::is_aggregate_v<value_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<value_type>)) {
|
||||
const auto it = emplace_element(entt, false, Type{std::forward<Args>(args)...});
|
||||
return element_at(static_cast<size_type>(it.index()));
|
||||
} else {
|
||||
@@ -699,12 +697,15 @@ 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.
|
||||
*/
|
||||
template<typename It>
|
||||
void insert(It first, It last, const value_type &value = {}) {
|
||||
iterator insert(It first, It last, const value_type &value = {}) {
|
||||
for(; first != last; ++first) {
|
||||
emplace_element(*first, true, value);
|
||||
}
|
||||
|
||||
return begin();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -718,12 +719,15 @@ 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 from An iterator to the first element of the range of objects.
|
||||
* @return Iterator pointing to the first element inserted, if any.
|
||||
*/
|
||||
template<typename EIt, typename CIt, typename = std::enable_if_t<std::is_same_v<typename std::iterator_traits<CIt>::value_type, value_type>>>
|
||||
void insert(EIt first, EIt last, CIt from) {
|
||||
iterator insert(EIt first, EIt last, CIt from) {
|
||||
for(; first != last; ++first, ++from) {
|
||||
emplace_element(*first, true, *from);
|
||||
}
|
||||
|
||||
return begin();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -743,34 +747,54 @@ public:
|
||||
return {internal::extended_storage_iterator{base_type::cbegin(), cbegin()}, internal::extended_storage_iterator{base_type::cend(), cend()}};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reverse iterable object to use to _visit_ a storage.
|
||||
*
|
||||
* @sa each
|
||||
*
|
||||
* @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()}};
|
||||
}
|
||||
|
||||
/*! @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()}};
|
||||
}
|
||||
|
||||
private:
|
||||
compressed_pair<container_type, allocator_type> packed;
|
||||
container_type payload;
|
||||
};
|
||||
|
||||
/*! @copydoc basic_storage */
|
||||
template<typename Type, typename Entity, typename Allocator>
|
||||
class basic_storage<Type, Entity, Allocator, std::enable_if_t<ignore_as_empty_v<Type>>>
|
||||
class basic_storage<Type, Entity, Allocator, std::enable_if_t<component_traits<Type>::page_size == 0u>>
|
||||
: public basic_sparse_set<Entity, typename std::allocator_traits<Allocator>::template rebind_alloc<Entity>> {
|
||||
using alloc_traits = std::allocator_traits<Allocator>;
|
||||
static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
|
||||
using underlying_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
|
||||
using comp_traits = component_traits<Type>;
|
||||
|
||||
public:
|
||||
/*! @brief Base type. */
|
||||
using base_type = underlying_type;
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
using base_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
|
||||
/*! @brief Type of the objects assigned to entities. */
|
||||
using value_type = Type;
|
||||
/*! @brief Component traits. */
|
||||
using traits_type = component_traits<value_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 Extended iterable storage proxy. */
|
||||
using iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::iterator>>;
|
||||
/*! @brief Constant extended iterable storage proxy. */
|
||||
using const_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_iterator>>;
|
||||
/*! @brief Extended reverse iterable storage proxy. */
|
||||
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 Default constructor. */
|
||||
basic_storage()
|
||||
@@ -781,7 +805,7 @@ public:
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit basic_storage(const allocator_type &allocator)
|
||||
: base_type{type_id<value_type>(), deletion_policy{comp_traits::in_place_delete}, allocator} {}
|
||||
: base_type{type_id<value_type>(), deletion_policy{traits_type::in_place_delete}, allocator} {}
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
@@ -809,7 +833,12 @@ public:
|
||||
* @return The associated allocator.
|
||||
*/
|
||||
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
|
||||
return allocator_type{base_type::get_allocator()};
|
||||
// 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>) {
|
||||
return allocator_type{};
|
||||
} else {
|
||||
return allocator_type{base_type::get_allocator()};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -896,6 +925,264 @@ public:
|
||||
[[nodiscard]] const_iterable each() const noexcept {
|
||||
return {internal::extended_storage_iterator{base_type::cbegin()}, internal::extended_storage_iterator{base_type::cend()}};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reverse iterable object to use to _visit_ a storage.
|
||||
*
|
||||
* @sa each
|
||||
*
|
||||
* @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()}};
|
||||
}
|
||||
|
||||
/*! @copydoc reach */
|
||||
[[nodiscard]] const_reverse_iterable reach() const noexcept {
|
||||
return {internal::extended_storage_iterator{base_type::crbegin()}, internal::extended_storage_iterator{base_type::crend()}};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Swap-only entity storage specialization.
|
||||
* @tparam Entity A valid entity type.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Entity, typename Allocator>
|
||||
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;
|
||||
|
||||
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), {});
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Assigns an entity to a storage.
|
||||
* @param hint A valid identifier.
|
||||
* @return Iterator pointing to the emplaced element.
|
||||
*/
|
||||
underlying_iterator try_emplace(const Entity hint, const bool, const void *) override {
|
||||
return base_type::find(emplace(hint));
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Base type. */
|
||||
using base_type = basic_sparse_set<Entity, Allocator>;
|
||||
/*! @brief Type of the objects assigned to entities. */
|
||||
using value_type = Entity;
|
||||
/*! @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. */
|
||||
using const_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_iterator>>;
|
||||
/*! @brief Extended reverse iterable storage proxy. */
|
||||
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 Default constructor. */
|
||||
basic_storage()
|
||||
: basic_storage{allocator_type{}} {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructs an empty container with a given allocator.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit basic_storage(const allocator_type &allocator)
|
||||
: base_type{type_id<void>(), deletion_policy::swap_only, allocator} {}
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
basic_storage(basic_storage &&other) noexcept
|
||||
: base_type{std::move(other)} {}
|
||||
|
||||
/**
|
||||
* @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} {}
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
* @param other The instance to move from.
|
||||
* @return This storage.
|
||||
*/
|
||||
basic_storage &operator=(basic_storage &&other) noexcept {
|
||||
base_type::operator=(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the object assigned to an entity, that is `void`.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to use an entity that doesn't belong to the storage results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @param entt A valid identifier.
|
||||
*/
|
||||
void get([[maybe_unused]] const entity_type entt) const noexcept {
|
||||
ENTT_ASSERT(base_type::index(entt) < base_type::free_list(), "The requested entity is not a live one");
|
||||
}
|
||||
|
||||
/**
|
||||
* @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::index(entt) < base_type::free_list(), "The requested entity is not a live one");
|
||||
return std::tuple{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a new identifier or recycles a destroyed one.
|
||||
* @return A valid identifier.
|
||||
*/
|
||||
entity_type emplace() {
|
||||
const auto len = base_type::free_list();
|
||||
const auto entt = (len == base_type::size()) ? entity_at(len) : base_type::data()[len];
|
||||
return *base_type::try_emplace(entt, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a new identifier or recycles a destroyed one.
|
||||
*
|
||||
* If the requested identifier isn't in use, the suggested one is used.
|
||||
* Otherwise, a new identifier is returned.
|
||||
*
|
||||
* @param hint Required identifier.
|
||||
* @return A valid identifier.
|
||||
*/
|
||||
entity_type emplace(const entity_type hint) {
|
||||
if(hint == null || hint == tombstone) {
|
||||
return emplace();
|
||||
} else if(const auto curr = underlying_type::traits_type::construct(underlying_type::traits_type::to_entity(hint), base_type::current(hint)); curr == tombstone) {
|
||||
const auto pos = static_cast<size_type>(underlying_type::traits_type::to_entity(hint));
|
||||
const auto entt = *base_type::try_emplace(hint, true);
|
||||
|
||||
while(!(pos < base_type::size())) {
|
||||
base_type::try_emplace(entity_at(base_type::size() - 1u), false);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates a given identifier.
|
||||
* @tparam Func Types of the function objects to invoke.
|
||||
* @param entt A valid identifier.
|
||||
* @param func Valid function objects.
|
||||
*/
|
||||
template<typename... Func>
|
||||
void patch([[maybe_unused]] const entity_type entt, Func &&...func) {
|
||||
ENTT_ASSERT(base_type::index(entt) < base_type::free_list(), "The requested entity is not a live one");
|
||||
(std::forward<Func>(func)(), ...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns each element in a range an identifier.
|
||||
* @tparam It Type of mutable forward iterator.
|
||||
* @param first An iterator to the first element of the range to generate.
|
||||
* @param last An iterator past the last element of the range to generate.
|
||||
*/
|
||||
template<typename It>
|
||||
void insert(It first, It last) {
|
||||
for(const auto sz = base_type::size(); first != last && base_type::free_list() != sz; ++first) {
|
||||
*first = *base_type::try_emplace(base_type::data()[base_type::free_list()], true);
|
||||
}
|
||||
|
||||
for(; first != last; ++first) {
|
||||
*first = *base_type::try_emplace(entity_at(base_type::free_list()), true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Makes all elements in a range contiguous.
|
||||
* @tparam It Type of forward iterator.
|
||||
* @param first An iterator to the first element of the range to pack.
|
||||
* @param last An iterator past the last element of the range to pack.
|
||||
* @return The number of elements within the newly created range.
|
||||
*/
|
||||
template<typename It>
|
||||
[[deprecated("use sort_as instead")]] size_type pack(It first, It last) {
|
||||
base_type::sort_as(first, last);
|
||||
return static_cast<size_type>(std::distance(first, last));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements considered still in use.
|
||||
* @return The number of elements considered still in use.
|
||||
*/
|
||||
[[deprecated("use free_list() instead")]] [[nodiscard]] size_type in_use() const noexcept {
|
||||
return base_type::free_list();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the number of elements considered still in use.
|
||||
* @param len The number of elements considered still in use.
|
||||
*/
|
||||
[[deprecated("use free_list(len) instead")]] void in_use(const size_type len) noexcept {
|
||||
base_type::free_list(len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterable object to use to _visit_ a storage.
|
||||
*
|
||||
* The iterable object returns a tuple that contains the current entity.
|
||||
*
|
||||
* @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)}};
|
||||
}
|
||||
|
||||
/*! @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)}};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reverse iterable object to use to _visit_ a storage.
|
||||
*
|
||||
* @sa each
|
||||
*
|
||||
* @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)}};
|
||||
}
|
||||
|
||||
/*! @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)}};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -1,236 +0,0 @@
|
||||
#ifndef ENTT_ENTITY_SIGH_STORAGE_MIXIN_HPP
|
||||
#define ENTT_ENTITY_SIGH_STORAGE_MIXIN_HPP
|
||||
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/any.hpp"
|
||||
#include "../signal/sigh.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @brief Mixin type used to add signal support to storage types.
|
||||
*
|
||||
* The function type of a listener is equivalent to:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(basic_registry<entity_type> &, entity_type);
|
||||
* @endcode
|
||||
*
|
||||
* This applies to all signals made available.
|
||||
*
|
||||
* @tparam Type The type of the underlying storage.
|
||||
*/
|
||||
template<typename Type>
|
||||
class sigh_storage_mixin final: public Type {
|
||||
using basic_registry_type = basic_registry<typename Type::entity_type, typename Type::base_type::allocator_type>;
|
||||
using sigh_type = sigh<void(basic_registry_type &, const typename Type::entity_type), typename Type::allocator_type>;
|
||||
using basic_iterator = typename Type::basic_iterator;
|
||||
|
||||
void pop(basic_iterator first, basic_iterator last) override {
|
||||
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
|
||||
|
||||
for(; first != last; ++first) {
|
||||
const auto entt = *first;
|
||||
destruction.publish(*owner, entt);
|
||||
const auto it = Type::find(entt);
|
||||
Type::pop(it, it + 1u);
|
||||
}
|
||||
}
|
||||
|
||||
basic_iterator try_emplace(const typename basic_registry_type::entity_type entt, const bool force_back, const void *value) final {
|
||||
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
|
||||
Type::try_emplace(entt, force_back, value);
|
||||
construction.publish(*owner, entt);
|
||||
return Type::find(entt);
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = typename Type::allocator_type;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename Type::entity_type;
|
||||
/*! @brief Expected registry type. */
|
||||
using registry_type = basic_registry_type;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
sigh_storage_mixin()
|
||||
: sigh_storage_mixin{allocator_type{}} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an empty storage with a given allocator.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit sigh_storage_mixin(const allocator_type &allocator)
|
||||
: Type{allocator},
|
||||
owner{},
|
||||
construction{allocator},
|
||||
destruction{allocator},
|
||||
update{allocator} {}
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
sigh_storage_mixin(sigh_storage_mixin &&other) noexcept
|
||||
: Type{std::move(other)},
|
||||
owner{other.owner},
|
||||
construction{std::move(other.construction)},
|
||||
destruction{std::move(other.destruction)},
|
||||
update{std::move(other.update)} {}
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
sigh_storage_mixin(sigh_storage_mixin &&other, const allocator_type &allocator) noexcept
|
||||
: Type{std::move(other), allocator},
|
||||
owner{other.owner},
|
||||
construction{std::move(other.construction), allocator},
|
||||
destruction{std::move(other.destruction), allocator},
|
||||
update{std::move(other.update), allocator} {}
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
* @param other The instance to move from.
|
||||
* @return This storage.
|
||||
*/
|
||||
sigh_storage_mixin &operator=(sigh_storage_mixin &&other) noexcept {
|
||||
Type::operator=(std::move(other));
|
||||
owner = other.owner;
|
||||
construction = std::move(other.construction);
|
||||
destruction = std::move(other.destruction);
|
||||
update = std::move(other.update);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exchanges the contents with those of a given storage.
|
||||
* @param other Storage to exchange the content with.
|
||||
*/
|
||||
void swap(sigh_storage_mixin &other) {
|
||||
using std::swap;
|
||||
Type::swap(other);
|
||||
swap(owner, other.owner);
|
||||
swap(construction, other.construction);
|
||||
swap(destruction, other.destruction);
|
||||
swap(update, other.update);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a sink object.
|
||||
*
|
||||
* The sink returned by this function can be used to receive notifications
|
||||
* whenever a new instance is created and assigned to an entity.<br/>
|
||||
* Listeners are invoked after the object has been assigned to the entity.
|
||||
*
|
||||
* @sa sink
|
||||
*
|
||||
* @return A temporary sink object.
|
||||
*/
|
||||
[[nodiscard]] auto on_construct() noexcept {
|
||||
return sink{construction};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a sink object.
|
||||
*
|
||||
* The sink returned by this function can be used to receive notifications
|
||||
* whenever an instance is explicitly updated.<br/>
|
||||
* Listeners are invoked after the object has been updated.
|
||||
*
|
||||
* @sa sink
|
||||
*
|
||||
* @return A temporary sink object.
|
||||
*/
|
||||
[[nodiscard]] auto on_update() noexcept {
|
||||
return sink{update};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a sink object.
|
||||
*
|
||||
* The sink returned by this function can be used to receive notifications
|
||||
* whenever an instance is removed from an entity and thus destroyed.<br/>
|
||||
* Listeners are invoked before the object has been removed from the entity.
|
||||
*
|
||||
* @sa sink
|
||||
*
|
||||
* @return A temporary sink object.
|
||||
*/
|
||||
[[nodiscard]] auto on_destroy() noexcept {
|
||||
return sink{destruction};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns entities to a storage.
|
||||
* @tparam Args Types of arguments to use to construct the object.
|
||||
* @param entt A valid identifier.
|
||||
* @param args Parameters to use to initialize the object.
|
||||
* @return A reference to the newly created object.
|
||||
*/
|
||||
template<typename... Args>
|
||||
decltype(auto) emplace(const entity_type entt, Args &&...args) {
|
||||
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
|
||||
Type::emplace(entt, std::forward<Args>(args)...);
|
||||
construction.publish(*owner, entt);
|
||||
return this->get(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Patches the given instance for an entity.
|
||||
* @tparam Func Types of the function objects to invoke.
|
||||
* @param entt A valid identifier.
|
||||
* @param func Valid function objects.
|
||||
* @return A reference to the patched instance.
|
||||
*/
|
||||
template<typename... Func>
|
||||
decltype(auto) patch(const entity_type entt, Func &&...func) {
|
||||
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
|
||||
Type::patch(entt, std::forward<Func>(func)...);
|
||||
update.publish(*owner, entt);
|
||||
return this->get(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns entities to a storage.
|
||||
* @tparam It Type of input iterator.
|
||||
* @tparam Args Types of arguments to use to construct the objects assigned
|
||||
* to the entities.
|
||||
* @param first An iterator to the first element of the range of entities.
|
||||
* @param last An iterator past the last element of the range of entities.
|
||||
* @param args Parameters to use to initialize the objects assigned to the
|
||||
* entities.
|
||||
*/
|
||||
template<typename It, typename... Args>
|
||||
void insert(It first, It last, Args &&...args) {
|
||||
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
|
||||
Type::insert(first, last, std::forward<Args>(args)...);
|
||||
|
||||
for(auto it = construction.empty() ? last : first; it != last; ++it) {
|
||||
construction.publish(*owner, *it);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Forwards variables to derived classes, if any.
|
||||
* @param value A variable wrapped in an opaque container.
|
||||
*/
|
||||
void bind(any value) noexcept final {
|
||||
auto *reg = any_cast<basic_registry_type>(&value);
|
||||
owner = reg ? reg : owner;
|
||||
Type::bind(std::move(value));
|
||||
}
|
||||
|
||||
private:
|
||||
basic_registry_type *owner;
|
||||
sigh_type construction;
|
||||
sigh_type destruction;
|
||||
sigh_type update;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -24,6 +24,7 @@
|
||||
#include "entity/group.hpp"
|
||||
#include "entity/handle.hpp"
|
||||
#include "entity/helper.hpp"
|
||||
#include "entity/mixin.hpp"
|
||||
#include "entity/observer.hpp"
|
||||
#include "entity/organizer.hpp"
|
||||
#include "entity/registry.hpp"
|
||||
@@ -31,7 +32,6 @@
|
||||
#include "entity/snapshot.hpp"
|
||||
#include "entity/sparse_set.hpp"
|
||||
#include "entity/storage.hpp"
|
||||
#include "entity/storage_mixin.hpp"
|
||||
#include "entity/view.hpp"
|
||||
#include "graph/adjacency_matrix.hpp"
|
||||
#include "graph/dot.hpp"
|
||||
|
||||
@@ -13,11 +13,7 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename It>
|
||||
@@ -30,6 +26,7 @@ public:
|
||||
using reference = value_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using iterator_concept = std::forward_iterator_tag;
|
||||
|
||||
constexpr edge_iterator() noexcept
|
||||
: it{},
|
||||
@@ -87,11 +84,7 @@ template<typename Container>
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Basic implementation of a directed adjacency matrix.
|
||||
@@ -258,7 +251,7 @@ public:
|
||||
[[nodiscard]] iterable_adaptor<out_edge_iterator> out_edges(const vertex_type vertex) const noexcept {
|
||||
const auto it = matrix.cbegin();
|
||||
const auto from = vertex * vert;
|
||||
const auto to = vertex * vert + vert;
|
||||
const auto to = from + vert;
|
||||
return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}};
|
||||
}
|
||||
|
||||
@@ -270,7 +263,7 @@ public:
|
||||
[[nodiscard]] iterable_adaptor<in_edge_iterator> in_edges(const vertex_type vertex) const noexcept {
|
||||
const auto it = matrix.cbegin();
|
||||
const auto from = vertex;
|
||||
const auto to = vert * (vert - 1u) + vertex;
|
||||
const auto to = vert * vert + from;
|
||||
return {{it, vert, from, to, vert}, {it, vert, to, to, vert}};
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ class basic_flow {
|
||||
using task_container_type = dense_set<id_type, identity, std::equal_to<id_type>, typename alloc_traits::template rebind_alloc<id_type>>;
|
||||
using ro_rw_container_type = std::vector<std::pair<std::size_t, bool>, typename alloc_traits::template rebind_alloc<std::pair<std::size_t, bool>>>;
|
||||
using deps_container_type = dense_map<id_type, ro_rw_container_type, identity, std::equal_to<id_type>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, ro_rw_container_type>>>;
|
||||
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) {
|
||||
ENTT_ASSERT(index.first() < vertices.size(), "Invalid node");
|
||||
@@ -43,6 +44,76 @@ class basic_flow {
|
||||
deps[res].emplace_back(index.first(), is_rw);
|
||||
}
|
||||
|
||||
void setup_graph(adjacency_matrix_type &matrix) const {
|
||||
for(const auto &elem: deps) {
|
||||
const auto last = elem.second.cend();
|
||||
auto it = elem.second.cbegin();
|
||||
|
||||
while(it != last) {
|
||||
if(it->second) {
|
||||
// rw item
|
||||
if(auto curr = it++; it != last) {
|
||||
if(it->second) {
|
||||
matrix.insert(curr->first, it->first);
|
||||
} else if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) {
|
||||
for(; it != next; ++it) {
|
||||
matrix.insert(curr->first, it->first);
|
||||
matrix.insert(it->first, next->first);
|
||||
}
|
||||
} else {
|
||||
for(; it != next; ++it) {
|
||||
matrix.insert(curr->first, it->first);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// ro item (first iteration only)
|
||||
if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) {
|
||||
for(; it != next; ++it) {
|
||||
matrix.insert(it->first, next->first);
|
||||
}
|
||||
} else {
|
||||
it = last;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void transitive_closure(adjacency_matrix_type &matrix) const {
|
||||
const auto length = matrix.size();
|
||||
|
||||
for(std::size_t vk{}; vk < length; ++vk) {
|
||||
for(std::size_t vi{}; vi < length; ++vi) {
|
||||
for(std::size_t vj{}; vj < length; ++vj) {
|
||||
if(matrix.contains(vi, vk) && matrix.contains(vk, vj)) {
|
||||
matrix.insert(vi, vj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void transitive_reduction(adjacency_matrix_type &matrix) const {
|
||||
const auto length = matrix.size();
|
||||
|
||||
for(std::size_t vert{}; vert < length; ++vert) {
|
||||
matrix.erase(vert, vert);
|
||||
}
|
||||
|
||||
for(std::size_t vj{}; vj < length; ++vj) {
|
||||
for(std::size_t vi{}; vi < length; ++vi) {
|
||||
if(matrix.contains(vi, vj)) {
|
||||
for(std::size_t vk{}; vk < length; ++vk) {
|
||||
if(matrix.contains(vj, vk)) {
|
||||
matrix.erase(vi, vk);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
@@ -50,6 +121,8 @@ public:
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Iterable task list. */
|
||||
using iterable = iterable_adaptor<typename task_container_type::const_iterator>;
|
||||
/*! @brief Adjacency matrix type. */
|
||||
using graph_type = adjacency_matrix_type;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
basic_flow()
|
||||
@@ -61,8 +134,8 @@ public:
|
||||
*/
|
||||
explicit basic_flow(const allocator_type &allocator)
|
||||
: index{0u, allocator},
|
||||
vertices{},
|
||||
deps{},
|
||||
vertices{allocator},
|
||||
deps{allocator},
|
||||
sync_on{} {}
|
||||
|
||||
/*! @brief Default copy constructor. */
|
||||
@@ -124,9 +197,10 @@ public:
|
||||
|
||||
/*! @brief Clears the flow builder. */
|
||||
void clear() noexcept {
|
||||
index.first() = sync_on = {};
|
||||
index.first() = {};
|
||||
vertices.clear();
|
||||
deps.clear();
|
||||
sync_on = {};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -245,72 +319,12 @@ public:
|
||||
* @brief Generates a task graph for the current content.
|
||||
* @return The adjacency matrix of the task graph.
|
||||
*/
|
||||
[[nodiscard]] adjacency_matrix<directed_tag> graph() const {
|
||||
const auto length = vertices.size();
|
||||
adjacency_matrix<directed_tag> matrix{length};
|
||||
[[nodiscard]] graph_type graph() const {
|
||||
graph_type matrix{vertices.size(), get_allocator()};
|
||||
|
||||
// creates the adjacency matrix
|
||||
for(const auto &elem: deps) {
|
||||
const auto last = elem.second.cend();
|
||||
auto it = elem.second.cbegin();
|
||||
|
||||
while(it != last) {
|
||||
if(it->second) {
|
||||
// rw item
|
||||
if(auto curr = it++; it != last) {
|
||||
if(it->second) {
|
||||
matrix.insert(curr->first, it->first);
|
||||
} else if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) {
|
||||
for(; it != next; ++it) {
|
||||
matrix.insert(curr->first, it->first);
|
||||
matrix.insert(it->first, next->first);
|
||||
}
|
||||
} else {
|
||||
for(; it != next; ++it) {
|
||||
matrix.insert(curr->first, it->first);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// ro item (first iteration only)
|
||||
if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) {
|
||||
for(; it != next; ++it) {
|
||||
matrix.insert(it->first, next->first);
|
||||
}
|
||||
} else {
|
||||
it = last;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// computes the transitive closure
|
||||
for(std::size_t vk{}; vk < length; ++vk) {
|
||||
for(std::size_t vi{}; vi < length; ++vi) {
|
||||
for(std::size_t vj{}; vj < length; ++vj) {
|
||||
if(matrix.contains(vi, vk) && matrix.contains(vk, vj)) {
|
||||
matrix.insert(vi, vj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// applies the transitive reduction
|
||||
for(std::size_t vert{}; vert < length; ++vert) {
|
||||
matrix.erase(vert, vert);
|
||||
}
|
||||
|
||||
for(std::size_t vj{}; vj < length; ++vj) {
|
||||
for(std::size_t vi{}; vi < length; ++vi) {
|
||||
if(matrix.contains(vi, vj)) {
|
||||
for(std::size_t vk{}; vk < length; ++vk) {
|
||||
if(matrix.contains(vj, vk)) {
|
||||
matrix.erase(vi, vk);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
setup_graph(matrix);
|
||||
transitive_closure(matrix);
|
||||
transitive_reduction(matrix);
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
@@ -70,40 +70,40 @@ public:
|
||||
* cases, they are discarded.
|
||||
*
|
||||
* @tparam Args Types of arguments to use to construct the fallback service.
|
||||
* @tparam Impl Fallback service type.
|
||||
* @tparam Type Fallback service type.
|
||||
* @param args Parameters to use to construct the fallback service.
|
||||
* @return A reference to a valid service.
|
||||
*/
|
||||
template<typename Impl = Service, typename... Args>
|
||||
template<typename Type = Service, typename... Args>
|
||||
[[nodiscard]] static Service &value_or(Args &&...args) {
|
||||
return service ? *service : emplace<Impl>(std::forward<Args>(args)...);
|
||||
return service ? *service : emplace<Type>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets or replaces a service.
|
||||
* @tparam Impl Service type.
|
||||
* @tparam Type Service type.
|
||||
* @tparam Args Types of arguments to use to construct the service.
|
||||
* @param args Parameters to use to construct the service.
|
||||
* @return A reference to a valid service.
|
||||
*/
|
||||
template<typename Impl = Service, typename... Args>
|
||||
template<typename Type = Service, typename... Args>
|
||||
static Service &emplace(Args &&...args) {
|
||||
service = std::make_shared<Impl>(std::forward<Args>(args)...);
|
||||
service = std::make_shared<Type>(std::forward<Args>(args)...);
|
||||
return *service;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets or replaces a service using a given allocator.
|
||||
* @tparam Impl Service type.
|
||||
* @tparam Type Service type.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
* @tparam Args Types of arguments to use to construct the service.
|
||||
* @param alloc The allocator to use.
|
||||
* @param args Parameters to use to construct the service.
|
||||
* @return A reference to a valid service.
|
||||
*/
|
||||
template<typename Impl = Service, typename Allocator, typename... Args>
|
||||
static Service &allocate_emplace(Allocator alloc, Args &&...args) {
|
||||
service = std::allocate_shared<Impl>(alloc, std::forward<Args>(args)...);
|
||||
template<typename Type = Service, typename Allocator, typename... Args>
|
||||
static Service &emplace(std::allocator_arg_t, Allocator alloc, Args &&...args) {
|
||||
service = std::allocate_shared<Type>(alloc, std::forward<Args>(args)...);
|
||||
return *service;
|
||||
}
|
||||
|
||||
@@ -125,6 +125,18 @@ public:
|
||||
service = other.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resets or replaces a service.
|
||||
* @tparam Type Service type.
|
||||
* @tparam Deleter Deleter type.
|
||||
* @param elem A pointer to a service to manage.
|
||||
* @param deleter A deleter to use to destroy the service.
|
||||
*/
|
||||
template<typename Type, typename Deleter = std::default_delete<Type>>
|
||||
static void reset(Type *elem, Deleter deleter = {}) {
|
||||
service = std::shared_ptr<Service>{elem, std::move(deleter)};
|
||||
}
|
||||
|
||||
private:
|
||||
// std::shared_ptr because of its type erased allocator which is useful here
|
||||
inline static std::shared_ptr<Service> service{};
|
||||
|
||||
@@ -19,145 +19,284 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename, typename = void>
|
||||
struct is_dynamic_sequence_container: std::false_type {};
|
||||
struct fixed_size_sequence_container: std::true_type {};
|
||||
|
||||
template<typename Type>
|
||||
struct is_dynamic_sequence_container<Type, std::void_t<decltype(&Type::clear)>>: std::true_type {};
|
||||
struct fixed_size_sequence_container<Type, std::void_t<decltype(&Type::clear)>>: std::false_type {};
|
||||
|
||||
template<typename Type>
|
||||
inline constexpr bool fixed_size_sequence_container_v = fixed_size_sequence_container<Type>::value;
|
||||
|
||||
template<typename, typename = void>
|
||||
struct is_key_only_meta_associative_container: std::true_type {};
|
||||
struct key_only_associative_container: std::true_type {};
|
||||
|
||||
template<typename Type>
|
||||
struct is_key_only_meta_associative_container<Type, std::void_t<typename Type::mapped_type>>: std::false_type {};
|
||||
struct key_only_associative_container<Type, std::void_t<typename Type::mapped_type>>: std::false_type {};
|
||||
|
||||
template<typename Type>
|
||||
struct basic_meta_sequence_container_traits {
|
||||
using iterator = meta_sequence_container::iterator;
|
||||
using size_type = std::size_t;
|
||||
inline constexpr bool key_only_associative_container_v = key_only_associative_container<Type>::value;
|
||||
|
||||
[[nodiscard]] static size_type size(const any &container) noexcept {
|
||||
return any_cast<const Type &>(container).size();
|
||||
}
|
||||
|
||||
[[nodiscard]] static bool resize([[maybe_unused]] any &container, [[maybe_unused]] size_type sz) {
|
||||
if constexpr(is_dynamic_sequence_container<Type>::value) {
|
||||
if(auto *const cont = any_cast<Type>(&container); cont) {
|
||||
cont->resize(sz);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] static iterator iter(const meta_ctx &ctx, any &container, const bool as_end) {
|
||||
if(auto *const cont = any_cast<Type>(&container); cont) {
|
||||
return iterator{ctx, as_end ? cont->end() : cont->begin()};
|
||||
}
|
||||
|
||||
const Type &as_const = any_cast<const Type &>(container);
|
||||
return iterator{ctx, as_end ? as_const.end() : as_const.begin()};
|
||||
}
|
||||
|
||||
[[nodiscard]] static iterator insert_or_erase([[maybe_unused]] const meta_ctx &ctx, [[maybe_unused]] any &container, [[maybe_unused]] const any &handle, [[maybe_unused]] meta_any &value) {
|
||||
if constexpr(is_dynamic_sequence_container<Type>::value) {
|
||||
if(auto *const cont = any_cast<Type>(&container); cont) {
|
||||
typename Type::const_iterator it{};
|
||||
|
||||
if(auto *non_const = any_cast<typename Type::iterator>(&handle); non_const) {
|
||||
it = *non_const;
|
||||
} else {
|
||||
it = any_cast<const typename Type::const_iterator &>(handle);
|
||||
}
|
||||
|
||||
if(value) {
|
||||
// this abomination is necessary because only on macos value_type and const_reference are different types for std::vector<bool>
|
||||
if(value.allow_cast<typename Type::const_reference>() || value.allow_cast<typename Type::value_type>()) {
|
||||
const auto *element = value.try_cast<std::remove_reference_t<typename Type::const_reference>>();
|
||||
return iterator{ctx, cont->insert(it, element ? *element : value.cast<typename Type::value_type>())};
|
||||
}
|
||||
} else {
|
||||
return iterator{ctx, cont->erase(it)};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return iterator{};
|
||||
}
|
||||
};
|
||||
template<typename, typename = void>
|
||||
struct reserve_aware_container: std::false_type {};
|
||||
|
||||
template<typename Type>
|
||||
struct basic_meta_associative_container_traits {
|
||||
using iterator = meta_associative_container::iterator;
|
||||
using size_type = std::size_t;
|
||||
struct reserve_aware_container<Type, std::void_t<decltype(&Type::reserve)>>: std::true_type {};
|
||||
|
||||
static constexpr auto key_only = is_key_only_meta_associative_container<Type>::value;
|
||||
|
||||
[[nodiscard]] static size_type size(const any &container) noexcept {
|
||||
return any_cast<const Type &>(container).size();
|
||||
}
|
||||
|
||||
[[nodiscard]] static bool clear(any &container) {
|
||||
if(auto *const cont = any_cast<Type>(&container); cont) {
|
||||
cont->clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] static iterator iter(const meta_ctx &ctx, any &container, const bool as_end) {
|
||||
if(auto *const cont = any_cast<Type>(&container); cont) {
|
||||
return iterator{ctx, std::bool_constant<key_only>{}, as_end ? cont->end() : cont->begin()};
|
||||
}
|
||||
|
||||
const auto &as_const = any_cast<const Type &>(container);
|
||||
return iterator{ctx, std::bool_constant<key_only>{}, as_end ? as_const.end() : as_const.begin()};
|
||||
}
|
||||
|
||||
[[nodiscard]] static size_type insert_or_erase(any &container, meta_any &key, meta_any &value) {
|
||||
if(auto *const cont = any_cast<Type>(&container); cont && key.allow_cast<const typename Type::key_type &>()) {
|
||||
if(value) {
|
||||
if constexpr(key_only) {
|
||||
return cont->insert(key.cast<const typename Type::key_type &>()).second;
|
||||
} else {
|
||||
return value.allow_cast<const typename Type::mapped_type &>() && cont->emplace(key.cast<const typename Type::key_type &>(), value.cast<const typename Type::mapped_type &>()).second;
|
||||
}
|
||||
} else {
|
||||
return cont->erase(key.cast<const typename Type::key_type &>());
|
||||
}
|
||||
}
|
||||
|
||||
return 0u;
|
||||
}
|
||||
|
||||
[[nodiscard]] static iterator find(const meta_ctx &ctx, any &container, meta_any &key) {
|
||||
if(key.allow_cast<const typename Type::key_type &>()) {
|
||||
if(auto *const cont = any_cast<Type>(&container); cont) {
|
||||
return iterator{ctx, std::bool_constant<key_only>{}, cont->find(key.cast<const typename Type::key_type &>())};
|
||||
}
|
||||
|
||||
return iterator{ctx, std::bool_constant<key_only>{}, any_cast<const Type &>(container).find(key.cast<const typename Type::key_type &>())};
|
||||
}
|
||||
|
||||
return iterator{};
|
||||
}
|
||||
};
|
||||
template<typename Type>
|
||||
inline constexpr bool reserve_aware_container_v = reserve_aware_container<Type>::value;
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
* @brief General purpose implementation of meta sequence container traits.
|
||||
* @tparam Type Type of underlying sequence container.
|
||||
*/
|
||||
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 Returns the number of elements in a container.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @return Number of elements.
|
||||
*/
|
||||
[[nodiscard]] static size_type size(const void *container) {
|
||||
return static_cast<const Type *>(container)->size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clears a container.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] static bool clear([[maybe_unused]] void *container) {
|
||||
if constexpr(fixed_size) {
|
||||
return false;
|
||||
} else {
|
||||
static_cast<Type *>(container)->clear();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Increases the capacity of a container.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @param sz Desired capacity.
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] static bool reserve([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) {
|
||||
if constexpr(internal::reserve_aware_container_v<Type>) {
|
||||
static_cast<Type *>(container)->reserve(sz);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resizes a container.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @param sz The new number of elements.
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] static bool resize([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) {
|
||||
if constexpr(fixed_size || !std::is_default_constructible_v<typename Type::value_type>) {
|
||||
return false;
|
||||
} else {
|
||||
static_cast<Type *>(container)->resize(sz);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a possibly const iterator to the beginning.
|
||||
* @param area The context to pass to the newly created iterator.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @param as_const Const opaque pointer fallback.
|
||||
* @return An iterator to the first element of the container.
|
||||
*/
|
||||
static iterator begin(const meta_ctx &area, void *container, const void *as_const) {
|
||||
return container ? iterator{area, static_cast<Type *>(container)->begin()}
|
||||
: iterator{area, static_cast<const Type *>(as_const)->begin()};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a possibly const iterator to the end.
|
||||
* @param area The context to pass to the newly created iterator.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @param as_const Const opaque pointer fallback.
|
||||
* @return An iterator that is past the last element of the container.
|
||||
*/
|
||||
static iterator end(const meta_ctx &area, void *container, const void *as_const) {
|
||||
return container ? iterator{area, static_cast<Type *>(container)->end()}
|
||||
: iterator{area, static_cast<const Type *>(as_const)->end()};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns one element to a container and constructs its object from
|
||||
* a given opaque instance.
|
||||
* @param area The context to pass to the newly created iterator.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @param value Optional opaque instance of the object to construct (as
|
||||
* value type).
|
||||
* @param cref Optional opaque instance of the object to construct (as
|
||||
* decayed const reference type).
|
||||
* @param it Iterator before which the element will be inserted.
|
||||
* @return A possibly invalid iterator to the inserted element.
|
||||
*/
|
||||
[[nodiscard]] static iterator insert(const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const void *value, [[maybe_unused]] const void *cref, [[maybe_unused]] const iterator &it) {
|
||||
if constexpr(fixed_size) {
|
||||
return iterator{area};
|
||||
} else {
|
||||
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))};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erases an element from a container.
|
||||
* @param area The context to pass to the newly created iterator.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @param it An opaque iterator to the element to erase.
|
||||
* @return A possibly invalid iterator following the last removed element.
|
||||
*/
|
||||
[[nodiscard]] static iterator erase(const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const iterator &it) {
|
||||
if constexpr(fixed_size) {
|
||||
return iterator{area};
|
||||
} else {
|
||||
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()))};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief General purpose implementation of meta associative container traits.
|
||||
* @tparam Type Type of underlying associative container.
|
||||
*/
|
||||
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 Returns the number of elements in a container.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @return Number of elements.
|
||||
*/
|
||||
[[nodiscard]] static size_type size(const void *container) {
|
||||
return static_cast<const Type *>(container)->size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clears a container.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] static bool clear(void *container) {
|
||||
static_cast<Type *>(container)->clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Increases the capacity of a container.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @param sz Desired capacity.
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] static bool reserve([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) {
|
||||
if constexpr(internal::reserve_aware_container_v<Type>) {
|
||||
static_cast<Type *>(container)->reserve(sz);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a possibly const iterator to the beginning.
|
||||
* @param area The context to pass to the newly created iterator.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @param as_const Const opaque pointer fallback.
|
||||
* @return An iterator to the first element of the container.
|
||||
*/
|
||||
static iterator begin(const meta_ctx &area, void *container, const void *as_const) {
|
||||
return container ? iterator{area, std::bool_constant<key_only>{}, static_cast<Type *>(container)->begin()}
|
||||
: iterator{area, std::bool_constant<key_only>{}, static_cast<const Type *>(as_const)->begin()};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a possibly const iterator to the end.
|
||||
* @param area The context to pass to the newly created iterator.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @param as_const Const opaque pointer fallback.
|
||||
* @return An iterator that is past the last element of the container.
|
||||
*/
|
||||
static iterator end(const meta_ctx &area, void *container, const void *as_const) {
|
||||
return container ? iterator{area, std::bool_constant<key_only>{}, static_cast<Type *>(container)->end()}
|
||||
: iterator{area, std::bool_constant<key_only>{}, static_cast<const Type *>(as_const)->end()};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inserts an element into a container, if the key does not exist.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @param key An opaque key value of an element to insert.
|
||||
* @param value Optional opaque value to insert (key-value containers).
|
||||
* @return True if the insertion took place, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] static bool insert(void *container, const void *key, [[maybe_unused]] const void *value) {
|
||||
if constexpr(key_only) {
|
||||
return static_cast<Type *>(container)->insert(*static_cast<const typename Type::key_type *>(key)).second;
|
||||
} else {
|
||||
return static_cast<Type *>(container)->emplace(*static_cast<const typename Type::key_type *>(key), *static_cast<const typename Type::mapped_type *>(value)).second;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes an element from a container.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @param key An opaque key value of an element to remove.
|
||||
* @return Number of elements removed (either 0 or 1).
|
||||
*/
|
||||
[[nodiscard]] static size_type erase(void *container, const void *key) {
|
||||
return static_cast<Type *>(container)->erase(*static_cast<const typename Type::key_type *>(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finds an element with a given key.
|
||||
* @param area The context to pass to the newly created iterator.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @param as_const Const opaque pointer fallback.
|
||||
* @param key Opaque key value of an element to search for.
|
||||
* @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))};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Meta sequence container traits for `std::vector`s of any type.
|
||||
@@ -165,7 +304,7 @@ struct basic_meta_associative_container_traits {
|
||||
*/
|
||||
template<typename... Args>
|
||||
struct meta_sequence_container_traits<std::vector<Args...>>
|
||||
: internal::basic_meta_sequence_container_traits<std::vector<Args...>> {};
|
||||
: basic_meta_sequence_container_traits<std::vector<Args...>> {};
|
||||
|
||||
/**
|
||||
* @brief Meta sequence container traits for `std::array`s of any type.
|
||||
@@ -174,7 +313,7 @@ struct meta_sequence_container_traits<std::vector<Args...>>
|
||||
*/
|
||||
template<typename Type, auto N>
|
||||
struct meta_sequence_container_traits<std::array<Type, N>>
|
||||
: internal::basic_meta_sequence_container_traits<std::array<Type, N>> {};
|
||||
: basic_meta_sequence_container_traits<std::array<Type, N>> {};
|
||||
|
||||
/**
|
||||
* @brief Meta sequence container traits for `std::list`s of any type.
|
||||
@@ -182,7 +321,7 @@ struct meta_sequence_container_traits<std::array<Type, N>>
|
||||
*/
|
||||
template<typename... Args>
|
||||
struct meta_sequence_container_traits<std::list<Args...>>
|
||||
: internal::basic_meta_sequence_container_traits<std::list<Args...>> {};
|
||||
: basic_meta_sequence_container_traits<std::list<Args...>> {};
|
||||
|
||||
/**
|
||||
* @brief Meta sequence container traits for `std::deque`s of any type.
|
||||
@@ -190,7 +329,7 @@ struct meta_sequence_container_traits<std::list<Args...>>
|
||||
*/
|
||||
template<typename... Args>
|
||||
struct meta_sequence_container_traits<std::deque<Args...>>
|
||||
: internal::basic_meta_sequence_container_traits<std::deque<Args...>> {};
|
||||
: basic_meta_sequence_container_traits<std::deque<Args...>> {};
|
||||
|
||||
/**
|
||||
* @brief Meta associative container traits for `std::map`s of any type.
|
||||
@@ -198,7 +337,7 @@ struct meta_sequence_container_traits<std::deque<Args...>>
|
||||
*/
|
||||
template<typename... Args>
|
||||
struct meta_associative_container_traits<std::map<Args...>>
|
||||
: internal::basic_meta_associative_container_traits<std::map<Args...>> {};
|
||||
: basic_meta_associative_container_traits<std::map<Args...>> {};
|
||||
|
||||
/**
|
||||
* @brief Meta associative container traits for `std::unordered_map`s of any
|
||||
@@ -207,7 +346,7 @@ struct meta_associative_container_traits<std::map<Args...>>
|
||||
*/
|
||||
template<typename... Args>
|
||||
struct meta_associative_container_traits<std::unordered_map<Args...>>
|
||||
: internal::basic_meta_associative_container_traits<std::unordered_map<Args...>> {};
|
||||
: basic_meta_associative_container_traits<std::unordered_map<Args...>> {};
|
||||
|
||||
/**
|
||||
* @brief Meta associative container traits for `std::set`s of any type.
|
||||
@@ -215,7 +354,7 @@ struct meta_associative_container_traits<std::unordered_map<Args...>>
|
||||
*/
|
||||
template<typename... Args>
|
||||
struct meta_associative_container_traits<std::set<Args...>>
|
||||
: internal::basic_meta_associative_container_traits<std::set<Args...>> {};
|
||||
: basic_meta_associative_container_traits<std::set<Args...>> {};
|
||||
|
||||
/**
|
||||
* @brief Meta associative container traits for `std::unordered_set`s of any
|
||||
@@ -224,7 +363,7 @@ struct meta_associative_container_traits<std::set<Args...>>
|
||||
*/
|
||||
template<typename... Args>
|
||||
struct meta_associative_container_traits<std::unordered_set<Args...>>
|
||||
: internal::basic_meta_associative_container_traits<std::unordered_set<Args...>> {};
|
||||
: basic_meta_associative_container_traits<std::unordered_set<Args...>> {};
|
||||
|
||||
/**
|
||||
* @brief Meta associative container traits for `dense_map`s of any type.
|
||||
@@ -232,7 +371,7 @@ struct meta_associative_container_traits<std::unordered_set<Args...>>
|
||||
*/
|
||||
template<typename... Args>
|
||||
struct meta_associative_container_traits<dense_map<Args...>>
|
||||
: internal::basic_meta_associative_container_traits<dense_map<Args...>> {};
|
||||
: basic_meta_associative_container_traits<dense_map<Args...>> {};
|
||||
|
||||
/**
|
||||
* @brief Meta associative container traits for `dense_set`s of any type.
|
||||
@@ -240,7 +379,7 @@ struct meta_associative_container_traits<dense_map<Args...>>
|
||||
*/
|
||||
template<typename... Args>
|
||||
struct meta_associative_container_traits<dense_set<Args...>>
|
||||
: internal::basic_meta_associative_container_traits<dense_set<Args...>> {};
|
||||
: basic_meta_associative_container_traits<dense_set<Args...>> {};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
|
||||
@@ -9,11 +9,7 @@ namespace entt {
|
||||
|
||||
class meta_ctx;
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
struct meta_type_node;
|
||||
@@ -21,16 +17,12 @@ struct meta_type_node;
|
||||
struct meta_context {
|
||||
dense_map<id_type, meta_type_node, identity> value{};
|
||||
|
||||
static inline meta_context &from(meta_ctx &ctx);
|
||||
static inline const meta_context &from(const meta_ctx &ctx);
|
||||
[[nodiscard]] inline static meta_context &from(meta_ctx &ctx);
|
||||
[[nodiscard]] inline static const meta_context &from(const meta_ctx &ctx);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
|
||||
/*! @brief Disambiguation tag for constructors and the like. */
|
||||
class meta_ctx_arg_t final {};
|
||||
@@ -40,27 +32,19 @@ inline constexpr meta_ctx_arg_t meta_ctx_arg{};
|
||||
|
||||
/*! @brief Opaque meta context type. */
|
||||
class meta_ctx: private internal::meta_context {
|
||||
/*! @brief Attorney idiom like model to access the base class. */
|
||||
// attorney idiom like model to access the base class
|
||||
friend struct internal::meta_context;
|
||||
};
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
inline internal::meta_context &internal::meta_context::from(meta_ctx &ctx) {
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
[[nodiscard]] inline internal::meta_context &internal::meta_context::from(meta_ctx &ctx) {
|
||||
return ctx;
|
||||
}
|
||||
|
||||
inline const internal::meta_context &internal::meta_context::from(const meta_ctx &ctx) {
|
||||
[[nodiscard]] inline const internal::meta_context &internal::meta_context::from(const meta_ctx &ctx) {
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
|
||||
} // namespace entt
|
||||
|
||||
|
||||
@@ -22,35 +22,15 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
inline decltype(auto) owner(meta_ctx &ctx, const type_info &info) {
|
||||
[[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()];
|
||||
}
|
||||
|
||||
inline meta_base_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_base_node node) {
|
||||
return parent.details->base.insert_or_assign(id, std::move(node)).first->second;
|
||||
}
|
||||
|
||||
inline meta_conv_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_conv_node node) {
|
||||
return parent.details->conv.insert_or_assign(id, std::move(node)).first->second;
|
||||
}
|
||||
|
||||
inline meta_ctor_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_ctor_node node) {
|
||||
return parent.details->ctor.insert_or_assign(id, std::move(node)).first->second;
|
||||
}
|
||||
|
||||
inline meta_dtor_node &meta_extend(internal::meta_type_node &parent, meta_dtor_node node) {
|
||||
return (parent.dtor = std::move(node));
|
||||
}
|
||||
|
||||
inline meta_data_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_data_node node) {
|
||||
return parent.details->data.insert_or_assign(id, std::move(node)).first->second;
|
||||
}
|
||||
@@ -72,16 +52,8 @@ inline meta_func_node &meta_extend(internal::meta_type_node &parent, const id_ty
|
||||
return parent.details->func.insert_or_assign(id, std::move(node)).first->second;
|
||||
}
|
||||
|
||||
inline meta_prop_node &meta_extend(dense_map<id_type, meta_prop_node, identity> &prop, const id_type id, meta_prop_node node) {
|
||||
return (prop[id] = std::move(node));
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Basic meta factory to be used for reflection purposes.
|
||||
@@ -135,7 +107,7 @@ public:
|
||||
/**
|
||||
* @brief Assigns a custom unique identifier to a meta type.
|
||||
* @param id A custom unique identifier.
|
||||
* @return An extended meta factory for the given type.
|
||||
* @return A meta factory for the given type.
|
||||
*/
|
||||
auto type(const id_type id) noexcept {
|
||||
auto &&elem = internal::owner(*ctx, *info);
|
||||
@@ -156,16 +128,8 @@ public:
|
||||
template<typename Base>
|
||||
auto base() noexcept {
|
||||
static_assert(!std::is_same_v<Type, Base> && std::is_base_of_v<Base, Type>, "Invalid base type");
|
||||
|
||||
internal::meta_extend(
|
||||
internal::owner(*ctx, *info),
|
||||
type_id<Base>().hash(),
|
||||
internal::meta_base_node{
|
||||
&internal::resolve<Base>,
|
||||
+[](const void *instance) noexcept {
|
||||
return static_cast<const void *>(static_cast<const Base *>(static_cast<const Type *>(instance)));
|
||||
}});
|
||||
|
||||
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;
|
||||
return *this;
|
||||
}
|
||||
@@ -185,15 +149,8 @@ public:
|
||||
template<auto Candidate>
|
||||
auto conv() noexcept {
|
||||
using conv_type = std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(Candidate), Type &>>>;
|
||||
|
||||
internal::meta_extend(
|
||||
internal::owner(*ctx, *info),
|
||||
type_id<conv_type>().hash(),
|
||||
internal::meta_conv_node{
|
||||
+[](const meta_ctx &area, const void *instance) {
|
||||
return forward_as_meta(area, std::invoke(Candidate, *static_cast<const Type *>(instance)));
|
||||
}});
|
||||
|
||||
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;
|
||||
return *this;
|
||||
}
|
||||
@@ -210,15 +167,8 @@ public:
|
||||
template<typename To>
|
||||
auto conv() noexcept {
|
||||
using conv_type = std::remove_cv_t<std::remove_reference_t<To>>;
|
||||
|
||||
internal::meta_extend(
|
||||
internal::owner(*ctx, *info),
|
||||
type_id<conv_type>().hash(),
|
||||
internal::meta_conv_node{
|
||||
+[](const meta_ctx &area, const void *instance) {
|
||||
return forward_as_meta(area, static_cast<To>(*static_cast<const Type *>(instance)));
|
||||
}});
|
||||
|
||||
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;
|
||||
return *this;
|
||||
}
|
||||
@@ -234,22 +184,14 @@ public:
|
||||
*
|
||||
* @tparam Candidate The actual function to use as a constructor.
|
||||
* @tparam Policy Optional policy (no policy set by default).
|
||||
* @return An extended meta factory for the parent type.
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template<auto Candidate, typename Policy = as_is_t>
|
||||
auto 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::meta_extend(
|
||||
internal::owner(*ctx, *info),
|
||||
type_id<typename descriptor::args_type>().hash(),
|
||||
internal::meta_ctor_node{
|
||||
descriptor::args_type::size,
|
||||
&meta_arg<typename descriptor::args_type>,
|
||||
&meta_construct<Type, Candidate, Policy>});
|
||||
|
||||
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;
|
||||
return *this;
|
||||
}
|
||||
@@ -262,21 +204,14 @@ public:
|
||||
* type that can be invoked with parameters whose types are those given.
|
||||
*
|
||||
* @tparam Args Types of arguments to use to construct an instance.
|
||||
* @return An extended meta factory for the parent type.
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template<typename... Args>
|
||||
auto ctor() noexcept {
|
||||
// default constructor is already implicitly generated, no need for redundancy
|
||||
if constexpr(sizeof...(Args) != 0u) {
|
||||
using descriptor = meta_function_helper_t<Type, Type (*)(Args...)>;
|
||||
|
||||
internal::meta_extend(
|
||||
internal::owner(*ctx, *info),
|
||||
type_id<typename descriptor::args_type>().hash(),
|
||||
internal::meta_ctor_node{
|
||||
descriptor::args_type::size,
|
||||
&meta_arg<typename descriptor::args_type>,
|
||||
&meta_construct<Type, Args...>});
|
||||
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...>});
|
||||
}
|
||||
|
||||
bucket = nullptr;
|
||||
@@ -304,12 +239,8 @@ public:
|
||||
template<auto Func>
|
||||
auto dtor() noexcept {
|
||||
static_assert(std::is_invocable_v<decltype(Func), Type &>, "The function doesn't accept an object of the type provided");
|
||||
|
||||
internal::meta_extend(
|
||||
internal::owner(*ctx, *info),
|
||||
internal::meta_dtor_node{
|
||||
+[](void *instance) { std::invoke(Func, *static_cast<Type *>(instance)); }});
|
||||
|
||||
auto *const op = +[](void *instance) { std::invoke(Func, *static_cast<Type *>(instance)); };
|
||||
internal::owner(*ctx, *info).dtor = internal::meta_dtor_node{op};
|
||||
bucket = nullptr;
|
||||
return *this;
|
||||
}
|
||||
@@ -325,37 +256,44 @@ public:
|
||||
* @tparam Data The actual variable to attach to the meta type.
|
||||
* @tparam Policy Optional policy (no policy set by default).
|
||||
* @param id Unique identifier.
|
||||
* @return An extended meta factory for the parent type.
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template<auto Data, typename Policy = as_is_t>
|
||||
auto data(const id_type id) noexcept {
|
||||
if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
|
||||
using data_type = std::remove_reference_t<std::invoke_result_t<decltype(Data), Type &>>;
|
||||
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,
|
||||
internal::meta_data_node{
|
||||
/* this is never static */
|
||||
std::is_const_v<data_type> ? internal::meta_traits::is_const : internal::meta_traits::is_none,
|
||||
std::is_const_v<std::remove_reference_t<data_type>> ? internal::meta_traits::is_const : internal::meta_traits::is_none,
|
||||
1u,
|
||||
&internal::resolve<std::remove_cv_t<data_type>>,
|
||||
&meta_arg<type_list<std::remove_cv_t<data_type>>>,
|
||||
&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;
|
||||
} else {
|
||||
using data_type = std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>;
|
||||
using data_type = std::remove_pointer_t<decltype(Data)>;
|
||||
|
||||
if constexpr(std::is_pointer_v<decltype(Data)>) {
|
||||
static_assert(Policy::template value<decltype(*Data)>, "Invalid return type for the given policy");
|
||||
} else {
|
||||
static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
|
||||
}
|
||||
|
||||
auto &&elem = internal::meta_extend(
|
||||
internal::owner(*ctx, *info),
|
||||
id,
|
||||
internal::meta_data_node{
|
||||
((std::is_same_v<Type, std::remove_cv_t<data_type>> || std::is_const_v<data_type>) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static,
|
||||
((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<data_type>>,
|
||||
&meta_arg<type_list<std::remove_cv_t<data_type>>>,
|
||||
&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>});
|
||||
|
||||
@@ -383,7 +321,7 @@ public:
|
||||
* @tparam Getter The actual function to use as a getter.
|
||||
* @tparam Policy Optional policy (no policy set by default).
|
||||
* @param id Unique identifier.
|
||||
* @return An extended meta factory for the parent type.
|
||||
* @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 {
|
||||
@@ -440,7 +378,7 @@ public:
|
||||
* @tparam Getter The actual getter function.
|
||||
* @tparam Policy Optional policy (no policy set by default).
|
||||
* @param id Unique identifier.
|
||||
* @return An extended meta factory for the parent type.
|
||||
* @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 {
|
||||
@@ -459,7 +397,7 @@ public:
|
||||
* @tparam Candidate The actual function to attach to the meta type.
|
||||
* @tparam Policy Optional policy (no policy set by default).
|
||||
* @param id Unique identifier.
|
||||
* @return An extended meta factory for the parent type.
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template<auto Candidate, typename Policy = as_is_t>
|
||||
auto func(const id_type id) noexcept {
|
||||
@@ -488,25 +426,18 @@ public:
|
||||
* @tparam Value Optional type of the property value.
|
||||
* @param id Property key.
|
||||
* @param value Optional property value.
|
||||
* @return An extended meta factory for the given type.
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template<typename... Value>
|
||||
meta_factory prop(id_type id, [[maybe_unused]] Value &&...value) {
|
||||
ENTT_ASSERT(bucket != nullptr, "Meta object does not support properties");
|
||||
|
||||
if constexpr(sizeof...(Value) == 0u) {
|
||||
internal::meta_extend(
|
||||
*bucket,
|
||||
id,
|
||||
internal::meta_prop_node{
|
||||
&internal::resolve<void>});
|
||||
(*bucket)[id] = internal::meta_prop_node{&internal::resolve<void>};
|
||||
} else {
|
||||
internal::meta_extend(
|
||||
*bucket,
|
||||
id,
|
||||
internal::meta_prop_node{
|
||||
&internal::resolve<std::decay_t<Value>>...,
|
||||
std::make_shared<std::decay_t<Value>>(std::forward<Value>(value))...});
|
||||
(*bucket)[id] = internal::meta_prop_node{
|
||||
&internal::resolve<std::decay_t<Value>>...,
|
||||
std::make_shared<std::decay_t<Value>>(std::forward<Value>(value))...};
|
||||
}
|
||||
|
||||
return *this;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,11 +22,7 @@ class meta_any;
|
||||
class meta_type;
|
||||
struct meta_handle;
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
enum class meta_traits : std::uint32_t {
|
||||
@@ -131,6 +127,23 @@ struct meta_type_node {
|
||||
std::shared_ptr<meta_type_descriptor> details{};
|
||||
};
|
||||
|
||||
template<auto Member>
|
||||
auto *look_for(const meta_context &context, const meta_type_node &node, const id_type id) {
|
||||
if(node.details) {
|
||||
if(const auto it = (node.details.get()->*Member).find(id); it != (node.details.get()->*Member).cend()) {
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
for(auto &&curr: node.details->base) {
|
||||
if(auto *elem = look_for<Member>(context, curr.second.type(context), id); elem) {
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return static_cast<typename std::remove_reference_t<decltype(node.details.get()->*Member)>::mapped_type *>(nullptr);
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
meta_type_node resolve(const meta_context &) noexcept;
|
||||
|
||||
@@ -159,6 +172,31 @@ template<typename... Args>
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename Func>
|
||||
[[nodiscard]] inline auto try_convert(const meta_context &context, const meta_type_node &from, const type_info &to, const bool arithmetic_or_enum, const void *instance, Func func) {
|
||||
if(from.info && *from.info == to) {
|
||||
return func(instance, from);
|
||||
}
|
||||
|
||||
if(from.details) {
|
||||
if(auto it = from.details->conv.find(to.hash()); it != from.details->conv.cend()) {
|
||||
return func(instance, it->second);
|
||||
}
|
||||
|
||||
for(auto &&curr: from.details->base) {
|
||||
if(auto other = try_convert(context, curr.second.type(context), to, arithmetic_or_enum, curr.second.cast(instance), func); other) {
|
||||
return other;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(from.conversion_helper && arithmetic_or_enum) {
|
||||
return func(instance, from.conversion_helper);
|
||||
}
|
||||
|
||||
return func(instance);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline const meta_type_node *try_resolve(const meta_context &context, const type_info &info) noexcept {
|
||||
const auto it = context.value.find(info.hash());
|
||||
return it != context.value.end() ? &it->second : nullptr;
|
||||
@@ -204,7 +242,7 @@ template<typename Type>
|
||||
};
|
||||
}
|
||||
|
||||
if constexpr(!std::is_same_v<Type, void> && !std::is_function_v<Type>) {
|
||||
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)};
|
||||
@@ -225,11 +263,7 @@ template<typename Type>
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
|
||||
} // namespace entt
|
||||
|
||||
|
||||
@@ -7,58 +7,34 @@ namespace entt {
|
||||
|
||||
/*! @brief Empty class type used to request the _as ref_ policy. */
|
||||
struct as_ref_t final {
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
template<typename Type>
|
||||
static constexpr bool value = std::is_reference_v<Type> && !std::is_const_v<std::remove_reference_t<Type>>;
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
};
|
||||
|
||||
/*! @brief Empty class type used to request the _as cref_ policy. */
|
||||
struct as_cref_t final {
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
template<typename Type>
|
||||
static constexpr bool value = std::is_reference_v<Type>;
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
};
|
||||
|
||||
/*! @brief Empty class type used to request the _as-is_ policy. */
|
||||
struct as_is_t final {
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
template<typename>
|
||||
static constexpr bool value = true;
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
};
|
||||
|
||||
/*! @brief Empty class type used to request the _as void_ policy. */
|
||||
struct as_void_t final {
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
template<typename>
|
||||
static constexpr bool value = true;
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -68,11 +44,7 @@ struct as_void_t final {
|
||||
*/
|
||||
template<typename Type>
|
||||
struct is_meta_policy
|
||||
: std::disjunction<
|
||||
std::is_same<Type, as_ref_t>,
|
||||
std::is_same<Type, as_cref_t>,
|
||||
std::is_same<Type, as_is_t>,
|
||||
std::is_same<Type, as_void_t>> {};
|
||||
: std::bool_constant<std::is_same_v<Type, as_ref_t> || std::is_same_v<Type, as_cref_t> || std::is_same_v<Type, as_is_t> || std::is_same_v<Type, as_void_t>> {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
|
||||
@@ -10,11 +10,7 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename Type, typename It>
|
||||
@@ -24,20 +20,21 @@ struct meta_range_iterator final {
|
||||
using pointer = input_iterator_pointer<value_type>;
|
||||
using reference = value_type;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using iterator_concept = std::random_access_iterator_tag;
|
||||
|
||||
meta_range_iterator() noexcept
|
||||
constexpr meta_range_iterator() noexcept
|
||||
: it{},
|
||||
ctx{} {}
|
||||
|
||||
meta_range_iterator(const meta_ctx &area, const It iter) noexcept
|
||||
constexpr meta_range_iterator(const meta_ctx &area, const It iter) noexcept
|
||||
: it{iter},
|
||||
ctx{&area} {}
|
||||
|
||||
meta_range_iterator &operator++() noexcept {
|
||||
constexpr meta_range_iterator &operator++() noexcept {
|
||||
return ++it, *this;
|
||||
}
|
||||
|
||||
meta_range_iterator operator++(int) noexcept {
|
||||
constexpr meta_range_iterator operator++(int) noexcept {
|
||||
meta_range_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
@@ -131,11 +128,7 @@ template<typename... Args>
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Iterable range to use to iterate all types of meta objects.
|
||||
|
||||
@@ -30,7 +30,6 @@ struct meta_associative_container_traits;
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if a given type is a
|
||||
* pointer-like type from the point of view of the meta system, false otherwise.
|
||||
* @tparam Type Potentially pointer-like type.
|
||||
*/
|
||||
template<typename>
|
||||
struct is_meta_pointer_like: std::false_type {};
|
||||
|
||||
@@ -92,9 +92,12 @@ template<typename Type, typename Ret, typename MaybeType, typename... Args>
|
||||
struct meta_function_descriptor<Type, Ret (*)(MaybeType, Args...)>
|
||||
: meta_function_descriptor_traits<
|
||||
Ret,
|
||||
std::conditional_t<std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>, type_list<Args...>, type_list<MaybeType, Args...>>,
|
||||
!std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>,
|
||||
std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> && std::is_const_v<std::remove_reference_t<MaybeType>>> {};
|
||||
std::conditional_t<
|
||||
std::is_same_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>,
|
||||
type_list<Args...>,
|
||||
type_list<MaybeType, Args...>>,
|
||||
!(std::is_same_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>),
|
||||
std::is_const_v<std::remove_reference_t<MaybeType>> && (std::is_same_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>)> {};
|
||||
|
||||
/**
|
||||
* @brief Meta function descriptor.
|
||||
@@ -162,7 +165,7 @@ using meta_function_helper_t = typename meta_function_helper<Type, Candidate>::t
|
||||
* @return A meta any containing the returned value, if any.
|
||||
*/
|
||||
template<typename Policy = as_is_t, typename Type>
|
||||
std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) {
|
||||
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) {
|
||||
if constexpr(std::is_same_v<Policy, as_void_t>) {
|
||||
return meta_any{ctx, std::in_place_type<void>};
|
||||
} else if constexpr(std::is_same_v<Policy, as_ref_t>) {
|
||||
@@ -183,7 +186,7 @@ std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(const meta_ct
|
||||
* @return A meta any containing the returned value, if any.
|
||||
*/
|
||||
template<typename Policy = as_is_t, typename Type>
|
||||
std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(Type &&value) {
|
||||
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(Type &&value) {
|
||||
return meta_dispatch<Policy, Type>(locator<meta_ctx>::value_or(), std::forward<Type>(value));
|
||||
}
|
||||
|
||||
@@ -310,16 +313,12 @@ template<typename Type, auto Data, typename Policy = as_is_t>
|
||||
return meta_getter<Type, Data, Policy>(locator<meta_ctx>::value_or(), std::move(instance));
|
||||
}
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename Policy, typename Candidate, typename... Args>
|
||||
[[nodiscard]] meta_any meta_invoke_with_args(const meta_ctx &ctx, Candidate &&candidate, Args &&...args) {
|
||||
if constexpr(std::is_same_v<decltype(std::invoke(std::forward<Candidate>(candidate), args...)), void>) {
|
||||
if constexpr(std::is_void_v<decltype(std::invoke(std::forward<Candidate>(candidate), args...))>) {
|
||||
std::invoke(std::forward<Candidate>(candidate), args...);
|
||||
return meta_any{ctx, std::in_place_type<void>};
|
||||
} else {
|
||||
@@ -336,7 +335,7 @@ template<typename Type, typename Policy, typename Candidate, std::size_t... Inde
|
||||
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>>() && ...)) {
|
||||
if(auto *const clazz = instance->try_cast<Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) { // NOLINT
|
||||
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 {
|
||||
@@ -358,11 +357,7 @@ template<typename Type, typename... Args, std::size_t... Index>
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Tries to _invoke_ an object given a list of erased parameters.
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
#ifndef ENTT_PLATFORM_ANDROID_NDK_R17_HPP
|
||||
#define ENTT_PLATFORM_ANDROID_NDK_R17_HPP
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
#ifdef __ANDROID__
|
||||
# include <android/ndk-version.h>
|
||||
# if __NDK_MAJOR__ == 17
|
||||
@@ -58,10 +54,6 @@ using invoke_result_t = typename std::invoke_result<Func, Args...>::type;
|
||||
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
|
||||
#endif
|
||||
|
||||
@@ -19,7 +19,7 @@ struct poly_inspector {
|
||||
* @brief Generic conversion operator (definition only).
|
||||
* @tparam Type Type to which conversion is requested.
|
||||
*/
|
||||
template<class Type>
|
||||
template<typename Type>
|
||||
operator Type &&() const;
|
||||
|
||||
/**
|
||||
@@ -190,7 +190,6 @@ decltype(auto) poly_call(Poly &&self, Args &&...args) {
|
||||
*/
|
||||
template<typename Concept, std::size_t Len, std::size_t Align>
|
||||
class basic_poly: private Concept::template type<poly_base<basic_poly<Concept, Len, Align>>> {
|
||||
/*! @brief A poly base is allowed to snoop into a poly object. */
|
||||
friend struct poly_base<basic_poly>;
|
||||
|
||||
public:
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
#ifndef ENTT_PROCESS_FWD_HPP
|
||||
#define ENTT_PROCESS_FWD_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
namespace entt {
|
||||
|
||||
template<typename, typename>
|
||||
class process;
|
||||
|
||||
template<typename>
|
||||
class scheduler;
|
||||
template<typename = std::uint32_t, typename = std::allocator<void>>
|
||||
class basic_scheduler;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using scheduler = basic_scheduler<>;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
@@ -175,13 +176,13 @@ public:
|
||||
* The function is idempotent and it does nothing if the process isn't
|
||||
* alive.
|
||||
*
|
||||
* @param immediately Requests an immediate operation.
|
||||
* @param immediate Requests an immediate operation.
|
||||
*/
|
||||
void abort(const bool immediately = false) {
|
||||
void abort(const bool immediate = false) {
|
||||
if(alive()) {
|
||||
current = state::aborted;
|
||||
|
||||
if(immediately) {
|
||||
if(immediate) {
|
||||
tick({});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,56 @@
|
||||
#ifndef ENTT_PROCESS_SCHEDULER_HPP
|
||||
#define ENTT_PROCESS_SCHEDULER_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../config/config.h"
|
||||
#include "../core/compressed_pair.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "process.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
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;
|
||||
|
||||
// std::shared_ptr because of its type erased allocator which is useful here
|
||||
std::shared_ptr<basic_process_handler> next;
|
||||
};
|
||||
|
||||
template<typename Delta, typename Type>
|
||||
struct process_handler final: basic_process_handler<Delta> {
|
||||
template<typename... Args>
|
||||
process_handler(Args &&...args)
|
||||
: process{std::forward<Args>(args)...} {}
|
||||
|
||||
bool update(const Delta delta, void *data) override {
|
||||
if(process.tick(delta, data); process.rejected()) {
|
||||
this->next.reset();
|
||||
}
|
||||
|
||||
return (process.rejected() || process.finished());
|
||||
}
|
||||
|
||||
void abort(const bool immediate) override {
|
||||
process.abort(immediate);
|
||||
}
|
||||
|
||||
Type process;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Cooperative scheduler for processes.
|
||||
*
|
||||
@@ -36,93 +76,90 @@ namespace entt {
|
||||
* @sa process
|
||||
*
|
||||
* @tparam Delta Type to use to provide elapsed time.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Delta>
|
||||
class scheduler {
|
||||
struct process_handler {
|
||||
using instance_type = std::unique_ptr<void, void (*)(void *)>;
|
||||
using update_fn_type = bool(scheduler &, std::size_t, Delta, void *);
|
||||
using abort_fn_type = void(scheduler &, std::size_t, bool);
|
||||
using next_type = std::unique_ptr<process_handler>;
|
||||
template<typename Delta, typename Allocator>
|
||||
class basic_scheduler {
|
||||
template<typename Type>
|
||||
using handler_type = internal::process_handler<Delta, Type>;
|
||||
|
||||
instance_type instance;
|
||||
update_fn_type *update;
|
||||
abort_fn_type *abort;
|
||||
next_type next;
|
||||
};
|
||||
// std::shared_ptr because of its type erased allocator which is useful here
|
||||
using process_type = std::shared_ptr<internal::basic_process_handler<Delta>>;
|
||||
|
||||
struct continuation {
|
||||
continuation(process_handler *ref) noexcept
|
||||
: handler{ref} {}
|
||||
|
||||
template<typename Proc, typename... Args>
|
||||
continuation then(Args &&...args) {
|
||||
static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type");
|
||||
auto proc = typename process_handler::instance_type{new Proc{std::forward<Args>(args)...}, &scheduler::deleter<Proc>};
|
||||
handler->next.reset(new process_handler{std::move(proc), &scheduler::update<Proc>, &scheduler::abort<Proc>, nullptr});
|
||||
handler = handler->next.get();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename Func>
|
||||
continuation then(Func &&func) {
|
||||
return then<process_adaptor<std::decay_t<Func>, Delta>>(std::forward<Func>(func));
|
||||
}
|
||||
|
||||
private:
|
||||
process_handler *handler;
|
||||
};
|
||||
|
||||
template<typename Proc>
|
||||
[[nodiscard]] static bool update(scheduler &owner, std::size_t pos, const Delta delta, void *data) {
|
||||
auto *process = static_cast<Proc *>(owner.handlers[pos].instance.get());
|
||||
process->tick(delta, data);
|
||||
|
||||
if(process->rejected()) {
|
||||
return true;
|
||||
} else if(process->finished()) {
|
||||
if(auto &&handler = owner.handlers[pos]; handler.next) {
|
||||
handler = std::move(*handler.next);
|
||||
// forces the process to exit the uninitialized state
|
||||
return handler.update(owner, pos, {}, nullptr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename Proc>
|
||||
static void abort(scheduler &owner, std::size_t pos, const bool immediately) {
|
||||
static_cast<Proc *>(owner.handlers[pos].instance.get())->abort(immediately);
|
||||
}
|
||||
|
||||
template<typename Proc>
|
||||
static void deleter(void *proc) {
|
||||
delete static_cast<Proc *>(proc);
|
||||
}
|
||||
using alloc_traits = std::allocator_traits<Allocator>;
|
||||
using container_allocator = typename alloc_traits::template rebind_alloc<process_type>;
|
||||
using container_type = std::vector<process_type, container_allocator>;
|
||||
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using delta_type = Delta;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
scheduler()
|
||||
: handlers{} {}
|
||||
basic_scheduler()
|
||||
: basic_scheduler{allocator_type{}} {}
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
scheduler(scheduler &&) = default;
|
||||
/**
|
||||
* @brief Constructs a scheduler with a given allocator.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit basic_scheduler(const allocator_type &allocator)
|
||||
: handlers{allocator, allocator} {}
|
||||
|
||||
/*! @brief Default move assignment operator. @return This scheduler. */
|
||||
scheduler &operator=(scheduler &&) = default;
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
basic_scheduler(basic_scheduler &&other) noexcept
|
||||
: handlers{std::move(other.handlers)} {}
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
basic_scheduler(basic_scheduler &&other, const allocator_type &allocator) noexcept
|
||||
: 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");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
* @param other The instance to move from.
|
||||
* @return This 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);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exchanges the contents with those of a given scheduler.
|
||||
* @param other Scheduler to exchange the content with.
|
||||
*/
|
||||
void swap(basic_scheduler &other) {
|
||||
using std::swap;
|
||||
swap(handlers, other.handlers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the associated allocator.
|
||||
* @return The associated allocator.
|
||||
*/
|
||||
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
|
||||
return handlers.second();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Number of processes currently scheduled.
|
||||
* @return Number of processes currently scheduled.
|
||||
*/
|
||||
[[nodiscard]] size_type size() const noexcept {
|
||||
return handlers.size();
|
||||
return handlers.first().size();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,7 +167,7 @@ public:
|
||||
* @return True if there are scheduled processes, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool empty() const noexcept {
|
||||
return handlers.empty();
|
||||
return handlers.first().empty();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -140,15 +177,15 @@ public:
|
||||
* and never executed again.
|
||||
*/
|
||||
void clear() {
|
||||
handlers.clear();
|
||||
handlers.first().clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Schedules a process for the next tick.
|
||||
*
|
||||
* Returned value is an opaque object that can be used to attach a child to
|
||||
* the given process. The child is automatically scheduled when the process
|
||||
* terminates and only if the process returns with success.
|
||||
* Returned value can be used to attach a continuation for the last process.
|
||||
* The continutation is scheduled automatically when the process terminates
|
||||
* and only if the process returns with success.
|
||||
*
|
||||
* Example of use (pseudocode):
|
||||
*
|
||||
@@ -166,16 +203,15 @@ public:
|
||||
* @tparam Proc Type of process to schedule.
|
||||
* @tparam Args Types of arguments to use to initialize the process.
|
||||
* @param args Parameters to use to initialize the process.
|
||||
* @return An opaque object to use to concatenate processes.
|
||||
* @return This process scheduler.
|
||||
*/
|
||||
template<typename Proc, typename... Args>
|
||||
auto attach(Args &&...args) {
|
||||
basic_scheduler &attach(Args &&...args) {
|
||||
static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type");
|
||||
auto proc = typename process_handler::instance_type{new Proc{std::forward<Args>(args)...}, &scheduler::deleter<Proc>};
|
||||
auto &&ref = handlers.emplace_back(process_handler{std::move(proc), &scheduler::update<Proc>, &scheduler::abort<Proc>, nullptr});
|
||||
auto &ref = handlers.first().emplace_back(std::allocate_shared<handler_type<Proc>>(handlers.second(), std::forward<Args>(args)...));
|
||||
// forces the process to exit the uninitialized state
|
||||
ref.update(*this, handlers.size() - 1u, {}, nullptr);
|
||||
return continuation{&handlers.back()};
|
||||
ref->update({}, nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,9 +240,9 @@ public:
|
||||
* void();
|
||||
* @endcode
|
||||
*
|
||||
* Returned value is an opaque object that can be used to attach a child to
|
||||
* the given process. The child is automatically scheduled when the process
|
||||
* terminates and only if the process returns with success.
|
||||
* Returned value can be used to attach a continuation for the last process.
|
||||
* The continutation is scheduled automatically when the process terminates
|
||||
* and only if the process returns with success.
|
||||
*
|
||||
* Example of use (pseudocode):
|
||||
*
|
||||
@@ -227,14 +263,43 @@ public:
|
||||
*
|
||||
* @tparam Func Type of process to schedule.
|
||||
* @param func Either a lambda or a functor to use as a process.
|
||||
* @return An opaque object to use to concatenate processes.
|
||||
* @return This process scheduler.
|
||||
*/
|
||||
template<typename Func>
|
||||
auto attach(Func &&func) {
|
||||
basic_scheduler &attach(Func &&func) {
|
||||
using Proc = process_adaptor<std::decay_t<Func>, Delta>;
|
||||
return attach<Proc>(std::forward<Func>(func));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a process as a continuation of the last scheduled process.
|
||||
* @tparam Proc Type of process to use as a continuation.
|
||||
* @tparam Args Types of arguments to use to initialize the process.
|
||||
* @param args Parameters to use to initialize the process.
|
||||
* @return This process scheduler.
|
||||
*/
|
||||
template<typename Proc, typename... Args>
|
||||
basic_scheduler &then(Args &&...args) {
|
||||
static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type");
|
||||
ENTT_ASSERT(!handlers.first().empty(), "Process not available");
|
||||
auto *curr = handlers.first().back().get();
|
||||
for(; curr->next; curr = curr->next.get()) {}
|
||||
curr->next = std::allocate_shared<handler_type<Proc>>(handlers.second(), std::forward<Args>(args)...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a process as a continuation of the last scheduled process.
|
||||
* @tparam Func Type of process to use as a continuation.
|
||||
* @param func Either a lambda or a functor to use as a process.
|
||||
* @return This process scheduler.
|
||||
*/
|
||||
template<typename Func>
|
||||
basic_scheduler &then(Func &&func) {
|
||||
using Proc = process_adaptor<std::decay_t<Func>, Delta>;
|
||||
return then<Proc>(std::forward<Func>(func));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates all scheduled processes.
|
||||
*
|
||||
@@ -246,13 +311,18 @@ public:
|
||||
* @param delta Elapsed time.
|
||||
* @param data Optional data.
|
||||
*/
|
||||
void update(const Delta delta, void *data = nullptr) {
|
||||
for(auto pos = handlers.size(); pos; --pos) {
|
||||
const auto curr = pos - 1u;
|
||||
|
||||
if(const auto dead = handlers[curr].update(*this, curr, delta, data); dead) {
|
||||
std::swap(handlers[curr], handlers.back());
|
||||
handlers.pop_back();
|
||||
void update(const delta_type delta, void *data = nullptr) {
|
||||
for(auto next = handlers.first().size(); next; --next) {
|
||||
if(const auto pos = next - 1u; handlers.first()[pos]->update(delta, data)) {
|
||||
// updating might spawn/reallocate, cannot hold refs until here
|
||||
if(auto &curr = handlers.first()[pos]; curr->next) {
|
||||
curr = std::move(curr->next);
|
||||
// forces the process to exit the uninitialized state
|
||||
curr->update({}, nullptr);
|
||||
} else {
|
||||
curr = std::move(handlers.first().back());
|
||||
handlers.first().pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -265,17 +335,16 @@ public:
|
||||
* Once a process is fully aborted and thus finished, it's discarded along
|
||||
* with its child, if any.
|
||||
*
|
||||
* @param immediately Requests an immediate operation.
|
||||
* @param immediate Requests an immediate operation.
|
||||
*/
|
||||
void abort(const bool immediately = false) {
|
||||
for(auto pos = handlers.size(); pos; --pos) {
|
||||
const auto curr = pos - 1u;
|
||||
handlers[curr].abort(*this, curr, immediately);
|
||||
void abort(const bool immediate = false) {
|
||||
for(auto &&curr: handlers.first()) {
|
||||
curr->abort(immediate);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<process_handler> handlers{};
|
||||
compressed_pair<container_type, allocator_type> handlers;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -19,11 +19,7 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename Type, typename It>
|
||||
@@ -37,6 +33,7 @@ public:
|
||||
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 resource_cache_iterator() noexcept = default;
|
||||
|
||||
@@ -95,60 +92,56 @@ public:
|
||||
return operator*();
|
||||
}
|
||||
|
||||
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
|
||||
friend constexpr std::ptrdiff_t operator-(const resource_cache_iterator<TLhs, ILhs> &, const resource_cache_iterator<TRhs, IRhs> &) noexcept;
|
||||
template<typename... Lhs, typename... Rhs>
|
||||
friend constexpr std::ptrdiff_t operator-(const resource_cache_iterator<Lhs...> &, const resource_cache_iterator<Rhs...> &) noexcept;
|
||||
|
||||
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
|
||||
friend constexpr bool operator==(const resource_cache_iterator<TLhs, ILhs> &, const resource_cache_iterator<TRhs, IRhs> &) noexcept;
|
||||
template<typename... Lhs, typename... Rhs>
|
||||
friend constexpr bool operator==(const resource_cache_iterator<Lhs...> &, const resource_cache_iterator<Rhs...> &) noexcept;
|
||||
|
||||
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
|
||||
friend constexpr bool operator<(const resource_cache_iterator<TLhs, ILhs> &, const resource_cache_iterator<TRhs, IRhs> &) noexcept;
|
||||
template<typename... Lhs, typename... Rhs>
|
||||
friend constexpr bool operator<(const resource_cache_iterator<Lhs...> &, const resource_cache_iterator<Rhs...> &) noexcept;
|
||||
|
||||
private:
|
||||
It it;
|
||||
};
|
||||
|
||||
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
|
||||
[[nodiscard]] constexpr std::ptrdiff_t operator-(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) noexcept {
|
||||
template<typename... Lhs, typename... Rhs>
|
||||
[[nodiscard]] constexpr std::ptrdiff_t operator-(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
|
||||
return lhs.it - rhs.it;
|
||||
}
|
||||
|
||||
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
|
||||
[[nodiscard]] constexpr bool operator==(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) noexcept {
|
||||
template<typename... Lhs, typename... Rhs>
|
||||
[[nodiscard]] constexpr bool operator==(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
|
||||
return lhs.it == rhs.it;
|
||||
}
|
||||
|
||||
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
|
||||
[[nodiscard]] constexpr bool operator!=(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) noexcept {
|
||||
template<typename... Lhs, typename... Rhs>
|
||||
[[nodiscard]] constexpr bool operator!=(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
|
||||
[[nodiscard]] constexpr bool operator<(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) noexcept {
|
||||
template<typename... Lhs, typename... Rhs>
|
||||
[[nodiscard]] constexpr bool operator<(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
|
||||
return lhs.it < rhs.it;
|
||||
}
|
||||
|
||||
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
|
||||
[[nodiscard]] constexpr bool operator>(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) noexcept {
|
||||
template<typename... Lhs, typename... Rhs>
|
||||
[[nodiscard]] constexpr bool operator>(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
|
||||
return rhs < lhs;
|
||||
}
|
||||
|
||||
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
|
||||
[[nodiscard]] constexpr bool operator<=(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) noexcept {
|
||||
template<typename... Lhs, typename... Rhs>
|
||||
[[nodiscard]] constexpr bool operator<=(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
|
||||
return !(lhs > rhs);
|
||||
}
|
||||
|
||||
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
|
||||
[[nodiscard]] constexpr bool operator>=(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) noexcept {
|
||||
template<typename... Lhs, typename... Rhs>
|
||||
[[nodiscard]] constexpr bool operator>=(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Basic cache for resources of any type.
|
||||
@@ -158,7 +151,7 @@ template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
|
||||
*/
|
||||
template<typename Type, typename Loader, typename Allocator>
|
||||
class resource_cache {
|
||||
using alloc_traits = typename std::allocator_traits<Allocator>;
|
||||
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>;
|
||||
@@ -241,8 +234,7 @@ public:
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning.
|
||||
*
|
||||
* The returned iterator points to the first instance of the cache. If the
|
||||
* cache is empty, the returned iterator will be equal to `end()`.
|
||||
* If the cache is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @return An iterator to the first instance of the internal cache.
|
||||
*/
|
||||
@@ -262,11 +254,6 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the end.
|
||||
*
|
||||
* The returned iterator points to the element following the last instance
|
||||
* of the cache. Attempting to dereference the returned iterator results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @return An iterator to the element following the last instance of the
|
||||
* internal cache.
|
||||
*/
|
||||
|
||||
@@ -20,7 +20,6 @@ namespace entt {
|
||||
*/
|
||||
template<typename Type>
|
||||
class resource {
|
||||
/*! @brief Resource handles are friends with each other. */
|
||||
template<typename>
|
||||
friend class resource;
|
||||
|
||||
@@ -163,81 +162,81 @@ private:
|
||||
|
||||
/**
|
||||
* @brief Compares two handles.
|
||||
* @tparam Res Type of resource managed by the first handle.
|
||||
* @tparam Other Type of resource managed by the second handle.
|
||||
* @tparam Lhs Type of resource managed by the first handle.
|
||||
* @tparam Rhs Type of resource managed by the second handle.
|
||||
* @param lhs A valid handle.
|
||||
* @param rhs A valid handle.
|
||||
* @return True if both handles refer to the same resource, false otherwise.
|
||||
*/
|
||||
template<typename Res, typename Other>
|
||||
[[nodiscard]] bool operator==(const resource<Res> &lhs, const resource<Other> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] bool operator==(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
|
||||
return (std::addressof(*lhs) == std::addressof(*rhs));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two handles.
|
||||
* @tparam Res Type of resource managed by the first handle.
|
||||
* @tparam Other Type of resource managed by the second handle.
|
||||
* @tparam Lhs Type of resource managed by the first handle.
|
||||
* @tparam Rhs Type of resource managed by the second handle.
|
||||
* @param lhs A valid handle.
|
||||
* @param rhs A valid handle.
|
||||
* @return False if both handles refer to the same registry, true otherwise.
|
||||
* @return False if both handles refer to the same resource, true otherwise.
|
||||
*/
|
||||
template<typename Res, typename Other>
|
||||
[[nodiscard]] bool operator!=(const resource<Res> &lhs, const resource<Other> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] bool operator!=(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two handles.
|
||||
* @tparam Res Type of resource managed by the first handle.
|
||||
* @tparam Other Type of resource managed by the second handle.
|
||||
* @tparam Lhs Type of resource managed by the first handle.
|
||||
* @tparam Rhs Type of resource managed by the second handle.
|
||||
* @param lhs A valid handle.
|
||||
* @param rhs A valid handle.
|
||||
* @return True if the first handle is less than the second, false otherwise.
|
||||
*/
|
||||
template<typename Res, typename Other>
|
||||
[[nodiscard]] bool operator<(const resource<Res> &lhs, const resource<Other> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] bool operator<(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
|
||||
return (std::addressof(*lhs) < std::addressof(*rhs));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two handles.
|
||||
* @tparam Res Type of resource managed by the first handle.
|
||||
* @tparam Other Type of resource managed by the second handle.
|
||||
* @tparam Lhs Type of resource managed by the first handle.
|
||||
* @tparam Rhs Type of resource managed by the second handle.
|
||||
* @param lhs A valid handle.
|
||||
* @param rhs A valid handle.
|
||||
* @return True if the first handle is greater than the second, false otherwise.
|
||||
*/
|
||||
template<typename Res, typename Other>
|
||||
[[nodiscard]] bool operator>(const resource<Res> &lhs, const resource<Other> &rhs) noexcept {
|
||||
return (std::addressof(*lhs) > std::addressof(*rhs));
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] bool operator>(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
|
||||
return rhs < lhs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two handles.
|
||||
* @tparam Res Type of resource managed by the first handle.
|
||||
* @tparam Other Type of resource managed by the second handle.
|
||||
* @tparam Lhs Type of resource managed by the first handle.
|
||||
* @tparam Rhs Type of resource managed by the second handle.
|
||||
* @param lhs A valid handle.
|
||||
* @param rhs A valid handle.
|
||||
* @return True if the first handle is less than or equal to the second, false
|
||||
* otherwise.
|
||||
*/
|
||||
template<typename Res, typename Other>
|
||||
[[nodiscard]] bool operator<=(const resource<Res> &lhs, const resource<Other> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] bool operator<=(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
|
||||
return !(lhs > rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two handles.
|
||||
* @tparam Res Type of resource managed by the first handle.
|
||||
* @tparam Other Type of resource managed by the second handle.
|
||||
* @tparam Lhs Type of resource managed by the first handle.
|
||||
* @tparam Rhs Type of resource managed by the second handle.
|
||||
* @param lhs A valid handle.
|
||||
* @param rhs A valid handle.
|
||||
* @return True if the first handle is greater than or equal to the second,
|
||||
* false otherwise.
|
||||
*/
|
||||
template<typename Res, typename Other>
|
||||
[[nodiscard]] bool operator>=(const resource<Res> &lhs, const resource<Other> &rhs) noexcept {
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] bool operator>=(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,11 +12,7 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
@@ -43,11 +39,7 @@ template<typename... Class, typename Ret, typename... Args>
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Basic delegate implementation.
|
||||
@@ -76,7 +68,13 @@ class delegate<Ret(Args...)> {
|
||||
[[nodiscard]] auto wrap(std::index_sequence<Index...>) noexcept {
|
||||
return [](const void *, Args... args) -> Ret {
|
||||
[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
|
||||
return static_cast<Ret>(std::invoke(Candidate, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
|
||||
|
||||
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))...));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -85,7 +83,13 @@ class delegate<Ret(Args...)> {
|
||||
return [](const void *payload, Args... args) -> Ret {
|
||||
[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
|
||||
Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
|
||||
return static_cast<Ret>(std::invoke(Candidate, *curr, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
|
||||
|
||||
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))...));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -94,7 +98,13 @@ class delegate<Ret(Args...)> {
|
||||
return [](const void *payload, Args... args) -> Ret {
|
||||
[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
|
||||
Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
|
||||
return static_cast<Ret>(std::invoke(Candidate, curr, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
|
||||
|
||||
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))...));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -233,6 +243,14 @@ public:
|
||||
fn = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a pointer to the stored callable function target, if any.
|
||||
* @return An opaque pointer to the stored callable function target.
|
||||
*/
|
||||
[[nodiscard]] function_type *target() const noexcept {
|
||||
return fn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the instance or the payload linked to a delegate, if any.
|
||||
* @return An opaque pointer to the underlying data.
|
||||
|
||||
@@ -17,11 +17,7 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
struct basic_dispatcher_handler {
|
||||
@@ -75,14 +71,14 @@ public:
|
||||
|
||||
template<typename... Args>
|
||||
void enqueue(Args &&...args) {
|
||||
if constexpr(std::is_aggregate_v<Type>) {
|
||||
if constexpr(std::is_aggregate_v<Type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<Type>)) {
|
||||
events.push_back(Type{std::forward<Args>(args)...});
|
||||
} else {
|
||||
events.emplace_back(std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t size() const noexcept override {
|
||||
[[nodiscard]] std::size_t size() const noexcept override {
|
||||
return events.size();
|
||||
}
|
||||
|
||||
@@ -92,11 +88,7 @@ private:
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Basic dispatcher implementation.
|
||||
@@ -179,7 +171,9 @@ public:
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
basic_dispatcher(basic_dispatcher &&other, const allocator_type &allocator) noexcept
|
||||
: pools{container_type{std::move(other.pools.first()), allocator}, 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");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
@@ -187,6 +181,7 @@ public:
|
||||
* @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);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,9 @@ public:
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
emitter(emitter &&other, const allocator_type &allocator) noexcept
|
||||
: handlers{container_type{std::move(other.handlers.first()), allocator}, 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 an emitter is not allowed");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
@@ -84,6 +86,8 @@ public:
|
||||
* @return This dispatcher.
|
||||
*/
|
||||
emitter &operator=(emitter &&other) noexcept {
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying an emitter is not allowed");
|
||||
|
||||
handlers = std::move(other.handlers);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#ifndef ENTT_SIGNAL_SIGH_HPP
|
||||
#define ENTT_SIGNAL_SIGH_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -52,11 +52,11 @@ class sigh;
|
||||
*/
|
||||
template<typename Ret, typename... Args, typename Allocator>
|
||||
class sigh<Ret(Args...), Allocator> {
|
||||
/*! @brief A sink is allowed to modify a signal. */
|
||||
friend class sink<sigh<Ret(Args...), Allocator>>;
|
||||
|
||||
using alloc_traits = std::allocator_traits<Allocator>;
|
||||
using container_type = std::vector<delegate<Ret(Args...)>, typename alloc_traits::template rebind_alloc<delegate<Ret(Args...)>>>;
|
||||
using delegate_type = delegate<Ret(Args...)>;
|
||||
using container_type = std::vector<delegate_type, typename alloc_traits::template rebind_alloc<delegate_type>>;
|
||||
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
@@ -168,8 +168,8 @@ public:
|
||||
* @param args Arguments to use to invoke listeners.
|
||||
*/
|
||||
void publish(Args... args) const {
|
||||
for(auto &&call: std::as_const(calls)) {
|
||||
call(args...);
|
||||
for(auto pos = calls.size(); pos; --pos) {
|
||||
calls[pos - 1u](args...);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,24 +189,24 @@ public:
|
||||
*/
|
||||
template<typename Func>
|
||||
void collect(Func func, Args... args) const {
|
||||
for(auto &&call: calls) {
|
||||
if constexpr(std::is_void_v<Ret>) {
|
||||
for(auto pos = calls.size(); pos; --pos) {
|
||||
if constexpr(std::is_void_v<Ret> || !std::is_invocable_v<Func, Ret>) {
|
||||
calls[pos - 1u](args...);
|
||||
|
||||
if constexpr(std::is_invocable_r_v<bool, Func>) {
|
||||
call(args...);
|
||||
if(func()) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
call(args...);
|
||||
func();
|
||||
}
|
||||
} else {
|
||||
if constexpr(std::is_invocable_r_v<bool, Func, Ret>) {
|
||||
if(func(call(args...))) {
|
||||
if(func(calls[pos - 1u](args...))) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
func(call(args...));
|
||||
func(calls[pos - 1u](args...));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -224,7 +224,6 @@ private:
|
||||
* the sink that generated it.
|
||||
*/
|
||||
class connection {
|
||||
/*! @brief A sink is allowed to create connection objects. */
|
||||
template<typename>
|
||||
friend class sink;
|
||||
|
||||
@@ -358,6 +357,7 @@ private:
|
||||
template<typename Ret, typename... Args, typename Allocator>
|
||||
class sink<sigh<Ret(Args...), Allocator>> {
|
||||
using signal_type = sigh<Ret(Args...), Allocator>;
|
||||
using delegate_type = typename signal_type::delegate_type;
|
||||
using difference_type = typename signal_type::container_type::difference_type;
|
||||
|
||||
template<auto Candidate, typename Type>
|
||||
@@ -370,13 +370,14 @@ class sink<sigh<Ret(Args...), Allocator>> {
|
||||
sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>();
|
||||
}
|
||||
|
||||
auto before(delegate<Ret(Args...)> call) {
|
||||
const auto &calls = signal->calls;
|
||||
const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call));
|
||||
|
||||
sink other{*this};
|
||||
other.offset = calls.cend() - it;
|
||||
return other;
|
||||
template<typename Func>
|
||||
void disconnect_if(Func callback) {
|
||||
for(auto pos = signal->calls.size(); pos; --pos) {
|
||||
if(auto &elem = signal->calls[pos - 1u]; callback(elem)) {
|
||||
elem = std::move(signal->calls.back());
|
||||
signal->calls.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -385,8 +386,7 @@ public:
|
||||
* @param ref A valid reference to a signal object.
|
||||
*/
|
||||
sink(sigh<Ret(Args...), Allocator> &ref) noexcept
|
||||
: offset{},
|
||||
signal{&ref} {}
|
||||
: signal{&ref} {}
|
||||
|
||||
/**
|
||||
* @brief Returns false if at least a listener is connected to the sink.
|
||||
@@ -396,89 +396,9 @@ public:
|
||||
return signal->calls.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a sink that connects before a given free function or an
|
||||
* unbound member.
|
||||
* @tparam Function A valid free function pointer.
|
||||
* @return A properly initialized sink object.
|
||||
*/
|
||||
template<auto Function>
|
||||
[[nodiscard]] sink before() {
|
||||
delegate<Ret(Args...)> call{};
|
||||
call.template connect<Function>();
|
||||
return before(std::move(call));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a sink that connects before a free function with payload
|
||||
* or a bound member.
|
||||
* @tparam Candidate Member or free function to look for.
|
||||
* @tparam Type Type of class or type of payload.
|
||||
* @param value_or_instance A valid object that fits the purpose.
|
||||
* @return A properly initialized sink object.
|
||||
*/
|
||||
template<auto Candidate, typename Type>
|
||||
[[nodiscard]] sink before(Type &&value_or_instance) {
|
||||
delegate<Ret(Args...)> call{};
|
||||
call.template connect<Candidate>(value_or_instance);
|
||||
return before(std::move(call));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a sink that connects before a given instance or specific
|
||||
* payload.
|
||||
* @tparam Type Type of class or type of payload.
|
||||
* @param value_or_instance A valid object that fits the purpose.
|
||||
* @return A properly initialized sink object.
|
||||
*/
|
||||
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<std::remove_pointer_t<Type>>, void>, sink>>
|
||||
[[nodiscard]] sink before(Type &value_or_instance) {
|
||||
return before(&value_or_instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a sink that connects before a given instance or specific
|
||||
* payload.
|
||||
* @param value_or_instance A valid pointer that fits the purpose.
|
||||
* @return A properly initialized sink object.
|
||||
*/
|
||||
[[nodiscard]] sink before(const void *value_or_instance) {
|
||||
sink other{*this};
|
||||
|
||||
if(value_or_instance) {
|
||||
const auto &calls = signal->calls;
|
||||
const auto it = std::find_if(calls.cbegin(), calls.cend(), [value_or_instance](const auto &delegate) {
|
||||
return delegate.data() == value_or_instance;
|
||||
});
|
||||
|
||||
other.offset = calls.cend() - it;
|
||||
}
|
||||
|
||||
return other;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a sink that connects before anything else.
|
||||
* @return A properly initialized sink object.
|
||||
*/
|
||||
[[nodiscard]] sink before() {
|
||||
sink other{*this};
|
||||
other.offset = signal->calls.size();
|
||||
return other;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connects a free function (with or without payload), a bound or an
|
||||
* unbound member to a signal.
|
||||
*
|
||||
* The signal isn't responsible for the connected object or the payload, if
|
||||
* any. Users must guarantee that the lifetime of the instance overcomes the
|
||||
* one of the signal. On the other side, the signal handler performs
|
||||
* checks to avoid multiple connections for the same function.<br/>
|
||||
* When used to connect a free function with payload, its signature must be
|
||||
* such that the instance is the first argument before the ones used to
|
||||
* define the signal itself.
|
||||
*
|
||||
* @tparam Candidate Function or member to connect to the signal.
|
||||
* @tparam Type Type of class or type of payload, if any.
|
||||
* @param value_or_instance A valid object that fits the purpose, if any.
|
||||
@@ -488,9 +408,9 @@ public:
|
||||
connection connect(Type &&...value_or_instance) {
|
||||
disconnect<Candidate>(value_or_instance...);
|
||||
|
||||
delegate<Ret(Args...)> call{};
|
||||
delegate_type call{};
|
||||
call.template connect<Candidate>(value_or_instance...);
|
||||
signal->calls.insert(signal->calls.end() - offset, std::move(call));
|
||||
signal->calls.push_back(std::move(call));
|
||||
|
||||
delegate<void(void *)> conn{};
|
||||
conn.template connect<&release<Candidate, Type...>>(value_or_instance...);
|
||||
@@ -506,21 +426,9 @@ public:
|
||||
*/
|
||||
template<auto Candidate, typename... Type>
|
||||
void disconnect(Type &&...value_or_instance) {
|
||||
auto &calls = signal->calls;
|
||||
delegate<Ret(Args...)> call{};
|
||||
delegate_type call{};
|
||||
call.template connect<Candidate>(value_or_instance...);
|
||||
calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disconnects free functions with payload or bound members from a
|
||||
* signal.
|
||||
* @tparam Type Type of class or type of payload.
|
||||
* @param value_or_instance A valid object that fits the purpose.
|
||||
*/
|
||||
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<std::remove_pointer_t<Type>>, void>>>
|
||||
void disconnect(Type &value_or_instance) {
|
||||
disconnect(&value_or_instance);
|
||||
disconnect_if([&call](const auto &elem) { return elem == call; });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -530,9 +438,7 @@ public:
|
||||
*/
|
||||
void disconnect(const void *value_or_instance) {
|
||||
if(value_or_instance) {
|
||||
auto &calls = signal->calls;
|
||||
auto predicate = [value_or_instance](const auto &delegate) { return delegate.data() == value_or_instance; };
|
||||
calls.erase(std::remove_if(calls.begin(), calls.end(), std::move(predicate)), calls.end());
|
||||
disconnect_if([value_or_instance](const auto &elem) { return elem.data() == value_or_instance; });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -542,7 +448,6 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
difference_type offset;
|
||||
signal_type *signal;
|
||||
};
|
||||
|
||||
|
||||
1
test/.bazelrc
Normal file
1
test/.bazelrc
Normal file
@@ -0,0 +1 @@
|
||||
import "%workspace%/../.bazelrc"
|
||||
0
test/BUILD.bazel
Normal file
0
test/BUILD.bazel
Normal file
@@ -1,6 +1,4 @@
|
||||
#
|
||||
# Tests configuration
|
||||
#
|
||||
|
||||
include(FetchContent)
|
||||
include(CheckCXXSourceCompiles)
|
||||
@@ -34,9 +32,16 @@ else()
|
||||
add_library(GTest::Main ALIAS gtest_main)
|
||||
|
||||
target_compile_features(gtest PUBLIC cxx_std_17)
|
||||
set_target_properties(gtest PROPERTIES CXX_CLANG_TIDY "")
|
||||
|
||||
target_compile_features(gtest_main PUBLIC cxx_std_17)
|
||||
set_target_properties(gtest_main PROPERTIES CXX_CLANG_TIDY "")
|
||||
|
||||
target_compile_features(gmock PUBLIC cxx_std_17)
|
||||
set_target_properties(gmock PROPERTIES CXX_CLANG_TIDY "")
|
||||
|
||||
target_compile_features(gmock_main PUBLIC cxx_std_17)
|
||||
set_target_properties(gmock_main PROPERTIES CXX_CLANG_TIDY "")
|
||||
endif()
|
||||
|
||||
include_directories($<TARGET_PROPERTY:EnTT,INTERFACE_INCLUDE_DIRECTORIES>)
|
||||
@@ -51,10 +56,33 @@ function(SETUP_TARGET TARGET_NAME)
|
||||
target_compile_options(
|
||||
${TARGET_NAME}
|
||||
PRIVATE
|
||||
/EHsc /W1 /wd4996 /w14800
|
||||
$<$<CONFIG:Debug>:/Od>
|
||||
# vs2017 emits too many false positives for my tastes
|
||||
$<IF:$<EQUAL:${MSVC_TOOLSET_VERSION},141>, /W1, /W4>
|
||||
# clang-cl goes a little wrong with some warnings instead
|
||||
$<$<STREQUAL:"${CMAKE_CXX_COMPILER_ID}","Clang">:
|
||||
-Wno-deprecated-declarations
|
||||
-Wno-ignored-qualifiers
|
||||
-Wno-unknown-warning-option
|
||||
-Wno-exceptions
|
||||
-Wno-unused-local-typedef
|
||||
-Wno-unused-private-field
|
||||
>
|
||||
# documentation diagnostic turned on for clang-cl only
|
||||
$<$<STREQUAL:"${CMAKE_CXX_COMPILER_ID}","Clang">:-Wdocumentation>
|
||||
# warnings from compilers that think I don't know what I'm doing
|
||||
$<$<STREQUAL:"${CMAKE_CXX_COMPILER_ID}","Clang">:-Wcomma>
|
||||
/EHsc /wd4324 /wd4996
|
||||
# disabling INCREMENTAL is required by SizeBench
|
||||
$<$<CONFIG:Debug>:/Od /INCREMENTAL:NO>
|
||||
$<$<CONFIG:Release>:/O2>
|
||||
)
|
||||
|
||||
target_link_options(
|
||||
${TARGET_NAME}
|
||||
PRIVATE
|
||||
# disabling INCREMENTAL is required by SizeBench
|
||||
$<$<CONFIG:Debug>:/INCREMENTAL:NO>
|
||||
)
|
||||
else()
|
||||
target_compile_options(
|
||||
${TARGET_NAME}
|
||||
@@ -92,29 +120,36 @@ function(SETUP_BASIC_TEST TEST_NAME TEST_SOURCES)
|
||||
target_link_libraries(${TEST_NAME} PRIVATE GTest::Main Threads::Threads)
|
||||
SETUP_TARGET(${TEST_NAME} ${ARGN})
|
||||
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME})
|
||||
set_tests_properties(${TEST_NAME} PROPERTIES TIMEOUT 60)
|
||||
endfunction()
|
||||
|
||||
function(SETUP_LIB_TEST TEST_NAME)
|
||||
add_library(_${TEST_NAME} SHARED $<TARGET_OBJECTS:odr> lib/${TEST_NAME}/lib.cpp)
|
||||
SETUP_TARGET(_${TEST_NAME} ENTT_API_EXPORT)
|
||||
SETUP_BASIC_TEST(lib_${TEST_NAME} lib/${TEST_NAME}/main.cpp ENTT_API_IMPORT)
|
||||
target_link_libraries(lib_${TEST_NAME} PRIVATE _${TEST_NAME})
|
||||
function(SETUP_LIB_SHARED_TEST TEST_NAME SUB_PATH)
|
||||
set(TARGET_NAME ${TEST_NAME}_${SUB_PATH})
|
||||
add_library(_${TARGET_NAME} SHARED $<TARGET_OBJECTS:odr> lib/${TEST_NAME}/${SUB_PATH}/lib.cpp)
|
||||
SETUP_TARGET(_${TARGET_NAME} ENTT_API_EXPORT)
|
||||
SETUP_BASIC_TEST(lib_${TARGET_NAME} lib/${TEST_NAME}/${SUB_PATH}/main.cpp ENTT_API_IMPORT)
|
||||
set_target_properties(lib_${TARGET_NAME} PROPERTIES CXX_CLANG_TIDY "")
|
||||
target_link_libraries(lib_${TARGET_NAME} PRIVATE _${TARGET_NAME})
|
||||
endfunction()
|
||||
|
||||
function(SETUP_PLUGIN_TEST TEST_NAME)
|
||||
add_library(_${TEST_NAME} MODULE $<TARGET_OBJECTS:odr> lib/${TEST_NAME}/plugin.cpp)
|
||||
SETUP_TARGET(_${TEST_NAME} ${ARGVN})
|
||||
SETUP_BASIC_TEST(lib_${TEST_NAME} lib/${TEST_NAME}/main.cpp PLUGIN="$<TARGET_FILE:_${TEST_NAME}>" ${ARGVN})
|
||||
target_include_directories(_${TEST_NAME} PRIVATE ${cr_INCLUDE_DIR})
|
||||
target_include_directories(lib_${TEST_NAME} PRIVATE ${cr_INCLUDE_DIR})
|
||||
target_link_libraries(lib_${TEST_NAME} PRIVATE ${CMAKE_DL_LIBS})
|
||||
add_dependencies(lib_${TEST_NAME} _${TEST_NAME})
|
||||
function(SETUP_LIB_PLUGIN_TEST TEST_NAME SUB_PATH)
|
||||
set(TARGET_NAME ${TEST_NAME}_${SUB_PATH})
|
||||
add_library(_${TARGET_NAME} MODULE $<TARGET_OBJECTS:odr> lib/${TEST_NAME}/${SUB_PATH}/plugin.cpp)
|
||||
SETUP_TARGET(_${TARGET_NAME} ${ARGVN})
|
||||
SETUP_BASIC_TEST(lib_${TARGET_NAME} lib/${TEST_NAME}/${SUB_PATH}/main.cpp PLUGIN="$<TARGET_FILE:_${TARGET_NAME}>" ${ARGVN})
|
||||
set_target_properties(_${TARGET_NAME} PROPERTIES CXX_CLANG_TIDY "")
|
||||
set_target_properties(lib_${TARGET_NAME} PROPERTIES CXX_CLANG_TIDY "")
|
||||
target_include_directories(_${TARGET_NAME} PRIVATE ${cr_INCLUDE_DIR})
|
||||
target_include_directories(lib_${TARGET_NAME} PRIVATE ${cr_INCLUDE_DIR})
|
||||
target_link_libraries(lib_${TARGET_NAME} PRIVATE ${CMAKE_DL_LIBS})
|
||||
add_dependencies(lib_${TARGET_NAME} _${TARGET_NAME})
|
||||
endfunction()
|
||||
|
||||
# Test benchmark
|
||||
|
||||
if(ENTT_BUILD_BENCHMARK)
|
||||
SETUP_BASIC_TEST(benchmark benchmark/benchmark.cpp)
|
||||
set_target_properties(benchmark PROPERTIES CXX_CLANG_TIDY "")
|
||||
endif()
|
||||
|
||||
# Test example
|
||||
@@ -122,6 +157,7 @@ endif()
|
||||
if(ENTT_BUILD_EXAMPLE)
|
||||
SETUP_BASIC_TEST(custom_identifier example/custom_identifier.cpp)
|
||||
SETUP_BASIC_TEST(entity_copy example/entity_copy.cpp)
|
||||
SETUP_BASIC_TEST(reserved_bits example/reserved_bits.cpp)
|
||||
SETUP_BASIC_TEST(signal_less example/signal_less.cpp)
|
||||
endif()
|
||||
|
||||
@@ -142,19 +178,21 @@ if(ENTT_BUILD_LIB)
|
||||
set(cr_INCLUDE_DIR ${cr_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
SETUP_LIB_TEST(dispatcher)
|
||||
SETUP_LIB_TEST(emitter)
|
||||
SETUP_LIB_TEST(locator)
|
||||
SETUP_LIB_TEST(meta)
|
||||
SETUP_LIB_TEST(registry)
|
||||
SETUP_LIB_SHARED_TEST(dispatcher shared)
|
||||
SETUP_LIB_PLUGIN_TEST(dispatcher plugin)
|
||||
|
||||
SETUP_PLUGIN_TEST(dispatcher_plugin)
|
||||
SETUP_PLUGIN_TEST(emitter_plugin)
|
||||
SETUP_PLUGIN_TEST(locator_plugin)
|
||||
SETUP_PLUGIN_TEST(meta_plugin)
|
||||
SETUP_PLUGIN_TEST(registry_plugin)
|
||||
SETUP_LIB_SHARED_TEST(emitter shared)
|
||||
SETUP_LIB_PLUGIN_TEST(emitter plugin)
|
||||
|
||||
SETUP_PLUGIN_TEST(meta_plugin_std ENTT_STANDARD_CPP)
|
||||
SETUP_LIB_SHARED_TEST(locator shared)
|
||||
SETUP_LIB_PLUGIN_TEST(locator plugin)
|
||||
|
||||
SETUP_LIB_SHARED_TEST(meta shared)
|
||||
SETUP_LIB_PLUGIN_TEST(meta plugin)
|
||||
SETUP_LIB_PLUGIN_TEST(meta plugin_std ENTT_STANDARD_CPP)
|
||||
|
||||
SETUP_LIB_SHARED_TEST(registry shared)
|
||||
SETUP_LIB_PLUGIN_TEST(registry plugin)
|
||||
endif()
|
||||
|
||||
# Test snapshot
|
||||
@@ -163,7 +201,7 @@ if(ENTT_BUILD_SNAPSHOT)
|
||||
FetchContent_Declare(
|
||||
cereal
|
||||
GIT_REPOSITORY https://github.com/USCiLab/cereal.git
|
||||
GIT_TAG v1.2.2
|
||||
GIT_TAG v1.3.2
|
||||
GIT_SHALLOW 1
|
||||
)
|
||||
|
||||
@@ -175,6 +213,8 @@ if(ENTT_BUILD_SNAPSHOT)
|
||||
endif()
|
||||
|
||||
SETUP_BASIC_TEST(cereal snapshot/snapshot.cpp)
|
||||
|
||||
set_target_properties(cereal PROPERTIES CXX_CLANG_TIDY "")
|
||||
target_include_directories(cereal PRIVATE ${cereal_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
@@ -215,10 +255,13 @@ SETUP_BASIC_TEST(observer entt/entity/observer.cpp)
|
||||
SETUP_BASIC_TEST(organizer entt/entity/organizer.cpp)
|
||||
SETUP_BASIC_TEST(registry entt/entity/registry.cpp)
|
||||
SETUP_BASIC_TEST(runtime_view entt/entity/runtime_view.cpp)
|
||||
SETUP_BASIC_TEST(sigh_mixin entt/entity/sigh_mixin.cpp)
|
||||
SETUP_BASIC_TEST(snapshot entt/entity/snapshot.cpp)
|
||||
SETUP_BASIC_TEST(sparse_set entt/entity/sparse_set.cpp)
|
||||
SETUP_BASIC_TEST(storage entt/entity/storage.cpp)
|
||||
SETUP_BASIC_TEST(storage_mixin entt/entity/storage_mixin.cpp)
|
||||
SETUP_BASIC_TEST(storage_entity entt/entity/storage_entity.cpp)
|
||||
SETUP_BASIC_TEST(storage_no_instance entt/entity/storage_no_instance.cpp)
|
||||
SETUP_BASIC_TEST(storage_utility entt/entity/storage_utility.cpp)
|
||||
SETUP_BASIC_TEST(view entt/entity/view.cpp)
|
||||
|
||||
# Test graph
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user