Compare commits
2286 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 | ||
|
|
76298bc26a | ||
|
|
7c84ce666e | ||
|
|
a02a7c67cb | ||
|
|
2bb913b898 | ||
|
|
94a8bb57dd | ||
|
|
f06852b5c2 | ||
|
|
fb14d26015 | ||
|
|
6a53c8eaca | ||
|
|
4a98874431 | ||
|
|
baf60502c6 | ||
|
|
2a01b78b32 | ||
|
|
b02e4efe4d | ||
|
|
3c8f6d2831 | ||
|
|
95b443af16 | ||
|
|
b54766d2b4 | ||
|
|
25929d6188 | ||
|
|
4398c962e7 | ||
|
|
fe8919c540 | ||
|
|
bdaeef856d | ||
|
|
57ad7696be | ||
|
|
7c58c83ee0 | ||
|
|
dd7aaa30ca | ||
|
|
76f95945fc | ||
|
|
869836ba79 | ||
|
|
2358552c27 | ||
|
|
e22ea5e189 | ||
|
|
496fefdd01 | ||
|
|
26f85050c1 | ||
|
|
d478b5bc6a | ||
|
|
c8337e529e | ||
|
|
f0995297cf | ||
|
|
b395df0f56 | ||
|
|
69a6cc6656 | ||
|
|
bf73216a76 | ||
|
|
621860718b | ||
|
|
c34c5da277 | ||
|
|
8dcabba31b | ||
|
|
294064f11f | ||
|
|
d125229312 | ||
|
|
11c1e23a91 | ||
|
|
82bc2e1fe4 | ||
|
|
92a00f591d | ||
|
|
04716a7fde | ||
|
|
b6155080b2 | ||
|
|
0a61de4a09 | ||
|
|
7121da100a | ||
|
|
7bee969ebf | ||
|
|
4a0a534796 | ||
|
|
a0041d9062 | ||
|
|
3dd768bb9b | ||
|
|
2a307a681f | ||
|
|
dd1211d021 | ||
|
|
09a600d68a | ||
|
|
7c25b46513 | ||
|
|
dc42486de8 | ||
|
|
36411faf43 | ||
|
|
a9cd565d65 | ||
|
|
0adbb0ad16 | ||
|
|
db31f77d63 | ||
|
|
c275afef28 | ||
|
|
4bad4f3067 | ||
|
|
d2048c037a | ||
|
|
2a39717893 | ||
|
|
49c2ae0e77 | ||
|
|
869c18f150 | ||
|
|
d79cb5ca98 | ||
|
|
acc6390312 | ||
|
|
2e59fbbccc | ||
|
|
bec0037797 | ||
|
|
d08c7ddb58 | ||
|
|
696681fefa | ||
|
|
5f9faf7a32 | ||
|
|
f160503aaf | ||
|
|
b1e9f75a28 | ||
|
|
91da98353d | ||
|
|
bc86ceb707 | ||
|
|
0baf316e5f | ||
|
|
082605f9d3 | ||
|
|
0fdd7d7a3a | ||
|
|
eee59e6af9 | ||
|
|
223a8628e3 | ||
|
|
c0bb603210 | ||
|
|
2981d8746a | ||
|
|
c06280894b | ||
|
|
2f2bdcac4d | ||
|
|
2739964a1f | ||
|
|
c3a2a44830 | ||
|
|
37a2c3b76c | ||
|
|
146a7b2aa3 | ||
|
|
7e093c951f | ||
|
|
4901188409 | ||
|
|
15d78f80a6 | ||
|
|
d61dff45c1 | ||
|
|
34abddfbbd | ||
|
|
238ebe4e8f | ||
|
|
502accabbc | ||
|
|
9d1df1dae4 | ||
|
|
a39cfb9e99 | ||
|
|
27d0e5f55e | ||
|
|
a0e5d2c495 | ||
|
|
d2c89d2a40 | ||
|
|
54f66c9094 | ||
|
|
10be00b352 | ||
|
|
9032051bf7 | ||
|
|
2d2e4c784e | ||
|
|
0d4674d8e2 | ||
|
|
ac06ad35c1 | ||
|
|
27e492d750 | ||
|
|
f8281f2a37 | ||
|
|
9fc717fa6e | ||
|
|
4a0f2f3fe9 | ||
|
|
ebdd4118fd | ||
|
|
54ae48936f | ||
|
|
3c9045a3a8 | ||
|
|
f80f179499 | ||
|
|
69c6ee0675 | ||
|
|
4e28d96bef | ||
|
|
6552cc4599 | ||
|
|
75b10cfa09 | ||
|
|
dc394f62c6 | ||
|
|
ea6d6662b4 | ||
|
|
de3ecbdd28 | ||
|
|
e5c6559836 | ||
|
|
0d422236d6 | ||
|
|
199b065fe3 | ||
|
|
c46e107107 | ||
|
|
a3f66387b2 | ||
|
|
33383bd0c3 | ||
|
|
35b8d95671 | ||
|
|
6e254fb39d | ||
|
|
f50a03dcb4 | ||
|
|
71554b2c19 | ||
|
|
e7762980ec | ||
|
|
c62099969e | ||
|
|
c45ee64800 | ||
|
|
b53c243ef7 | ||
|
|
9ee084fc40 | ||
|
|
9c969fb6ac | ||
|
|
6a8a9c4b31 | ||
|
|
bc0233e34b | ||
|
|
afd91b4e5a | ||
|
|
50349dc2af | ||
|
|
4b10981d75 | ||
|
|
b16cefd534 | ||
|
|
46d6625467 | ||
|
|
a1a8e134d2 | ||
|
|
0b6bbcc6a3 | ||
|
|
5fac237153 | ||
|
|
d2cc1c880c | ||
|
|
ac18ad5823 | ||
|
|
9ac5f6d046 | ||
|
|
08adb29d91 | ||
|
|
1721d07746 | ||
|
|
9401d560b0 | ||
|
|
7eae8523cb | ||
|
|
45b5eeeaa0 | ||
|
|
12b436e5b8 | ||
|
|
534dc7e7b7 | ||
|
|
5dfac37dac | ||
|
|
f17a4e7f8e | ||
|
|
4b586efe66 | ||
|
|
db6dc1e21b | ||
|
|
da11771dab | ||
|
|
475e3a70b0 | ||
|
|
5255afc80d | ||
|
|
7a3f59cc5d | ||
|
|
9470133e33 | ||
|
|
5ab0f9e4c0 | ||
|
|
ef91cd5375 | ||
|
|
cd9553ca10 | ||
|
|
8b27a846a6 | ||
|
|
9d961ad8aa | ||
|
|
0952f3ce03 | ||
|
|
ab33111192 | ||
|
|
9a457c5aca | ||
|
|
4f1c86c52e | ||
|
|
c605e3b5a1 | ||
|
|
5a8c9b8a95 | ||
|
|
2b8e9cab18 | ||
|
|
c3dec4b04e | ||
|
|
699aa5cd96 | ||
|
|
fea0210b6e | ||
|
|
cf12765738 | ||
|
|
17f47fbc92 | ||
|
|
1e36b6c4b6 | ||
|
|
28692fc142 | ||
|
|
6eff5a88f6 | ||
|
|
5742100973 | ||
|
|
c52eea7db7 | ||
|
|
2f59f5a8fa | ||
|
|
e77fe56e8f | ||
|
|
eea1e7b1f8 | ||
|
|
b0fbccdf10 | ||
|
|
4f3faef153 | ||
|
|
3ae3c82fbb | ||
|
|
1063fbccbe | ||
|
|
b9e60c679a | ||
|
|
88fd79069b | ||
|
|
ab859f7fa7 | ||
|
|
cc4d4dfc2b | ||
|
|
bd62e817dc | ||
|
|
de34a109e1 | ||
|
|
e0e45719fa | ||
|
|
b21b254258 | ||
|
|
ca3ef33f46 | ||
|
|
8c4803d906 | ||
|
|
ffb9e4b0eb | ||
|
|
6010dbc368 | ||
|
|
a6343bd152 | ||
|
|
9e4a7f48a4 | ||
|
|
6ad7424d4a | ||
|
|
218fa1ba35 | ||
|
|
b45db747a8 | ||
|
|
eedecc07fd | ||
|
|
dcea36de7a | ||
|
|
ed0e3f4435 | ||
|
|
8595e4bd99 | ||
|
|
e31b81294d | ||
|
|
ac6e9cf2f0 | ||
|
|
dac4ae94c0 | ||
|
|
b31b585805 | ||
|
|
1368c56655 | ||
|
|
6cab12a14f | ||
|
|
924a09a9c5 | ||
|
|
e4f6293f42 | ||
|
|
f197b32b02 | ||
|
|
b80ab36418 | ||
|
|
d5588333f7 | ||
|
|
10755275ea | ||
|
|
a30cecdf36 | ||
|
|
0068afa06c | ||
|
|
38597a6499 | ||
|
|
65ee240e8e | ||
|
|
336bcf4d11 | ||
|
|
ed74a4c213 | ||
|
|
3e29617da8 | ||
|
|
715b3e450f | ||
|
|
1ab10f3b28 | ||
|
|
304d8a80be | ||
|
|
560a3f664e | ||
|
|
41081204f9 | ||
|
|
b895dfc901 | ||
|
|
93a1d65103 | ||
|
|
443d53400d | ||
|
|
9725582089 | ||
|
|
cd2634a5d4 | ||
|
|
798d1a4f9e | ||
|
|
e13996f68a | ||
|
|
1ee077d511 | ||
|
|
ccf301c73d | ||
|
|
461fe715b7 | ||
|
|
9549bb4f4d | ||
|
|
0215722103 | ||
|
|
7c6606f27e | ||
|
|
f0b10965fb | ||
|
|
53f2bb7701 | ||
|
|
945e61d668 | ||
|
|
e74c4bb684 | ||
|
|
a8eebcb0e2 | ||
|
|
1581aa1cf5 | ||
|
|
839a2cc86d | ||
|
|
3062114f0c | ||
|
|
ab3abf23ed | ||
|
|
b5a7812ab4 | ||
|
|
565de36155 | ||
|
|
c76f078834 | ||
|
|
358a9742c5 | ||
|
|
7f9e200cfb | ||
|
|
a12710af70 | ||
|
|
871234ec76 | ||
|
|
0c062cc351 | ||
|
|
67271e4672 | ||
|
|
2489a95dab | ||
|
|
70b8bd27b8 | ||
|
|
ee88cf16d1 | ||
|
|
d83e818517 | ||
|
|
6462ceb1f5 | ||
|
|
e2f8ea110a | ||
|
|
f1acbbedf9 | ||
|
|
8510f535a5 | ||
|
|
9a97a57423 | ||
|
|
a50cc628ec | ||
|
|
dc922c9e4c | ||
|
|
2f2b0cf98a | ||
|
|
798d5eeb86 | ||
|
|
02d8cefcde | ||
|
|
f627593d63 | ||
|
|
9aeddc2cb1 | ||
|
|
6650071d24 | ||
|
|
006822c7fc | ||
|
|
8bed15e469 | ||
|
|
4200bb9ebb | ||
|
|
fdae346aa6 | ||
|
|
01acb15fab | ||
|
|
3a6764a685 | ||
|
|
e2e8a575c6 | ||
|
|
7205fb63fa | ||
|
|
c7b59fdc14 | ||
|
|
e9a59bb014 | ||
|
|
76ba063f29 | ||
|
|
2988a74fe4 | ||
|
|
72bc9e3973 | ||
|
|
31fb2bc2c3 | ||
|
|
768f8cd74c | ||
|
|
751e044ce2 | ||
|
|
3406250cf6 | ||
|
|
e510bdf4f2 | ||
|
|
fccdff05e3 | ||
|
|
e530b3d6f6 | ||
|
|
0f0b70be23 | ||
|
|
e39f235287 | ||
|
|
af5acb8d32 | ||
|
|
a2f70cb5e4 | ||
|
|
25d10f03bb | ||
|
|
85c1ad060f | ||
|
|
d9b1cc0db3 | ||
|
|
34205014e9 | ||
|
|
749106aa47 | ||
|
|
b828e0295a | ||
|
|
2345002c1c | ||
|
|
2ce18a47b4 | ||
|
|
76b380d7c0 | ||
|
|
d3244ab4da | ||
|
|
d06ccfb8e3 | ||
|
|
6f807d6f9e | ||
|
|
2b0045e23e | ||
|
|
57067bc362 | ||
|
|
abe185c003 | ||
|
|
85ca2f3562 | ||
|
|
3d03b01439 | ||
|
|
879e7d775f | ||
|
|
e9dbd10db4 | ||
|
|
acc95bd9b7 | ||
|
|
504c7b5d6d | ||
|
|
b20ab29d82 | ||
|
|
95b16a0b04 | ||
|
|
6ee64d1dc2 | ||
|
|
83ecd2d1d9 | ||
|
|
325a5dd353 | ||
|
|
e0ee35da61 | ||
|
|
b3fde98020 | ||
|
|
3c7c21cea3 | ||
|
|
c73d6c52bf | ||
|
|
c38c1454b4 | ||
|
|
06d1d23273 | ||
|
|
4081b46302 | ||
|
|
914bf49656 | ||
|
|
dc634c61c4 | ||
|
|
566878b29c | ||
|
|
0060479e33 | ||
|
|
1a0d892201 | ||
|
|
0424b63bf1 | ||
|
|
de9d3c04e2 | ||
|
|
b0e4a853cf | ||
|
|
ac8dfe29ae | ||
|
|
9d33a38ec4 | ||
|
|
e3076fa5d1 | ||
|
|
2067b2aea6 | ||
|
|
a14af95f9d | ||
|
|
8fd2ce8d47 | ||
|
|
d6edc64d65 | ||
|
|
f882158387 | ||
|
|
6f852c2dcb | ||
|
|
e47e74fc76 | ||
|
|
e604060369 | ||
|
|
80f8051c57 | ||
|
|
612c499a3d | ||
|
|
cceffe6ac4 | ||
|
|
a74416c730 | ||
|
|
0830704484 | ||
|
|
79f7eaaf9a | ||
|
|
389e038445 | ||
|
|
8f84bd7091 | ||
|
|
2146e3ded9 | ||
|
|
97c0582765 | ||
|
|
c3f230956c | ||
|
|
b914a4a93e | ||
|
|
befa0fe213 | ||
|
|
61ef5a44e0 | ||
|
|
131fd0d778 | ||
|
|
8973757b43 | ||
|
|
14ce88730f | ||
|
|
84cbfb2f91 | ||
|
|
e8f8520251 | ||
|
|
c26558cd6d | ||
|
|
71feb0516d | ||
|
|
382187089c | ||
|
|
4faeb5c44e | ||
|
|
ac8f569280 | ||
|
|
669420d31c | ||
|
|
df48cc9471 | ||
|
|
274a08070f | ||
|
|
c0e20825ac | ||
|
|
15d821f2f8 | ||
|
|
acbd38c1ed | ||
|
|
92f5f97d83 | ||
|
|
1128b9de81 | ||
|
|
fcd60467a7 | ||
|
|
2f51341633 | ||
|
|
5799f407ae | ||
|
|
c7a5e09c0e | ||
|
|
3318ac2699 | ||
|
|
437f1fea54 | ||
|
|
3ab43f894f | ||
|
|
86f98f40a1 | ||
|
|
655f1dd906 | ||
|
|
243a382485 | ||
|
|
4b74163306 | ||
|
|
50e67d7c15 | ||
|
|
c5afd8f589 | ||
|
|
c6ab65d552 | ||
|
|
1a853fe145 | ||
|
|
72abc8e4c4 | ||
|
|
e15ad2e21b | ||
|
|
5afc5529c2 | ||
|
|
7eed368dc2 | ||
|
|
600303bb5b | ||
|
|
bcfd6d1b4f | ||
|
|
e65b3a790d | ||
|
|
a28467d393 | ||
|
|
bf13d9363d | ||
|
|
ff9c472f86 | ||
|
|
46e601db6e | ||
|
|
d4482d0d26 | ||
|
|
281289c46a | ||
|
|
bc85d96938 | ||
|
|
c2475381fd | ||
|
|
6ac14f35ad | ||
|
|
de18834c91 | ||
|
|
fc7d28c723 | ||
|
|
7ac2ce50a0 | ||
|
|
9cf9661484 | ||
|
|
8dbf00ad3a | ||
|
|
e0e623fa36 | ||
|
|
4094669222 | ||
|
|
9e19244659 | ||
|
|
d41ccb6b34 | ||
|
|
72c1da2507 | ||
|
|
2e8c5ea2de | ||
|
|
65561fe431 | ||
|
|
cf401aa0b5 | ||
|
|
9b2c9936c2 | ||
|
|
e4aabc7feb | ||
|
|
610951771e | ||
|
|
f76959af63 | ||
|
|
0eb5b0437b | ||
|
|
c1ac684f9a | ||
|
|
473c36aba5 | ||
|
|
38e6722358 | ||
|
|
d2a0f86908 | ||
|
|
c6707bec97 | ||
|
|
5c6a11cb8f | ||
|
|
600cc5e167 | ||
|
|
c441a9ad97 | ||
|
|
c2d2ba29ea | ||
|
|
25af23d3ff | ||
|
|
cbd6fd8aee | ||
|
|
7a7fc20438 | ||
|
|
ca4b2b68a7 | ||
|
|
f3ae553706 | ||
|
|
fe6696b107 | ||
|
|
3a85d0f179 | ||
|
|
9845d2c87d | ||
|
|
ea8730c2a9 | ||
|
|
c81868f8f7 | ||
|
|
33d1839b75 | ||
|
|
df10a01af9 | ||
|
|
4aa40aba4c | ||
|
|
696c94e2ea | ||
|
|
bc5dfb9371 | ||
|
|
88c63b941d | ||
|
|
b8e12dc42a | ||
|
|
2974e959e5 | ||
|
|
67b2fc085c | ||
|
|
cb202c15a5 | ||
|
|
850abd440b | ||
|
|
01d125f53d | ||
|
|
3f0572c5ce | ||
|
|
cc6c45f591 | ||
|
|
8029777c4e | ||
|
|
a6171551bd | ||
|
|
6bb67ab5da | ||
|
|
a31c492c83 | ||
|
|
c36d5e4a9c | ||
|
|
17b3fc93b0 | ||
|
|
d2f4ffc433 | ||
|
|
1c8a296f49 | ||
|
|
de0e5862dd | ||
|
|
e822b8c99e | ||
|
|
48d767749d | ||
|
|
fa48c76dd1 | ||
|
|
754fe9f4ec | ||
|
|
1ad3adfff0 | ||
|
|
0c0fd4d7f7 | ||
|
|
72f5df82b2 | ||
|
|
c7407f5b6a | ||
|
|
f57975acca | ||
|
|
a8a0c9e571 | ||
|
|
e05277d56a | ||
|
|
0b1985ca7e | ||
|
|
637764ee4e | ||
|
|
c6e613c317 | ||
|
|
aa3e769463 | ||
|
|
630ba195d1 | ||
|
|
13276d2f10 | ||
|
|
d59407a339 | ||
|
|
92c2fd9409 | ||
|
|
063e6160e0 | ||
|
|
6155a10037 | ||
|
|
7428cb353c | ||
|
|
fe3b5b03eb | ||
|
|
497fb90b12 | ||
|
|
48ab892de1 | ||
|
|
a85ad88d5a | ||
|
|
cf2566ff74 | ||
|
|
619f51518a | ||
|
|
b26330adab | ||
|
|
83009bac02 | ||
|
|
8780d536c7 | ||
|
|
99f4134d9d | ||
|
|
ca02ab7d5d | ||
|
|
dc295ca0e1 | ||
|
|
8ee91095c5 | ||
|
|
49c7b2f8f4 | ||
|
|
5d80cf11ef | ||
|
|
bc929bcb90 | ||
|
|
26a3057acf | ||
|
|
96cf0a6d02 | ||
|
|
12e773f3fb | ||
|
|
c321997591 | ||
|
|
b2d98452f1 | ||
|
|
16e48aa10f | ||
|
|
4da85a5f4a | ||
|
|
a5c9d3a809 | ||
|
|
f288ba744d | ||
|
|
ba5b85de00 | ||
|
|
bd2f412225 | ||
|
|
a86cf32f55 | ||
|
|
0c8178c753 | ||
|
|
b59e06ec89 | ||
|
|
10153a371b | ||
|
|
a93c1478c0 | ||
|
|
215b7a19c9 | ||
|
|
14ed666b91 | ||
|
|
ac1d61b2c4 | ||
|
|
acb6e90158 | ||
|
|
d3609737f5 | ||
|
|
95b91cd7dd | ||
|
|
affd2a3b91 | ||
|
|
d392259364 | ||
|
|
e03f4fac64 | ||
|
|
538840351a | ||
|
|
1ddad3577c | ||
|
|
d44d1325fc | ||
|
|
dc07af6ad1 | ||
|
|
c6f1809d60 | ||
|
|
261e73bf3e | ||
|
|
8c7d2a1e96 | ||
|
|
72507977bd | ||
|
|
270829f05b | ||
|
|
b92e4f7e65 | ||
|
|
8e890135fe | ||
|
|
a6601939d5 | ||
|
|
c608e735d2 | ||
|
|
e0a1a338a5 | ||
|
|
7aa276df5a | ||
|
|
d939a72219 | ||
|
|
96fc59eee5 | ||
|
|
989f8568cf | ||
|
|
66f451124f | ||
|
|
e59ff47f6f | ||
|
|
b176be9ef7 | ||
|
|
942879bbba | ||
|
|
2065c4294f | ||
|
|
cb02f0621c | ||
|
|
971f371eab | ||
|
|
f21c56bd98 | ||
|
|
363299a27d | ||
|
|
10e74dad41 | ||
|
|
dc041b7ea0 | ||
|
|
584cfdf64c | ||
|
|
74c498cf15 | ||
|
|
a1e5855f2b | ||
|
|
d8e5a97ef3 | ||
|
|
96804b0b28 | ||
|
|
b0d69c50da | ||
|
|
de6b2e139b | ||
|
|
7204840a8a | ||
|
|
76369a01ad | ||
|
|
3a92a93604 | ||
|
|
f1c968372f | ||
|
|
759d9a642f | ||
|
|
86d86c3044 | ||
|
|
c38708c483 | ||
|
|
1673a5ade9 | ||
|
|
c56a49ddce | ||
|
|
03477ce400 | ||
|
|
14d213d13e | ||
|
|
8d86816d8d | ||
|
|
12cf3fd8e7 | ||
|
|
8c23f2de8b | ||
|
|
837481a854 | ||
|
|
b682e58df7 | ||
|
|
c4f430d0d0 | ||
|
|
c7930ab349 | ||
|
|
045baba7ea | ||
|
|
d17ac421ec | ||
|
|
049e529f66 | ||
|
|
ef6e47f625 | ||
|
|
1330cb9126 | ||
|
|
f0ee8bc89d | ||
|
|
ebc0c18534 | ||
|
|
379a4bdda6 | ||
|
|
2ce106c50f | ||
|
|
0ebdfe59ee | ||
|
|
80c700b1f8 | ||
|
|
103f935d61 | ||
|
|
164476721a | ||
|
|
54dd716d02 | ||
|
|
ab681ce637 | ||
|
|
a355de8e97 | ||
|
|
56921d6449 | ||
|
|
7e900626bc | ||
|
|
dc30c29f4e | ||
|
|
370865bb57 | ||
|
|
80015e83d4 | ||
|
|
c37c35b52b | ||
|
|
069004d4eb | ||
|
|
f11695200e | ||
|
|
ef2f8a1a18 | ||
|
|
54cc687c50 | ||
|
|
51f279160f | ||
|
|
e3113f6976 | ||
|
|
485e29a672 | ||
|
|
00e67c50ca | ||
|
|
3e7b3edb9c | ||
|
|
3c6c9d9462 | ||
|
|
6b06a9ff2b | ||
|
|
803db476bb | ||
|
|
c134ea3098 | ||
|
|
6e64f36b39 | ||
|
|
36af39e2b4 | ||
|
|
88929ab730 | ||
|
|
02f00e5339 | ||
|
|
ee67032af1 | ||
|
|
8aea00f527 | ||
|
|
50faed4a50 | ||
|
|
b982817a56 | ||
|
|
e5172a9240 | ||
|
|
dcac7942fa | ||
|
|
4e13529adb | ||
|
|
c553835228 | ||
|
|
1b87193fc1 | ||
|
|
25fca56319 | ||
|
|
bf4252394c | ||
|
|
22757e064f | ||
|
|
fc9fa37e4e | ||
|
|
2d5e3402fa | ||
|
|
b7dd26121a | ||
|
|
23bdbeef4e | ||
|
|
9ac81c5219 | ||
|
|
6b9d346b8b | ||
|
|
9e68eb4d2c | ||
|
|
f1f47ee44a | ||
|
|
436b2b3140 | ||
|
|
f6cfa8ae49 | ||
|
|
13949a8d1f | ||
|
|
88db623dc1 | ||
|
|
976413173a | ||
|
|
91d3349585 | ||
|
|
6e1a774921 | ||
|
|
6b53b509fa | ||
|
|
b8b6203ecd | ||
|
|
81e1500675 | ||
|
|
d2cd18cb1e | ||
|
|
f5ac73f681 | ||
|
|
413630813a | ||
|
|
73b6ef2293 | ||
|
|
428bfbd810 | ||
|
|
c54409bdfb | ||
|
|
b63aebfdaf | ||
|
|
335f876a46 | ||
|
|
c5e99342a3 | ||
|
|
1710eb9249 | ||
|
|
d03770f91f | ||
|
|
9fe6f7a6ea | ||
|
|
2fc605fc3e | ||
|
|
edcd5c4713 | ||
|
|
bd3df1e06a | ||
|
|
b785d1c82b | ||
|
|
93bf14f84f | ||
|
|
5a84646282 | ||
|
|
2e6cfc08f0 | ||
|
|
4b6d3d47ec | ||
|
|
985abaa12a | ||
|
|
a381a7253a | ||
|
|
b8bb509d5c | ||
|
|
f48dbd0e19 | ||
|
|
31c1278374 | ||
|
|
f6aaafd60f | ||
|
|
ae3dec19fd | ||
|
|
990baa0929 | ||
|
|
7ecb65a141 | ||
|
|
a1e55103be | ||
|
|
1b4fc54405 | ||
|
|
e5dcea1fa0 | ||
|
|
9f25195629 | ||
|
|
6936fa1c1c | ||
|
|
7b25eacfc8 | ||
|
|
33989a61e3 | ||
|
|
08d9e8d076 | ||
|
|
da43ef0982 | ||
|
|
02f12bde81 | ||
|
|
b55d5375ac | ||
|
|
281f40bd56 | ||
|
|
dcb7c0c27e | ||
|
|
d3c44a6fa6 | ||
|
|
f432dc6bdc | ||
|
|
ce21ee6001 | ||
|
|
a431f5a674 | ||
|
|
f6153f17ad | ||
|
|
c17ecbc78c | ||
|
|
0feb9f0c56 | ||
|
|
e69cb3d950 | ||
|
|
e7c03dd512 | ||
|
|
0c1371ffae | ||
|
|
8a350acf39 | ||
|
|
594b3b5531 | ||
|
|
e13d06fe20 | ||
|
|
805bb84c8f | ||
|
|
1a62338349 | ||
|
|
4795ba83ba | ||
|
|
ad5b362bca | ||
|
|
8a735452de | ||
|
|
03d0f3e9ca | ||
|
|
ff0983cc42 | ||
|
|
159a413c7d | ||
|
|
6a753d1316 | ||
|
|
c89bf30e05 | ||
|
|
ba4b5ef9f2 | ||
|
|
5b39ee221f | ||
|
|
65c4432325 | ||
|
|
32bbb27c9f | ||
|
|
ef3c030576 | ||
|
|
554a182229 | ||
|
|
9154d78e58 | ||
|
|
82d933aa3d | ||
|
|
ef0fe9b13c | ||
|
|
f6700b7094 | ||
|
|
21f00cf251 | ||
|
|
900d2bf816 | ||
|
|
3846fa3f28 | ||
|
|
52f77d61ba | ||
|
|
039341de69 | ||
|
|
5adf332a5f | ||
|
|
efbb32a498 | ||
|
|
3156dd1321 | ||
|
|
2fbeeafbb8 | ||
|
|
db680f3bcd | ||
|
|
4a20fa5275 | ||
|
|
3fb2da7174 | ||
|
|
7f8bebbdd5 | ||
|
|
597fc0265f | ||
|
|
d5d5770dac | ||
|
|
772ac64e3b | ||
|
|
92d7337f04 | ||
|
|
200425114c | ||
|
|
b92a73a2a2 | ||
|
|
e83c77b99f | ||
|
|
28aa35160d | ||
|
|
18c9bfa134 | ||
|
|
bdb7763237 | ||
|
|
bc9ee46b8f | ||
|
|
b92b734db2 | ||
|
|
2748a6cd3c | ||
|
|
888876185d | ||
|
|
03fea6358b | ||
|
|
59b0dc6735 | ||
|
|
35a42b2172 | ||
|
|
daa4056f45 | ||
|
|
bb5abe633f | ||
|
|
dc238db355 | ||
|
|
4c692c23bb | ||
|
|
b093e82db3 | ||
|
|
7a1c2108a1 | ||
|
|
4aa2b49649 | ||
|
|
cdaced4df6 | ||
|
|
cdd029853b | ||
|
|
507bafdd9c | ||
|
|
07a36123e5 | ||
|
|
a205e7ada9 | ||
|
|
25e9330bb4 | ||
|
|
b2edfa454c | ||
|
|
07767a09d9 | ||
|
|
e8d85d9269 | ||
|
|
5cf0ba079f | ||
|
|
e08b1f82ce | ||
|
|
c2196149ee | ||
|
|
65b5ce7880 | ||
|
|
9c254ec3e0 | ||
|
|
f4418e0205 | ||
|
|
a314f7b42e | ||
|
|
dc5a72cda5 | ||
|
|
380cb41ca9 | ||
|
|
83f7e633a3 | ||
|
|
ab1c920435 | ||
|
|
7094d658f8 | ||
|
|
11617291e3 | ||
|
|
790aa03834 | ||
|
|
bcd16ea5ea | ||
|
|
81c044760b | ||
|
|
b0e0e80b69 | ||
|
|
280bb1d84f | ||
|
|
bb42dceaca | ||
|
|
d0873ed551 | ||
|
|
0fbddfc47b | ||
|
|
3f1dee2650 | ||
|
|
a4b8b93e7f | ||
|
|
02d1417ef4 | ||
|
|
50812a908c | ||
|
|
1a1ed55485 | ||
|
|
09fa322a0e | ||
|
|
1a9a423c35 | ||
|
|
7b0449213c | ||
|
|
c0251f5c29 | ||
|
|
4509d8a60b | ||
|
|
b1738b0902 | ||
|
|
45259462aa | ||
|
|
772a7427a2 | ||
|
|
afa58b81f6 | ||
|
|
6822c0f0e3 | ||
|
|
0a155ecb68 | ||
|
|
15fff94447 | ||
|
|
ff06df2acb | ||
|
|
df9301d591 | ||
|
|
e51b1bbb25 | ||
|
|
aa275f4b1c | ||
|
|
ed11bda9fd | ||
|
|
ed34a3f5d4 | ||
|
|
a99b8b7984 | ||
|
|
f750254cc2 | ||
|
|
21ece182f5 | ||
|
|
6439dca43a | ||
|
|
ca424a4577 | ||
|
|
87bc2cb1ab | ||
|
|
58f22cc3b5 | ||
|
|
fa8362f000 | ||
|
|
1816206dfc | ||
|
|
fcfa994152 | ||
|
|
e7262660c2 | ||
|
|
a1e14d5740 | ||
|
|
60c11f3f30 | ||
|
|
3fe584d366 | ||
|
|
28e38321ee | ||
|
|
1faeeeeabc | ||
|
|
75a0c672b1 | ||
|
|
db829b21aa | ||
|
|
24bdc29c27 | ||
|
|
692a78984f | ||
|
|
77a399738c | ||
|
|
5d7358899d | ||
|
|
aca25b9999 | ||
|
|
182c6c534b | ||
|
|
0500f155e6 | ||
|
|
125b826f62 | ||
|
|
922bfbf724 | ||
|
|
f1e584fad9 | ||
|
|
0c4dcdea71 | ||
|
|
ec4b383198 | ||
|
|
3328c7e78b | ||
|
|
357fcd44d7 | ||
|
|
5a0a682d35 | ||
|
|
1fcb7e5aba | ||
|
|
eec867bc9e | ||
|
|
5a956d2914 | ||
|
|
e0e29cc363 | ||
|
|
9fc8e03f72 | ||
|
|
b054e93f0e | ||
|
|
653680a356 | ||
|
|
40035f2e52 | ||
|
|
a70b7daec4 | ||
|
|
0f3e7b5dac | ||
|
|
2fbc08b606 | ||
|
|
e4897f709e | ||
|
|
84445aa28b | ||
|
|
0795f9a9f2 | ||
|
|
44ae0d67f6 | ||
|
|
a74f0ca370 | ||
|
|
266c1fcdcb | ||
|
|
977c3407e7 | ||
|
|
99c2299ed4 | ||
|
|
39a08f17da | ||
|
|
57de1187fc | ||
|
|
2bba6d4c0d | ||
|
|
2f03b225f7 | ||
|
|
533bc1ce94 | ||
|
|
add9df6058 | ||
|
|
d3496f3ff5 | ||
|
|
e97f7ee725 | ||
|
|
37497295fb | ||
|
|
2e89b3b5b9 | ||
|
|
518bbc651e | ||
|
|
ffd1d97e03 | ||
|
|
e503d431f1 | ||
|
|
49a066714a | ||
|
|
7eec610d21 | ||
|
|
530507c36b | ||
|
|
4d2584d8fc | ||
|
|
0f5057920e | ||
|
|
a98000cf3b | ||
|
|
0cec8de164 | ||
|
|
1caa8d923d | ||
|
|
4fa51f8c52 | ||
|
|
a8be765aa8 | ||
|
|
d2d35d490a | ||
|
|
26ba137424 | ||
|
|
e6006663ec | ||
|
|
74eb2772b6 | ||
|
|
3878a696af | ||
|
|
49ac34c405 | ||
|
|
a7d4a980ba | ||
|
|
2196db562e | ||
|
|
2ad3e0ed4c | ||
|
|
6e03fe77ae | ||
|
|
af8ec7b5dd | ||
|
|
7fa29e7341 | ||
|
|
9c6950d163 | ||
|
|
5d4cbfe8e7 | ||
|
|
4747422110 | ||
|
|
7d1416ac74 | ||
|
|
2efaa7af7b | ||
|
|
68e259870f | ||
|
|
29c2c94784 | ||
|
|
3f2e1d078f | ||
|
|
0a259da05a | ||
|
|
1b22fe6de2 | ||
|
|
9b700c3bd2 | ||
|
|
3b72e30c36 | ||
|
|
9eba103de9 | ||
|
|
ecadee3876 | ||
|
|
699f9105ae | ||
|
|
8a19e8dafe | ||
|
|
588c056205 | ||
|
|
866a7fb641 | ||
|
|
a5fe42268f | ||
|
|
0bd6816bdd | ||
|
|
1c6670c7a1 | ||
|
|
699a0eb934 | ||
|
|
72440ab937 | ||
|
|
9ad807bd58 | ||
|
|
9fb1970ac0 | ||
|
|
48dfac2588 | ||
|
|
c6d3714e6f | ||
|
|
1345c257c3 | ||
|
|
19112a8f27 | ||
|
|
e0979fcf3f | ||
|
|
d514ecbd5e | ||
|
|
a53dbb6950 | ||
|
|
a03b88e0eb | ||
|
|
4472eb1c40 | ||
|
|
3443987127 | ||
|
|
c73a79fd42 | ||
|
|
dedbe3d431 | ||
|
|
87ef713a17 | ||
|
|
4ba8ac4fa1 | ||
|
|
e74e0531bd | ||
|
|
b67cb4bd1f | ||
|
|
22628c9ac4 | ||
|
|
4dd0862fad | ||
|
|
0ec57fbb8d | ||
|
|
19ab584d32 | ||
|
|
97fe978d14 | ||
|
|
2a74c7d897 | ||
|
|
cdf9c2fc6f | ||
|
|
235f84a0d4 | ||
|
|
ccaa490a2f | ||
|
|
8eaf11b510 | ||
|
|
57a5faa448 | ||
|
|
be87191d0a | ||
|
|
40aabe76c7 | ||
|
|
9d304cb35c | ||
|
|
ee08a4966f | ||
|
|
aeca69903e | ||
|
|
e1b3f2b95a | ||
|
|
25c7436652 | ||
|
|
771c449621 | ||
|
|
61ecaa5756 | ||
|
|
c05dddc56e |
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
|
||||
8
.github/FUNDING.yml
vendored
8
.github/FUNDING.yml
vendored
@@ -1,12 +1,4 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: skypjack
|
||||
patreon:
|
||||
open_collective:
|
||||
ko_fi:
|
||||
tidelift:
|
||||
community_bridge:
|
||||
liberapay:
|
||||
issuehunt:
|
||||
otechie:
|
||||
custom: https://www.paypal.me/skypjack
|
||||
|
||||
61
.github/workflows/analyzer.yml
vendored
Normal file
61
.github/workflows/analyzer.yml
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
name: analyzer
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- wip
|
||||
|
||||
jobs:
|
||||
|
||||
iwyu:
|
||||
timeout-minutes: 30
|
||||
|
||||
env:
|
||||
IWYU: "0.20"
|
||||
LLVM: "16"
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install llvm/clang
|
||||
# see: https://apt.llvm.org/
|
||||
run: |
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-$LLVM main"
|
||||
sudo apt update
|
||||
sudo apt remove -y "llvm*"
|
||||
sudo apt remove -y "libclang-dev*"
|
||||
sudo apt remove -y "clang*"
|
||||
sudo apt install -y llvm-$LLVM-dev
|
||||
sudo apt install -y libclang-$LLVM-dev
|
||||
sudo apt install -y clang-$LLVM
|
||||
- name: Compile iwyu
|
||||
# see: https://github.com/include-what-you-use/include-what-you-use
|
||||
working-directory: build
|
||||
run: |
|
||||
git clone https://github.com/include-what-you-use/include-what-you-use.git --branch $IWYU --depth 1
|
||||
mkdir include-what-you-use/build
|
||||
cd include-what-you-use/build
|
||||
cmake -DCMAKE_C_COMPILER=clang-$LLVM \
|
||||
-DCMAKE_CXX_COMPILER=clang++-$LLVM \
|
||||
-DCMAKE_INSTALL_PREFIX=./ \
|
||||
..
|
||||
make -j4
|
||||
bin/include-what-you-use --version
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
run: |
|
||||
export PATH=$PATH:${GITHUB_WORKSPACE}/build/include-what-you-use/build/bin
|
||||
cmake -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" \
|
||||
..
|
||||
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
|
||||
172
.github/workflows/build.yml
vendored
172
.github/workflows/build.yml
vendored
@@ -5,40 +5,65 @@ on: [push, pull_request]
|
||||
jobs:
|
||||
|
||||
linux:
|
||||
timeout-minutes: 10
|
||||
timeout-minutes: 15
|
||||
|
||||
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++
|
||||
exe: g++
|
||||
- pkg: clang-8
|
||||
exe: clang++-8
|
||||
- pkg: clang-9
|
||||
exe: clang++-9
|
||||
- pkg: clang-10
|
||||
exe: clang++-10
|
||||
- pkg: clang
|
||||
exe: clang++
|
||||
- { 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-get update
|
||||
sudo apt-get install ${{ matrix.compiler.pkg }} -y
|
||||
sudo apt update
|
||||
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
|
||||
@@ -46,55 +71,26 @@ jobs:
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest --timeout 30 -C Debug -j4
|
||||
|
||||
linux-extra:
|
||||
timeout-minutes: 10
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
compiler: [g++, clang++]
|
||||
id_type: [uint32, uint64]
|
||||
cxx_std: [cxx_std_17, cxx_std_20]
|
||||
include:
|
||||
- id_type: uint64
|
||||
id_type_variable: -DENTT_ID_TYPE=std::uint64_t
|
||||
- cxx_std: cxx_std_20
|
||||
cxx_std_variable: -DENTT_CXX_STD=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 ${{ matrix.id_type_variable }} ${{ matrix.cxx_std_variable }} ..
|
||||
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: 10
|
||||
timeout-minutes: 15
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
toolset: [clang-cl, default, v141]
|
||||
toolset: [default, v141, v142, clang-cl]
|
||||
include:
|
||||
- toolset: clang-cl
|
||||
toolset_option: -T"ClangCl"
|
||||
- toolset: v141
|
||||
toolset_option: -T"v141"
|
||||
- toolset: v142
|
||||
toolset_option: -T"v142"
|
||||
- toolset: clang-cl
|
||||
toolset_option: -T"ClangCl"
|
||||
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
run: |
|
||||
@@ -104,42 +100,14 @@ jobs:
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest --timeout 30 -C Debug -j4
|
||||
|
||||
windows-extra:
|
||||
timeout-minutes: 10
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
id_type: [uint32, uint64]
|
||||
cxx_std: [cxx_std_17, cxx_std_20]
|
||||
include:
|
||||
- id_type: uint64
|
||||
id_type_variable: -DENTT_ID_TYPE=std::uint64_t
|
||||
- cxx_std: cxx_std_20
|
||||
cxx_std_variable: -DENTT_CXX_STD=cxx_std_20
|
||||
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
run: |
|
||||
cmake -DENTT_BUILD_TESTING=ON ${{ matrix.id_type_variable }} ${{ matrix.cxx_std_variable }} ..
|
||||
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: 10
|
||||
timeout-minutes: 15
|
||||
runs-on: macOS-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
run: |
|
||||
@@ -149,32 +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:
|
||||
timeout-minutes: 10
|
||||
extra:
|
||||
timeout-minutes: 15
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
id_type: [uint32, uint64]
|
||||
os: [windows-latest, macOS-latest, ubuntu-latest]
|
||||
id_type: ["std::uint32_t", "std::uint64_t"]
|
||||
cxx_std: [cxx_std_17, cxx_std_20]
|
||||
include:
|
||||
- id_type: uint64
|
||||
id_type_variable: -DENTT_ID_TYPE=std::uint64_t
|
||||
- cxx_std: cxx_std_20
|
||||
cxx_std_variable: -DENTT_CXX_STD=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 ${{ matrix.id_type_variable }} ${{ matrix.cxx_std_variable }} ..
|
||||
make -j4
|
||||
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
|
||||
|
||||
56
.github/workflows/coverage.yml
vendored
56
.github/workflows/coverage.yml
vendored
@@ -5,34 +5,34 @@ on: [push, pull_request]
|
||||
jobs:
|
||||
|
||||
codecov:
|
||||
timeout-minutes: 10
|
||||
timeout-minutes: 15
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
env:
|
||||
CXXFLAGS: "--coverage -fno-inline"
|
||||
CXX: g++
|
||||
run: |
|
||||
cmake -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON ..
|
||||
make -j4
|
||||
- name: Run tests
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest --timeout 30 -C Debug -j4
|
||||
- name: Collect data
|
||||
working-directory: build
|
||||
run: |
|
||||
sudo apt install lcov
|
||||
lcov -c -d . -o coverage.info
|
||||
lcov -l coverage.info
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v2
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: build/coverage.info
|
||||
name: EnTT
|
||||
fail_ci_if_error: true
|
||||
- uses: actions/checkout@v4
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
env:
|
||||
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 ..
|
||||
make -j4
|
||||
- name: Run tests
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest -C Debug -j4
|
||||
- name: Collect data
|
||||
working-directory: build
|
||||
run: |
|
||||
sudo apt install lcov
|
||||
lcov -c -d . -o coverage.info
|
||||
lcov -l coverage.info
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: build/coverage.info
|
||||
name: EnTT
|
||||
fail_ci_if_error: true
|
||||
|
||||
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:
|
||||
|
||||
19
.github/workflows/sanitizer.yml
vendored
19
.github/workflows/sanitizer.yml
vendored
@@ -4,33 +4,30 @@ on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
|
||||
linux:
|
||||
timeout-minutes: 10
|
||||
clang:
|
||||
timeout-minutes: 15
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
compiler: [clang++]
|
||||
id_type: [uint32, uint64]
|
||||
id_type: ["std::uint32_t", "std::uint64_t"]
|
||||
cxx_std: [cxx_std_17, cxx_std_20]
|
||||
include:
|
||||
- id_type: uint64
|
||||
id_type_variable: -DENTT_ID_TYPE=std::uint64_t
|
||||
- cxx_std: cxx_std_20
|
||||
cxx_std_variable: -DENTT_CXX_STD=cxx_std_20
|
||||
|
||||
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:
|
||||
CXX: ${{ matrix.compiler }}
|
||||
run: |
|
||||
cmake -DENTT_USE_SANITIZER=ON -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON ${{ matrix.id_type_variable }} ${{ matrix.cxx_std_variable }} ..
|
||||
cmake -DENTT_USE_SANITIZER=ON -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON -DENTT_CXX_STD=${{ matrix.cxx_std }} -DENTT_ID_TYPE=${{ matrix.id_type }} ..
|
||||
make -j4
|
||||
- name: Run tests
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest --timeout 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
|
||||
|
||||
5
AUTHORS
5
AUTHORS
@@ -11,10 +11,12 @@ ceeac
|
||||
ColinH
|
||||
corystegel
|
||||
Croydon
|
||||
cschreib
|
||||
cugone
|
||||
dbacchet
|
||||
dBagrat
|
||||
djarek
|
||||
DNKpp
|
||||
DonKult
|
||||
drglove
|
||||
eliasdaler
|
||||
@@ -31,6 +33,7 @@ Lawrencemm
|
||||
markand
|
||||
mhammerc
|
||||
Milerius
|
||||
Minimonium
|
||||
morbo84
|
||||
m-waka
|
||||
netpoetica
|
||||
@@ -39,6 +42,7 @@ Oortonaut
|
||||
Paolo-Oliverio
|
||||
pgruenbacher
|
||||
prowolf
|
||||
Qix-
|
||||
stefanofiorentino
|
||||
suVrik
|
||||
szunhammer
|
||||
@@ -46,6 +50,7 @@ The5-1
|
||||
vblanco20-1
|
||||
willtunnels
|
||||
WizardIke
|
||||
WoLfulus
|
||||
w1th0utnam3
|
||||
xissburg
|
||||
zaucy
|
||||
|
||||
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",
|
||||
)
|
||||
|
||||
113
CMakeLists.txt
113
CMakeLists.txt
@@ -1,29 +1,15 @@
|
||||
#
|
||||
# EnTT
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.12.4)
|
||||
cmake_minimum_required(VERSION 3.15.7)
|
||||
|
||||
#
|
||||
# Building in-tree is not allowed (we take care of your craziness).
|
||||
#
|
||||
|
||||
if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
|
||||
message(FATAL_ERROR "Prevented in-tree built. Please create a build directory outside of the source code and call cmake from there. Thank you.")
|
||||
endif()
|
||||
|
||||
#
|
||||
# Read project version
|
||||
#
|
||||
|
||||
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
|
||||
@@ -39,15 +25,18 @@ endif()
|
||||
|
||||
message(VERBOSE "*")
|
||||
message(VERBOSE "* ${PROJECT_NAME} v${PROJECT_VERSION} (${CMAKE_BUILD_TYPE})")
|
||||
message(VERBOSE "* Copyright (c) 2017-2021 Michele Caini <michele.caini@gmail.com>")
|
||||
message(VERBOSE "* Copyright (c) 2017-2023 Michele Caini <michele.caini@gmail.com>")
|
||||
message(VERBOSE "*")
|
||||
|
||||
#
|
||||
# Compiler stuff
|
||||
#
|
||||
# CMake stuff
|
||||
|
||||
option(ENTT_USE_LIBCPP "Use libc++ by adding -stdlib=libc++ flag if availbale." OFF)
|
||||
option(ENTT_USE_SANITIZER "Enable sanitizers by adding -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined flags if availbale." OFF)
|
||||
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)
|
||||
@@ -67,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()
|
||||
|
||||
@@ -78,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)
|
||||
@@ -119,9 +114,10 @@ if(ENTT_INCLUDE_HEADERS)
|
||||
EnTT
|
||||
INTERFACE
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/config/config.h>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/config/macro.h>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/config/version.h>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/container/dense_hash_map.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/container/dense_hash_set.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/container/dense_map.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/container/dense_set.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/container/fwd.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/algorithm.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/any.hpp>
|
||||
@@ -144,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>
|
||||
@@ -152,12 +149,15 @@ 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/utility.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/view.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/graph/adjacency_matrix.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/graph/dot.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/graph/flow.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/graph/fwd.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/locator/locator.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/adl_pointer.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/container.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/ctx.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/context.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/factory.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/fwd.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/meta.hpp>
|
||||
@@ -172,12 +172,13 @@ if(ENTT_INCLUDE_HEADERS)
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/platform/android-ndk-r17.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/poly/fwd.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/poly/poly.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/process/fwd.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/process/process.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/process/scheduler.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/resource/cache.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/resource/fwd.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/resource/handle.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/resource/loader.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/resource/resource.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/signal/delegate.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/signal/dispatcher.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/signal/emitter.hpp>
|
||||
@@ -196,6 +197,7 @@ if(ENTT_HAS_NATVIS)
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/container.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/core.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/entity.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/graph.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/locator.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/meta.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/platform.natvis>
|
||||
@@ -211,16 +213,22 @@ 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)
|
||||
|
||||
set(EnTT_PKGCONFIG ${CMAKE_CURRENT_BINARY_DIR}/entt.pc)
|
||||
|
||||
join_paths(EnTT_PKGCONFIG_INCLUDEDIR "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
|
||||
configure_file(
|
||||
${EnTT_SOURCE_DIR}/cmake/in/entt.pc.in
|
||||
${EnTT_PKGCONFIG}
|
||||
@@ -232,9 +240,7 @@ install(
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
|
||||
)
|
||||
|
||||
#
|
||||
# Install EnTT
|
||||
#
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
@@ -276,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)
|
||||
|
||||
@@ -301,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)
|
||||
|
||||
@@ -314,22 +330,3 @@ if(ENTT_BUILD_DOCS)
|
||||
add_subdirectory(docs)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#
|
||||
# AOB
|
||||
#
|
||||
|
||||
add_custom_target(
|
||||
aob
|
||||
SOURCES
|
||||
.github/workflows/build.yml
|
||||
.github/workflows/coverage.yml
|
||||
.github/workflows/deploy.yml
|
||||
.github/workflows/sanitizer.yml
|
||||
.github/FUNDING.yml
|
||||
AUTHORS
|
||||
CONTRIBUTING.md
|
||||
LICENSE
|
||||
README.md
|
||||
TODO
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Contributing
|
||||
|
||||
First of all, thank you very much for taking the time to contribute to the
|
||||
`EnTT` framework.<br/>
|
||||
`EnTT` library.<br/>
|
||||
How to do it mostly depends on the type of contribution:
|
||||
|
||||
* If you have a question, **please** ensure there isn't already an answer for
|
||||
@@ -28,7 +28,7 @@ How to do it mostly depends on the type of contribution:
|
||||
* If you found a bug and you wrote a patch to fix it, open a new
|
||||
[pull request](https://github.com/skypjack/entt/pulls) with your code.
|
||||
**Please**, add some tests to avoid regressions in future if possible, it
|
||||
would be really appreciated. Note that the `EnTT` framework has a
|
||||
would be really appreciated. Note that the `EnTT` library has a
|
||||
[coverage at 100%](https://coveralls.io/github/skypjack/entt?branch=master)
|
||||
(at least it was at 100% at the time I wrote this file) and this is the reason
|
||||
for which you can be confident with using it in a production environment.
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2021 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")
|
||||
98
README.md
98
README.md
@@ -6,10 +6,16 @@
|
||||
[](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
|
||||
>
|
||||
> -- Every EnTT User Ever
|
||||
|
||||
`EnTT` is a header-only, tiny and easy to use library for game programming and
|
||||
much more written in **modern C++**.<br/>
|
||||
@@ -33,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:
|
||||
|
||||
@@ -45,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)
|
||||
@@ -75,18 +82,20 @@ codebase has grown as more and more classes and functionalities were added.<br/>
|
||||
Here is a brief, yet incomplete list of what it offers today:
|
||||
|
||||
* Built-in **RTTI system** mostly similar to the standard one.
|
||||
* A `constexpr` utility for human readable **resource names**.
|
||||
* A `constexpr` utility for human-readable **resource names**.
|
||||
* Minimal **configuration system** built using the monostate pattern.
|
||||
* Incredibly fast **entity-component system** with its own _pay for what you
|
||||
use_ policy.
|
||||
use_ policy, unconstrained component types with optional pointer stability and
|
||||
hooks for storage customization.
|
||||
* Views and groups to iterate entities and components and allow different access
|
||||
patterns, from **perfect SoA** to fully random.
|
||||
* A lot of **facilities** built on top of the entity-component system to help
|
||||
the users and avoid reinventing the wheel.
|
||||
* General purpose **execution graph builder** for optimal scheduling.
|
||||
* The smallest and most basic implementation of a **service locator** ever seen.
|
||||
* A built-in, non-intrusive and macro-free runtime **reflection system**.
|
||||
* **Static polymorphism** made simple and within everyone's reach.
|
||||
* A few homemade containers, like a sparse set based **dense hash map**.
|
||||
* A few homemade containers, like a sparse set based **hash map**.
|
||||
* A **cooperative scheduler** for processes of any type.
|
||||
* All that is needed for **resource management** (cache, loaders, handles).
|
||||
* Delegates, **signal handlers** and a tiny event dispatcher.
|
||||
@@ -94,16 +103,12 @@ Here is a brief, yet incomplete list of what it offers today:
|
||||
* And **much more**! Check out the
|
||||
[**wiki**](https://github.com/skypjack/entt/wiki).
|
||||
|
||||
**Breaking news**:
|
||||
Consider this list a work in progress as well as the project. The whole API is
|
||||
fully documented in-code for those who are brave enough to read it.<br/>
|
||||
Please, do note that all tools are also DLL-friendly now and run smoothly across
|
||||
boundaries.
|
||||
|
||||
* The ECS allows attaching multiple components of the same type to an entity.
|
||||
* All tools work perfectly across boundaries (DLL-friendly)!!
|
||||
:slightly_smiling_face:
|
||||
|
||||
Consider these lists a work in progress as well as the project. The whole API is
|
||||
fully documented in-code for those who are brave enough to read it.
|
||||
|
||||
It is also known that `EnTT` is used in **Minecraft**.<br/>
|
||||
One thing known to most is that `EnTT` is also used in **Minecraft**.<br/>
|
||||
Given that the game is available literally everywhere, I can confidently say
|
||||
that the library has been sufficiently tested on every platform that can come to
|
||||
mind.
|
||||
@@ -160,7 +165,7 @@ int main() {
|
||||
## Motivation
|
||||
|
||||
I started developing `EnTT` for the _wrong_ reason: my goal was to design an
|
||||
entity-component system to beat another well known open source solution both in
|
||||
entity-component system to beat another well known open source library both in
|
||||
terms of performance and possibly memory usage.<br/>
|
||||
In the end, I did it, but it wasn't very satisfying. Actually it wasn't
|
||||
satisfying at all. The fastest and nothing more, fairly little indeed. When I
|
||||
@@ -171,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
|
||||
|
||||
@@ -323,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
|
||||
@@ -340,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
|
||||
@@ -350,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.
|
||||
<!--
|
||||
@@ -363,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:
|
||||
|
||||
@@ -417,7 +421,7 @@ know who has participated so far.
|
||||
|
||||
# License
|
||||
|
||||
Code and documentation Copyright (c) 2017-2021 Michele Caini.<br/>
|
||||
Code and documentation Copyright (c) 2017-2023 Michele Caini.<br/>
|
||||
Colorful logo Copyright (c) 2018-2021 Richard Caseres.
|
||||
|
||||
Code released under
|
||||
|
||||
50
TODO
50
TODO
@@ -1,25 +1,31 @@
|
||||
* long term feature: shared_ptr less locator and resource cache
|
||||
* debugging tools (#60): the issue online already contains interesting tips on this, look at it
|
||||
* work stealing job system (see #100) + mt scheduler based on const awareness for types
|
||||
* add examples (and credits) from @alanjfs :)
|
||||
EXAMPLES
|
||||
* filter on runtime values/variables (not only types)
|
||||
* support to polymorphic types (see #859)
|
||||
|
||||
WIP:
|
||||
* add the possibility of disabling entities without deleting components thanks to the new full check
|
||||
* add registry::storage id -> basic_sparse_set (no template arg), add example of use
|
||||
* fast-contains for sparse sets (low prio but nice-to-have)
|
||||
* runtime events (dispatcher/emitter), runtime context variables...
|
||||
* runtime_view/registry, remove reference to basic_sparse_set<E>
|
||||
* dedicated entity storage, in-place O(1) release/destroy for non-orphaned entities, out-of-sync model
|
||||
* custom allocators all over
|
||||
DOC:
|
||||
* custom storage/view
|
||||
* 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
|
||||
|
||||
WIP:
|
||||
* customizable any_vtable, sfinae-friendly definition and op::custom for user-def
|
||||
* resource, forward the id to the loader from the cache and if constexpr the call to load, update doc and describe customization points
|
||||
* add user data to type_info
|
||||
* headless (sparse set only) view
|
||||
* write documentation for custom storages and views!!
|
||||
* make runtime views use opaque storage and therefore return also elements.
|
||||
* add exclude-only views to combine with packs
|
||||
* entity-aware observer, add observer functions aside observer class
|
||||
TODO:
|
||||
* resource cache: avoid using shared ptr with loader and the others
|
||||
* further optimize exclusion lists in multi type views (no existence check)
|
||||
* further improve meta resolve function by id (bimap)
|
||||
* get rid of observers, storage based views made them pointless - document alternatives
|
||||
* deprecate non-owning groups in favor of owning views and view packs, introduce lazy owning views
|
||||
* snapshot: support for range-based archives
|
||||
* 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,5 +1,5 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
|
||||
includedir=@EnTT_PKGCONFIG_INCLUDEDIR@
|
||||
|
||||
Name: EnTT
|
||||
Description: Gaming meets modern C++
|
||||
|
||||
23
cmake/modules/JoinPaths.cmake
Normal file
23
cmake/modules/JoinPaths.cmake
Normal file
@@ -0,0 +1,23 @@
|
||||
# This module provides function for joining paths
|
||||
# known from most languages
|
||||
#
|
||||
# SPDX-License-Identifier: (MIT OR CC0-1.0)
|
||||
# Copyright 2020 Jan Tojnar
|
||||
# https://github.com/jtojnar/cmake-snips
|
||||
#
|
||||
# Modelled after Python’s os.path.join
|
||||
# https://docs.python.org/3.7/library/os.path.html#os.path.join
|
||||
# Windows not supported
|
||||
function(join_paths joined_path first_path_segment)
|
||||
set(temp_path "${first_path_segment}")
|
||||
foreach(current_segment IN LISTS ARGN)
|
||||
if(NOT ("${current_segment}" STREQUAL ""))
|
||||
if(IS_ABSOLUTE "${current_segment}")
|
||||
set(temp_path "${current_segment}")
|
||||
else()
|
||||
set(temp_path "${temp_path}/${current_segment}")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
set(${joined_path} "${temp_path}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
@@ -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})
|
||||
|
||||
|
||||
592
docs/doxy.in
592
docs/doxy.in
File diff suppressed because it is too large
Load Diff
@@ -7,24 +7,25 @@
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Definitions](#definitions)
|
||||
* [ENTT_NOEXCEPTION](#entt_noexcept)
|
||||
* [ENTT_NOEXCEPTION](#entt_noexception)
|
||||
* [ENTT_USE_ATOMIC](#entt_use_atomic)
|
||||
* [ENTT_ID_TYPE](#entt_id_type)
|
||||
* [ENTT_SPARSE_PAGE](#entt_sparse_page)
|
||||
* [ENTT_PACKED_PAGE](#entt_packed_page)
|
||||
* [ENTT_ASSERT](#entt_assert)
|
||||
* [ENTT_ASSERT_CONSTEXPR](#entt_assert_constexpr)
|
||||
* [ENTT_DISABLE_ASSERT](#entt_disable_assert)
|
||||
* [ENTT_NO_ETO](#entt_no_eto)
|
||||
* [ENTT_STANDARD_CPP](#entt_standard_cpp)
|
||||
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
`EnTT` doesn't offer many hooks for customization but it certainly offers
|
||||
some.<br/>
|
||||
`EnTT` has become almost completely customizable over time, in many
|
||||
respects. These variables are just one of the many ways to customize how it
|
||||
works.<br/>
|
||||
In the vast majority of cases, users will have no interest in changing the
|
||||
default parameters. For all other cases, the list of possible configurations
|
||||
with which it's possible to adjust the behavior of the library at runtime can be
|
||||
@@ -40,17 +41,17 @@ will remain stable over time unlike the options below.
|
||||
|
||||
## ENTT_NOEXCEPTION
|
||||
|
||||
This parameter can be used to switch off exception handling in `EnTT`.<br/>
|
||||
To do this, simply define the variable without assigning any value to it. This
|
||||
is roughly equivalent to setting the compiler flag `-ff-noexceptions`.
|
||||
Define this variable without assigning any value to it to turn off exception
|
||||
handling in `EnTT`.<br/>
|
||||
This is roughly equivalent to setting the compiler flag `-fno-exceptions` but is
|
||||
also limited to this library only.
|
||||
|
||||
## ENTT_USE_ATOMIC
|
||||
|
||||
In general, `EnTT` doesn't offer primitives to support multi-threading. Many of
|
||||
the features can be split over multiple threads without any explicit control and
|
||||
the user is the only one who knows if and when a synchronization point is
|
||||
required.<br/>
|
||||
However, some features aren't easily accessible to users and can be made
|
||||
the user is the one who knows if a synchronization point is required.<br/>
|
||||
However, some features aren't easily accessible to users and are made
|
||||
thread-safe by means of this definition.
|
||||
|
||||
## ENTT_ID_TYPE
|
||||
@@ -70,9 +71,9 @@ power of 2.
|
||||
|
||||
## ENTT_PACKED_PAGE
|
||||
|
||||
Similar to sparse arrays, packed arrays of components are paginated as well. In
|
||||
However, int this case the aim isn't to reduce memory usage but to have pointer
|
||||
stability upon component creation.<br/>
|
||||
As it happens with sparse arrays, packed arrays are also paginated. However, in
|
||||
this case the aim isn't to reduce memory usage but to have pointer stability
|
||||
upon component creation.<br/>
|
||||
Default size of pages (that is, the number of elements they contain) is 1024 but
|
||||
users can adjust it if appropriate. In all case, the chosen value **must** be a
|
||||
power of 2.
|
||||
@@ -83,21 +84,29 @@ For performance reasons, `EnTT` doesn't use exceptions or any other control
|
||||
structures. In fact, it offers many features that result in undefined behavior
|
||||
if not used correctly.<br/>
|
||||
To get around this, the library relies on a lot of asserts for the purpose of
|
||||
detecting errors in debug builds. By default, it uses `assert` internally, but
|
||||
users are allowed to overwrite its behavior by setting this variable.
|
||||
detecting errors in debug builds. By default, it uses `assert` internally. Users
|
||||
are allowed to overwrite its behavior by setting this variable.
|
||||
|
||||
### ENTT_ASSERT_CONSTEXPR
|
||||
|
||||
Usually, an assert within a `constexpr` function isn't a big deal. However, in
|
||||
case of extreme customizations, it might be useful to differentiate.<br/>
|
||||
For this purpose, `EnTT` introduces an admittedly badly named variable to make
|
||||
the job easier in this regard. By default, this variable forwards its arguments
|
||||
to `ENTT_ASSERT`.
|
||||
|
||||
### ENTT_DISABLE_ASSERT
|
||||
|
||||
Assertions may in turn affect performance to an extent when enabled. Whether
|
||||
`ENTT_ASSERT` is redefined or not, all asserts can be disabled at once by means
|
||||
of this definition.<br/>
|
||||
Note that `ENTT_DISABLE_ASSERT` takes precedence over the redefinition of
|
||||
`ENTT_ASSERT` and is therefore meant to disable all controls no matter what.
|
||||
`ENTT_ASSERT` and `ENTT_ASSERT_CONSTEXPR` are redefined or not, all asserts can
|
||||
be disabled at once by means of this definition.<br/>
|
||||
Note that `ENTT_DISABLE_ASSERT` takes precedence over the redefinition of the
|
||||
other variables and is therefore meant to disable all controls no matter what.
|
||||
|
||||
## ENTT_NO_ETO
|
||||
|
||||
In order to reduce memory consumption and increase performance, empty types are
|
||||
never stored by the ECS module of `EnTT`.<br/>
|
||||
never instantiated nor stored by the ECS module of `EnTT`.<br/>
|
||||
Use this variable to treat these types like all others and therefore to create a
|
||||
dedicated storage for them.
|
||||
|
||||
@@ -105,7 +114,7 @@ dedicated storage for them.
|
||||
|
||||
`EnTT` mixes non-standard language features with others that are perfectly
|
||||
compliant to offer some of its functionalities.<br/>
|
||||
This definition will prevent the library from using non-standard techniques,
|
||||
that is, functionalities that aren't fully compliant with the standard C++.<br/>
|
||||
This definition prevents the library from using non-standard techniques, that
|
||||
is, functionalities that aren't fully compliant with the standard C++.<br/>
|
||||
While there are no known portability issues at the time of this writing, this
|
||||
should make the library fully portable anyway if needed.
|
||||
|
||||
@@ -7,9 +7,8 @@
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Containers](#containers)
|
||||
* [Dense hash map](#dense-hash-map)
|
||||
* [Dense hash set](#dense-hash-set)
|
||||
|
||||
* [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
|
||||
@@ -31,26 +30,38 @@ guaranteed as usual.
|
||||
|
||||
# Containers
|
||||
|
||||
## Dense hash map
|
||||
## Dense map
|
||||
|
||||
The dense hash map made available in `EnTT` is a map that aims to return a
|
||||
The dense map made available in `EnTT` is a hash map that aims to return a
|
||||
packed array of elements, so as to reduce the number of jumps in memory during
|
||||
the iteration.<br/>
|
||||
iterations.<br/>
|
||||
The implementation is based on _sparse sets_ and each bucket is identified by an
|
||||
implicit list within the packed array itself.
|
||||
|
||||
The interface is very close to its counterpart in the standard library, that is,
|
||||
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/>
|
||||
This is because they return a pair of references rather than a reference to a
|
||||
pair. In other words, dense maps return a so called _proxy iterator_ the value
|
||||
type of which is:
|
||||
|
||||
* `std::pair<const Key &, Type &>` for non-const iterator types.
|
||||
* `std::pair<const Key &, const Type &>` for const iterator types.
|
||||
|
||||
This is quite different from what any standard library map returns and should be
|
||||
taken into account when looking for a drop-in replacement.
|
||||
|
||||
## Dense set
|
||||
|
||||
The dense set made available in `EnTT` is a hash set that aims to return a
|
||||
packed array of elements, so as to reduce the number of jumps in memory during
|
||||
iterations.<br/>
|
||||
The implementation is based on _sparse sets_ and each bucket is identified by an
|
||||
implicit list within the packed array itself.
|
||||
|
||||
The interface is in all respects similar to its counterpart in the standard
|
||||
library, that is, `std::unordered_map`.<br/>
|
||||
Therefore, there is no need to go into the API description.
|
||||
|
||||
## Dense hash set
|
||||
|
||||
The dense hash set made available in `EnTT` is a set that aims to return a
|
||||
packed array of elements, so as to reduce the number of jumps in memory during
|
||||
the iteration.<br/>
|
||||
The implementation is based on _sparse sets_ and each bucket is identified by an
|
||||
implicit list within the packed array itself.
|
||||
|
||||
The interface is in all respects similar to its counterpart in the standard
|
||||
library, that is, `std::unordered_set`.<br/>
|
||||
Therefore, there is no need to go into the API description.
|
||||
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`).
|
||||
|
||||
830
docs/md/core.md
830
docs/md/core.md
File diff suppressed because it is too large
Load Diff
1923
docs/md/entity.md
1923
docs/md/entity.md
File diff suppressed because it is too large
Load Diff
104
docs/md/faq.md
104
docs/md/faq.md
@@ -14,18 +14,20 @@
|
||||
* [Warning C4003: the min, the max and the macro](#warning-C4003-the-min-the-max-and-the-macro)
|
||||
* [The standard and the non-copyable types](#the-standard-and-the-non-copyable-types)
|
||||
* [Which functions trigger which signals](#which-functions-trigger-which-signals)
|
||||
* [Duplicate storage for the same component](#duplicate-storage-for-the-same-component)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
This is a constantly updated section where I'll try to put the answers to the
|
||||
This is a constantly updated section where I'm trying to put the answers to the
|
||||
most frequently asked questions.<br/>
|
||||
If you don't find your answer here, there are two cases: nobody has done it yet
|
||||
or this section needs updating. In both cases, try to
|
||||
[open a new issue](https://github.com/skypjack/entt/issues/new) or enter the
|
||||
[gitter channel](https://gitter.im/skypjack/entt) and ask your question.
|
||||
or this section needs updating. In both cases, you can
|
||||
[open a new issue](https://github.com/skypjack/entt/issues/new) or enter either
|
||||
the [gitter channel](https://gitter.im/skypjack/entt) or the
|
||||
[discord server](https://discord.gg/5BjPWBd) to ask for help.<br/>
|
||||
Probably someone already has an answer for you and we can then integrate this
|
||||
part of the documentation.
|
||||
|
||||
@@ -45,14 +47,14 @@ lot, achieving good results in many cases.
|
||||
First of all, there are two things to do in a Windows project:
|
||||
|
||||
* Disable the [`/JMC`](https://docs.microsoft.com/cpp/build/reference/jmc)
|
||||
option (_Just My Code_ debugging), available starting in Visual Studio 2017
|
||||
option (_Just My Code_ debugging), available starting with Visual Studio 2017
|
||||
version 15.8.
|
||||
|
||||
* Set the [`_ITERATOR_DEBUG_LEVEL`](https://docs.microsoft.com/cpp/standard-library/iterator-debug-level)
|
||||
macro to 0. This will disable checked iterators and iterator debugging.
|
||||
|
||||
Moreover, the macro `ENTT_ASSERT` should be redefined to disable internal checks
|
||||
made by `EnTT` in debug:
|
||||
Moreover, set the `ENTT_DISABLE_ASSERT` variable or redefine the `ENTT_ASSERT`
|
||||
macro to disable internal debug checks in `EnTT`:
|
||||
|
||||
```cpp
|
||||
#define ENTT_ASSERT(...) ((void)0)
|
||||
@@ -61,22 +63,22 @@ made by `EnTT` in debug:
|
||||
These asserts are introduced to help the users but they require to access to the
|
||||
underlying containers and therefore risk ruining the performance in some cases.
|
||||
|
||||
With these changes, debug performance should increase enough for most cases. If
|
||||
you want something more, you can can also switch to an optimization level `O0`
|
||||
or preferably `O1`.
|
||||
With these changes, debug performance should increase enough in most cases. If
|
||||
you want something more, you can also switch to an optimization level `O0` or
|
||||
preferably `O1`.
|
||||
|
||||
## How can I represent hierarchies with my components?
|
||||
|
||||
This is one of the first questions that anyone makes when starting to work with
|
||||
the entity-component-system architectural pattern.<br/>
|
||||
There are several approaches to the problem and what’s the best one depends
|
||||
mainly on the real problem one is facing. In all cases, how to do it doesn't
|
||||
strictly depend on the library in use, but the latter can certainly allow or
|
||||
not different techniques depending on how the data are laid out.
|
||||
There are several approaches to the problem and the best one depends mainly on
|
||||
the real problem one is facing. In all cases, how to do it doesn't strictly
|
||||
depend on the library in use, but the latter certainly allows or not different
|
||||
techniques depending on how the data are laid out.
|
||||
|
||||
I tried to describe some of the techniques that fit well with the model of
|
||||
`EnTT`. [Here](https://skypjack.github.io/2019-06-25-ecs-baf-part-4/) is the
|
||||
first post of a series that tries to explore the problem. More will probably
|
||||
I tried to describe some of the approaches that fit well with the model of
|
||||
`EnTT`. [This](https://skypjack.github.io/2019-06-25-ecs-baf-part-4/) is the
|
||||
first post of a series that tries to _explore_ the problem. More will probably
|
||||
come in future.<br/>
|
||||
In addition, `EnTT` also offers the possibility to create stable storage types
|
||||
and therefore have pointer stability for one, all or some components. This is by
|
||||
@@ -90,6 +92,7 @@ Custom entity identifiers are definitely a good idea in two cases at least:
|
||||
|
||||
* If `std::uint32_t` isn't large enough for your purposes, since this is the
|
||||
underlying type of `entt::entity`.
|
||||
|
||||
* If you want to avoid conflicts when using multiple registries.
|
||||
|
||||
Identifiers can be defined through enum classes and class types that define an
|
||||
@@ -105,7 +108,7 @@ There is no limit to the number of identifiers that can be defined.
|
||||
## Warning C4307: integral constant overflow
|
||||
|
||||
According to [this](https://github.com/skypjack/entt/issues/121) issue, using a
|
||||
hashed string under VS could generate a warning.<br/>
|
||||
hashed string under VS (toolset v141) could generate a warning.<br/>
|
||||
First of all, I want to reassure you: it's expected and harmless. However, it
|
||||
can be annoying.
|
||||
|
||||
@@ -136,7 +139,7 @@ errors during compilation.
|
||||
|
||||
It's a pretty big problem but fortunately it's not a problem of `EnTT` and there
|
||||
is a fairly simple solution to it.<br/>
|
||||
It consists in defining the `NOMINMAX` macro before to include any other header
|
||||
It consists in defining the `NOMINMAX` macro before including any other header
|
||||
so as to get rid of the extra definitions:
|
||||
|
||||
```cpp
|
||||
@@ -149,15 +152,14 @@ more details.
|
||||
## The standard and the non-copyable types
|
||||
|
||||
`EnTT` uses internally the trait `std::is_copy_constructible_v` to check if a
|
||||
component is actually copyable. This trait doesn't check if an object can
|
||||
actually be copied but only verifies if there is a copy constructor
|
||||
available.<br/>
|
||||
This can lead to surprising results due to some idiosyncrasies of the standard
|
||||
mainly related to the need to guarantee backward compatibility.
|
||||
component is actually copyable. However, this trait doesn't really check whether
|
||||
a type is actually copyable. Instead, it just checks that a suitable copy
|
||||
constructor and copy operator exist.<br/>
|
||||
This can lead to surprising results due to some idiosyncrasies of the standard.
|
||||
|
||||
For example, `std::vector` defines a copy constructor no matter if its value
|
||||
type is copyable or not. As a result, `std::is_copy_constructible_v` is true
|
||||
for the following specialization:
|
||||
For example, `std::vector` defines a copy constructor that is conditionally
|
||||
enabled depending on whether the value type is copyable or not. As a result,
|
||||
`std::is_copy_constructible_v` returns true for the following specialization:
|
||||
|
||||
```cpp
|
||||
struct type {
|
||||
@@ -165,31 +167,42 @@ struct type {
|
||||
};
|
||||
```
|
||||
|
||||
When trying to assign an instance of this type to an entity in the ECS part,
|
||||
this may trigger a compilation error because we cannot really make a copy of
|
||||
it.<br/>
|
||||
As a workaround, users can mark the type explicitly as non-copyable:
|
||||
However, the copy constructor is effectively disabled upon specialization.
|
||||
Therefore, trying to assign an instance of this type to an entity may trigger a
|
||||
compilation error.<br/>
|
||||
As a workaround, users can mark the type explicitly as non-copyable. This also
|
||||
suppresses the implicit generation of the move constructor and operator, which
|
||||
will therefore have to be defaulted accordingly:
|
||||
|
||||
```cpp
|
||||
struct type {
|
||||
type(const type &) = delete;
|
||||
type(type &&) = default;
|
||||
|
||||
type & operator=(const type &) = delete;
|
||||
type & operator=(type &&) = default;
|
||||
|
||||
std::vector<std::unique_ptr<action>> vec;
|
||||
};
|
||||
```
|
||||
|
||||
Unfortunately, this will also disable aggregate initialization.
|
||||
Note that aggregate initialization is also disabled as a consequence.<br/>
|
||||
Fortunately, this type of trick is quite rare. The bad news is that there is no
|
||||
way to deal with it at the library level, this being due to the design of the
|
||||
language. On the other hand, the fact that the language itself also offers a way
|
||||
to mitigate the problem makes it manageable.
|
||||
|
||||
## Which functions trigger which signals
|
||||
|
||||
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:
|
||||
|
||||
* `on_created` is invoked when a component is first added (neither modified nor
|
||||
replaced) to an entity.
|
||||
|
||||
* `on_update` is called whenever an existing component is modified or replaced.
|
||||
|
||||
* `on_destroyed` is called when a component is explicitly or implicitly removed
|
||||
from an entity.
|
||||
|
||||
@@ -201,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.
|
||||
|
||||
372
docs/md/graph.md
Normal file
372
docs/md/graph.md
Normal file
@@ -0,0 +1,372 @@
|
||||
# Crash Course: graph
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Data structures](#data-structures)
|
||||
* [Adjacency matrix](#adjacency-matrix)
|
||||
* [Graphviz dot language](#graphviz-dot-language)
|
||||
* [Flow builder](#flow-builder)
|
||||
* [Tasks and resources](#tasks-and-resources)
|
||||
* [Fake resources and order of execution](#fake-resources-and-order-of-execution)
|
||||
* [Sync points](#sync-points)
|
||||
* [Execution graph](#execution-graph)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
`EnTT` doesn't aim to offer everything one needs to work with graphs. Therefore,
|
||||
anyone looking for this in the _graph_ submodule will be disappointed.<br/>
|
||||
Quite the opposite is true 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
|
||||
tight scheduling on the subject.<br/>
|
||||
The data structures presented in this section are mainly useful for the
|
||||
development and support of some tools which are also part of the same submodule.
|
||||
|
||||
## Adjacency matrix
|
||||
|
||||
The adjacency matrix is designed to represent either a directed or an undirected
|
||||
graph:
|
||||
|
||||
```cpp
|
||||
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{};
|
||||
```
|
||||
|
||||
The `directed_tag` type _creates_ the graph as directed. There is also an
|
||||
`undirected_tag` counterpart which creates it as undirected.<br/>
|
||||
The interface deviates slightly from the typical double indexing of C and offers
|
||||
an API that is perhaps more familiar to a C++ programmer. Therefore, the access
|
||||
and modification of an element takes place via the `contains`, `insert` and
|
||||
`erase` functions rather than a double call to an `operator[]`:
|
||||
|
||||
```cpp
|
||||
if(adjacency_matrix.contains(0u, 1u)) {
|
||||
adjacency_matrix.erase(0u, 1u);
|
||||
} else {
|
||||
adjacency_matrix.insert(0u, 1u);
|
||||
}
|
||||
```
|
||||
|
||||
Both `insert` and` erase` are _idempotent_ functions which have no effect if the
|
||||
element already exists or has already been deleted.<br/>
|
||||
The first one returns an `std::pair` containing the iterator to the element and
|
||||
a boolean value indicating whether the element was newly inserted or not. The
|
||||
second one returns the number of deleted elements (0 or 1).
|
||||
|
||||
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};
|
||||
```
|
||||
|
||||
To visit all vertices, the class offers a function named `vertices` that returns
|
||||
an iterable object suitable for the purpose:
|
||||
|
||||
```cpp
|
||||
for(auto &&vertex: adjacency_matrix.vertices()) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
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) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
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 is used to get them as pairs of
|
||||
vertices:
|
||||
|
||||
```cpp
|
||||
for(auto [lhs, rhs]: adjacency_matrix.edges()) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
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)) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Both the functions expect the vertex to visit (that is, to return the in- or
|
||||
out-edges for) as an argument.<br/>
|
||||
Finally, the adjacency matrix is an allocator-aware container and offers most of
|
||||
the functionalities one would expect from this type of containers, such as
|
||||
`clear` or 'get_allocator` and so on.
|
||||
|
||||
## Graphviz dot language
|
||||
|
||||
As it's one of the most popular formats, the library offers minimal support for
|
||||
converting a graph to a Graphviz dot snippet.<br/>
|
||||
The simplest way is to pass both an output stream and a graph to the `dot`
|
||||
function:
|
||||
|
||||
```cpp
|
||||
std::ostringstream output{};
|
||||
entt::dot(output, adjacency_matrix);
|
||||
```
|
||||
|
||||
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
|
||||
externally managed data to the graph being converted.
|
||||
|
||||
# Flow builder
|
||||
|
||||
A flow builder is used to create execution graphs from tasks and resources.<br/>
|
||||
The implementation is as generic as possible and doesn't bind to any other part
|
||||
of the library.
|
||||
|
||||
This class is designed as a sort of _state machine_ to which a specific task is
|
||||
attached for which the resources accessed in read-only or read-write mode are
|
||||
specified.<br/>
|
||||
Most of the functions in the API also return the flow builder itself, according
|
||||
to what is the common sense API when it comes to builder classes.
|
||||
|
||||
Once all tasks 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 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`.
|
||||
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 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);
|
||||
```
|
||||
|
||||
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 necessary.
|
||||
|
||||
Once a task is associated with the flow builder, it's also assigned read-only or
|
||||
read-write resources as appropriate:
|
||||
|
||||
```cpp
|
||||
builder
|
||||
.bind("task_1"_hs)
|
||||
.ro("resource_1"_hs)
|
||||
.ro("resource_2"_hs)
|
||||
.bind("task_2"_hs)
|
||||
.rw("resource_2"_hs)
|
||||
```
|
||||
|
||||
As mentioned, many functions return the builder itself and it's therefore easy
|
||||
to concatenate the different calls.<br/>
|
||||
Also in the case of resources, 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
|
||||
before or after another task.<br/>
|
||||
In fact, the order of _registration_ on the resources also determines the order
|
||||
in which the tasks are processed during the generation of the execution graph.
|
||||
|
||||
However, there is a way to _force_ the execution order of two processes.<br/>
|
||||
Briefly, since accessing a resource in opposite modes requires sequential rather
|
||||
than parallel scheduling, it's possible to make use of fake resources to rule on
|
||||
the execution order:
|
||||
|
||||
```cpp
|
||||
builder
|
||||
.bind("task_1"_hs)
|
||||
.ro("resource_1"_hs)
|
||||
.rw("fake"_hs)
|
||||
.bind("task_2"_hs)
|
||||
.ro("resource_2"_hs)
|
||||
.ro("fake"_hs)
|
||||
.bind("task_3"_hs)
|
||||
.ro("resource_2"_hs)
|
||||
.ro("fake"_hs)
|
||||
```
|
||||
|
||||
This snippet forces the execution of `task_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:
|
||||
|
||||
```cpp
|
||||
builder
|
||||
.bind("task_1"_hs)
|
||||
.ro("resource_1"_hs)
|
||||
.ro("fake"_hs)
|
||||
.bind("task_2"_hs)
|
||||
.ro("resource_1"_hs)
|
||||
.ro("fake"_hs)
|
||||
.bind("task_3"_hs)
|
||||
.ro("resource_2"_hs)
|
||||
.rw("fake"_hs)
|
||||
```
|
||||
|
||||
In this case, since there are a number of processes that want to read a specific
|
||||
resource, they will do so in parallel by forcing `task_3` to run after all the
|
||||
others tasks.
|
||||
|
||||
## Sync points
|
||||
|
||||
Sometimes it's useful to assign the role of _sync point_ to a node.<br/>
|
||||
Whether it accesses new resources or is simply a watershed, the procedure for
|
||||
assigning this role to a vertex is always the same. First it's tied to the flow
|
||||
builder, then the `sync` function is invoked:
|
||||
|
||||
```cpp
|
||||
builder.bind("sync_point"_hs).sync();
|
||||
```
|
||||
|
||||
The choice to assign an _identity_ to this type of nodes lies in the fact that,
|
||||
more often than not, they also perform operations on resources.<br/>
|
||||
If this isn't the case, it will still be possible to create no-op vertices to
|
||||
which empty tasks are assigned.
|
||||
|
||||
## Execution graph
|
||||
|
||||
Once both the resources and their consumers have been properly registered, the
|
||||
purpose of this tool is to generate an execution graph that takes into account
|
||||
all specified constraints to return the best scheduling for the vertices:
|
||||
|
||||
```cpp
|
||||
entt::adjacency_matrix<entt::directed_tag> graph = builder.graph();
|
||||
```
|
||||
|
||||
Searching for the main vertices (that is, those without in-edges) is usually the
|
||||
first thing required:
|
||||
|
||||
```cpp
|
||||
for(auto &&vertex: graph) {
|
||||
if(auto in_edges = graph.in_edges(vertex); in_edges.begin() == in_edges.end()) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
@@ -50,40 +48,29 @@ However, what is offered should hopefully serve as a basis for all of them.
|
||||
|
||||
The runtime reflection system deserves a special mention when it comes to using
|
||||
it across boundaries.<br/>
|
||||
Since it's linked already to a static context to which the visible components
|
||||
are attached and different contexts don't relate to each other, they must be
|
||||
_shared_ to allow the use of meta types across boundaries.
|
||||
Since it's linked already to a static context to which the elements are attached
|
||||
and different contexts don't relate to each other, they must be _shared_ to
|
||||
allow the use of meta types across boundaries.
|
||||
|
||||
Sharing a context is trivial though. First of all, the local one must be
|
||||
acquired in the main space:
|
||||
Fortunately, sharing a context is also trivial to do. First of all, the local
|
||||
one is acquired in the main space:
|
||||
|
||||
```cpp
|
||||
entt::meta_ctx ctx{};
|
||||
auto handle = entt::locator<entt::meta_ctx>::handle();
|
||||
```
|
||||
|
||||
Then, it must passed to the receiving space that will set it as its global
|
||||
context, thus releasing the local one that remains available but is no longer
|
||||
referred to by the runtime reflection system:
|
||||
Then, it's passed to the receiving space that sets it as its default context,
|
||||
thus discarding or storing aside the local one:
|
||||
|
||||
```cpp
|
||||
entt::meta_ctx::bind(ctx);
|
||||
entt::locator<entt::meta_ctx>::reset(handle);
|
||||
```
|
||||
|
||||
From now on, both spaces will refer to the same context and on it will be
|
||||
attached the new visible meta types, no matter where they are created.<br/>
|
||||
A context can also be reset and then associated again locally as:
|
||||
|
||||
```cpp
|
||||
entt::meta_ctx::bind(entt::meta_ctx{});
|
||||
```
|
||||
|
||||
This is allowed because local and global contexts are separated. Therefore, it's
|
||||
always possible to make the local context the current one again.
|
||||
|
||||
Before to release a context, all locally registered types should be reset to
|
||||
avoid dangling references. Otherwise, if a type is accessed from another space
|
||||
by name, there could be an attempt to address its parts that are no longer
|
||||
available.
|
||||
From now on, both spaces refer to the same context and on it are attached all
|
||||
new meta types, no matter where they are created.<br/>
|
||||
Note that _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
|
||||
|
||||
|
||||
134
docs/md/links.md
134
docs/md/links.md
@@ -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,25 +24,43 @@ 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.
|
||||
* [Ember Sword](https://embersword.com/): a modern Free-to-Play MMORPG with a
|
||||
player-driven economy, a classless combat system, and scarce, tradable
|
||||
cosmetic collectibles.
|
||||
* Apparently [Diablo II: Resurrected](https://diablo2.blizzard.com/) by
|
||||
[Blizzard](https://www.blizzard.com/): monsters, heroes, items, spells, all
|
||||
resurrected. Thanks unknown insider.
|
||||
* [Apparently](https://www.youtube.com/watch?v=P8xvOA3ikrQ&t=1105s)
|
||||
[Call of Duty: Vanguard](https://www.callofduty.com/vanguard) by
|
||||
[Sledgehammer Games](https://www.sledgehammergames.com/): I can neither
|
||||
confirm nor deny but there is a license I know in the credits.
|
||||
* Apparently [D&D Dark Alliance](https://darkalliance.wizards.com) by
|
||||
[Wizards of the Coast](https://company.wizards.com): your party, their
|
||||
funeral.
|
||||
* [TiltedOnline](https://github.com/tiltedphoques/TiltedOnline) by
|
||||
[Tilted Phoques](https://github.com/tiltedphoques): Skyrim and Fallout 4 mod
|
||||
to play online.
|
||||
* [Apparently](https://www.youtube.com/watch?v=P8xvOA3ikrQ&t=1105s)
|
||||
[Call of Duty: Vanguard](https://www.callofduty.com/vanguard): I can neither
|
||||
confirm nor deny but there is a license I know in the credits.
|
||||
* [Antkeeper](https://github.com/antkeeper/antkeeper-source): an ant colony
|
||||
simulation [game](https://antkeeper.com/).
|
||||
* [Openblack](https://github.com/openblack/openblack): open source
|
||||
@@ -44,7 +79,7 @@ I hope this list can grow much more in the future:
|
||||
puzzler with logic gates and other cool stuff.
|
||||
[Check it out](https://indi-kernick.itch.io/the-machine-web-version).
|
||||
* [EnTTPong](https://github.com/DomRe/EnttPong): a basic game made to showcase
|
||||
different parts of EnTT and C++17.
|
||||
different parts of `EnTT` and C++17.
|
||||
* [Randballs](https://github.com/gale93/randballs): simple `SFML` and `EnTT`
|
||||
playground.
|
||||
* [EnTT Tower Defense](https://github.com/Daivuk/tddod): a data oriented tower
|
||||
@@ -80,8 +115,31 @@ I hope this list can grow much more in the future:
|
||||
space ship through a large 2D open world.
|
||||
* [PokeMaster](https://github.com/utilForever/PokeMaster): Pokemon Battle
|
||||
simulator using C++ with some reinforcement learning.
|
||||
* [HomeHearth](https://youtu.be/GrEWl8npL9Y): choose your hero, protect the
|
||||
town, before it's too late.
|
||||
* [City Builder Game](https://github.com/PhiGei2000/CityBuilderGame): a simple
|
||||
city-building game using C++ and OpenGL.
|
||||
* [BattleSub](https://github.com/bfeldpw/battlesub): two player 2D submarine
|
||||
game with some fluid dynamics.
|
||||
* [Crimson Rush](https://github.com/WilKam01/LuaCGame): a dungeon-crawler and
|
||||
rougelike inspired game about exploring and surviving as long as possible.
|
||||
* [Space Fight](https://github.com/cholushkin/SpaceFight): one screen
|
||||
multi-player arcade shooter game prototype.
|
||||
* [Confetti Party](https://github.com/hexerei/entt-confetti): C++ sample
|
||||
application as a starting point using `EnTT` and `SDL2`.
|
||||
* [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
|
||||
@@ -135,13 +193,37 @@ I hope this list can grow much more in the future:
|
||||
oriented game engine.
|
||||
* [Becketron](https://github.com/Doctor-Foxling/Becketron): a game engine
|
||||
written mostly in C++.
|
||||
* [Spatial Engine](https://github.com/luizgabriel/Spatial.Engine): A
|
||||
* [Spatial Engine](https://github.com/luizgabriel/Spatial.Engine): a
|
||||
cross-platform engine created on top of google's filament rendering engine.
|
||||
* [Kaguya](https://github.com/KaiH0717/Kaguya): D3D12 Rendering Engine.
|
||||
* [OpenAWE](https://github.com/OpenAWE-Project/OpenAWE): Open implementation
|
||||
* [OpenAWE](https://github.com/OpenAWE-Project/OpenAWE): open implementation
|
||||
of the Alan Wake Engine.
|
||||
* [Nazara Engine](https://github.com/DigitalPulseSoftware/NazaraEngine): fast,
|
||||
cross-platform, object-oriented API to help in daily developer life.
|
||||
* [Billy Engine](https://github.com/billy4479/BillyEngine): some kind of a 2D
|
||||
engine based on `SDL2` and `EnTT`.
|
||||
* [Ducktape](https://github.com/DucktapeEngine/Ducktape): an open source C++
|
||||
2D & 3D game engine that focuses on being fast and powerful.
|
||||
* [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.
|
||||
@@ -165,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
|
||||
@@ -187,8 +286,12 @@ I hope this list can grow much more in the future:
|
||||
project retrospect by [Eric Hildebrand](https://www.erichildebrand.net/).
|
||||
* [EnTT Entity Component System Gaming Library](https://gamefromscratch.com/entt-entity-component-system-gaming-library/):
|
||||
`EnTT` on GameFromScratch.com.
|
||||
* [Custom C++ server for UE5](https://youtu.be/fbXZVNCOvjM) optimized for
|
||||
MMO(RPG)s and its [follow-up](https://youtu.be/yGlZeopx2hU) episode about
|
||||
player bots and full external ECS: a series definitely worth looking at.
|
||||
|
||||
## Any Other Business:
|
||||
|
||||
* 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
|
||||
@@ -230,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.
|
||||
|
||||
@@ -7,69 +7,82 @@
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Service locator](#service-locator)
|
||||
* [Opaque handles](#opaque-handles)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
Usually service locators are tightly bound to the services they expose and it's
|
||||
hard to define a general purpose solution. This template based implementation
|
||||
tries to fill the gap and to get rid of the burden of defining a different
|
||||
specific locator for each application.<br/>
|
||||
This class is tiny, partially unsafe and thus risky to use. Moreover it doesn't
|
||||
fit probably most of the scenarios in which a service locator is required. Look
|
||||
at it as a small tool that can sometimes be useful if users know how to handle
|
||||
it.
|
||||
Usually, service locators are tightly bound to the services they expose and it's
|
||||
hard to define a general purpose solution.<br/>
|
||||
This tiny class tries to fill the gap and gets rid of the burden of defining a
|
||||
different specific locator for each application.
|
||||
|
||||
# Service locator
|
||||
|
||||
The API is straightforward. The basic idea is that services are implemented by
|
||||
means of interfaces and rely on polymorphism.<br/>
|
||||
The locator is instantiated with the base type of the service if any and a
|
||||
concrete implementation is provided along with all the parameters required to
|
||||
initialize it. As an example:
|
||||
The service locator API tries to mimic that of `std::optional` and adds some
|
||||
extra functionalities on top of it such as allocator support.<br/>
|
||||
There are a couple of functions to set up a service, namely `emplace` and
|
||||
`allocate_emplace`:
|
||||
|
||||
```cpp
|
||||
// the service has no base type, a locator is used to treat it as a kind of singleton
|
||||
entt::service_locator<my_service>::set(params...);
|
||||
|
||||
// sets up an opaque service
|
||||
entt::service_locator<audio_interface>::set<audio_implementation>(params...);
|
||||
|
||||
// resets (destroys) the service
|
||||
entt::service_locator<audio_interface>::reset();
|
||||
entt::locator<interface>::emplace<service>(argument);
|
||||
entt::locator<interface>::allocate_emplace<service>(allocator, argument);
|
||||
```
|
||||
|
||||
The locator can also be queried to know if an active service is currently set
|
||||
and to retrieve it if necessary (either as a pointer or as a reference):
|
||||
The difference is that the latter expects an allocator as the first argument and
|
||||
uses it to allocate the service itself.<br/>
|
||||
Once a service is set up, it's retrieved using the `value` function:
|
||||
|
||||
```cpp
|
||||
// no service currently set
|
||||
auto empty = entt::service_locator<audio_interface>::empty();
|
||||
|
||||
// gets a (possibly empty) shared pointer to the service ...
|
||||
std::shared_ptr<audio_interface> ptr = entt::service_locator<audio_interface>::get();
|
||||
|
||||
// ... or a reference, but it's undefined behaviour if the service isn't set yet
|
||||
audio_interface &ref = entt::service_locator<audio_interface>::ref();
|
||||
interface &service = entt::locator<interface>::value();
|
||||
```
|
||||
|
||||
A common use is to wrap the different locators in a container class, creating
|
||||
aliases for the various services:
|
||||
Since the service may not be set (and therefore this function may result in an
|
||||
undefined behavior), the `has_value` and `value_or` functions are also available
|
||||
to test a service locator and to get a fallback service in case there is none:
|
||||
|
||||
```cpp
|
||||
struct locator {
|
||||
using camera = entt::service_locator<camera_interface>;
|
||||
using audio = entt::service_locator<audio_interface>;
|
||||
// ...
|
||||
};
|
||||
|
||||
// ...
|
||||
|
||||
void init() {
|
||||
locator::camera::set<camera_null>();
|
||||
locator::audio::set<audio_implementation>(params...);
|
||||
if(entt::locator<interface>::has_value()) {
|
||||
// ...
|
||||
}
|
||||
|
||||
interface &service = entt::locator<interface>::value_or<fallback_impl>(argument);
|
||||
```
|
||||
|
||||
All arguments are used only if necessary, that is, if a service doesn't already
|
||||
exist and therefore the fallback service is constructed and returned. In all
|
||||
other cases, they are discarded.<br/>
|
||||
Finally, to reset a service, use the `reset` function.
|
||||
|
||||
## Opaque handles
|
||||
|
||||
Sometimes it's useful to _transfer_ a copy of a service to another locator. For
|
||||
example, when working across boundaries it's common to _share_ a service with a
|
||||
dynamically loaded module.<br/>
|
||||
Options aren't much in this case. Among these is the possibility of _exporting_
|
||||
services and assigning them to a different locator.
|
||||
|
||||
This is what the `handle` and `reset` functions are meant for.<br/>
|
||||
The former returns an opaque object useful for _exporting_ (or rather, obtaining
|
||||
a reference to) a service. The latter also accepts an optional argument to a
|
||||
handle which then allows users to reset a service by initializing it with an
|
||||
opaque handle:
|
||||
|
||||
```cpp
|
||||
auto handle = entt::locator<interface>::handle();
|
||||
entt::locator<interface>::reset(handle);
|
||||
```
|
||||
|
||||
It's worth noting that it's possible to get handles for uninitialized services
|
||||
and use them with other locators. Of course, all a user will get is to have an
|
||||
uninitialized service elsewhere as well.
|
||||
|
||||
Note that exporting a service allows users to _share_ the object currently set
|
||||
in a locator. Replacing it won't replace the element even where a service has
|
||||
been configured with a handle to the previous item.<br/>
|
||||
In other words, if an audio service is replaced with a null object to silence an
|
||||
application and the original service was shared, this operation won't propagate
|
||||
to the other locators. Therefore, a module that share the ownership of the
|
||||
original audio service is still able to emit sounds.
|
||||
|
||||
601
docs/md/meta.md
601
docs/md/meta.md
@@ -15,10 +15,12 @@
|
||||
* [Template information](#template-information)
|
||||
* [Automatic conversions](#automatic-conversions)
|
||||
* [Implicitly generated default constructor](#implicitly-generated-default-constructor)
|
||||
* [From void to any](#from-void-to-any)
|
||||
* [Policies: the more, the less](#policies-the-more-the-less)
|
||||
* [Named constants and enums](#named-constants-and-enums)
|
||||
* [Properties and meta objects](#properties-and-meta-objects)
|
||||
* [Unregister types](#unregister-types)
|
||||
* [Meta context](#meta-context)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
@@ -26,7 +28,7 @@
|
||||
# Introduction
|
||||
|
||||
Reflection (or rather, its lack) is a trending topic in the C++ world and a tool
|
||||
that can unlock a lot of interesting feature in the specific case of `EnTT`. I
|
||||
that can unlock a lot of interesting features in the specific case of `EnTT`. I
|
||||
looked for a third-party library that met my needs on the subject, but I always
|
||||
came across some details that I didn't like: macros, being intrusive, too many
|
||||
allocations, and so on.<br/>
|
||||
@@ -42,22 +44,22 @@ when it comes to working with names and identifiers. It does this by offering an
|
||||
API that works with opaque identifiers that may or may not be generated by means
|
||||
of a hashed string.<br/>
|
||||
This means that users can assign any type of identifier to the meta objects, as
|
||||
long as they are numeric. It doesn't matter if they are generated at runtime, at
|
||||
long as they're numeric. It doesn't matter if they're generated at runtime, at
|
||||
compile-time or with custom functions.
|
||||
|
||||
That being said, the examples in the following sections are all based on the
|
||||
`hashed_string` class as provided by this library. Therefore, where an
|
||||
identifier is required, it's likely that an user defined literal is used as
|
||||
identifier is required, it's likely that a user defined literal is used as
|
||||
follows:
|
||||
|
||||
```cpp
|
||||
auto factory = entt::meta<my_type>().type("reflected_type"_hs);
|
||||
```
|
||||
|
||||
For what it's worth, this is likely completely equivalent to:
|
||||
For what it's worth, this is completely equivalent to:
|
||||
|
||||
```cpp
|
||||
auto factory = entt::meta<my_type>().type(42);
|
||||
auto factory = entt::meta<my_type>().type(42u);
|
||||
```
|
||||
|
||||
Obviously, human-readable identifiers are more convenient to use and highly
|
||||
@@ -65,69 +67,61 @@ 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>();
|
||||
```
|
||||
|
||||
This isn't enough to _export_ the given type and make it visible though.<br/>
|
||||
The returned value is a factory object to use to continue building the meta
|
||||
type. In order to make the type _visible_, users can assign it an identifier:
|
||||
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
|
||||
runtime type identification system built-in in `EnTT`.<br/>
|
||||
However, it's also possible to assign custom identifiers to meta types:
|
||||
|
||||
```cpp
|
||||
auto factory = entt::meta<my_type>().type("reflected_type"_hs);
|
||||
```
|
||||
|
||||
Or use the default one, that is, the built-in identifier for the given type:
|
||||
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`.
|
||||
|
||||
```cpp
|
||||
auto factory = entt::meta<my_type>().type();
|
||||
```
|
||||
A factory is such that all its member functions return the factory itself. It's
|
||||
generally used to create the following:
|
||||
|
||||
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`.
|
||||
|
||||
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:
|
||||
|
||||
* _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>()
|
||||
@@ -136,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);
|
||||
@@ -154,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>()
|
||||
@@ -169,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
|
||||
|
||||
@@ -215,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{};
|
||||
@@ -230,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,25 +235,23 @@ auto by_id = entt::resolve("reflected_type"_hs);
|
||||
auto by_type_id = entt::resolve(entt::type_id<my_type>());
|
||||
```
|
||||
|
||||
There exits also an overload of the `resolve` function to use to iterate all the
|
||||
reflected types at once. It returns an iterable object that can be used in a
|
||||
range-for loop:
|
||||
There exists also an overload of the `resolve` function to use to iterate all
|
||||
reflected types at once. It returns an iterable object to be used in a range-for
|
||||
loop:
|
||||
|
||||
```cpp
|
||||
for(auto type: entt::resolve()) {
|
||||
for(auto &&[id, type]: entt::resolve()) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
In all cases, the returned value is an instance of `meta_type`. This kind of
|
||||
objects offer an API to know their _runtime identifiers_, to iterate all the
|
||||
meta objects associated with them and even to build instances of the underlying
|
||||
type.<br/>
|
||||
Refer to the inline documentation for all the details.
|
||||
In all cases, the returned value is an instance of `meta_type` (possibly with
|
||||
its id). This kind of objects offer an API to know their _runtime identifiers_,
|
||||
to iterate all the meta objects associated with them and even to build instances
|
||||
of the underlying type.<br/>
|
||||
Meta data members and functions are accessed by name:
|
||||
|
||||
The meta objects that compose a meta type are accessed in the following ways:
|
||||
|
||||
* _Meta data_. They are accessed by _name_:
|
||||
* Meta data members:
|
||||
|
||||
```cpp
|
||||
auto data = entt::resolve<my_type>().data("member"_hs);
|
||||
@@ -287,7 +263,7 @@ The meta objects that compose a meta type are accessed in the following ways:
|
||||
know if it's a const or a static one), to get the meta type of the variable
|
||||
and to set or get the contained value.
|
||||
|
||||
* _Meta functions_. They are accessed by _name_:
|
||||
* Meta function members:
|
||||
|
||||
```cpp
|
||||
auto func = entt::resolve<my_type>().func("member"_hs);
|
||||
@@ -298,20 +274,11 @@ The meta objects that compose a meta type are accessed in the following ways:
|
||||
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.
|
||||
|
||||
* _Meta bases_. They are accessed through the _name_ of the base types:
|
||||
|
||||
```cpp
|
||||
auto base = entt::resolve<derived_type>().base("base"_hs);
|
||||
```
|
||||
|
||||
The returned type is `meta_type` and may be invalid if there is no meta base
|
||||
object associated with the given identifier.
|
||||
|
||||
All the objects thus obtained as well as the meta types can be explicitly
|
||||
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,35 +286,33 @@ if(auto func = entt::resolve<my_type>().func("member"_hs); func) {
|
||||
}
|
||||
```
|
||||
|
||||
Furthermore, all them are also returned by specific overloads that provide the
|
||||
caller with iterable ranges of top-level elements. As an example:
|
||||
Furthermore, all them (and a few more, like meta basis) are returned by a bunch
|
||||
of overloads that provide the caller with iterable ranges of top-level elements.
|
||||
As an example:
|
||||
|
||||
```cpp
|
||||
for(auto data: entt::resolve<my_type>().data()) {
|
||||
for(auto &&[id, type]: entt::resolve<my_type>().base()) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
A meta type can be used to `construct` actual instances of the underlying
|
||||
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
|
||||
|
||||
@@ -358,13 +323,15 @@ meta system in many cases.
|
||||
|
||||
To make a container be recognized as such by the meta system, users are required
|
||||
to provide specializations for either the `meta_sequence_container_traits` class
|
||||
or the `meta_associative_container_traits` class, according with the actual type
|
||||
or the `meta_associative_container_traits` class, according to the actual _type_
|
||||
of the container.<br/>
|
||||
`EnTT` already exports the specializations for some common classes. In
|
||||
particular:
|
||||
|
||||
* `std::vector` and `std::array` are exported as _sequence containers_.
|
||||
* `std::map`, `std::set` and their unordered counterparts are exported as
|
||||
* `std::vector`, `std::array`, `std::deque` and `std::list` (but not
|
||||
`std::forward_list`) are supported as _sequence containers_.
|
||||
|
||||
* `std::map`, `std::set` and their unordered counterparts are supported as
|
||||
_associative containers_.
|
||||
|
||||
It's important to include the header file `container.hpp` to make these
|
||||
@@ -393,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.
|
||||
@@ -409,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 succes:
|
||||
|
||||
```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) {
|
||||
@@ -448,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
|
||||
@@ -458,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();
|
||||
@@ -475,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) {
|
||||
@@ -489,8 +445,12 @@ 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.
|
||||
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
|
||||
expects).
|
||||
|
||||
Similarly, also the interface of the `meta_associative_container` proxy object
|
||||
is the same for all types of associative containers. However, there are some
|
||||
@@ -511,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) {
|
||||
@@ -542,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();
|
||||
@@ -555,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:
|
||||
@@ -617,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:
|
||||
@@ -651,15 +606,15 @@ 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 these information
|
||||
important to include the header file `template.hpp` to make this information
|
||||
available to the compiler when needed.
|
||||
|
||||
Meta template information are easily found:
|
||||
@@ -691,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:
|
||||
|
||||
@@ -707,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
|
||||
|
||||
@@ -726,7 +681,7 @@ entt::meta<int>()
|
||||
```
|
||||
|
||||
Repeated for each type eligible to undergo this type of conversions. This is
|
||||
both error prone and repetitive.<br/>
|
||||
both error-prone and repetitive.<br/>
|
||||
Similarly, the language allows users to silently convert unscoped enums to their
|
||||
underlying types and offers what it takes to do the same for scoped enums. It
|
||||
would result in the following if it were to be done explicitly:
|
||||
@@ -755,34 +710,51 @@ 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
|
||||
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` 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`:
|
||||
|
||||
```cpp
|
||||
entt::meta_any any = entt::resolve(id).from_void(element);
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
## Policies: the more, the less
|
||||
|
||||
Policies are a kind of compile-time directives that can be used when registering
|
||||
@@ -811,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);
|
||||
@@ -839,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>()
|
||||
@@ -863,68 +830,40 @@ 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 a decorated 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");
|
||||
```
|
||||
|
||||
Properties are always in the key/value form. There are no restrictions on the
|
||||
type of the key or value, as long as they are copy constructible objects.<br/>
|
||||
Multiple formats are supported when it comes to defining a property:
|
||||
Properties are always in the key/value form. The key is a numeric identifier,
|
||||
mostly similar to the identifier used to register meta objects. There are no
|
||||
restrictions on the type of the value instead, as long as it's movable.<br/>
|
||||
Key only properties are also supported out of the box:
|
||||
|
||||
* Properties as key/value pairs:
|
||||
```cpp
|
||||
entt::meta<my_type>().type("reflected_type"_hs).prop(my_enum::key_only);
|
||||
```
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>().type("reflected_type"_hs).prop("tooltip"_hs, "message");
|
||||
```
|
||||
|
||||
* Properties as `std::pair`s:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>().type("reflected_type"_hs).prop(std::make_pair("tooltip"_hs, "message"));
|
||||
```
|
||||
|
||||
* Key only properties:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>().type("reflected_type"_hs).prop(my_enum::key_only);
|
||||
```
|
||||
|
||||
* Properties as `std::tuple`s:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>().type("reflected_type"_hs).prop(std::make_tuple(std::make_pair("tooltip"_hs, "message"), my_enum::key_only));
|
||||
```
|
||||
|
||||
A tuple contains one or more properties. All of them are treated individually.
|
||||
|
||||
Note that it's not possible to invoke `prop` multiple times for the same meta
|
||||
object and trying to do that will result in a compilation error.<br/>
|
||||
However, the `props` function is available to associate several properties at
|
||||
once. In this case, properties in the key/value form aren't allowed, since they
|
||||
would be interpreted as two different properties rather than a single one.
|
||||
To attach multiple properties to a meta object, 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/>
|
||||
@@ -933,7 +872,7 @@ properties at once or to search a specific property by key:
|
||||
|
||||
```cpp
|
||||
// iterate all properties of a meta type
|
||||
for(auto prop: entt::resolve<my_type>().prop()) {
|
||||
for(auto &&[id, prop]: entt::resolve<my_type>().prop()) {
|
||||
// ...
|
||||
}
|
||||
|
||||
@@ -942,37 +881,89 @@ auto prop = entt::resolve<my_type>().prop("tooltip"_hs);
|
||||
```
|
||||
|
||||
Meta properties are objects having a fairly poor interface, all in all. They
|
||||
only provide the `key` and the `value` member functions to be used to retrieve
|
||||
the key and the value contained in the form of `meta_any` objects, respectively.
|
||||
only provide the `value` member function to retrieve the contained value in the
|
||||
form of a `meta_any` object.
|
||||
|
||||
## Unregister types
|
||||
|
||||
A type registered with the reflection system can also be unregistered. This
|
||||
A type registered with the reflection system can also be _unregistered_. This
|
||||
means unregistering all its data members, member functions, conversion functions
|
||||
and so on. However, base classes aren't unregistered as well, since they don't
|
||||
necessarily depend on it. Similarly, implicitly generated types (as an example,
|
||||
the meta types implicitly generated for function parameters when needed) aren't
|
||||
unregistered.<br/>
|
||||
necessarily depend on it.<br/>
|
||||
Roughly speaking, unregistering a type means disconnecting all associated meta
|
||||
objects from it and making its identifier no longer visible. The underlying node
|
||||
will remain available though, as if it were implicitly generated:
|
||||
objects from it and making its identifier no longer available:
|
||||
|
||||
```cpp
|
||||
entt::meta_reset<my_type>();
|
||||
```
|
||||
|
||||
It's also possible to reset types by their unique identifiers if required:
|
||||
It's also possible to reset types by their unique identifiers:
|
||||
|
||||
```cpp
|
||||
entt::meta_reset("my_type"_hs);
|
||||
```
|
||||
|
||||
Finally, there exists a non-template overload of the `meta_reset` function that
|
||||
doesn't accept argument and resets all searchable types (that is, all types that
|
||||
were assigned an unique identifier):
|
||||
doesn't accept arguments and resets all meta types at once:
|
||||
|
||||
```cpp
|
||||
entt::meta_reset();
|
||||
```
|
||||
|
||||
All types can be re-registered later with a completely different name and form.
|
||||
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 is obtained via a service locator as:
|
||||
|
||||
```cpp
|
||||
auto &&context = entt::locator<entt::meta_context>::value_or();
|
||||
```
|
||||
|
||||
By itself, a context is an opaque object that the user cannot do much with.
|
||||
However, users can replace an existing context with another at any time:
|
||||
|
||||
```cpp
|
||||
entt::meta_context other{};
|
||||
auto &&context = entt::locator<entt::meta_context>::value_or();
|
||||
std::swap(context, other);
|
||||
```
|
||||
|
||||
This 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
|
||||
system.<br/>
|
||||
For example, to create new meta types within a context other than the default
|
||||
one, simply pass it as an argument to the `meta` call:
|
||||
|
||||
```cpp
|
||||
entt::meta_ctx context{};
|
||||
auto factory = entt::meta<my_type>(context).type("reflected_type"_hs);
|
||||
```
|
||||
|
||||
By doing so, the new meta type 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's necessary to pass it to the `resolve` function:
|
||||
|
||||
```cpp
|
||||
entt::meta_type type = entt::resolve(context, "reflected_type"_hs)
|
||||
```
|
||||
|
||||
More generally, when using externally managed contexts, it's always required to
|
||||
provide the system with the context to use, at least at the _entry point_.<br/>
|
||||
For example, once the `meta_type` instant is obtained, it's no longer necessary
|
||||
to pass the context around as the meta type takes the information with it and
|
||||
eventually propagates it to all its parts.<br/>
|
||||
On the other hand, it's necessary to instruct the library on where meta types
|
||||
are to be fetched when `meta_any`s and `meta_handle`s are constructed, a factory
|
||||
created or a meta type resolved.
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* [Concept and implementation](#concept-and-implementation)
|
||||
* [Deduced interface](#deduced-interface)
|
||||
* [Defined interface](#defined-interface)
|
||||
* [Fullfill a concept](#fullfill-a-concept)
|
||||
* [Fulfill a concept](#fulfill-a-concept)
|
||||
* [Inheritance](#inheritance)
|
||||
* [Static polymorphism in the wild](#static-polymorphism-in-the-wild)
|
||||
* [Storage size and alignment requirement](#storage-size-and-alignment-requirement)
|
||||
@@ -24,19 +24,18 @@ Static polymorphism is a very powerful tool in C++, albeit sometimes cumbersome
|
||||
to obtain.<br/>
|
||||
This module aims to make it simple and easy to use.
|
||||
|
||||
The library allows to define _concepts_ as interfaces to fullfill with concrete
|
||||
classes withouth having to inherit from a common base.<br/>
|
||||
This is, among others, one of the advantages of static polymorphism in general
|
||||
The library allows to define _concepts_ as interfaces to fulfill with concrete
|
||||
classes without having to inherit from a common base.<br/>
|
||||
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 fullfill 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<> {
|
||||
@@ -94,7 +93,7 @@ struct Drawable: entt::type_list<> {
|
||||
```
|
||||
|
||||
It's recognizable by the fact that it inherits from an empty type list.<br/>
|
||||
Functions can also be const, accept any number of paramters and return a type
|
||||
Functions can also be const, accept any number of parameters and return a type
|
||||
other than `void`:
|
||||
|
||||
```cpp
|
||||
@@ -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.
|
||||
@@ -187,7 +186,7 @@ the interface itself.
|
||||
Explicitly defining a static virtual table suppresses the deduction step and
|
||||
allows maximum flexibility when providing the implementation for a concept.
|
||||
|
||||
## Fullfill a concept
|
||||
## Fulfill a concept
|
||||
|
||||
The `impl` alias template of a concept is used to define how it's fulfilled:
|
||||
|
||||
@@ -200,9 +199,9 @@ struct Drawable: entt::type_list<> {
|
||||
};
|
||||
```
|
||||
|
||||
In this case, it's stated that the `draw` method of a generic type will be
|
||||
enough to satisfy the requirements of the `Drawable` concept.<br/>
|
||||
Both member functions and free functions are supported to fullfill concepts:
|
||||
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
|
||||
template<typename Type>
|
||||
@@ -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.
|
||||
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.
|
||||
@@ -28,10 +45,14 @@ I hope this list can grow much more in the future:
|
||||
ECS that uses sparse sets to keep track of entities in systems.
|
||||
* [EntityX](https://github.com/alecthomas/entityx): a bitset based ECS that
|
||||
uses a single large matrix of components indexed with entities.
|
||||
* [Gaia-ECS](https://github.com/richardbiely/gaia-ecs): a chunk based
|
||||
archetype ECS.
|
||||
* [Polypropylene](https://github.com/pmbittner/Polypropylene): a hybrid
|
||||
solution between an ECS and dynamic mixins.
|
||||
|
||||
* 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
|
||||
@@ -61,11 +82,10 @@ I hope this list can grow much more in the future:
|
||||
* [Legion](https://github.com/TomGillen/legion): a chunk based archetype ECS.
|
||||
* [Shipyard](https://github.com/leudz/shipyard): it borrows some ideas from
|
||||
`EnTT` and offers a sparse sets based ECS with grouping functionalities.
|
||||
* [Sparsey](https://github.com/LechintanTudor/sparsey): sparse set based ECS
|
||||
written in Rust.
|
||||
* [Specs](https://github.com/amethyst/specs): a parallel ECS based mainly on
|
||||
hierarchical bitsets that allows different types of storage as needed.
|
||||
|
||||
* 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.
|
||||
|
||||
@@ -7,225 +7,185 @@
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [The resource, the loader and the cache](#the-resource-the-loader-and-the-cache)
|
||||
* [Resource handle](#resource-handle)
|
||||
* [Loaders](#loader)
|
||||
* [The cache class](#the-cache)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
Resource management is usually one of the most critical part of a software like
|
||||
a game. Solutions are often tuned to the particular application. There exist
|
||||
several approaches and all of them are perfectly fine as long as they fit the
|
||||
Resource management is usually one of the most critical parts of a game.
|
||||
Solutions are often tuned to the particular application. There exist several
|
||||
approaches and all of them are perfectly fine as long as they fit the
|
||||
requirements of the piece of software in which they are used.<br/>
|
||||
Examples are loading everything on start, loading on request, predictive
|
||||
loading, and so on.
|
||||
|
||||
`EnTT` doesn't pretend to offer a _one-fits-all_ solution for the different
|
||||
cases. Instead, it offers a minimal and perhaps trivial cache that can be useful
|
||||
most of the time during prototyping and sometimes even in a production
|
||||
environments.<br/>
|
||||
For those interested in the subject, the plan is to improve it considerably over
|
||||
time in terms of performance, memory usage and functionalities. Hoping to make
|
||||
it, of course, one step at a time.
|
||||
cases.<br/>
|
||||
Instead, the library comes with a minimal, general purpose resource cache that
|
||||
might be useful in many cases.
|
||||
|
||||
# The resource, the loader and the cache
|
||||
|
||||
There are three main actors in the model: the resource, the loader and the
|
||||
cache.
|
||||
|
||||
The _resource_ is whatever users want it to be. An image, a video, an audio,
|
||||
whatever. There are no limits.<br/>
|
||||
As a minimal example:
|
||||
Resource, loader and cache are the three main actors for the purpose.<br/>
|
||||
The _resource_ is an image, an audio, a video or any other type:
|
||||
|
||||
```cpp
|
||||
struct my_resource { const int value; };
|
||||
```
|
||||
|
||||
A _loader_ is a class the aim of which is to load a specific resource. It has to
|
||||
inherit directly from a dedicated base class as in the following example:
|
||||
The _loader_ is a callable type the aim of which is to load a specific resource:
|
||||
|
||||
```cpp
|
||||
struct my_loader final: entt::resource_loader<my_loader, my_resource> {
|
||||
// ...
|
||||
};
|
||||
```
|
||||
struct my_loader final {
|
||||
using result_type = std::shared_ptr<my_resource>;
|
||||
|
||||
Where `my_resource` is the type of resources it creates.<br/>
|
||||
A resource loader must also expose a public const member function named `load`
|
||||
that accepts a variable number of arguments and returns a shared pointer to a
|
||||
resource.<br/>
|
||||
As an example:
|
||||
|
||||
```cpp
|
||||
struct my_loader: entt::resource_loader<my_loader, my_resource> {
|
||||
std::shared_ptr<my_resource> load(int value) const {
|
||||
result_type operator()(int value) const {
|
||||
// ...
|
||||
return std::shared_ptr<my_resource>(new my_resource{ value });
|
||||
return std::make_shared<my_resource>(value);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
In general, resource loaders should not have a state or retain data of any type.
|
||||
They should let the cache manage their resources instead.<br/>
|
||||
As a side note, base class and CRTP idiom aren't strictly required with the
|
||||
current implementation. One could argue that a cache can easily work with
|
||||
loaders of any type. However, future changes won't be breaking ones by forcing
|
||||
the use of a base class today and that's why the model is already in its place.
|
||||
Its function operator can accept any arguments and should return a value of the
|
||||
declared result type (`std::shared_ptr<my_resource>` in the example).<br/>
|
||||
A loader can also overload its function call operator to make it possible to
|
||||
construct the same or another resource from different lists of arguments.
|
||||
|
||||
Finally, a cache is a specialization of a class template tailored to a specific
|
||||
resource:
|
||||
resource and (optionally) a loader:
|
||||
|
||||
```cpp
|
||||
using my_cache = entt::resource_cache<my_resource>;
|
||||
using my_cache = entt::resource_cache<my_resource, my_loader>;
|
||||
|
||||
// ...
|
||||
|
||||
my_cache cache{};
|
||||
```
|
||||
|
||||
The idea is to create different caches for different types of resources and to
|
||||
manage each one independently in the most appropriate way.<br/>
|
||||
The class is designed to create different caches for different resource types
|
||||
and to manage each one independently in the most appropriate way.<br/>
|
||||
As a (very) trivial example, audio tracks can survive in most of the scenes of
|
||||
an application while meshes can be associated with a single scene and then
|
||||
discarded when users leave it.
|
||||
an application while meshes can be associated with a single scene only, then
|
||||
discarded when a player leaves it.
|
||||
|
||||
A cache offers a set of basic functionalities to query its internal state and to
|
||||
_organize_ it:
|
||||
## Resource handle
|
||||
|
||||
Resources aren't returned directly to the caller. Instead, they are wrapped in a
|
||||
_resource handle_, an instance of the `entt::resource` class template.<br/>
|
||||
For those who know the _flyweight design pattern_ already, that's exactly what
|
||||
it is. To all others, this is the time to brush up on some notions instead.
|
||||
|
||||
A shared pointer could have been used as a resource handle. In fact, the default
|
||||
implementation mostly maps the interface of its standard counterpart and only
|
||||
adds a few things on top of it.<br/>
|
||||
However, the handle in `EnTT` is designed as a standalone class template. This
|
||||
is due to the fact that specializing a class in the standard library is often
|
||||
undefined behavior while having the ability to specialize the handle for one,
|
||||
more or all resource types could help over time.
|
||||
|
||||
## Loaders
|
||||
|
||||
A loader is responsible for _loading_ resources (quite obviously).<br/>
|
||||
By default, it's just a callable object that forwards its arguments to the
|
||||
resource itself. That is, a _passthrough type_. All the work is demanded to the
|
||||
constructor(s) of the resource itself.<br/>
|
||||
Loaders also are fully customizable as expected.
|
||||
|
||||
A custom loader is a class with at least one function call operator and a member
|
||||
type named `result_type`.<br/>
|
||||
The loader isn't required to return a resource handle. As long as `return_type`
|
||||
is suitable for constructing a handle, that's fine.
|
||||
|
||||
When using the default handle, it expects a resource type which is convertible
|
||||
to or suitable for constructing an `std::shared_ptr<Type>` (where `Type` is the
|
||||
actual resource type).<br/>
|
||||
In other terms, the loader should return shared pointers to the given resource
|
||||
type. However, this isn't mandatory. Users can easily get around this constraint
|
||||
by specializing both the handle and the loader.
|
||||
|
||||
A cache forwards all its arguments to the loader if required. This means that
|
||||
loaders can also support tag dispatching to offer different loading policies:
|
||||
|
||||
```cpp
|
||||
// gets the number of resources managed by a cache
|
||||
const auto size = cache.size();
|
||||
struct my_loader {
|
||||
using result_type = std::shared_ptr<my_resource>;
|
||||
|
||||
// checks if a cache contains at least a valid resource
|
||||
const auto empty = cache.empty();
|
||||
struct from_disk_tag{};
|
||||
struct from_network_tag{};
|
||||
|
||||
// clears a cache and discards its content
|
||||
cache.clear();
|
||||
template<typename Args>
|
||||
result_type operator()(from_disk_tag, Args&&... args) {
|
||||
// ...
|
||||
return std::make_shared<my_resource>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename Args>
|
||||
result_type operator()(from_network_tag, Args&&... args) {
|
||||
// ...
|
||||
return std::make_shared<my_resource>(std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Besides these member functions, a cache contains what is needed to load, use and
|
||||
discard resources of the given type.<br/>
|
||||
Before exploring this part of the interface, it makes sense to mention how
|
||||
resources are identified. They have type `id_type` and therefore they can be
|
||||
created explicitly as in the following example:
|
||||
This makes the whole loading logic quite flexible and easy to extend over time.
|
||||
|
||||
## The cache class
|
||||
|
||||
The cache is the class that is asked to _connect the dots_.<br/>
|
||||
It loads the resources, stores them aside and returns handles as needed:
|
||||
|
||||
```cpp
|
||||
constexpr auto identifier = "my/resource/identifier"_hs;
|
||||
// this is equivalent to the following
|
||||
constexpr entt::id_type hs = entt::hashed_string{"my/resource/identifier"};
|
||||
entt::resource_cache<my_resource, my_loader> cache{};
|
||||
```
|
||||
|
||||
The class `hashed_string` is described in a dedicated section, so I won't go in
|
||||
details here.
|
||||
|
||||
Resources are loaded and thus stored in a cache through the `load` member
|
||||
function. It accepts the loader to use as a template parameter, the resource
|
||||
identifier and the parameters used to construct the resource as arguments:
|
||||
Under the hood, a cache is nothing more than a map where the key value has type
|
||||
`entt::id_type` while the mapped value is whatever type its loader returns.<br/>
|
||||
For this reason, it offers most of the functionalities a user would expect from
|
||||
a map, such as `empty` or `size` and so on. Similarly, it's an iterable type
|
||||
that also supports indexing by resource id:
|
||||
|
||||
```cpp
|
||||
// uses the identifier declared above
|
||||
cache.load<my_loader>(identifier, 0);
|
||||
for(auto [id, res]: cache) {
|
||||
// ...
|
||||
}
|
||||
|
||||
// uses a hashed string directly
|
||||
cache.load<my_loader>("another/identifier"_hs, 42);
|
||||
```
|
||||
|
||||
The function returns a handle to the resource, whether it already exists or is
|
||||
loaded. In case the loader returns an invalid pointer, the handle is invalid as
|
||||
well and therefore it can be easily used with an `if` statement:
|
||||
|
||||
```cpp
|
||||
if(entt::resource_handle handle = cache.load<my_loader>("another/identifier"_hs, 42); handle) {
|
||||
if(entt::resource<my_resource> res = cache["resource/id"_hs]; res) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Before trying to load a resource, the `contains` member function can be used to
|
||||
know if a cache already contains a specific resource:
|
||||
Please, refer to the inline documentation for all the details about the other
|
||||
functions (such as `contains` or `erase`).
|
||||
|
||||
Set aside the part of the API that this class _shares_ with a map, it also adds
|
||||
something on top of it in order to address the most common requirements of a
|
||||
resource cache.<br/>
|
||||
In particular, it doesn't have an `emplace` member function which is replaced by
|
||||
`load` and `force_load` instead (where the former loads a new resource only if
|
||||
not present while the second triggers a forced loading in any case):
|
||||
|
||||
```cpp
|
||||
auto exists = cache.contains("my/identifier"_hs);
|
||||
auto ret = cache.load("resource/id"_hs);
|
||||
|
||||
// true only if the resource was not already present
|
||||
const bool loaded = ret.second;
|
||||
|
||||
// takes the resource handle pointed to by the returned iterator
|
||||
entt::resource<my_resource> res = ret.first->second;
|
||||
```
|
||||
|
||||
There exists also a member function to use to force a reload of an already
|
||||
existing resource if needed:
|
||||
Note that the hashed string is used for convenience in the example above.<br/>
|
||||
Resource identifiers are nothing more than integral values. Therefore, plain
|
||||
numbers as well as non-class enum value are accepted.
|
||||
|
||||
```cpp
|
||||
auto handle = cache.reload<my_loader>("another/identifier"_hs, 42);
|
||||
```
|
||||
|
||||
As above, the function returns a handle to the resource that is invalid in case
|
||||
of errors. The `reload` member function is a kind of alias of the following
|
||||
snippet:
|
||||
|
||||
```cpp
|
||||
cache.discard(identifier);
|
||||
cache.load<my_loader>(identifier, 42);
|
||||
```
|
||||
|
||||
Where the `discard` member function is used to get rid of a resource if loaded.
|
||||
In case the cache doesn't contain a resource for the given identifier, `discard`
|
||||
does nothing and returns immediately.
|
||||
|
||||
So far, so good. Resources are finally loaded and stored within the cache.<br/>
|
||||
They are returned to users in the form of handles. To get one of them later on:
|
||||
|
||||
```cpp
|
||||
auto handle = cache.handle("my/identifier"_hs);
|
||||
```
|
||||
|
||||
The idea behind a handle is the same of the flyweight pattern. In other terms,
|
||||
resources aren't copied around. Instead, instances are shared between handles.
|
||||
Users of a resource own a handle that guarantees that a resource isn't destroyed
|
||||
until all the handles are destroyed, even if the resource itself is removed from
|
||||
the cache.<br/>
|
||||
Handles are tiny objects both movable and copyable. They return the contained
|
||||
resource as a (possibly const) reference on request:
|
||||
|
||||
* By means of the `get` member function:
|
||||
|
||||
```cpp
|
||||
auto &resource = handle.get();
|
||||
```
|
||||
|
||||
* Using the proper cast operator:
|
||||
|
||||
```cpp
|
||||
auto &resource = handle;
|
||||
```
|
||||
|
||||
* Through the dereference operator:
|
||||
|
||||
```cpp
|
||||
auto &resource = *handle;
|
||||
```
|
||||
|
||||
The resource can also be accessed directly using the arrow operator if required:
|
||||
|
||||
```cpp
|
||||
auto value = handle->value;
|
||||
```
|
||||
|
||||
To test if a handle is still valid, the cast operator to `bool` allows users to
|
||||
use it in a guard:
|
||||
|
||||
```cpp
|
||||
if(handle) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Finally, in case there is the need to load a resource and thus to get a handle
|
||||
without storing the resource itself in the cache, users can rely on the `temp`
|
||||
member function template.<br/>
|
||||
The declaration is similar to that of `load`, a (possibly invalid) handle for
|
||||
the resource is returned also in this case:
|
||||
|
||||
```cpp
|
||||
if(auto handle = cache.temp<my_loader>(42); handle) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Do not forget to test the handle for validity. Otherwise, getting a reference to
|
||||
the resource it points may result in undefined behavior.
|
||||
It's worth mentioning that the iterators of a cache as well as its indexing
|
||||
operators return resource handles rather than instances of the mapped type.<br/>
|
||||
Since the cache has no control over the loader and a resource isn't required to
|
||||
also be convertible to bool, these handles can be invalid. This usually means an
|
||||
error in the user logic but it may also be an _expected_ event.<br/>
|
||||
It's therefore recommended to verify handles validity with a check in debug (for
|
||||
example, when loading) or an appropriate logic in retail.
|
||||
|
||||
@@ -9,8 +9,10 @@
|
||||
* [Delegate](#delegate)
|
||||
* [Runtime arguments](#runtime-arguments)
|
||||
* [Lambda support](#lambda-support)
|
||||
* [Raw access](#raw-access)
|
||||
* [Signals](#signals)
|
||||
* [Event dispatcher](#event-dispatcher)
|
||||
* [Named queues](#named-queues)
|
||||
* [Event emitter](#event-emitter)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
@@ -18,27 +20,27 @@
|
||||
|
||||
# Introduction
|
||||
|
||||
Signals are usually a core part of games and software architectures in
|
||||
general.<br/>
|
||||
Roughly speaking, they help to decouple the various parts of a system while
|
||||
allowing them to communicate with each other somehow.
|
||||
Signals are more often than not a core part of games and software architectures
|
||||
in general.<br/>
|
||||
They help to decouple the various parts of a system while allowing them to
|
||||
communicate with each other somehow.
|
||||
|
||||
The so called _modern C++_ comes with a tool that can be useful in these terms,
|
||||
The so called _modern C++_ comes with a tool that can be useful in this regard,
|
||||
the `std::function`. As an example, it can be used to create delegates.<br/>
|
||||
However, there is no guarantee that an `std::function` does not perform
|
||||
However, there is no guarantee that an `std::function` doesn't perform
|
||||
allocations under the hood and this could be problematic sometimes. Furthermore,
|
||||
it solves a problem but may not adapt well to other requirements that may arise
|
||||
from time to time.
|
||||
|
||||
In case that the flexibility and power of an `std::function` isn't required or
|
||||
if the price to pay for them is too high,` EnTT` offers a complete set of
|
||||
if the price to pay for them is too high, `EnTT` offers a complete set of
|
||||
lightweight classes to solve the same and many other problems.
|
||||
|
||||
# Delegate
|
||||
|
||||
A delegate can be used as a general purpose invoker with no memory overhead for
|
||||
free functions and members provided along with an instance on which to invoke
|
||||
them.<br/>
|
||||
free functions, 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
|
||||
likely even a better fit than an `std::function` in a lot of cases, so expect to
|
||||
@@ -51,15 +53,13 @@ delegates:
|
||||
entt::delegate<int(int)> delegate{};
|
||||
```
|
||||
|
||||
All what is needed to create an instance is to specify the type of the function
|
||||
the delegate will _contain_, that is the signature of the free function or the
|
||||
member one wants to assign to it.
|
||||
What is needed to create an instance is to specify the type of the function the
|
||||
delegate _accepts_, that is the signature of the functions it models.<br/>
|
||||
However, attempting to use an empty delegate by invoking its function call
|
||||
operator results in undefined behavior or most likely a crash.
|
||||
|
||||
Attempting to use an empty delegate by invoking its function call operator
|
||||
results in undefined behavior or most likely a crash. Before to use a delegate,
|
||||
it must be initialized.<br/>
|
||||
There exists a bunch of overloads of the `connect` member function to do that.
|
||||
As an example of use:
|
||||
There exist a few overloads of the `connect` member function to initialize a
|
||||
delegate:
|
||||
|
||||
```cpp
|
||||
int f(int i) { return i; }
|
||||
@@ -76,7 +76,7 @@ my_struct instance;
|
||||
delegate.connect<&my_struct::f>(instance);
|
||||
```
|
||||
|
||||
The delegate class accepts also data members, if needed. In this case, the
|
||||
The delegate class also accepts data members, if needed. In this case, the
|
||||
function type of the delegate is such that the parameter list is empty and the
|
||||
value of the data member is at least convertible to the return type.
|
||||
|
||||
@@ -93,14 +93,11 @@ delegate.connect<&g>(c);
|
||||
delegate(42);
|
||||
```
|
||||
|
||||
The function `g` will be invoked with a reference to `c` and `42`. However, the
|
||||
function type of the delegate is still `void(int)`. This is also the signature
|
||||
of its function call operator.
|
||||
|
||||
Another interesting aspect of the delegate class is that it accepts also
|
||||
functions with a list of parameters that is shorter than that of the function
|
||||
type used to specialize the delegate itself.<br/>
|
||||
The following code is therefore perfectly valid:
|
||||
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:
|
||||
|
||||
```cpp
|
||||
void g() { /* ... */ }
|
||||
@@ -109,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
|
||||
@@ -139,7 +142,7 @@ already shown in the examples above:
|
||||
auto ret = delegate(42);
|
||||
```
|
||||
|
||||
In all cases, the listeners don't have to strictly follow the signature of the
|
||||
In all cases, listeners don't have to strictly follow the signature of the
|
||||
delegate. As long as a listener can be invoked with the given arguments to yield
|
||||
a result that is convertible to the given result type, everything works just
|
||||
fine.
|
||||
@@ -157,7 +160,7 @@ my_struct instance;
|
||||
delegate(instance, 42);
|
||||
```
|
||||
|
||||
In this case, it's not possible to deduce the function type since the first
|
||||
In this case, it's not possible to _deduce_ the function type since the first
|
||||
argument doesn't necessarily have to be a reference (for example, it can be a
|
||||
pointer, as well as a const reference).<br/>
|
||||
Therefore, the function type must be declared explicitly for unbound members.
|
||||
@@ -165,9 +168,9 @@ Therefore, the function type must be declared explicitly for unbound members.
|
||||
## Runtime arguments
|
||||
|
||||
The `delegate` class is meant to be used primarily with template arguments.
|
||||
However, as a consequence of its design, it can also offer minimal support for
|
||||
However, as a consequence of its design, it also offers minimal support for
|
||||
runtime arguments.<br/>
|
||||
When used in this modality, some feature aren't supported though. In particular:
|
||||
When used like this, some features aren't supported though. In particular:
|
||||
|
||||
* Curried functions aren't accepted.
|
||||
* Functions with an argument list that differs from that of the delegate aren't
|
||||
@@ -208,7 +211,7 @@ their nuances. The reason is pretty simple: a `delegate` isn't a drop-in
|
||||
replacement for an `std::function`. Instead, it tries to overcome the problems
|
||||
with the latter.<br/>
|
||||
That being said, non-capturing lambda functions are supported, even though some
|
||||
feature aren't available in this case.
|
||||
features aren't available in this case.
|
||||
|
||||
This is a logical consequence of the support for connecting functions at
|
||||
runtime. Therefore, lambda functions undergo the same rules and
|
||||
@@ -235,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
|
||||
@@ -246,16 +267,17 @@ Signals make use of delegates internally and therefore they undergo the same
|
||||
rules and offer similar functionalities. It may be a good idea to consult the
|
||||
documentation of the `delegate` class for further information.
|
||||
|
||||
A signal handler can be used as a private data member without exposing any
|
||||
_publish_ functionality to the clients of a class. The basic idea is to impose a
|
||||
clear separation between the signal itself and the `sink` class, that is a tool
|
||||
to be used to connect and disconnect listeners on the fly.
|
||||
A signal handler is can be used as a private data member without exposing any
|
||||
_publish_ functionality to the clients of a class.<br/>
|
||||
The basic idea is to impose a clear separation between the signal itself and the
|
||||
`sink` class, that is a tool to be used to connect and disconnect listeners on
|
||||
the fly.
|
||||
|
||||
The API of a signal handler is straightforward. If a collector is supplied to
|
||||
the signal when something is published, all the values returned by the listeners
|
||||
can be literally _collected_ and used later by the caller. Otherwise, the class
|
||||
the signal when something is published, all the values returned by its listeners
|
||||
are literally _collected_ and used later by the caller. Otherwise, the class
|
||||
works just like a plain signal that emits events from time to time.<br/>
|
||||
To create instances of signal handlers it is sufficient to provide the type of
|
||||
To create instances of signal handlers it's sufficient to provide the type of
|
||||
function to which they refer:
|
||||
|
||||
```cpp
|
||||
@@ -293,41 +315,31 @@ 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();
|
||||
```
|
||||
|
||||
As shown above, the listeners don't have to strictly follow the signature of the
|
||||
As shown above, listeners don't have to strictly follow the signature of the
|
||||
signal. As long as a listener can be invoked with the given arguments to yield a
|
||||
result that is convertible to the given return type, everything works just
|
||||
fine.<br/>
|
||||
It's also possible to connect a listener before other listeners already
|
||||
contained by the signal. The `before` function returns a `sink` object correctly
|
||||
initialized for the purpose that can be used to connect one or more listeners in
|
||||
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. A `scoped_connection` can also be created from a
|
||||
connection. In this case, the link is broken automatically as soon as the object
|
||||
goes out of scope.
|
||||
`release` member function.<br/>
|
||||
A `scoped_connection` can also be created from a connection. In this case, the
|
||||
link is broken automatically as soon as the object goes out of scope.
|
||||
|
||||
Once listeners are attached (or even if there are no listeners at all), events
|
||||
and data in general can be published through a signal by means of the `publish`
|
||||
and data in general are published through a signal by means of the `publish`
|
||||
member function:
|
||||
|
||||
```cpp
|
||||
signal.publish(42, 'c');
|
||||
```
|
||||
|
||||
To collect data, the `collect` member function should be used instead. Below is
|
||||
a minimal example to show how to use it:
|
||||
To collect data, the `collect` member function is used instead:
|
||||
|
||||
```cpp
|
||||
int f() { return 0; }
|
||||
@@ -361,7 +373,7 @@ case:
|
||||
struct my_collector {
|
||||
std::vector<int> vec{};
|
||||
|
||||
bool operator()(int v) noexcept {
|
||||
bool operator()(int v) {
|
||||
vec.push_back(v);
|
||||
return true;
|
||||
}
|
||||
@@ -375,23 +387,20 @@ signal.collect(std::ref(collector));
|
||||
|
||||
# Event dispatcher
|
||||
|
||||
The event dispatcher class is designed so as to be used in a loop. It allows
|
||||
users both to trigger immediate events or to queue events to be published all
|
||||
together once per tick.<br/>
|
||||
This class shares part of its API with the one of the signal handler, but it
|
||||
doesn't require that all the types of events are specified when declared:
|
||||
The event dispatcher class allows users to trigger immediate events or to queue
|
||||
and publish them all together later.<br/>
|
||||
This class lazily instantiates its queues. Therefore, it's not necessary to
|
||||
_announce_ the event types in advance:
|
||||
|
||||
```cpp
|
||||
// define a general purpose dispatcher
|
||||
entt::dispatcher dispatcher{};
|
||||
```
|
||||
|
||||
In order to register an instance of a class to a dispatcher, its type must
|
||||
expose one or more member functions the arguments of which are such that `E &`
|
||||
can be converted to them for each type of event `E`, no matter what the return
|
||||
value is.<br/>
|
||||
The name of the member function aimed to receive the event must be provided to
|
||||
the `connect` member function of the sink in charge for the specific event:
|
||||
A listener registered with a dispatcher is such that its type offers one or more
|
||||
member functions that take arguments of type `Event &` for any type of event,
|
||||
regardless of the return value.<br/>
|
||||
These functions are linked directly via `connect` to a _sink_:
|
||||
|
||||
```cpp
|
||||
struct an_event { int value; };
|
||||
@@ -409,23 +418,21 @@ dispatcher.sink<an_event>().connect<&listener::receive>(listener);
|
||||
dispatcher.sink<another_event>().connect<&listener::method>(listener);
|
||||
```
|
||||
|
||||
The `disconnect` member function follows the same pattern and can be used to
|
||||
remove one listener at a time or all of them at once:
|
||||
Note that connecting listeners within event handlers can result in undefined
|
||||
behavior.<br/>
|
||||
The `disconnect` member function is used to remove one listener at a time or all
|
||||
of them at once:
|
||||
|
||||
```cpp
|
||||
dispatcher.sink<an_event>().disconnect<&listener::receive>(listener);
|
||||
dispatcher.sink<another_event>().disconnect(listener);
|
||||
dispatcher.sink<another_event>().disconnect(&listener);
|
||||
```
|
||||
|
||||
The `trigger` member function serves the purpose of sending an immediate event
|
||||
to all the listeners registered so far. It offers a convenient approach that
|
||||
relieves users from having to create the event itself. Instead, it's enough to
|
||||
specify the type of event and provide all the parameters required to construct
|
||||
it.<br/>
|
||||
As an example:
|
||||
to all the listeners registered so far:
|
||||
|
||||
```cpp
|
||||
dispatcher.trigger<an_event>(42);
|
||||
dispatcher.trigger(an_event{42});
|
||||
dispatcher.trigger<another_event>();
|
||||
```
|
||||
|
||||
@@ -434,16 +441,14 @@ method can be used to push around urgent messages like an _is terminating_
|
||||
notification on a mobile app.
|
||||
|
||||
On the other hand, the `enqueue` member function queues messages together and
|
||||
allows to maintain control over the moment they are sent to listeners. The
|
||||
signature of this method is more or less the same of `trigger`:
|
||||
helps to maintain control over the moment they are sent to listeners:
|
||||
|
||||
```cpp
|
||||
dispatcher.enqueue<an_event>(42);
|
||||
dispatcher.enqueue<another_event>();
|
||||
dispatcher.enqueue(another_event{});
|
||||
```
|
||||
|
||||
Events are stored aside until the `update` member function is invoked, then all
|
||||
the messages that are still pending are sent to the listeners at once:
|
||||
Events are stored aside until the `update` member function is invoked:
|
||||
|
||||
```cpp
|
||||
// emits all the events of the given type at once
|
||||
@@ -456,6 +461,30 @@ dispatcher.update();
|
||||
This way users can embed the dispatcher in a loop and literally dispatch events
|
||||
once per tick to their systems.
|
||||
|
||||
## Named queues
|
||||
|
||||
All queues within a dispatcher are associated by default with an event type and
|
||||
then retrieved from it.<br/>
|
||||
However, it's possible to create queues with different _names_ (and therefore
|
||||
also multiple queues for a single type). In fact, more or less all functions
|
||||
also take an additional parameter. As an example:
|
||||
|
||||
```cpp
|
||||
dispatcher.sink<an_event>("custom"_hs).connect<&listener::receive>(listener);
|
||||
```
|
||||
|
||||
In this case, the term _name_ is misused as these are actual numeric identifiers
|
||||
of type `id_type`.<br/>
|
||||
An exception to this rule is the `enqueue` function. There is no additional
|
||||
parameter for it but rather a different function:
|
||||
|
||||
```cpp
|
||||
dispatcher.enqueue_hint<an_event>("custom"_hs, 42);
|
||||
```
|
||||
|
||||
This is mainly due to the template argument deduction rules and unfortunately
|
||||
there is no real (elegant) way to avoid it.
|
||||
|
||||
# Event emitter
|
||||
|
||||
A general purpose event emitter thought mainly for those cases where it comes to
|
||||
@@ -464,8 +493,7 @@ Originally designed to fit the requirements of
|
||||
[`uvw`](https://github.com/skypjack/uvw) (a wrapper for `libuv` written in
|
||||
modern C++), it was adapted later to be included in this library.
|
||||
|
||||
To create a custom emitter type, derived classes must inherit directly from the
|
||||
base class as:
|
||||
To create an emitter type, derived classes must inherit from the base as:
|
||||
|
||||
```cpp
|
||||
struct my_emitter: emitter<my_emitter> {
|
||||
@@ -473,18 +501,10 @@ struct my_emitter: emitter<my_emitter> {
|
||||
}
|
||||
```
|
||||
|
||||
The full list of accepted types of events isn't required. Handlers are created
|
||||
internally on the fly and thus each type of event is accepted by default.
|
||||
|
||||
Whenever an event is published, an emitter provides the listeners with a
|
||||
reference to itself along with a reference to the event. Therefore listeners
|
||||
have an handy way to work with it without incurring in the need of capturing a
|
||||
reference to the emitter itself.<br/>
|
||||
In addition, an opaque object is returned each time a connection is established
|
||||
between an emitter and a listener, allowing the caller to disconnect them at a
|
||||
later time.<br/>
|
||||
The opaque object used to handle connections is both movable and copyable. On
|
||||
the other side, an event emitter is movable but not copyable by default.
|
||||
Handlers for the different events are created internally on the fly. It's not
|
||||
required to specify in advance the full list of accepted events.<br/>
|
||||
Moreover, whenever an event is published, an emitter also passes a reference
|
||||
to itself to its listeners.
|
||||
|
||||
To create new instances of an emitter, no arguments are required:
|
||||
|
||||
@@ -492,90 +512,54 @@ To create new instances of an emitter, no arguments are required:
|
||||
my_emitter emitter{};
|
||||
```
|
||||
|
||||
Listeners must be movable and callable objects (free functions, lambdas,
|
||||
functors, `std::function`s, whatever) whose function type is compatible with:
|
||||
Listeners are movable and callable objects (free functions, lambdas, functors,
|
||||
`std::function`s, whatever) whose function type is compatible with:
|
||||
|
||||
```cpp
|
||||
void(Event &, my_emitter &)
|
||||
void(Type &, my_emitter &)
|
||||
```
|
||||
|
||||
Where `Event` is the type of event they want to listen.<br/>
|
||||
There are two ways to attach a listener to an event emitter that differ
|
||||
slightly from each other:
|
||||
|
||||
* To register a long-lived listener, use the `on` member function. It is meant
|
||||
to register a listener designed to be invoked more than once for the given
|
||||
event type.<br/>
|
||||
As an example:
|
||||
|
||||
```cpp
|
||||
auto conn = emitter.on<my_event>([](const my_event &event, my_emitter &emitter) {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
The connection object can be freely discarded. Otherwise, it can be used later
|
||||
to disconnect the listener if required.
|
||||
|
||||
* To register a short-lived listener, use the `once` member function. It is
|
||||
meant to register a listener designed to be invoked only once for the given
|
||||
event type. The listener is automatically disconnected after the first
|
||||
invocation.<br/>
|
||||
As an example:
|
||||
|
||||
```cpp
|
||||
auto conn = emitter.once<my_event>([](const my_event &event, my_emitter &emitter) {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
The connection object can be freely discarded. Otherwise, it can be used later
|
||||
to disconnect the listener if required.
|
||||
|
||||
In both cases, the connection object can be used with the `erase` member
|
||||
function:
|
||||
Where `Type` is the type of event they want to receive.<br/>
|
||||
To attach a listener to an emitter, there exists the `on` member function:
|
||||
|
||||
```cpp
|
||||
emitter.erase(conn);
|
||||
emitter.on<my_event>([](const my_event &event, my_emitter &emitter) {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
There are also two member functions to use either to disconnect all the
|
||||
listeners for a given type of event or to clear the emitter:
|
||||
Similarly, the `reset` member function is used to disconnect listeners given a
|
||||
type while `clear` is used to disconnect all listeners at once:
|
||||
|
||||
```cpp
|
||||
// removes all the listener for the specific event
|
||||
emitter.clear<my_event>();
|
||||
// resets the listener for my_event
|
||||
emitter.erase<my_event>();
|
||||
|
||||
// removes all the listeners registered so far
|
||||
emitter.clear();
|
||||
// resets all listeners
|
||||
emitter.clear()
|
||||
```
|
||||
|
||||
To send an event to all the listeners that are interested in it, the `publish`
|
||||
member function offers a convenient approach that relieves users from having to
|
||||
create the event:
|
||||
To send an event to the listener registered on a given type, the `publish`
|
||||
function is the way to go:
|
||||
|
||||
```cpp
|
||||
struct my_event { int i; };
|
||||
|
||||
// ...
|
||||
|
||||
emitter.publish<my_event>(42);
|
||||
emitter.publish(my_event{42});
|
||||
```
|
||||
|
||||
Finally, the `empty` member function tests if there exists at least either a
|
||||
listener registered with the event emitter or to a given type of event:
|
||||
Finally, the `empty` member function tests if there exists at least a listener
|
||||
registered with the event emitter while `contains` is used to check if a given
|
||||
event type is associated with a valid listener:
|
||||
|
||||
```cpp
|
||||
bool empty;
|
||||
|
||||
// checks if there is any listener registered for the specific event
|
||||
empty = emitter.empty<my_event>();
|
||||
|
||||
// checks it there are listeners registered with the event emitter
|
||||
empty = emitter.empty();
|
||||
if(emitter.contains<my_event>()) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
In general, the event emitter is a handy tool when the derived classes _wrap_
|
||||
asynchronous operations, because it introduces a _nice-to-have_ model based on
|
||||
events and listeners that kindly hides the complexity behind the scenes. However
|
||||
it is not limited to such uses.
|
||||
This class introduces a _nice-to-have_ model based on events and listeners.<br/>
|
||||
More in general, it's a handy tool when the derived classes _wrap_ asynchronous
|
||||
operations but it's not limited to such uses.
|
||||
|
||||
@@ -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
|
||||
|
||||
42
entt.imp
Normal file
42
entt.imp
Normal file
@@ -0,0 +1,42 @@
|
||||
[
|
||||
# gtest only
|
||||
{ "include": [ "@<gtest/internal/.*>", "private", "<gtest/gtest.h>", "public" ] },
|
||||
{ "include": [ "@<gtest/gtest-.*>", "private", "<gtest/gtest.h>", "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,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
<Type Name="entt::dense_hash_map<*>">
|
||||
<Type Name="entt::dense_map<*>">
|
||||
<Intrinsic Name="size" Expression="packed.first_base::value.size()"/>
|
||||
<Intrinsic Name="bucket_count" Expression="sparse.first_base::value.size()"/>
|
||||
<DisplayString>{{ size={ size() } }}</DisplayString>
|
||||
@@ -15,7 +15,7 @@
|
||||
</IndexListItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::dense_hash_set<*>">
|
||||
<Type Name="entt::dense_set<*>">
|
||||
<Intrinsic Name="size" Expression="packed.first_base::value.size()"/>
|
||||
<Intrinsic Name="bucket_count" Expression="sparse.first_base::value.size()"/>
|
||||
<DisplayString>{{ size={ size() } }}</DisplayString>
|
||||
@@ -26,7 +26,7 @@
|
||||
<Item Name="[max_load_factor]" ExcludeView="simple">threshold</Item>
|
||||
<IndexListItems>
|
||||
<Size>size()</Size>
|
||||
<ValueNode>packed.first_base::value[$i].element</ValueNode>
|
||||
<ValueNode>packed.first_base::value[$i].second</ValueNode>
|
||||
</IndexListItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
@@ -15,10 +15,11 @@
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_hashed_string<*>">
|
||||
<DisplayString Condition="str != nullptr">{{ hash={ hash } }}</DisplayString>
|
||||
<DisplayString Condition="base_type::repr != nullptr">{{ hash={ base_type::hash } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[data]">str,na</Item>
|
||||
<Item Name="[data]">base_type::repr,na</Item>
|
||||
<Item Name="[length]">base_type::length</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::type_info">
|
||||
|
||||
@@ -1,102 +1,97 @@
|
||||
<?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.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={ entities.size() } }}</DisplayString>
|
||||
<DisplayString>{{ pools={ pools.size() } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item IncludeView="simple" Name="[entities]">entities,view(simple)nr</Item>
|
||||
<Synthetic Name="[entities]" ExcludeView="simple">
|
||||
<DisplayString>{ entities.size() }</DisplayString>
|
||||
<Expand>
|
||||
<CustomListItems>
|
||||
<Variable Name="pos" InitialValue="0" />
|
||||
<Variable Name="last" InitialValue="entities.size()"/>
|
||||
<Loop>
|
||||
<Break Condition="pos == last"/>
|
||||
<If Condition="to_entity(entities[pos]) == pos">
|
||||
<Item Name="[{ pos }]">entities[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 }]">entities[it]</Item>
|
||||
<Exec>it = to_entity(entities[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>
|
||||
<ValueNode>vars.packed.first_base::value[$i].element.second</ValueNode>
|
||||
<Size>vars.ctx.size()</Size>
|
||||
<ValueNode>vars.ctx.packed.first_base::value[$i].element.second</ValueNode>
|
||||
</IndexListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_sparse_set<*>">
|
||||
<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>
|
||||
<ExpandedItem IncludeView="simple">packed,view(simple)</ExpandedItem>
|
||||
<CustomListItems ExcludeView="simple">
|
||||
<Variable Name="pos" InitialValue="0" />
|
||||
<Variable Name="last" InitialValue="packed.size()"/>
|
||||
<Loop>
|
||||
<Break Condition="pos == last"/>
|
||||
<If Condition="*((entity_traits::entity_type *)&packed[pos]) < ~entity_traits::entity_mask">
|
||||
<Item Name="[{ pos }]">packed[pos]</Item>
|
||||
</If>
|
||||
<Exec>++pos</Exec>
|
||||
</Loop>
|
||||
</CustomListItems>
|
||||
<Item Name="[free_list]">head</Item>
|
||||
<Synthetic Name="[sparse]">
|
||||
<DisplayString>{ sparse.size() * traits_type::page_size }</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem IncludeView="simple">sparse,view(simple)</ExpandedItem>
|
||||
<CustomListItems ExcludeView="simple">
|
||||
<Variable Name="pos" InitialValue="0"/>
|
||||
<Variable Name="page" InitialValue="0"/>
|
||||
<Variable Name="offset" InitialValue="0"/>
|
||||
<Variable Name="last" InitialValue="sparse.size() * traits_type::page_size"/>
|
||||
<Loop>
|
||||
<Break Condition="pos == last"/>
|
||||
<Exec>page = pos / traits_type::page_size</Exec>
|
||||
<Exec>offset = pos & (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>
|
||||
</CustomListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
<Synthetic Name="[packed]">
|
||||
<DisplayString>{ packed.size() }</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem IncludeView="simple">packed,view(simple)</ExpandedItem>
|
||||
<CustomListItems ExcludeView="simple">
|
||||
<Variable Name="pos" InitialValue="0"/>
|
||||
<Variable Name="last" InitialValue="packed.size()"/>
|
||||
<Loop>
|
||||
<Break Condition="pos == last"/>
|
||||
<If Condition="*((traits_type::entity_type *)&packed[pos]) < cap()">
|
||||
<Item Name="[{ pos }]">packed[pos]</Item>
|
||||
</If>
|
||||
<Exec>++pos</Exec>
|
||||
</Loop>
|
||||
</CustomListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
</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 }]">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>
|
||||
@@ -104,7 +99,17 @@
|
||||
</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]" 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<*>">
|
||||
<DisplayString Condition="pools.size() != 0u">{{ size_hint={ pools[0]->packed.size() } }}</DisplayString>
|
||||
<DisplayString>{{ size_hint=0 }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[pools]">pools,na</Item>
|
||||
<Item Name="[filter]">filter,na</Item>
|
||||
|
||||
19
natvis/entt/graph.natvis
Normal file
19
natvis/entt/graph.natvis
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
<Type Name="entt::adjacency_matrix<*>">
|
||||
<DisplayString>{{ size={ vert } }}</DisplayString>
|
||||
<Expand>
|
||||
<CustomListItems>
|
||||
<Variable Name="pos" InitialValue="0" />
|
||||
<Variable Name="last" InitialValue="vert * vert"/>
|
||||
<Loop>
|
||||
<Break Condition="pos == last"/>
|
||||
<If Condition="matrix[pos] != 0u">
|
||||
<Item Name="{pos / vert}">pos % vert</Item>
|
||||
</If>
|
||||
<Exec>++pos</Exec>
|
||||
</Loop>
|
||||
</CustomListItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
</AutoVisualizer>
|
||||
@@ -1,115 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
<Type Name="entt::meta_any">
|
||||
<DisplayString Condition="node != nullptr">{{ type={ node->info->alias,na }, policy={ storage.mode,en } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem Condition="node != nullptr">*node</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_associative_container">
|
||||
<DisplayString Condition="mapped_type_node != nullptr">{{ key_type={ key_type_node->info->alias,na }, mapped_type={ mapped_type_node->info->alias,na } }}</DisplayString>
|
||||
<DisplayString Condition="key_type_node != nullptr">{{ key_type={ key_type_node->info->alias,na } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_base_node">
|
||||
<DisplayString Condition="type != nullptr">{{ type={ type->info->alias,na } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_conv_node">
|
||||
<DisplayString Condition="type != nullptr">{{ type={ type->info->alias,na } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_ctor_node">
|
||||
<DisplayString Condition="arg != nullptr">{{ arity={ arity } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<DisplayString>{{ arity={ arity } }}</DisplayString>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_data_node">
|
||||
<Intrinsic Name="has_property" Expression="!!(traits & property)">
|
||||
<Parameter Name="property" Type="int"/>
|
||||
</Intrinsic>
|
||||
<DisplayString Condition="type != nullptr">{{ type={ type->info->alias,na } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<DisplayString>{{ arity={ arity } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[id]">id</Item>
|
||||
<Item Name="[arity]">arity</Item>
|
||||
<Item Name="[is_const]">has_property(entt::internal::meta_traits::is_const)</Item>
|
||||
<Item Name="[is_static]">has_property(entt::internal::meta_traits::is_static)</Item>
|
||||
<Synthetic Name="[prop]" Condition="prop != nullptr">
|
||||
<Expand>
|
||||
<LinkedListItems>
|
||||
<HeadPointer>prop</HeadPointer>
|
||||
<NextPointer>next</NextPointer>
|
||||
<ValueNode>*this</ValueNode>
|
||||
</LinkedListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_data">
|
||||
<DisplayString Condition="node != nullptr">{ *node }</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
|
||||
<Item Name="[prop]">prop</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_func_node" >
|
||||
<Intrinsic Name="has_property" Expression="!!(traits & property)">
|
||||
<Parameter Name="property" Type="int"/>
|
||||
</Intrinsic>
|
||||
<DisplayString Condition="ret != nullptr">{{ arity={ arity }, ret={ ret->info->alias,na } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<DisplayString>{{ arity={ arity } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[id]">id</Item>
|
||||
<Item Name="[is_const]">has_property(entt::internal::meta_traits::is_const)</Item>
|
||||
<Item Name="[is_static]">has_property(entt::internal::meta_traits::is_static)</Item>
|
||||
<Synthetic Name="[prop]" Condition="prop != nullptr">
|
||||
<Expand>
|
||||
<LinkedListItems>
|
||||
<HeadPointer>prop</HeadPointer>
|
||||
<NextPointer>next</NextPointer>
|
||||
<ValueNode>*this</ValueNode>
|
||||
</LinkedListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
<Item Name="[next]" Condition="next != nullptr">*next</Item>
|
||||
<Item Name="[prop]">prop</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_func">
|
||||
<DisplayString Condition="node != nullptr">{ *node }</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_handle">
|
||||
<DisplayString>{ any }</DisplayString>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_prop_node">
|
||||
<DisplayString Condition="value.node != nullptr">{{ key_type={ id.node->info->alias,na }, mapped_type={ value.node->info->alias,na } }}</DisplayString>
|
||||
<DisplayString Condition="id.node != nullptr">{{ key_type={ id.node->info->alias,na } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[key]">id</Item>
|
||||
<Item Name="[value]">value</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_prop">
|
||||
<DisplayString Condition="node != nullptr">{ *node }</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_sequence_container">
|
||||
<DisplayString Condition="value_type_node != nullptr">{{ value_type={ value_type_node->info->alias,na } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<DisplayString>{ value }</DisplayString>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_template_node">
|
||||
<DisplayString Condition="type != nullptr">{{ type={ type->info->alias,na } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[arity]">arity</Item>
|
||||
</Expand>
|
||||
<DisplayString>{{ arity={ arity } }}</DisplayString>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_type_node">
|
||||
<Intrinsic Name="has_property" Expression="!!(traits & property)">
|
||||
@@ -121,77 +49,73 @@
|
||||
<Item Name="[id]">id</Item>
|
||||
<Item Name="[sizeof]">size_of</Item>
|
||||
<Item Name="[is_arithmetic]">has_property(entt::internal::meta_traits::is_arithmetic)</Item>
|
||||
<Item Name="[is_integral]">has_property(entt::internal::meta_traits::is_integral)</Item>
|
||||
<Item Name="[is_signed]">has_property(entt::internal::meta_traits::is_signed)</Item>
|
||||
<Item Name="[is_array]">has_property(entt::internal::meta_traits::is_array)</Item>
|
||||
<Item Name="[is_enum]">has_property(entt::internal::meta_traits::is_enum)</Item>
|
||||
<Item Name="[is_class]">has_property(entt::internal::meta_traits::is_class)</Item>
|
||||
<Item Name="[is_pointer]">has_property(entt::internal::meta_traits::is_pointer)</Item>
|
||||
<Item Name="[is_meta_pointer_like]">has_property(entt::internal::meta_traits::is_meta_pointer_like)</Item>
|
||||
<Item Name="[is_meta_sequence_container]">has_property(entt::internal::meta_traits::is_meta_sequence_container)</Item>
|
||||
<Item Name="[is_meta_associative_container]">has_property(entt::internal::meta_traits::is_meta_associative_container)</Item>
|
||||
<Item Name="[default_constructor]">default_constructor != nullptr</Item>
|
||||
<Item Name="[conversion_helper]">conversion_helper != nullptr</Item>
|
||||
<Item Name="[template_info]" Condition="templ != nullptr">*templ</Item>
|
||||
<Synthetic Name="[ctor]" Condition="ctor != nullptr">
|
||||
<Expand>
|
||||
<LinkedListItems>
|
||||
<HeadPointer>ctor</HeadPointer>
|
||||
<NextPointer>next</NextPointer>
|
||||
<ValueNode>*this</ValueNode>
|
||||
</LinkedListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
<Synthetic Name="[base]" Condition="base != nullptr">
|
||||
<Expand>
|
||||
<LinkedListItems>
|
||||
<HeadPointer>base</HeadPointer>
|
||||
<NextPointer>next</NextPointer>
|
||||
<ValueNode>*this</ValueNode>
|
||||
</LinkedListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
<Synthetic Name="[conv]" Condition="conv != nullptr">
|
||||
<Expand>
|
||||
<LinkedListItems>
|
||||
<HeadPointer>conv</HeadPointer>
|
||||
<NextPointer>next</NextPointer>
|
||||
<ValueNode>*this</ValueNode>
|
||||
</LinkedListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
<Synthetic Name="[data]" Condition="data != nullptr">
|
||||
<Expand>
|
||||
<LinkedListItems>
|
||||
<HeadPointer>data</HeadPointer>
|
||||
<NextPointer>next</NextPointer>
|
||||
<ValueNode>*this</ValueNode>
|
||||
</LinkedListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
<Synthetic Name="[func]" Condition="func != nullptr">
|
||||
<Expand>
|
||||
<LinkedListItems>
|
||||
<HeadPointer>func</HeadPointer>
|
||||
<NextPointer>next</NextPointer>
|
||||
<ValueNode>*this</ValueNode>
|
||||
</LinkedListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
<Synthetic Name="[prop]" Condition="prop != nullptr">
|
||||
<Expand>
|
||||
<LinkedListItems>
|
||||
<HeadPointer>prop</HeadPointer>
|
||||
<NextPointer>next</NextPointer>
|
||||
<ValueNode>*this</ValueNode>
|
||||
</LinkedListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
<Item Name="[from_void]">from_void != nullptr</Item>
|
||||
<Item Name="[template_info]">templ</Item>
|
||||
<Item Name="[details]" Condition="details != nullptr">*details</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_type">
|
||||
<Type Name="entt::meta_any">
|
||||
<DisplayString Condition="node.info != nullptr">{{ type={ node.info->alias,na }, policy={ storage.mode,en } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem>node</ExpandedItem>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_handle">
|
||||
<DisplayString>{ any }</DisplayString>
|
||||
</Type>
|
||||
<Type Name="entt::meta_associative_container">
|
||||
<DisplayString>{ storage }</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_sequence_container">
|
||||
<DisplayString>{ storage }</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_data">
|
||||
<DisplayString Condition="node != nullptr">{ *node }</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_func">
|
||||
<DisplayString Condition="node != nullptr">{ *node }</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_prop">
|
||||
<DisplayString Condition="node != nullptr">{ *node }</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_type">
|
||||
<DisplayString>{ node }</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem>node</ExpandedItem>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
</AutoVisualizer>
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
<Type Name="entt::resource<*>">
|
||||
<DisplayString>{ value }</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem>value</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::resource_cache<*>">
|
||||
<DisplayString>{ pool.first_base::value }</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem>pool.first_base::value</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
</AutoVisualizer>
|
||||
|
||||
@@ -1,35 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
<Type Name="entt::connection">
|
||||
<DisplayString>{{ bound={ signal != nullptr } }}</DisplayString>
|
||||
</Type>
|
||||
<Type Name="entt::delegate<*>">
|
||||
<DisplayString>{{ type={ "$T1" } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[empty]">fn == nullptr</Item>
|
||||
<Item Name="[data]">data</Item>
|
||||
<Item Name="[data]">instance</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::dispatcher">
|
||||
<DisplayString>{{ size={ pools.size() } }}</DisplayString>
|
||||
<Type Name="entt::basic_dispatcher<*>">
|
||||
<Intrinsic Name="size" Expression="pools.first_base::value.size()"/>
|
||||
<DisplayString>{{ size={ size() } }}</DisplayString>
|
||||
<Expand>
|
||||
<Synthetic Name="[pools]">
|
||||
<DisplayString>{ pools.size() }</DisplayString>
|
||||
<DisplayString>{ size() }</DisplayString>
|
||||
<Expand>
|
||||
<IndexListItems>
|
||||
<Size>pools.size()</Size>
|
||||
<ValueNode>*pools.packed.first_base::value[$i].element.second</ValueNode>
|
||||
<Size>size()</Size>
|
||||
<ValueNode>*pools.first_base::value.packed.first_base::value[$i].element.second</ValueNode>
|
||||
</IndexListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::dispatcher::pool_handler<*>">
|
||||
<Type Name="entt::internal::dispatcher_handler<*>">
|
||||
<DisplayString>{{ size={ events.size() }, event={ "$T1" } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[signal]">signal</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::emitter<*>">
|
||||
<DisplayString>{{ size={ handlers.first_base::value.packed.first_base::value.size() } }}</DisplayString>
|
||||
</Type>
|
||||
<Type Name="entt::connection">
|
||||
<DisplayString>{{ bound={ signal != nullptr } }}</DisplayString>
|
||||
</Type>
|
||||
<Type Name="entt::scoped_connection">
|
||||
<DisplayString>{ conn }</DisplayString>
|
||||
</Type>
|
||||
@@ -46,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,
|
||||
)
|
||||
@@ -1,35 +1,32 @@
|
||||
#ifndef ENTT_CONFIG_CONFIG_H
|
||||
#define ENTT_CONFIG_CONFIG_H
|
||||
|
||||
#include "version.h"
|
||||
|
||||
#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
|
||||
# define ENTT_NOEXCEPT noexcept
|
||||
# define ENTT_CONSTEXPR
|
||||
# define ENTT_THROW throw
|
||||
# define ENTT_TRY try
|
||||
# define ENTT_CATCH catch(...)
|
||||
#else
|
||||
# define ENTT_NOEXCEPT
|
||||
# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20)
|
||||
# define ENTT_THROW
|
||||
# define ENTT_TRY if(true)
|
||||
# define ENTT_CATCH if(false)
|
||||
#endif
|
||||
|
||||
#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L
|
||||
# include <new>
|
||||
# define ENTT_LAUNDER(expr) std::launder(expr)
|
||||
#else
|
||||
# define ENTT_LAUNDER(expr) expr
|
||||
#endif
|
||||
|
||||
#ifndef ENTT_USE_ATOMIC
|
||||
# define ENTT_MAYBE_ATOMIC(Type) Type
|
||||
#else
|
||||
#ifdef ENTT_USE_ATOMIC
|
||||
# include <atomic>
|
||||
# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
|
||||
#else
|
||||
# define ENTT_MAYBE_ATOMIC(Type) Type
|
||||
#endif
|
||||
|
||||
#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
|
||||
@@ -42,19 +39,31 @@
|
||||
|
||||
#ifdef ENTT_DISABLE_ASSERT
|
||||
# undef ENTT_ASSERT
|
||||
# define ENTT_ASSERT(...) (void(0))
|
||||
# define ENTT_ASSERT(condition, msg) (void(0))
|
||||
#elif !defined ENTT_ASSERT
|
||||
# include <cassert>
|
||||
# define ENTT_ASSERT(condition, ...) assert(condition)
|
||||
# define ENTT_ASSERT(condition, msg) assert(condition)
|
||||
#endif
|
||||
|
||||
#ifdef ENTT_DISABLE_ASSERT
|
||||
# undef ENTT_ASSERT_CONSTEXPR
|
||||
# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0))
|
||||
#elif !defined ENTT_ASSERT_CONSTEXPR
|
||||
# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg)
|
||||
#endif
|
||||
|
||||
#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg);
|
||||
|
||||
#ifdef ENTT_NO_ETO
|
||||
# define ENTT_IGNORE_IF_EMPTY false
|
||||
# define ENTT_ETO_TYPE(Type) void
|
||||
#else
|
||||
# define ENTT_IGNORE_IF_EMPTY true
|
||||
# define ENTT_ETO_TYPE(Type) Type
|
||||
#endif
|
||||
|
||||
#ifndef ENTT_STANDARD_CPP
|
||||
#ifdef ENTT_STANDARD_CPP
|
||||
# define ENTT_NONSTD false
|
||||
#else
|
||||
# define ENTT_NONSTD true
|
||||
# if defined __clang__ || defined __GNUC__
|
||||
# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
|
||||
# define ENTT_PRETTY_FUNCTION_PREFIX '='
|
||||
@@ -66,4 +75,11 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined _MSC_VER
|
||||
# pragma detect_mismatch("entt.version", ENTT_VERSION)
|
||||
# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
|
||||
# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
|
||||
# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
7
src/entt/config/macro.h
Normal file
7
src/entt/config/macro.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef ENTT_CONFIG_MACRO_H
|
||||
#define ENTT_CONFIG_MACRO_H
|
||||
|
||||
#define ENTT_STR(arg) #arg
|
||||
#define ENTT_XSTR(arg) ENTT_STR(arg)
|
||||
|
||||
#endif
|
||||
@@ -1,8 +1,14 @@
|
||||
#ifndef ENTT_CONFIG_VERSION_H
|
||||
#define ENTT_CONFIG_VERSION_H
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 9
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
"." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,6 @@
|
||||
#ifndef ENTT_CONTAINER_DENSE_HASH_SET_HPP
|
||||
#define ENTT_CONTAINER_DENSE_HASH_SET_HPP
|
||||
#ifndef ENTT_CONTAINER_DENSE_SET_HPP
|
||||
#define ENTT_CONTAINER_DENSE_SET_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
@@ -20,184 +19,170 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename Type>
|
||||
struct dense_hash_set_node final {
|
||||
template<typename... Args>
|
||||
dense_hash_set_node(const std::size_t pos, Args &&...args)
|
||||
: next{pos},
|
||||
element{std::forward<Args>(args)...} {}
|
||||
|
||||
std::size_t next;
|
||||
Type element;
|
||||
};
|
||||
|
||||
template<typename It>
|
||||
class dense_hash_set_iterator {
|
||||
friend dense_hash_set_iterator<const std::remove_pointer_t<It> *>;
|
||||
|
||||
using iterator_traits = std::iterator_traits<decltype(std::addressof(std::as_const(std::declval<It>()->element)))>;
|
||||
class dense_set_iterator final {
|
||||
template<typename>
|
||||
friend class dense_set_iterator;
|
||||
|
||||
public:
|
||||
using value_type = typename iterator_traits::value_type;
|
||||
using pointer = typename iterator_traits::pointer;
|
||||
using reference = typename iterator_traits::reference;
|
||||
using difference_type = typename iterator_traits::difference_type;
|
||||
using value_type = typename It::value_type::second_type;
|
||||
using pointer = const value_type *;
|
||||
using reference = const value_type &;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
|
||||
dense_hash_set_iterator() ENTT_NOEXCEPT = default;
|
||||
constexpr dense_set_iterator() noexcept
|
||||
: it{} {}
|
||||
|
||||
dense_hash_set_iterator(const It iter) ENTT_NOEXCEPT
|
||||
constexpr dense_set_iterator(const It iter) noexcept
|
||||
: it{iter} {}
|
||||
|
||||
template<bool Const = std::is_const_v<std::remove_pointer_t<It>>, typename = std::enable_if_t<Const>>
|
||||
dense_hash_set_iterator(const dense_hash_set_iterator<std::remove_const_t<std::remove_pointer_t<It>> *> &other)
|
||||
template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
|
||||
constexpr dense_set_iterator(const dense_set_iterator<Other> &other) noexcept
|
||||
: it{other.it} {}
|
||||
|
||||
dense_hash_set_iterator &operator++() ENTT_NOEXCEPT {
|
||||
constexpr dense_set_iterator &operator++() noexcept {
|
||||
return ++it, *this;
|
||||
}
|
||||
|
||||
dense_hash_set_iterator operator++(int) ENTT_NOEXCEPT {
|
||||
dense_hash_set_iterator orig = *this;
|
||||
constexpr dense_set_iterator operator++(int) noexcept {
|
||||
dense_set_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
dense_hash_set_iterator &operator--() ENTT_NOEXCEPT {
|
||||
constexpr dense_set_iterator &operator--() noexcept {
|
||||
return --it, *this;
|
||||
}
|
||||
|
||||
dense_hash_set_iterator operator--(int) ENTT_NOEXCEPT {
|
||||
dense_hash_set_iterator orig = *this;
|
||||
constexpr dense_set_iterator operator--(int) noexcept {
|
||||
dense_set_iterator orig = *this;
|
||||
return operator--(), orig;
|
||||
}
|
||||
|
||||
dense_hash_set_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
|
||||
constexpr dense_set_iterator &operator+=(const difference_type value) noexcept {
|
||||
it += value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
dense_hash_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
|
||||
dense_hash_set_iterator copy = *this;
|
||||
constexpr dense_set_iterator operator+(const difference_type value) const noexcept {
|
||||
dense_set_iterator copy = *this;
|
||||
return (copy += value);
|
||||
}
|
||||
|
||||
dense_hash_set_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
|
||||
constexpr dense_set_iterator &operator-=(const difference_type value) noexcept {
|
||||
return (*this += -value);
|
||||
}
|
||||
|
||||
dense_hash_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
|
||||
constexpr dense_set_iterator operator-(const difference_type value) const noexcept {
|
||||
return (*this + -value);
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator[](const difference_type value) const {
|
||||
return it[value].element;
|
||||
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
|
||||
return it[value].second;
|
||||
}
|
||||
|
||||
[[nodiscard]] pointer operator->() const {
|
||||
return std::addressof(it->element);
|
||||
[[nodiscard]] constexpr pointer operator->() const noexcept {
|
||||
return std::addressof(it->second);
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator*() const {
|
||||
[[nodiscard]] constexpr reference operator*() const noexcept {
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
friend auto operator-(const dense_hash_set_iterator<ILhs> &, const dense_hash_set_iterator<IRhs> &) ENTT_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 bool operator==(const dense_hash_set_iterator<ILhs> &, const dense_hash_set_iterator<IRhs> &) ENTT_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 bool operator<(const dense_hash_set_iterator<ILhs> &, const dense_hash_set_iterator<IRhs> &) ENTT_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]] auto operator-(const dense_hash_set_iterator<ILhs> &lhs, const dense_hash_set_iterator<IRhs> &rhs) ENTT_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]] bool operator==(const dense_hash_set_iterator<ILhs> &lhs, const dense_hash_set_iterator<IRhs> &rhs) ENTT_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]] bool operator!=(const dense_hash_set_iterator<ILhs> &lhs, const dense_hash_set_iterator<IRhs> &rhs) ENTT_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]] bool operator<(const dense_hash_set_iterator<ILhs> &lhs, const dense_hash_set_iterator<IRhs> &rhs) ENTT_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]] bool operator>(const dense_hash_set_iterator<ILhs> &lhs, const dense_hash_set_iterator<IRhs> &rhs) ENTT_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]] bool operator<=(const dense_hash_set_iterator<ILhs> &lhs, const dense_hash_set_iterator<IRhs> &rhs) ENTT_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]] bool operator>=(const dense_hash_set_iterator<ILhs> &lhs, const dense_hash_set_iterator<IRhs> &rhs) ENTT_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 It>
|
||||
class dense_hash_set_local_iterator {
|
||||
friend dense_hash_set_local_iterator<const std::remove_pointer_t<It> *>;
|
||||
|
||||
using iterator_traits = std::iterator_traits<decltype(std::addressof(std::as_const(std::declval<It>()->element)))>;
|
||||
class dense_set_local_iterator final {
|
||||
template<typename>
|
||||
friend class dense_set_local_iterator;
|
||||
|
||||
public:
|
||||
using value_type = typename iterator_traits::value_type;
|
||||
using pointer = typename iterator_traits::pointer;
|
||||
using reference = typename iterator_traits::reference;
|
||||
using difference_type = typename iterator_traits::difference_type;
|
||||
using value_type = typename It::value_type::second_type;
|
||||
using pointer = const value_type *;
|
||||
using reference = const value_type &;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
dense_hash_set_local_iterator() ENTT_NOEXCEPT = default;
|
||||
constexpr dense_set_local_iterator() noexcept
|
||||
: it{},
|
||||
offset{} {}
|
||||
|
||||
dense_hash_set_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT
|
||||
constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept
|
||||
: it{iter},
|
||||
offset{pos} {}
|
||||
|
||||
template<bool Const = std::is_const_v<std::remove_pointer_t<It>>, typename = std::enable_if_t<Const>>
|
||||
dense_hash_set_local_iterator(const dense_hash_set_local_iterator<std::remove_const_t<std::remove_pointer_t<It>> *> &other)
|
||||
template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
|
||||
constexpr dense_set_local_iterator(const dense_set_local_iterator<Other> &other) noexcept
|
||||
: it{other.it},
|
||||
offset{other.offset} {}
|
||||
|
||||
dense_hash_set_local_iterator &operator++() ENTT_NOEXCEPT {
|
||||
return offset = it[offset].next, *this;
|
||||
constexpr dense_set_local_iterator &operator++() noexcept {
|
||||
return offset = it[offset].first, *this;
|
||||
}
|
||||
|
||||
dense_hash_set_local_iterator operator++(int) ENTT_NOEXCEPT {
|
||||
dense_hash_set_local_iterator orig = *this;
|
||||
constexpr dense_set_local_iterator operator++(int) noexcept {
|
||||
dense_set_local_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
[[nodiscard]] pointer operator->() const {
|
||||
return std::addressof(it[offset].element);
|
||||
[[nodiscard]] constexpr pointer operator->() const noexcept {
|
||||
return std::addressof(it[offset].second);
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator*() const {
|
||||
[[nodiscard]] constexpr reference operator*() const noexcept {
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::size_t index() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr std::size_t index() const noexcept {
|
||||
return offset;
|
||||
}
|
||||
|
||||
@@ -206,22 +191,18 @@ private:
|
||||
std::size_t offset;
|
||||
};
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator==(const dense_hash_set_local_iterator<ILhs> &lhs, const dense_hash_set_local_iterator<IRhs> &rhs) ENTT_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]] bool operator!=(const dense_hash_set_local_iterator<ILhs> &lhs, const dense_hash_set_local_iterator<IRhs> &rhs) ENTT_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.
|
||||
@@ -236,27 +217,26 @@ template<typename ILhs, typename IRhs>
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Type, typename Hash, typename KeyEqual, typename Allocator>
|
||||
class dense_hash_set final {
|
||||
class dense_set {
|
||||
static constexpr float default_threshold = 0.875f;
|
||||
static constexpr std::size_t minimum_capacity = 8u;
|
||||
|
||||
using allocator_traits = std::allocator_traits<Allocator>;
|
||||
using alloc = typename allocator_traits::template rebind_alloc<Type>;
|
||||
using alloc_traits = typename std::allocator_traits<alloc>;
|
||||
|
||||
using node_type = internal::dense_hash_set_node<Type>;
|
||||
using node_type = std::pair<std::size_t, Type>;
|
||||
using alloc_traits = std::allocator_traits<Allocator>;
|
||||
static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
|
||||
using sparse_container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
|
||||
using packed_container_type = std::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
|
||||
|
||||
[[nodiscard]] std::size_t hash_to_bucket(const std::size_t hash) const ENTT_NOEXCEPT {
|
||||
return fast_mod(hash, bucket_count());
|
||||
template<typename Other>
|
||||
[[nodiscard]] std::size_t value_to_bucket(const Other &value) const noexcept {
|
||||
return fast_mod(static_cast<size_type>(sparse.second()(value)), bucket_count());
|
||||
}
|
||||
|
||||
template<typename Other>
|
||||
[[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) {
|
||||
for(auto it = begin(bucket), last = end(bucket); it != last; ++it) {
|
||||
if(packed.second()(*it, value)) {
|
||||
return begin() + it.index();
|
||||
return begin() + static_cast<typename iterator::difference_type>(it.index());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,61 +247,45 @@ class dense_hash_set final {
|
||||
[[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) const {
|
||||
for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) {
|
||||
if(packed.second()(*it, value)) {
|
||||
return cbegin() + it.index();
|
||||
return cbegin() + static_cast<typename iterator::difference_type>(it.index());
|
||||
}
|
||||
}
|
||||
|
||||
return cend();
|
||||
}
|
||||
|
||||
template<typename Arg>
|
||||
[[nodiscard]] auto get_or_emplace(Arg &&arg) {
|
||||
const auto hash = sparse.second()(arg);
|
||||
auto index = hash_to_bucket(hash);
|
||||
template<typename Other>
|
||||
[[nodiscard]] auto insert_or_do_nothing(Other &&value) {
|
||||
const auto index = value_to_bucket(value);
|
||||
|
||||
if(auto it = constrained_find(arg, index); it != end()) {
|
||||
if(auto it = constrained_find(value, index); it != end()) {
|
||||
return std::make_pair(it, false);
|
||||
}
|
||||
|
||||
if(const auto count = size() + 1u; count > (bucket_count() * max_load_factor())) {
|
||||
rehash(bucket_count() * 2u);
|
||||
index = hash_to_bucket(hash);
|
||||
}
|
||||
|
||||
packed.first().emplace_back(sparse.first()[index], std::forward<Arg>(arg));
|
||||
// update goes after emplace to enforce exception guarantees
|
||||
sparse.first()[index] = size() - 1u;
|
||||
packed.first().emplace_back(sparse.first()[index], std::forward<Other>(value));
|
||||
sparse.first()[index] = packed.first().size() - 1u;
|
||||
rehash_if_required();
|
||||
|
||||
return std::make_pair(--end(), true);
|
||||
}
|
||||
|
||||
template<typename Other>
|
||||
bool do_erase(const Other &value) {
|
||||
for(size_type *curr = sparse.first().data() + bucket(value); *curr != std::numeric_limits<size_type>::max(); curr = &packed.first()[*curr].next) {
|
||||
if(packed.second()(packed.first()[*curr].element, value)) {
|
||||
const auto index = *curr;
|
||||
*curr = packed.first()[*curr].next;
|
||||
move_and_pop(index);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void move_and_pop(const std::size_t pos) {
|
||||
if(const auto last = size() - 1u; pos != last) {
|
||||
size_type *curr = sparse.first().data() + bucket(packed.first().back().element);
|
||||
for(; *curr != last; curr = &packed.first()[*curr].next) {}
|
||||
*curr = pos;
|
||||
|
||||
// basic exception guarantees when value type has a throwing move constructor
|
||||
size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second);
|
||||
packed.first()[pos] = std::move(packed.first().back());
|
||||
for(; *curr != last; curr = &packed.first()[*curr].first) {}
|
||||
*curr = pos;
|
||||
}
|
||||
|
||||
packed.first().pop_back();
|
||||
}
|
||||
|
||||
void rehash_if_required() {
|
||||
if(size() > (bucket_count() * max_load_factor())) {
|
||||
rehash(bucket_count() * 2u);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Key type of the container. */
|
||||
using key_type = Type;
|
||||
@@ -336,179 +300,194 @@ public:
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator = internal::dense_hash_set_iterator<typename packed_container_type::pointer>;
|
||||
using iterator = internal::dense_set_iterator<typename packed_container_type::iterator>;
|
||||
/*! @brief Constant random access iterator type. */
|
||||
using const_iterator = internal::dense_hash_set_iterator<typename packed_container_type::const_pointer>;
|
||||
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_hash_set_local_iterator<typename packed_container_type::pointer>;
|
||||
using local_iterator = internal::dense_set_local_iterator<typename packed_container_type::iterator>;
|
||||
/*! @brief Constant forward iterator type. */
|
||||
using const_local_iterator = internal::dense_hash_set_local_iterator<typename packed_container_type::const_pointer>;
|
||||
using const_local_iterator = internal::dense_set_local_iterator<typename packed_container_type::const_iterator>;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
dense_hash_set()
|
||||
: dense_hash_set(minimum_capacity) {}
|
||||
dense_set()
|
||||
: dense_set{minimum_capacity} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an empty container with a given allocator.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit dense_hash_set(const allocator_type &allocator)
|
||||
: dense_hash_set{minimum_capacity, hasher{}, key_equal{}, allocator} {}
|
||||
explicit dense_set(const allocator_type &allocator)
|
||||
: dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an empty container with a given allocator and user
|
||||
* supplied minimal number of buckets.
|
||||
* @param bucket_count Minimal number of buckets.
|
||||
* @param cnt Minimal number of buckets.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
dense_hash_set(const size_type bucket_count, const allocator_type &allocator)
|
||||
: dense_hash_set{bucket_count, hasher{}, key_equal{}, allocator} {}
|
||||
dense_set(const size_type cnt, const allocator_type &allocator)
|
||||
: dense_set{cnt, hasher{}, key_equal{}, allocator} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an empty container with a given allocator, hash
|
||||
* function and user supplied minimal number of buckets.
|
||||
* @param bucket_count Minimal number of buckets.
|
||||
* @param cnt Minimal number of buckets.
|
||||
* @param hash Hash function to use.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
dense_hash_set(const size_type bucket_count, const hasher &hash, const allocator_type &allocator)
|
||||
: dense_hash_set{bucket_count, hash, key_equal{}, allocator} {}
|
||||
dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator)
|
||||
: dense_set{cnt, hash, key_equal{}, allocator} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an empty container with a given allocator, hash
|
||||
* function, compare function and user supplied minimal number of buckets.
|
||||
* @param bucket_count Minimal number of buckets.
|
||||
* @param cnt Minimal number of buckets.
|
||||
* @param hash Hash function to use.
|
||||
* @param equal Compare function to use.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit dense_hash_set(const size_type bucket_count, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type())
|
||||
explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{})
|
||||
: sparse{allocator, hash},
|
||||
packed{allocator, equal},
|
||||
threshold{default_threshold} {
|
||||
rehash(bucket_count);
|
||||
rehash(cnt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
* @param other The instance to copy from.
|
||||
*/
|
||||
dense_hash_set(const dense_hash_set &other)
|
||||
: dense_hash_set{other, alloc_traits::select_on_container_copy_construction(other.get_allocator())} {}
|
||||
/*! @brief Default copy constructor. */
|
||||
dense_set(const dense_set &) = default;
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended copy constructor.
|
||||
* @param other The instance to copy from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
dense_hash_set(const dense_hash_set &other, const allocator_type &allocator)
|
||||
: sparse{sparse_container_type{other.sparse.first(), allocator}, other.sparse.second()},
|
||||
// cannot copy the container directly due to a nasty issue of apple clang :(
|
||||
packed{packed_container_type{other.packed.first().begin(), other.packed.first().end(), allocator}, other.packed.second()},
|
||||
threshold{other.threshold} {
|
||||
}
|
||||
dense_set(const dense_set &other, const allocator_type &allocator)
|
||||
: sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())},
|
||||
packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())},
|
||||
threshold{other.threshold} {}
|
||||
|
||||
/**
|
||||
* @brief Default move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
dense_hash_set(dense_hash_set &&other) ENTT_NOEXCEPT = default;
|
||||
/*! @brief Default move constructor. */
|
||||
dense_set(dense_set &&) noexcept(std::is_nothrow_move_constructible_v<compressed_pair<sparse_container_type, hasher>> &&std::is_nothrow_move_constructible_v<compressed_pair<packed_container_type, key_equal>>) = default;
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
dense_hash_set(dense_hash_set &&other, const allocator_type &allocator) ENTT_NOEXCEPT
|
||||
: sparse{sparse_container_type{std::move(other.sparse.first()), allocator}, std::move(other.sparse.second())},
|
||||
// cannot move the container directly due to a nasty issue of apple clang :(
|
||||
packed{packed_container_type{std::make_move_iterator(other.packed.first().begin()), std::make_move_iterator(other.packed.first().end()), allocator}, std::move(other.packed.second())},
|
||||
dense_set(dense_set &&other, const allocator_type &allocator)
|
||||
: sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))},
|
||||
packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))},
|
||||
threshold{other.threshold} {}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~dense_hash_set() = default;
|
||||
|
||||
/**
|
||||
* @brief Copy assignment operator.
|
||||
* @param other The instance to copy from.
|
||||
* @brief Default copy assignment operator.
|
||||
* @return This container.
|
||||
*/
|
||||
dense_hash_set &operator=(const dense_hash_set &other) {
|
||||
threshold = other.threshold;
|
||||
sparse.first().clear();
|
||||
packed.first().clear();
|
||||
rehash(other.bucket_count());
|
||||
packed.first().reserve(other.packed.first().size());
|
||||
insert(other.cbegin(), other.cend());
|
||||
return *this;
|
||||
}
|
||||
dense_set &operator=(const dense_set &) = default;
|
||||
|
||||
/**
|
||||
* @brief Default move assignment operator.
|
||||
* @param other The instance to move from.
|
||||
* @return This container.
|
||||
*/
|
||||
dense_hash_set &operator=(dense_hash_set &&other) ENTT_NOEXCEPT = default;
|
||||
dense_set &operator=(dense_set &&) noexcept(std::is_nothrow_move_assignable_v<compressed_pair<sparse_container_type, hasher>> &&std::is_nothrow_move_assignable_v<compressed_pair<packed_container_type, key_equal>>) = default;
|
||||
|
||||
/**
|
||||
* @brief Returns the associated allocator.
|
||||
* @return The associated allocator.
|
||||
*/
|
||||
[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
|
||||
return sparse.first().get_allocator();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning.
|
||||
*
|
||||
* The returned iterator points to the first instance of the internal array.
|
||||
* If the array is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @return An iterator to the first instance of the internal array.
|
||||
*/
|
||||
[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
|
||||
return packed.first().data();
|
||||
[[nodiscard]] const_iterator cbegin() const noexcept {
|
||||
return packed.first().begin();
|
||||
}
|
||||
|
||||
/*! @copydoc cbegin */
|
||||
[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] const_iterator begin() const noexcept {
|
||||
return cbegin();
|
||||
}
|
||||
|
||||
/*! @copydoc begin */
|
||||
[[nodiscard]] iterator begin() ENTT_NOEXCEPT {
|
||||
return packed.first().data();
|
||||
[[nodiscard]] iterator begin() noexcept {
|
||||
return packed.first().begin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the end.
|
||||
*
|
||||
* The returned iterator points to the element following the last instance
|
||||
* of the internal array. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @return An iterator to the element following the last instance of the
|
||||
* internal array.
|
||||
*/
|
||||
[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
|
||||
return packed.first().data() + size();
|
||||
[[nodiscard]] const_iterator cend() const noexcept {
|
||||
return packed.first().end();
|
||||
}
|
||||
|
||||
/*! @copydoc cend */
|
||||
[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] const_iterator end() const noexcept {
|
||||
return cend();
|
||||
}
|
||||
|
||||
/*! @copydoc end */
|
||||
[[nodiscard]] iterator end() ENTT_NOEXCEPT {
|
||||
return packed.first().data() + size();
|
||||
[[nodiscard]] iterator end() noexcept {
|
||||
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.
|
||||
*/
|
||||
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool empty() const noexcept {
|
||||
return packed.first().empty();
|
||||
}
|
||||
|
||||
@@ -516,12 +495,20 @@ public:
|
||||
* @brief Returns the number of elements in a container.
|
||||
* @return Number of elements in a container.
|
||||
*/
|
||||
[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] size_type size() const noexcept {
|
||||
return packed.first().size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the maximum possible number of elements.
|
||||
* @return Maximum possible number of elements.
|
||||
*/
|
||||
[[nodiscard]] size_type max_size() const noexcept {
|
||||
return packed.first().max_size();
|
||||
}
|
||||
|
||||
/*! @brief Clears the container. */
|
||||
void clear() ENTT_NOEXCEPT {
|
||||
void clear() noexcept {
|
||||
sparse.first().clear();
|
||||
packed.first().clear();
|
||||
rehash(0u);
|
||||
@@ -535,12 +522,12 @@ public:
|
||||
* insertion took place.
|
||||
*/
|
||||
std::pair<iterator, bool> insert(const value_type &value) {
|
||||
return emplace(value);
|
||||
return insert_or_do_nothing(value);
|
||||
}
|
||||
|
||||
/*! @copydoc insert */
|
||||
std::pair<iterator, bool> insert(value_type &&value) {
|
||||
return emplace(std::move(value));
|
||||
return insert_or_do_nothing(std::move(value));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -552,12 +539,16 @@ public:
|
||||
template<typename It>
|
||||
void insert(It first, It last) {
|
||||
for(; first != last; ++first) {
|
||||
emplace(*first);
|
||||
insert(*first);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructs an element in-place, if it does not exist.
|
||||
*
|
||||
* The element is also constructed when the container already has the key,
|
||||
* in which case the newly constructed object is destroyed immediately.
|
||||
*
|
||||
* @tparam Args Types of arguments to forward to the constructor of the
|
||||
* element.
|
||||
* @param args Arguments to forward to the constructor of the element.
|
||||
@@ -567,10 +558,21 @@ public:
|
||||
*/
|
||||
template<typename... Args>
|
||||
std::pair<iterator, bool> emplace(Args &&...args) {
|
||||
if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v<std::remove_const_t<std::remove_reference_t<Args>>, value_type>)) {
|
||||
return get_or_emplace(std::forward<Args>(args)...);
|
||||
if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v<std::decay_t<Args>, value_type>)) {
|
||||
return insert_or_do_nothing(std::forward<Args>(args)...);
|
||||
} else {
|
||||
return get_or_emplace(value_type{std::forward<Args>(args)...});
|
||||
auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward<Args>(args)...));
|
||||
const auto index = value_to_bucket(node.second);
|
||||
|
||||
if(auto it = constrained_find(node.second, index); it != end()) {
|
||||
packed.first().pop_back();
|
||||
return std::make_pair(it, false);
|
||||
}
|
||||
|
||||
std::swap(node.first, sparse.first()[index]);
|
||||
rehash_if_required();
|
||||
|
||||
return std::make_pair(--end(), true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -580,9 +582,9 @@ public:
|
||||
* @return An iterator following the removed element.
|
||||
*/
|
||||
iterator erase(const_iterator pos) {
|
||||
const auto dist = std::distance(cbegin(), pos);
|
||||
const auto diff = pos - cbegin();
|
||||
erase(*pos);
|
||||
return begin() + dist;
|
||||
return begin() + diff;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -592,13 +594,13 @@ public:
|
||||
* @return An iterator following the last removed element.
|
||||
*/
|
||||
iterator erase(const_iterator first, const_iterator last) {
|
||||
const auto dist = std::distance(cbegin(), first);
|
||||
const auto dist = first - cbegin();
|
||||
|
||||
for(auto rfirst = std::make_reverse_iterator(last), rlast = std::make_reverse_iterator(first); rfirst != rlast; ++rfirst) {
|
||||
erase(*rfirst);
|
||||
for(auto from = last - cbegin(); from != dist; --from) {
|
||||
erase(packed.first()[from - 1u].second);
|
||||
}
|
||||
|
||||
return dist > static_cast<decltype(dist)>(size()) ? end() : (begin() + dist);
|
||||
return (begin() + dist);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -607,20 +609,50 @@ public:
|
||||
* @return Number of elements removed (either 0 or 1).
|
||||
*/
|
||||
size_type erase(const value_type &value) {
|
||||
return do_erase(value);
|
||||
for(size_type *curr = sparse.first().data() + value_to_bucket(value); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].first) {
|
||||
if(packed.second()(packed.first()[*curr].second, value)) {
|
||||
const auto index = *curr;
|
||||
*curr = packed.first()[*curr].first;
|
||||
move_and_pop(index);
|
||||
return 1u;
|
||||
}
|
||||
}
|
||||
|
||||
return 0u;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exchanges the contents with those of a given container.
|
||||
* @param other Container to exchange the content with.
|
||||
*/
|
||||
void swap(dense_hash_set &other) {
|
||||
void swap(dense_set &other) {
|
||||
using std::swap;
|
||||
swap(sparse, other.sparse);
|
||||
swap(packed, other.packed);
|
||||
swap(threshold, other.threshold);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements matching a value (either 1 or 0).
|
||||
* @param key Key value of an element to search for.
|
||||
* @return Number of elements matching the key (either 1 or 0).
|
||||
*/
|
||||
[[nodiscard]] size_type count(const value_type &key) const {
|
||||
return find(key) != end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements matching a key (either 1 or 0).
|
||||
* @tparam Other Type of the key value of an element to search for.
|
||||
* @param key Key value of an element to search for.
|
||||
* @return Number of elements matching the key (either 1 or 0).
|
||||
*/
|
||||
template<typename Other>
|
||||
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, size_type>>
|
||||
count(const Other &key) const {
|
||||
return find(key) != end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finds an element with a given value.
|
||||
* @param value Value of an element to search for.
|
||||
@@ -628,12 +660,12 @@ public:
|
||||
* element is found, a past-the-end iterator is returned.
|
||||
*/
|
||||
[[nodiscard]] iterator find(const value_type &value) {
|
||||
return constrained_find(value, bucket(value));
|
||||
return constrained_find(value, value_to_bucket(value));
|
||||
}
|
||||
|
||||
/*! @copydoc find */
|
||||
[[nodiscard]] const_iterator find(const value_type &value) const {
|
||||
return constrained_find(value, bucket(value));
|
||||
return constrained_find(value, value_to_bucket(value));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -646,14 +678,54 @@ public:
|
||||
template<typename Other>
|
||||
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, iterator>>
|
||||
find(const Other &value) {
|
||||
return constrained_find(value, bucket(value));
|
||||
return constrained_find(value, value_to_bucket(value));
|
||||
}
|
||||
|
||||
/*! @copydoc find */
|
||||
template<typename Other>
|
||||
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, const_iterator>>
|
||||
find(const Other &value) const {
|
||||
return constrained_find(value, bucket(value));
|
||||
return constrained_find(value, value_to_bucket(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a range containing all elements with a given value.
|
||||
* @param value Value of an element to search for.
|
||||
* @return A pair of iterators pointing to the first element and past the
|
||||
* last element of the range.
|
||||
*/
|
||||
[[nodiscard]] std::pair<iterator, iterator> equal_range(const value_type &value) {
|
||||
const auto it = find(value);
|
||||
return {it, it + !(it == end())};
|
||||
}
|
||||
|
||||
/*! @copydoc equal_range */
|
||||
[[nodiscard]] std::pair<const_iterator, const_iterator> equal_range(const value_type &value) const {
|
||||
const auto it = find(value);
|
||||
return {it, it + !(it == cend())};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a range containing all elements that compare _equivalent_
|
||||
* to a given value.
|
||||
* @tparam Other Type of an element to search for.
|
||||
* @param value Value of an element to search for.
|
||||
* @return A pair of iterators pointing to the first element and past the
|
||||
* last element of the range.
|
||||
*/
|
||||
template<typename Other>
|
||||
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, std::pair<iterator, iterator>>>
|
||||
equal_range(const Other &value) {
|
||||
const auto it = find(value);
|
||||
return {it, it + !(it == end())};
|
||||
}
|
||||
|
||||
/*! @copydoc equal_range */
|
||||
template<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);
|
||||
return {it, it + !(it == cend())};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -684,7 +756,7 @@ public:
|
||||
* @return An iterator to the beginning of the given bucket.
|
||||
*/
|
||||
[[nodiscard]] const_local_iterator cbegin(const size_type index) const {
|
||||
return {packed.first().data(), sparse.first()[index]};
|
||||
return {packed.first().begin(), sparse.first()[index]};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -702,7 +774,7 @@ public:
|
||||
* @return An iterator to the beginning of the given bucket.
|
||||
*/
|
||||
[[nodiscard]] local_iterator begin(const size_type index) {
|
||||
return {packed.first().data(), sparse.first()[index]};
|
||||
return {packed.first().begin(), sparse.first()[index]};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -711,7 +783,7 @@ public:
|
||||
* @return An iterator to the end of the given bucket.
|
||||
*/
|
||||
[[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const {
|
||||
return {packed.first().data(), std::numeric_limits<size_type>::max()};
|
||||
return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -719,7 +791,7 @@ public:
|
||||
* @param index An index of a bucket to access.
|
||||
* @return An iterator to the end of the given bucket.
|
||||
*/
|
||||
[[nodiscard]] const_local_iterator end([[maybe_unused]] const size_type index) const {
|
||||
[[nodiscard]] const_local_iterator end(const size_type index) const {
|
||||
return cend(index);
|
||||
}
|
||||
|
||||
@@ -729,7 +801,7 @@ public:
|
||||
* @return An iterator to the end of the given bucket.
|
||||
*/
|
||||
[[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) {
|
||||
return {packed.first().data(), std::numeric_limits<size_type>::max()};
|
||||
return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -763,7 +835,7 @@ public:
|
||||
* @return The bucket for the given element.
|
||||
*/
|
||||
[[nodiscard]] size_type bucket(const value_type &value) const {
|
||||
return hash_to_bucket(sparse.second()(value));
|
||||
return value_to_bucket(value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -795,19 +867,23 @@ public:
|
||||
/**
|
||||
* @brief Reserves at least the specified number of buckets and regenerates
|
||||
* the hash table.
|
||||
* @param count New number of buckets.
|
||||
* @param cnt New number of buckets.
|
||||
*/
|
||||
void rehash(const size_type count) {
|
||||
auto value = (std::max)(count, minimum_capacity);
|
||||
value = (std::max)(value, static_cast<size_type>(size() / max_load_factor()));
|
||||
void rehash(const size_type cnt) {
|
||||
auto value = cnt > minimum_capacity ? cnt : minimum_capacity;
|
||||
const auto cap = static_cast<size_type>(size() / max_load_factor());
|
||||
value = value > cap ? value : cap;
|
||||
|
||||
if(const auto sz = next_power_of_two(value); sz != bucket_count()) {
|
||||
sparse.first().resize(sz);
|
||||
std::fill(sparse.first().begin(), sparse.first().end(), std::numeric_limits<size_type>::max());
|
||||
|
||||
for(auto &&elem: sparse.first()) {
|
||||
elem = (std::numeric_limits<size_type>::max)();
|
||||
}
|
||||
|
||||
for(size_type pos{}, last = size(); pos < last; ++pos) {
|
||||
const auto index = bucket(packed.first()[pos].element);
|
||||
packed.first()[pos].next = std::exchange(sparse.first()[index], pos);
|
||||
const auto index = value_to_bucket(packed.first()[pos].second);
|
||||
packed.first()[pos].first = std::exchange(sparse.first()[index], pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -815,11 +891,11 @@ public:
|
||||
/**
|
||||
* @brief Reserves space for at least the specified number of elements and
|
||||
* regenerates the hash table.
|
||||
* @param count New number of elements.
|
||||
* @param cnt New number of elements.
|
||||
*/
|
||||
void reserve(const size_type count) {
|
||||
packed.first().reserve(count);
|
||||
rehash(static_cast<size_type>(std::ceil(count / max_load_factor())));
|
||||
void reserve(const size_type cnt) {
|
||||
packed.first().reserve(cnt);
|
||||
rehash(static_cast<size_type>(std::ceil(cnt / max_load_factor())));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3,22 +3,24 @@
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
namespace entt {
|
||||
|
||||
template<
|
||||
typename Key, typename Type,
|
||||
typename Key,
|
||||
typename Type,
|
||||
typename = std::hash<Key>,
|
||||
typename = std::equal_to<Key>,
|
||||
typename = std::allocator<std::pair<const Key, Type>>>
|
||||
class dense_hash_map;
|
||||
class dense_map;
|
||||
|
||||
template<
|
||||
typename Type,
|
||||
typename = std::hash<Type>,
|
||||
typename = std::equal_to<Type>,
|
||||
typename = std::allocator<Type>>
|
||||
class dense_hash_set;
|
||||
class dense_set;
|
||||
|
||||
} // 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,6 +13,32 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
enum class any_operation : std::uint8_t {
|
||||
copy,
|
||||
move,
|
||||
transfer,
|
||||
assign,
|
||||
destroy,
|
||||
compare,
|
||||
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
|
||||
};
|
||||
|
||||
/**
|
||||
* @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.
|
||||
@@ -20,82 +46,70 @@ namespace entt {
|
||||
*/
|
||||
template<std::size_t Len, std::size_t Align>
|
||||
class basic_any {
|
||||
enum class operation : std::uint8_t {
|
||||
copy,
|
||||
move,
|
||||
transfer,
|
||||
assign,
|
||||
destroy,
|
||||
compare,
|
||||
get
|
||||
};
|
||||
|
||||
enum class policy : std::uint8_t {
|
||||
owner,
|
||||
ref,
|
||||
cref
|
||||
};
|
||||
|
||||
using storage_type = std::aligned_storage_t<Len + !Len, Align>;
|
||||
using operation = internal::any_operation;
|
||||
using vtable_type = const void *(const operation, const basic_any &, const void *);
|
||||
|
||||
template<typename Type>
|
||||
static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v<Type>;
|
||||
struct storage_type {
|
||||
alignas(Align) std::byte data[Len + !Len];
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
static const void *basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &value, [[maybe_unused]] const void *other) {
|
||||
static_assert(!std::is_same_v<Type, void> && std::is_same_v<std::remove_reference_t<std::remove_const_t<Type>>, Type>, "Invalid type");
|
||||
const Type *instance = nullptr;
|
||||
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_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>) {
|
||||
instance = (value.mode == policy::owner) ? ENTT_LAUNDER(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 {
|
||||
instance = static_cast<const Type *>(value.instance);
|
||||
element = static_cast<const Type *>(value.instance);
|
||||
}
|
||||
|
||||
switch(op) {
|
||||
case operation::copy:
|
||||
if constexpr(std::is_copy_constructible_v<Type>) {
|
||||
static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*instance);
|
||||
static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*element);
|
||||
}
|
||||
break;
|
||||
case operation::move:
|
||||
if constexpr(in_situ<Type>) {
|
||||
if(value.mode == policy::owner) {
|
||||
return new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(instance))};
|
||||
if(value.mode == any_policy::owner) {
|
||||
return new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(element))};
|
||||
}
|
||||
}
|
||||
|
||||
return (static_cast<basic_any *>(const_cast<void *>(other))->instance = std::exchange(const_cast<basic_any &>(value).instance, nullptr));
|
||||
case operation::transfer:
|
||||
if constexpr(std::is_move_assignable_v<Type>) {
|
||||
*const_cast<Type *>(instance) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
|
||||
*const_cast<Type *>(element) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
|
||||
return other;
|
||||
}
|
||||
[[fallthrough]];
|
||||
case operation::assign:
|
||||
if constexpr(std::is_copy_assignable_v<Type>) {
|
||||
*const_cast<Type *>(instance) = *static_cast<const Type *>(other);
|
||||
*const_cast<Type *>(element) = *static_cast<const Type *>(other);
|
||||
return other;
|
||||
}
|
||||
break;
|
||||
case operation::destroy:
|
||||
if constexpr(in_situ<Type>) {
|
||||
instance->~Type();
|
||||
element->~Type();
|
||||
} else if constexpr(std::is_array_v<Type>) {
|
||||
delete[] instance;
|
||||
delete[] element;
|
||||
} else {
|
||||
delete instance;
|
||||
delete element;
|
||||
}
|
||||
break;
|
||||
case operation::compare:
|
||||
if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) {
|
||||
return *static_cast<const Type *>(instance) == *static_cast<const Type *>(other) ? other : nullptr;
|
||||
return *element == *static_cast<const Type *>(other) ? other : nullptr;
|
||||
} else {
|
||||
return (instance == other) ? other : nullptr;
|
||||
return (element == other) ? other : nullptr;
|
||||
}
|
||||
case operation::get:
|
||||
return instance;
|
||||
return element;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
@@ -103,31 +117,32 @@ class basic_any {
|
||||
|
||||
template<typename Type, typename... Args>
|
||||
void initialize([[maybe_unused]] Args &&...args) {
|
||||
info = &type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
|
||||
|
||||
if constexpr(!std::is_void_v<Type>) {
|
||||
vtable = basic_vtable<std::remove_const_t<std::remove_reference_t<Type>>>;
|
||||
info = &type_id<Type>();
|
||||
vtable = basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>;
|
||||
|
||||
if constexpr(std::is_lvalue_reference_v<Type>) {
|
||||
static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v<Args> && ...), "Invalid arguments");
|
||||
mode = std::is_const_v<std::remove_reference_t<Type>> ? policy::cref : policy::ref;
|
||||
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<Type>) {
|
||||
if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) {
|
||||
new(&storage) Type{std::forward<Args>(args)...};
|
||||
} else if constexpr(in_situ<std::remove_cv_t<std::remove_reference_t<Type>>>) {
|
||||
if constexpr(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) Type(std::forward<Args>(args)...);
|
||||
new(&storage) std::remove_cv_t<std::remove_reference_t<Type>>(std::forward<Args>(args)...);
|
||||
}
|
||||
} else {
|
||||
if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) {
|
||||
instance = new Type{std::forward<Args>(args)...};
|
||||
if constexpr(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 Type(std::forward<Args>(args)...);
|
||||
instance = new std::remove_cv_t<std::remove_reference_t<Type>>(std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
basic_any(const basic_any &other, const policy pol) ENTT_NOEXCEPT
|
||||
basic_any(const basic_any &other, const any_policy pol) noexcept
|
||||
: instance{other.data()},
|
||||
info{other.info},
|
||||
vtable{other.vtable},
|
||||
@@ -140,11 +155,8 @@ public:
|
||||
static constexpr auto alignment = Align;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
basic_any() ENTT_NOEXCEPT
|
||||
: instance{},
|
||||
info{&type_id<void>()},
|
||||
vtable{},
|
||||
mode{policy::owner} {}
|
||||
constexpr basic_any() noexcept
|
||||
: basic_any{std::in_place_type<void>} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a wrapper by directly initializing the new object.
|
||||
@@ -154,7 +166,10 @@ public:
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
explicit basic_any(std::in_place_type_t<Type>, Args &&...args)
|
||||
: basic_any{} {
|
||||
: instance{},
|
||||
info{},
|
||||
vtable{},
|
||||
mode{any_policy::owner} {
|
||||
initialize<Type>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -165,9 +180,7 @@ public:
|
||||
*/
|
||||
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
|
||||
basic_any(Type &&value)
|
||||
: basic_any{} {
|
||||
initialize<std::decay_t<Type>>(std::forward<Type>(value));
|
||||
}
|
||||
: basic_any{std::in_place_type<std::decay_t<Type>>, std::forward<Type>(value)} {}
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
@@ -184,7 +197,7 @@ public:
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
basic_any(basic_any &&other) ENTT_NOEXCEPT
|
||||
basic_any(basic_any &&other) noexcept
|
||||
: instance{},
|
||||
info{other.info},
|
||||
vtable{other.vtable},
|
||||
@@ -196,7 +209,7 @@ public:
|
||||
|
||||
/*! @brief Frees the internal storage, whatever it means. */
|
||||
~basic_any() {
|
||||
if(vtable && mode == policy::owner) {
|
||||
if(vtable && (mode == any_policy::owner)) {
|
||||
vtable(operation::destroy, *this, nullptr);
|
||||
}
|
||||
}
|
||||
@@ -221,7 +234,7 @@ public:
|
||||
* @param other The instance to move from.
|
||||
* @return This any object.
|
||||
*/
|
||||
basic_any &operator=(basic_any &&other) ENTT_NOEXCEPT {
|
||||
basic_any &operator=(basic_any &&other) noexcept {
|
||||
reset();
|
||||
|
||||
if(other.vtable) {
|
||||
@@ -251,7 +264,7 @@ public:
|
||||
* @brief Returns the object type if any, `type_id<void>()` otherwise.
|
||||
* @return The object type if any, `type_id<void>()` otherwise.
|
||||
*/
|
||||
[[nodiscard]] const type_info &type() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] const type_info &type() const noexcept {
|
||||
return *info;
|
||||
}
|
||||
|
||||
@@ -259,7 +272,7 @@ public:
|
||||
* @brief Returns an opaque pointer to the contained instance.
|
||||
* @return An opaque pointer the contained instance, if any.
|
||||
*/
|
||||
[[nodiscard]] const void *data() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] const void *data() const noexcept {
|
||||
return vtable ? vtable(operation::get, *this, nullptr) : nullptr;
|
||||
}
|
||||
|
||||
@@ -268,7 +281,7 @@ public:
|
||||
* @param req Expected type.
|
||||
* @return An opaque pointer the contained instance, if any.
|
||||
*/
|
||||
[[nodiscard]] const void *data(const type_info &req) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] const void *data(const type_info &req) const noexcept {
|
||||
return *info == req ? data() : nullptr;
|
||||
}
|
||||
|
||||
@@ -276,8 +289,8 @@ public:
|
||||
* @brief Returns an opaque pointer to the contained instance.
|
||||
* @return An opaque pointer the contained instance, if any.
|
||||
*/
|
||||
[[nodiscard]] void *data() ENTT_NOEXCEPT {
|
||||
return (!vtable || mode == policy::cref) ? nullptr : const_cast<void *>(vtable(operation::get, *this, nullptr));
|
||||
[[nodiscard]] void *data() noexcept {
|
||||
return mode == any_policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -285,8 +298,8 @@ public:
|
||||
* @param req Expected type.
|
||||
* @return An opaque pointer the contained instance, if any.
|
||||
*/
|
||||
[[nodiscard]] void *data(const type_info &req) ENTT_NOEXCEPT {
|
||||
return *info == req ? data() : nullptr;
|
||||
[[nodiscard]] void *data(const type_info &req) noexcept {
|
||||
return mode == any_policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data(req));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -302,25 +315,21 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy assigns a value to the contained object without replacing it.
|
||||
* @brief Assigns a value to the contained object without replacing it.
|
||||
* @param other The value to assign to the contained object.
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
bool assign(const any &other) {
|
||||
if(vtable && mode != policy::cref && *info == *other.info) {
|
||||
bool assign(const basic_any &other) {
|
||||
if(vtable && mode != any_policy::cref && *info == *other.info) {
|
||||
return (vtable(operation::assign, *this, other.data()) != nullptr);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move assigns a value to the contained object without replacing it.
|
||||
* @param other The value to assign to the contained object.
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
bool assign(any &&other) {
|
||||
if(vtable && mode != policy::cref && *info == *other.info) {
|
||||
/*! @copydoc assign */
|
||||
bool assign(basic_any &&other) {
|
||||
if(vtable && mode != any_policy::cref && *info == *other.info) {
|
||||
if(auto *val = other.data(); val) {
|
||||
return (vtable(operation::transfer, *this, val) != nullptr);
|
||||
} else {
|
||||
@@ -333,20 +342,22 @@ public:
|
||||
|
||||
/*! @brief Destroys contained object */
|
||||
void reset() {
|
||||
if(vtable && mode == policy::owner) {
|
||||
if(vtable && (mode == any_policy::owner)) {
|
||||
vtable(operation::destroy, *this, nullptr);
|
||||
}
|
||||
|
||||
// unnecessary but it helps to detect nasty bugs
|
||||
ENTT_ASSERT((instance = nullptr) == nullptr, "");
|
||||
info = &type_id<void>();
|
||||
vtable = nullptr;
|
||||
mode = policy::owner;
|
||||
mode = any_policy::owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns false if a wrapper is empty, true otherwise.
|
||||
* @return False if the wrapper is empty, true otherwise.
|
||||
*/
|
||||
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] explicit operator bool() const noexcept {
|
||||
return vtable != nullptr;
|
||||
}
|
||||
|
||||
@@ -355,7 +366,7 @@ public:
|
||||
* @param other Wrapper with which to compare.
|
||||
* @return False if the two objects differ in their content, true otherwise.
|
||||
*/
|
||||
bool operator==(const basic_any &other) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool operator==(const basic_any &other) const noexcept {
|
||||
if(vtable && *info == *other.info) {
|
||||
return (vtable(operation::compare, *this, other.data()) != nullptr);
|
||||
}
|
||||
@@ -363,25 +374,42 @@ public:
|
||||
return (!vtable && !other.vtable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if two wrappers differ in their content.
|
||||
* @param other Wrapper with which to compare.
|
||||
* @return True if the two objects differ in their content, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool operator!=(const basic_any &other) const noexcept {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Aliasing constructor.
|
||||
* @return A wrapper that shares a reference to an unmanaged object.
|
||||
*/
|
||||
[[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT {
|
||||
return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)};
|
||||
[[nodiscard]] basic_any as_ref() noexcept {
|
||||
return basic_any{*this, (mode == any_policy::cref ? any_policy::cref : any_policy::ref)};
|
||||
}
|
||||
|
||||
/*! @copydoc as_ref */
|
||||
[[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT {
|
||||
return basic_any{*this, policy::cref};
|
||||
[[nodiscard]] basic_any as_ref() const noexcept {
|
||||
return basic_any{*this, 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 ENTT_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:
|
||||
@@ -391,22 +419,9 @@ private:
|
||||
};
|
||||
const type_info *info;
|
||||
vtable_type *vtable;
|
||||
policy mode;
|
||||
any_policy mode;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Checks if two wrappers differ in their content.
|
||||
* @tparam Len Size of the storage reserved for the small buffer optimization.
|
||||
* @tparam Align Alignment requirement.
|
||||
* @param lhs A wrapper, either empty or not.
|
||||
* @param rhs A wrapper, either empty or not.
|
||||
* @return True if the two wrappers differ in their content, false otherwise.
|
||||
*/
|
||||
template<std::size_t Len, std::size_t Align>
|
||||
[[nodiscard]] inline bool operator!=(const basic_any<Len, Align> &lhs, const basic_any<Len, Align> &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Performs type-safe access to the contained object.
|
||||
* @tparam Type Type to which conversion is required.
|
||||
@@ -416,7 +431,7 @@ template<std::size_t Len, std::size_t Align>
|
||||
* @return The element converted to the requested type.
|
||||
*/
|
||||
template<typename Type, std::size_t Len, std::size_t Align>
|
||||
Type any_cast(const basic_any<Len, Align> &data) ENTT_NOEXCEPT {
|
||||
[[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);
|
||||
@@ -424,7 +439,7 @@ Type any_cast(const basic_any<Len, Align> &data) ENTT_NOEXCEPT {
|
||||
|
||||
/*! @copydoc any_cast */
|
||||
template<typename Type, std::size_t Len, std::size_t Align>
|
||||
Type any_cast(basic_any<Len, Align> &data) ENTT_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");
|
||||
@@ -433,8 +448,8 @@ Type any_cast(basic_any<Len, Align> &data) ENTT_NOEXCEPT {
|
||||
|
||||
/*! @copydoc any_cast */
|
||||
template<typename Type, std::size_t Len, std::size_t Align>
|
||||
Type any_cast(basic_any<Len, Align> &&data) ENTT_NOEXCEPT {
|
||||
if constexpr(std::is_copy_constructible_v<std::remove_const_t<std::remove_reference_t<Type>>>) {
|
||||
[[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));
|
||||
} else {
|
||||
@@ -449,17 +464,21 @@ Type any_cast(basic_any<Len, Align> &&data) ENTT_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) ENTT_NOEXCEPT {
|
||||
const auto &info = type_id<std::remove_const_t<std::remove_reference_t<Type>>>();
|
||||
[[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) ENTT_NOEXCEPT {
|
||||
const auto &info = type_id<std::remove_const_t<std::remove_reference_t<Type>>>();
|
||||
// last attempt to make wrappers for const references return their values
|
||||
return static_cast<Type *>(static_cast<constness_as_t<basic_any<Len, Align>, Type> *>(data)->data(info));
|
||||
[[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));
|
||||
} else {
|
||||
const auto &info = type_id<std::remove_cv_t<Type>>();
|
||||
return static_cast<Type *>(data->data(info));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -472,7 +491,7 @@ Type *any_cast(basic_any<Len, Align> *data) ENTT_NOEXCEPT {
|
||||
* @return A properly initialized wrapper for an object of the given type.
|
||||
*/
|
||||
template<typename Type, std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename... Args>
|
||||
basic_any<Len, Align> make_any(Args &&...args) {
|
||||
[[nodiscard]] basic_any<Len, Align> make_any(Args &&...args) {
|
||||
return basic_any<Len, Align>{std::in_place_type<Type>, std::forward<Args>(args)...};
|
||||
}
|
||||
|
||||
@@ -485,8 +504,8 @@ 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) {
|
||||
return basic_any<Len, Align>{std::in_place_type<std::conditional_t<std::is_rvalue_reference_v<Type>, std::decay_t<Type>, Type>>, std::forward<Type>(value)};
|
||||
[[nodiscard]] basic_any<Len, Align> forward_as_any(Type &&value) {
|
||||
return basic_any<Len, Align>{std::in_place_type<Type &&>, std::forward<Type>(value)};
|
||||
}
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -5,16 +5,11 @@
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "type_traits.hpp"
|
||||
|
||||
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>
|
||||
@@ -23,22 +18,22 @@ struct compressed_pair_element {
|
||||
using const_reference = const Type &;
|
||||
|
||||
template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>>
|
||||
compressed_pair_element()
|
||||
constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<Type>)
|
||||
: value{} {}
|
||||
|
||||
template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_reference_t<Args>>, compressed_pair_element>>>
|
||||
compressed_pair_element(Args &&args)
|
||||
: value{std::forward<Args>(args)} {}
|
||||
template<typename Arg, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Arg>>, compressed_pair_element>>>
|
||||
constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v<Type, Arg>)
|
||||
: value{std::forward<Arg>(arg)} {}
|
||||
|
||||
template<typename... Args, std::size_t... Index>
|
||||
compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>)
|
||||
: value{std::get<Index>(args)...} {}
|
||||
constexpr compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) noexcept(std::is_nothrow_constructible_v<Type, Args...>)
|
||||
: value{std::forward<Args>(std::get<Index>(args))...} {}
|
||||
|
||||
[[nodiscard]] reference get() ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr reference get() noexcept {
|
||||
return value;
|
||||
}
|
||||
|
||||
[[nodiscard]] const_reference get() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr const_reference get() const noexcept {
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -53,32 +48,28 @@ struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Ty
|
||||
using base_type = Type;
|
||||
|
||||
template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>>
|
||||
compressed_pair_element()
|
||||
constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<base_type>)
|
||||
: base_type{} {}
|
||||
|
||||
template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_reference_t<Args>>, compressed_pair_element>>>
|
||||
compressed_pair_element(Args &&args)
|
||||
: base_type{std::forward<Args>(args)} {}
|
||||
template<typename Arg, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Arg>>, compressed_pair_element>>>
|
||||
constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v<base_type, Arg>)
|
||||
: base_type{std::forward<Arg>(arg)} {}
|
||||
|
||||
template<typename... Args, std::size_t... Index>
|
||||
compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>)
|
||||
: base_type{std::get<Index>(args)...} {}
|
||||
constexpr compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) noexcept(std::is_nothrow_constructible_v<base_type, Args...>)
|
||||
: base_type{std::forward<Args>(std::get<Index>(args))...} {}
|
||||
|
||||
[[nodiscard]] reference get() ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr reference get() noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] const_reference get() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr const_reference get() const noexcept {
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief A compressed pair.
|
||||
@@ -111,7 +102,7 @@ public:
|
||||
* @tparam Dummy Dummy template parameter used for internal purposes.
|
||||
*/
|
||||
template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>>
|
||||
constexpr compressed_pair()
|
||||
constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v<first_base> &&std::is_nothrow_default_constructible_v<second_base>)
|
||||
: first_base{},
|
||||
second_base{} {}
|
||||
|
||||
@@ -119,13 +110,13 @@ public:
|
||||
* @brief Copy constructor.
|
||||
* @param other The instance to copy from.
|
||||
*/
|
||||
constexpr compressed_pair(const compressed_pair &other) = default;
|
||||
constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v<first_base> &&std::is_nothrow_copy_constructible_v<second_base>) = default;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
constexpr compressed_pair(compressed_pair &&other) ENTT_NOEXCEPT = default;
|
||||
constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v<first_base> &&std::is_nothrow_move_constructible_v<second_base>) = default;
|
||||
|
||||
/**
|
||||
* @brief Constructs a pair from its values.
|
||||
@@ -135,7 +126,7 @@ public:
|
||||
* @param other Value to use to initialize the second element.
|
||||
*/
|
||||
template<typename Arg, typename Other>
|
||||
constexpr compressed_pair(Arg &&arg, Other &&other)
|
||||
constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v<first_base, Arg> &&std::is_nothrow_constructible_v<second_base, Other>)
|
||||
: first_base{std::forward<Arg>(arg)},
|
||||
second_base{std::forward<Other>(other)} {}
|
||||
|
||||
@@ -147,7 +138,7 @@ public:
|
||||
* @param other Arguments to use to initialize the second element.
|
||||
*/
|
||||
template<typename... Args, typename... Other>
|
||||
constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other)
|
||||
constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other) noexcept(std::is_nothrow_constructible_v<first_base, Args...> &&std::is_nothrow_constructible_v<second_base, Other...>)
|
||||
: first_base{std::move(args), std::index_sequence_for<Args...>{}},
|
||||
second_base{std::move(other), std::index_sequence_for<Other...>{}} {}
|
||||
|
||||
@@ -156,25 +147,25 @@ public:
|
||||
* @param other The instance to copy from.
|
||||
* @return This compressed pair object.
|
||||
*/
|
||||
constexpr compressed_pair &operator=(const compressed_pair &other) = default;
|
||||
constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v<first_base> &&std::is_nothrow_copy_assignable_v<second_base>) = default;
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
* @param other The instance to move from.
|
||||
* @return This compressed pair object.
|
||||
*/
|
||||
constexpr compressed_pair &operator=(compressed_pair &&other) ENTT_NOEXCEPT = default;
|
||||
constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v<first_base> &&std::is_nothrow_move_assignable_v<second_base>) = default;
|
||||
|
||||
/**
|
||||
* @brief Returns the first element that a pair stores.
|
||||
* @return The first element that a pair stores.
|
||||
*/
|
||||
[[nodiscard]] first_type &first() ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr first_type &first() noexcept {
|
||||
return static_cast<first_base &>(*this).get();
|
||||
}
|
||||
|
||||
/*! @copydoc first */
|
||||
[[nodiscard]] const first_type &first() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr const first_type &first() const noexcept {
|
||||
return static_cast<const first_base &>(*this).get();
|
||||
}
|
||||
|
||||
@@ -182,12 +173,12 @@ public:
|
||||
* @brief Returns the second element that a pair stores.
|
||||
* @return The second element that a pair stores.
|
||||
*/
|
||||
[[nodiscard]] second_type &second() ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr second_type &second() noexcept {
|
||||
return static_cast<second_base &>(*this).get();
|
||||
}
|
||||
|
||||
/*! @copydoc second */
|
||||
[[nodiscard]] const second_type &second() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr const second_type &second() const noexcept {
|
||||
return static_cast<const second_base &>(*this).get();
|
||||
}
|
||||
|
||||
@@ -195,7 +186,7 @@ public:
|
||||
* @brief Swaps two compressed pair objects.
|
||||
* @param other The compressed pair to swap with.
|
||||
*/
|
||||
void swap(compressed_pair &other) {
|
||||
constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v<first_type> &&std::is_nothrow_swappable_v<second_type>) {
|
||||
using std::swap;
|
||||
swap(first(), other.first());
|
||||
swap(second(), other.second());
|
||||
@@ -208,7 +199,7 @@ public:
|
||||
* reference to the second element if `Index` is 1.
|
||||
*/
|
||||
template<std::size_t Index>
|
||||
decltype(auto) get() ENTT_NOEXCEPT {
|
||||
constexpr decltype(auto) get() noexcept {
|
||||
if constexpr(Index == 0u) {
|
||||
return first();
|
||||
} else {
|
||||
@@ -219,7 +210,7 @@ public:
|
||||
|
||||
/*! @copydoc get */
|
||||
template<std::size_t Index>
|
||||
decltype(auto) get() const ENTT_NOEXCEPT {
|
||||
constexpr decltype(auto) get() const noexcept {
|
||||
if constexpr(Index == 0u) {
|
||||
return first();
|
||||
} else {
|
||||
@@ -245,7 +236,7 @@ compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::d
|
||||
* @param rhs A valid compressed pair object.
|
||||
*/
|
||||
template<typename First, typename Second>
|
||||
inline void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) {
|
||||
inline constexpr void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
@@ -255,9 +246,20 @@ inline void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Sec
|
||||
#if !defined __clang_major__ || __clang_major__ > 6
|
||||
namespace std {
|
||||
|
||||
/**
|
||||
* @brief `std::tuple_size` specialization for `compressed_pair`s.
|
||||
* @tparam First The type of the first element that the pair stores.
|
||||
* @tparam Second The type of the second element that the pair stores.
|
||||
*/
|
||||
template<typename First, typename Second>
|
||||
struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_t, 2u> {};
|
||||
|
||||
/**
|
||||
* @brief `std::tuple_element` specialization for `compressed_pair`s.
|
||||
* @tparam Index The index of the type to return.
|
||||
* @tparam First The type of the first element that the pair stores.
|
||||
* @tparam Second The type of the second element that the pair stores.
|
||||
*/
|
||||
template<size_t Index, typename First, typename Second>
|
||||
struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<Index == 0u, First, Second> {
|
||||
static_assert(Index < 2u, "Index out of bounds");
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#ifndef ENTT_CORE_FLAG_HPP
|
||||
#define ENTT_CORE_FLAG_HPP
|
||||
#ifndef ENTT_CORE_ENUM_HPP
|
||||
#define ENTT_CORE_ENUM_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
|
||||
namespace entt {
|
||||
|
||||
@@ -15,7 +14,7 @@ struct enum_as_bitmask: std::false_type {};
|
||||
|
||||
/*! @copydoc enum_as_bitmask */
|
||||
template<typename Type>
|
||||
struct enum_as_bitmask<Type, std::void_t<decltype(Type::_entt_enum_as_bitmask)>>: std::true_type {};
|
||||
struct enum_as_bitmask<Type, std::void_t<decltype(Type::_entt_enum_as_bitmask)>>: std::is_enum<Type> {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
@@ -35,23 +34,23 @@ inline constexpr bool enum_as_bitmask_v = enum_as_bitmask<Type>::value;
|
||||
* two values provided.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr std::enable_if_t<std::is_enum_v<Type> && entt::enum_as_bitmask_v<Type>, Type>
|
||||
operator|(const Type lhs, const Type rhs) ENTT_NOEXCEPT {
|
||||
return Type{static_cast<std::underlying_type_t<Type>>(lhs) | static_cast<std::underlying_type_t<Type>>(rhs)};
|
||||
[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
|
||||
operator|(const Type lhs, const Type rhs) noexcept {
|
||||
return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) | static_cast<std::underlying_type_t<Type>>(rhs));
|
||||
}
|
||||
|
||||
/*! @copydoc operator| */
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr std::enable_if_t<std::is_enum_v<Type> && entt::enum_as_bitmask_v<Type>, Type>
|
||||
operator&(const Type lhs, const Type rhs) ENTT_NOEXCEPT {
|
||||
return Type{static_cast<std::underlying_type_t<Type>>(lhs) & static_cast<std::underlying_type_t<Type>>(rhs)};
|
||||
[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
|
||||
operator&(const Type lhs, const Type rhs) noexcept {
|
||||
return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) & static_cast<std::underlying_type_t<Type>>(rhs));
|
||||
}
|
||||
|
||||
/*! @copydoc operator| */
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr std::enable_if_t<std::is_enum_v<Type> && entt::enum_as_bitmask_v<Type>, Type>
|
||||
operator^(const Type lhs, const Type rhs) ENTT_NOEXCEPT {
|
||||
return Type{static_cast<std::underlying_type_t<Type>>(lhs) ^ static_cast<std::underlying_type_t<Type>>(rhs)};
|
||||
[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
|
||||
operator^(const Type lhs, const Type rhs) noexcept {
|
||||
return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) ^ static_cast<std::underlying_type_t<Type>>(rhs));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,51 +61,37 @@ operator^(const Type lhs, const Type rhs) ENTT_NOEXCEPT {
|
||||
* value provided.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr std::enable_if_t<std::is_enum_v<Type> && entt::enum_as_bitmask_v<Type>, Type>
|
||||
operator~(const Type value) ENTT_NOEXCEPT {
|
||||
return Type{~static_cast<std::underlying_type_t<Type>>(value)};
|
||||
[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
|
||||
operator~(const Type value) noexcept {
|
||||
return static_cast<Type>(~static_cast<std::underlying_type_t<Type>>(value));
|
||||
}
|
||||
|
||||
/*! @copydoc operator~ */
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr std::enable_if_t<std::is_enum_v<Type> && entt::enum_as_bitmask_v<Type>, bool>
|
||||
operator!(const Type value) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, bool>
|
||||
operator!(const Type value) noexcept {
|
||||
return !static_cast<std::underlying_type_t<Type>>(value);
|
||||
}
|
||||
|
||||
/*! @copydoc operator| */
|
||||
template<typename Type>
|
||||
constexpr std::enable_if_t<std::is_enum_v<Type> && entt::enum_as_bitmask_v<Type>, Type &>
|
||||
operator|=(Type &lhs, const Type rhs) ENTT_NOEXCEPT {
|
||||
constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &>
|
||||
operator|=(Type &lhs, const Type rhs) noexcept {
|
||||
return (lhs = (lhs | rhs));
|
||||
}
|
||||
|
||||
/*! @copydoc operator| */
|
||||
template<typename Type>
|
||||
constexpr std::enable_if_t<std::is_enum_v<Type> && entt::enum_as_bitmask_v<Type>, Type &>
|
||||
operator&=(Type &lhs, const Type rhs) ENTT_NOEXCEPT {
|
||||
constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &>
|
||||
operator&=(Type &lhs, const Type rhs) noexcept {
|
||||
return (lhs = (lhs & rhs));
|
||||
}
|
||||
|
||||
/*! @copydoc operator| */
|
||||
template<typename Type>
|
||||
constexpr std::enable_if_t<std::is_enum_v<Type> && entt::enum_as_bitmask_v<Type>, Type &>
|
||||
operator^=(Type &lhs, const Type rhs) ENTT_NOEXCEPT {
|
||||
constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &>
|
||||
operator^=(Type &lhs, const Type rhs) noexcept {
|
||||
return (lhs = (lhs ^ rhs));
|
||||
}
|
||||
|
||||
/*! @copydoc operator| */
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr std::enable_if_t<std::is_enum_v<Type> && entt::enum_as_bitmask_v<Type>, Type>
|
||||
operator==(const Type lhs, const Type rhs) ENTT_NOEXCEPT {
|
||||
return (static_cast<std::underlying_type_t<Type>>(lhs) == static_cast<std::underlying_type_t<Type>>(rhs));
|
||||
}
|
||||
|
||||
/*! @copydoc operator| */
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr std::enable_if_t<std::is_enum_v<Type> && entt::enum_as_bitmask_v<Type>, Type>
|
||||
operator!=(const Type lhs, const Type rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -19,12 +19,12 @@ class family {
|
||||
|
||||
public:
|
||||
/*! @brief Unsigned integer type. */
|
||||
using family_type = id_type;
|
||||
using value_type = id_type;
|
||||
|
||||
/*! @brief Statically generated unique identifier for the given type. */
|
||||
template<typename... Type>
|
||||
// at the time I'm writing, clang crashes during compilation if auto is used instead of family_type
|
||||
inline static const family_type type = identifier++;
|
||||
inline static const value_type value = identifier++;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#ifndef ENTT_CORE_FWD_HPP
|
||||
#define ENTT_CORE_FWD_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include <cstddef>
|
||||
#include "../config/config.h"
|
||||
|
||||
namespace entt {
|
||||
|
||||
template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)>
|
||||
template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(double[2])>
|
||||
class basic_any;
|
||||
|
||||
/*! @brief Alias declaration for type identifiers. */
|
||||
|
||||
@@ -3,16 +3,11 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#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>
|
||||
@@ -32,18 +27,25 @@ struct fnv1a_traits<std::uint64_t> {
|
||||
static constexpr std::uint64_t prime = 1099511628211ull;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
template<typename Char>
|
||||
struct basic_hashed_string {
|
||||
using value_type = Char;
|
||||
using size_type = std::size_t;
|
||||
using hash_type = id_type;
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
const value_type *repr;
|
||||
size_type length;
|
||||
hash_type hash;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Zero overhead unique identifier.
|
||||
*
|
||||
* A hashed string is a compile-time tool that allows users to use
|
||||
* human-readable identifers in the codebase while using their numeric
|
||||
* human-readable identifiers in the codebase while using their numeric
|
||||
* counterparts at runtime.<br/>
|
||||
* Because of that, a hashed string can also be used in constant expressions if
|
||||
* required.
|
||||
@@ -55,62 +57,67 @@ struct fnv1a_traits<std::uint64_t> {
|
||||
* @tparam Char Character type.
|
||||
*/
|
||||
template<typename Char>
|
||||
class basic_hashed_string {
|
||||
using hs_traits = internal::fnv1a_traits<id_type>;
|
||||
class basic_hashed_string: internal::basic_hashed_string<Char> {
|
||||
using base_type = internal::basic_hashed_string<Char>;
|
||||
using traits_type = internal::fnv1a_traits<id_type>;
|
||||
|
||||
struct const_wrapper {
|
||||
// non-explicit constructor on purpose
|
||||
constexpr const_wrapper(const Char *curr) ENTT_NOEXCEPT: str{curr} {}
|
||||
const Char *str;
|
||||
constexpr const_wrapper(const Char *str) noexcept
|
||||
: repr{str} {}
|
||||
|
||||
const Char *repr;
|
||||
};
|
||||
|
||||
// Fowler–Noll–Vo hash function v. 1a - the good
|
||||
[[nodiscard]] static constexpr id_type helper(const Char *curr) ENTT_NOEXCEPT {
|
||||
auto value = hs_traits::offset;
|
||||
[[nodiscard]] static constexpr auto helper(const Char *str) noexcept {
|
||||
base_type base{str, 0u, traits_type::offset};
|
||||
|
||||
while(*curr != 0) {
|
||||
value = (value ^ static_cast<hs_traits::type>(*(curr++))) * hs_traits::prime;
|
||||
for(; str[base.length]; ++base.length) {
|
||||
base.hash = (base.hash ^ static_cast<traits_type::type>(str[base.length])) * traits_type::prime;
|
||||
}
|
||||
|
||||
return value;
|
||||
return base;
|
||||
}
|
||||
|
||||
// Fowler–Noll–Vo hash function v. 1a - the good
|
||||
[[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept {
|
||||
base_type base{str, len, traits_type::offset};
|
||||
|
||||
for(size_type pos{}; pos < len; ++pos) {
|
||||
base.hash = (base.hash ^ static_cast<traits_type::type>(str[pos])) * traits_type::prime;
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Character type. */
|
||||
using value_type = Char;
|
||||
using value_type = typename base_type::value_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using hash_type = id_type;
|
||||
using size_type = typename base_type::size_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using hash_type = typename base_type::hash_type;
|
||||
|
||||
/**
|
||||
* @brief Returns directly the numeric representation of a string view.
|
||||
* @param str Human-readable identifer.
|
||||
* @param size Length of the string to hash.
|
||||
* @param str Human-readable identifier.
|
||||
* @param len Length of the string to hash.
|
||||
* @return The numeric representation of the string.
|
||||
*/
|
||||
[[nodiscard]] static constexpr hash_type value(const value_type *str, std::size_t size) ENTT_NOEXCEPT {
|
||||
id_type partial{hs_traits::offset};
|
||||
while(size--) { partial = (partial ^ (str++)[0]) * hs_traits::prime; }
|
||||
return partial;
|
||||
[[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept {
|
||||
return basic_hashed_string{str, len};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns directly the numeric representation of a string.
|
||||
*
|
||||
* Forcing template resolution avoids implicit conversions. An
|
||||
* human-readable identifier can be anything but a plain, old bunch of
|
||||
* characters.<br/>
|
||||
* Example of use:
|
||||
* @code{.cpp}
|
||||
* const auto value = basic_hashed_string<char>::value("my.png");
|
||||
* @endcode
|
||||
*
|
||||
* @tparam N Number of characters of the identifier.
|
||||
* @param str Human-readable identifer.
|
||||
* @param str Human-readable identifier.
|
||||
* @return The numeric representation of the string.
|
||||
*/
|
||||
template<std::size_t N>
|
||||
[[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT {
|
||||
return helper(str);
|
||||
[[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept {
|
||||
return basic_hashed_string{str};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,33 +125,30 @@ public:
|
||||
* @param wrapper Helps achieving the purpose by relying on overloading.
|
||||
* @return The numeric representation of the string.
|
||||
*/
|
||||
[[nodiscard]] static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT {
|
||||
return helper(wrapper.str);
|
||||
[[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept {
|
||||
return basic_hashed_string{wrapper};
|
||||
}
|
||||
|
||||
/*! @brief Constructs an empty hashed string. */
|
||||
constexpr basic_hashed_string() ENTT_NOEXCEPT
|
||||
: str{nullptr},
|
||||
hash{} {}
|
||||
constexpr basic_hashed_string() noexcept
|
||||
: base_type{} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a hashed string from a string view.
|
||||
* @param str Human-readable identifier.
|
||||
* @param len Length of the string to hash.
|
||||
*/
|
||||
constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept
|
||||
: base_type{helper(str, len)} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a hashed string from an array of const characters.
|
||||
*
|
||||
* Forcing template resolution avoids implicit conversions. An
|
||||
* human-readable identifier can be anything but a plain, old bunch of
|
||||
* characters.<br/>
|
||||
* Example of use:
|
||||
* @code{.cpp}
|
||||
* basic_hashed_string<char> hs{"my.png"};
|
||||
* @endcode
|
||||
*
|
||||
* @tparam N Number of characters of the identifier.
|
||||
* @param curr Human-readable identifer.
|
||||
* @param str Human-readable identifier.
|
||||
*/
|
||||
template<std::size_t N>
|
||||
constexpr basic_hashed_string(const value_type (&curr)[N]) ENTT_NOEXCEPT
|
||||
: str{curr},
|
||||
hash{helper(curr)} {}
|
||||
constexpr basic_hashed_string(const value_type (&str)[N]) noexcept
|
||||
: base_type{helper(str)} {}
|
||||
|
||||
/**
|
||||
* @brief Explicit constructor on purpose to avoid constructing a hashed
|
||||
@@ -155,53 +159,61 @@ public:
|
||||
*
|
||||
* @param wrapper Helps achieving the purpose by relying on overloading.
|
||||
*/
|
||||
explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT
|
||||
: str{wrapper.str},
|
||||
hash{helper(wrapper.str)} {}
|
||||
explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept
|
||||
: base_type{helper(wrapper.repr)} {}
|
||||
|
||||
/**
|
||||
* @brief Returns the size a hashed string.
|
||||
* @return The size of the hashed string.
|
||||
*/
|
||||
[[nodiscard]] constexpr size_type size() const noexcept {
|
||||
return base_type::length; // NOLINT
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the human-readable representation of a hashed string.
|
||||
* @return The string used to initialize the instance.
|
||||
* @return The string used to initialize the hashed string.
|
||||
*/
|
||||
[[nodiscard]] constexpr const value_type *data() const ENTT_NOEXCEPT {
|
||||
return str;
|
||||
[[nodiscard]] constexpr const value_type *data() const noexcept {
|
||||
return base_type::repr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the numeric representation of a hashed string.
|
||||
* @return The numeric representation of the instance.
|
||||
* @return The numeric representation of the hashed string.
|
||||
*/
|
||||
[[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT {
|
||||
return hash;
|
||||
[[nodiscard]] constexpr hash_type value() const noexcept {
|
||||
return base_type::hash;
|
||||
}
|
||||
|
||||
/*! @copydoc data */
|
||||
[[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr operator const value_type *() const noexcept {
|
||||
return data();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the numeric representation of a hashed string.
|
||||
* @return The numeric representation of the instance.
|
||||
* @return The numeric representation of the hashed string.
|
||||
*/
|
||||
[[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr operator hash_type() const noexcept {
|
||||
return value();
|
||||
}
|
||||
|
||||
private:
|
||||
const value_type *str;
|
||||
hash_type hash;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
*
|
||||
* It allows to deduce the character type of the hashed string directly from a
|
||||
* human-readable identifer provided to the constructor.
|
||||
*
|
||||
* @tparam Char Character type.
|
||||
* @param str Human-readable identifier.
|
||||
* @param len Length of the string to hash.
|
||||
*/
|
||||
template<typename Char>
|
||||
basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string<Char>;
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
* @tparam Char Character type.
|
||||
* @tparam N Number of characters of the identifier.
|
||||
* @param str Human-readable identifer.
|
||||
* @param str Human-readable identifier.
|
||||
*/
|
||||
template<typename Char, std::size_t N>
|
||||
basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string<Char>;
|
||||
@@ -214,7 +226,7 @@ basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string<Char>;
|
||||
* @return True if the two hashed strings are identical, false otherwise.
|
||||
*/
|
||||
template<typename Char>
|
||||
[[nodiscard]] constexpr bool operator==(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr bool operator==(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept {
|
||||
return lhs.value() == rhs.value();
|
||||
}
|
||||
|
||||
@@ -226,7 +238,7 @@ template<typename Char>
|
||||
* @return True if the two hashed strings differ, false otherwise.
|
||||
*/
|
||||
template<typename Char>
|
||||
[[nodiscard]] constexpr bool operator!=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr bool operator!=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
@@ -238,7 +250,7 @@ template<typename Char>
|
||||
* @return True if the first element is less than the second, false otherwise.
|
||||
*/
|
||||
template<typename Char>
|
||||
[[nodiscard]] constexpr bool operator<(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr bool operator<(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept {
|
||||
return lhs.value() < rhs.value();
|
||||
}
|
||||
|
||||
@@ -251,7 +263,7 @@ template<typename Char>
|
||||
* otherwise.
|
||||
*/
|
||||
template<typename Char>
|
||||
[[nodiscard]] constexpr bool operator<=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr bool operator<=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept {
|
||||
return !(rhs < lhs);
|
||||
}
|
||||
|
||||
@@ -264,7 +276,7 @@ template<typename Char>
|
||||
* otherwise.
|
||||
*/
|
||||
template<typename Char>
|
||||
[[nodiscard]] constexpr bool operator>(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr bool operator>(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept {
|
||||
return rhs < lhs;
|
||||
}
|
||||
|
||||
@@ -277,7 +289,7 @@ template<typename Char>
|
||||
* false otherwise.
|
||||
*/
|
||||
template<typename Char>
|
||||
[[nodiscard]] constexpr bool operator>=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr bool operator>=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept {
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
@@ -294,8 +306,8 @@ inline namespace literals {
|
||||
* @param str The literal without its suffix.
|
||||
* @return A properly initialized hashed string.
|
||||
*/
|
||||
[[nodiscard]] constexpr entt::hashed_string operator"" _hs(const char *str, std::size_t) ENTT_NOEXCEPT {
|
||||
return entt::hashed_string{str};
|
||||
[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept {
|
||||
return hashed_string{str};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -303,8 +315,8 @@ inline namespace literals {
|
||||
* @param str The literal without its suffix.
|
||||
* @return A properly initialized hashed wstring.
|
||||
*/
|
||||
[[nodiscard]] constexpr entt::hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) ENTT_NOEXCEPT {
|
||||
return entt::hashed_wstring{str};
|
||||
[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept {
|
||||
return hashed_wstring{str};
|
||||
}
|
||||
|
||||
} // namespace literals
|
||||
|
||||
@@ -4,54 +4,30 @@
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "fwd.hpp"
|
||||
#include "type_traits.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @brief Types identifiers.
|
||||
*
|
||||
* Variable template used to generate identifiers at compile-time for the given
|
||||
* types. Use the `get` member function to know what's the identifier associated
|
||||
* to the specific type.
|
||||
*
|
||||
* @note
|
||||
* Identifiers are constant expression and can be used in any context where such
|
||||
* an expression is required. As an example:
|
||||
* @code{.cpp}
|
||||
* using id = entt::identifier<a_type, another_type>;
|
||||
*
|
||||
* switch(a_type_identifier) {
|
||||
* case id::type<a_type>:
|
||||
* // ...
|
||||
* break;
|
||||
* case id::type<another_type>:
|
||||
* // ...
|
||||
* break;
|
||||
* default:
|
||||
* // ...
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Types List of types for which to generate identifiers.
|
||||
* @brief Type integral identifiers.
|
||||
* @tparam Type List of types for which to generate identifiers.
|
||||
*/
|
||||
template<typename... Types>
|
||||
class identifier {
|
||||
template<typename Type, std::size_t... Index>
|
||||
[[nodiscard]] static constexpr id_type get(std::index_sequence<Index...>) {
|
||||
static_assert(std::disjunction_v<std::is_same<Type, Types>...>, "Invalid type");
|
||||
return (0 + ... + (std::is_same_v<Type, type_list_element_t<Index, type_list<std::decay_t<Types>...>>> ? id_type{Index} : id_type{}));
|
||||
template<typename... Type>
|
||||
class ident {
|
||||
template<typename Curr, std::size_t... Index>
|
||||
[[nodiscard]] static constexpr id_type get(std::index_sequence<Index...>) noexcept {
|
||||
static_assert((std::is_same_v<Curr, Type> || ...), "Invalid type");
|
||||
return (0 + ... + (std::is_same_v<Curr, type_list_element_t<Index, type_list<std::decay_t<Type>...>>> ? id_type{Index} : id_type{}));
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Unsigned integer type. */
|
||||
using identifier_type = id_type;
|
||||
using value_type = id_type;
|
||||
|
||||
/*! @brief Statically generated unique identifier for the given type. */
|
||||
template<typename Type>
|
||||
static constexpr identifier_type type = get<std::decay_t<Type>>(std::index_sequence_for<Types...>{});
|
||||
template<typename Curr>
|
||||
static constexpr value_type value = get<std::decay_t<Curr>>(std::index_sequence_for<Type...>{});
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
|
||||
namespace entt {
|
||||
|
||||
@@ -14,76 +14,157 @@ namespace entt {
|
||||
*/
|
||||
template<typename Type>
|
||||
struct input_iterator_pointer final {
|
||||
/*! @brief Value type. */
|
||||
using value_type = Type;
|
||||
/*! @brief Pointer type. */
|
||||
using pointer = decltype(std::addressof(std::declval<Type &>()));
|
||||
|
||||
/*! @brief Default copy constructor, deleted on purpose. */
|
||||
input_iterator_pointer(const input_iterator_pointer &) = delete;
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
input_iterator_pointer(input_iterator_pointer &&) = default;
|
||||
using pointer = Type *;
|
||||
/*! @brief Reference type. */
|
||||
using reference = Type &;
|
||||
|
||||
/**
|
||||
* @brief Constructs a proxy object by move.
|
||||
* @param val Value to use to initialize the proxy object.
|
||||
*/
|
||||
input_iterator_pointer(Type &&val)
|
||||
constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v<value_type>)
|
||||
: value{std::move(val)} {}
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator, deleted on purpose.
|
||||
* @return This proxy object.
|
||||
*/
|
||||
input_iterator_pointer &operator=(const input_iterator_pointer &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Default move assignment operator.
|
||||
* @return This proxy object.
|
||||
*/
|
||||
input_iterator_pointer &operator=(input_iterator_pointer &&) = default;
|
||||
|
||||
/**
|
||||
* @brief Access operator for accessing wrapped values.
|
||||
* @return A pointer to the wrapped value.
|
||||
*/
|
||||
[[nodiscard]] pointer operator->() ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr pointer operator->() noexcept {
|
||||
return std::addressof(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Dereference operator for accessing wrapped values.
|
||||
* @return A reference to the wrapped value.
|
||||
*/
|
||||
[[nodiscard]] constexpr reference operator*() noexcept {
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
Type value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Utility class to create an iterable object from a pair of iterators.
|
||||
* @tparam It Type of iterators.
|
||||
* @brief Plain iota iterator (waiting for C++20).
|
||||
* @tparam Type Value type.
|
||||
*/
|
||||
template<typename It>
|
||||
template<typename Type>
|
||||
class iota_iterator final {
|
||||
static_assert(std::is_integral_v<Type>, "Not an integral type");
|
||||
|
||||
public:
|
||||
/*! @brief Value type, likely an integral one. */
|
||||
using value_type = Type;
|
||||
/*! @brief Invalid pointer type. */
|
||||
using pointer = void;
|
||||
/*! @brief Non-reference type, same as value type. */
|
||||
using reference = value_type;
|
||||
/*! @brief Difference type. */
|
||||
using difference_type = std::ptrdiff_t;
|
||||
/*! @brief Iterator category. */
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
constexpr iota_iterator() noexcept
|
||||
: current{} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an iota iterator from a given value.
|
||||
* @param init The initial value assigned to the iota iterator.
|
||||
*/
|
||||
constexpr iota_iterator(const value_type init) noexcept
|
||||
: current{init} {}
|
||||
|
||||
/**
|
||||
* @brief Pre-increment operator.
|
||||
* @return This iota iterator.
|
||||
*/
|
||||
constexpr iota_iterator &operator++() noexcept {
|
||||
return ++current, *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Post-increment operator.
|
||||
* @return This iota iterator.
|
||||
*/
|
||||
constexpr iota_iterator operator++(int) noexcept {
|
||||
iota_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Dereference operator.
|
||||
* @return The underlying value.
|
||||
*/
|
||||
[[nodiscard]] constexpr reference operator*() const noexcept {
|
||||
return current;
|
||||
}
|
||||
|
||||
private:
|
||||
value_type current;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Comparison operator.
|
||||
* @tparam Type Value type of the iota iterator.
|
||||
* @param lhs A properly initialized iota iterator.
|
||||
* @param rhs A properly initialized iota iterator.
|
||||
* @return True if the two iterators are identical, false otherwise.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr bool operator==(const iota_iterator<Type> &lhs, const iota_iterator<Type> &rhs) noexcept {
|
||||
return *lhs == *rhs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Comparison operator.
|
||||
* @tparam Type Value type of the iota iterator.
|
||||
* @param lhs A properly initialized iota iterator.
|
||||
* @param rhs A properly initialized iota iterator.
|
||||
* @return True if the two iterators differ, false otherwise.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr bool operator!=(const iota_iterator<Type> &lhs, const iota_iterator<Type> &rhs) noexcept {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Utility class to create an iterable object from a pair of iterators.
|
||||
* @tparam It Type of iterator.
|
||||
* @tparam Sentinel Type of sentinel.
|
||||
*/
|
||||
template<typename It, typename Sentinel = It>
|
||||
struct iterable_adaptor final {
|
||||
/*! @brief Type of the objects returned during iteration. */
|
||||
/*! @brief Value type. */
|
||||
using value_type = typename std::iterator_traits<It>::value_type;
|
||||
/*! @brief Iterator type. */
|
||||
using iterator = It;
|
||||
/*! @brief Const iterator type. */
|
||||
using const_iterator = iterator;
|
||||
/*! @brief Sentinel type. */
|
||||
using sentinel = Sentinel;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
iterable_adaptor() = default;
|
||||
constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v<iterator> &&std::is_nothrow_default_constructible_v<sentinel>)
|
||||
: first{},
|
||||
last{} {}
|
||||
|
||||
/**
|
||||
* @brief Creates an iterable object from a pair of iterators.
|
||||
* @param from Begin iterator.
|
||||
* @param to End iterator.
|
||||
*/
|
||||
iterable_adaptor(It from, It to)
|
||||
: first{from},
|
||||
last{to} {}
|
||||
constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v<iterator> &&std::is_nothrow_move_constructible_v<sentinel>)
|
||||
: first{std::move(from)},
|
||||
last{std::move(to)} {}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning.
|
||||
* @return An iterator to the first element of the range.
|
||||
*/
|
||||
[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr iterator begin() const noexcept {
|
||||
return first;
|
||||
}
|
||||
|
||||
@@ -92,23 +173,23 @@ struct iterable_adaptor final {
|
||||
* @return An iterator to the element following the last element of the
|
||||
* range.
|
||||
*/
|
||||
[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr sentinel end() const noexcept {
|
||||
return last;
|
||||
}
|
||||
|
||||
/*! @copydoc begin */
|
||||
[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr iterator cbegin() const noexcept {
|
||||
return begin();
|
||||
}
|
||||
|
||||
/*! @copydoc end */
|
||||
[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr sentinel cend() const noexcept {
|
||||
return end();
|
||||
}
|
||||
|
||||
private:
|
||||
It first;
|
||||
It last;
|
||||
Sentinel last;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
@@ -11,78 +12,23 @@
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20).
|
||||
* @tparam Type Pointer type.
|
||||
* @param ptr Fancy or raw pointer.
|
||||
* @return A raw pointer that represents the address of the original pointer.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT {
|
||||
if constexpr(std::is_pointer_v<std::remove_const_t<std::remove_reference_t<Type>>>) {
|
||||
return ptr;
|
||||
} else {
|
||||
return to_address(std::forward<Type>(ptr).operator->());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Utility function to design allocation-aware containers.
|
||||
* @tparam Allocator Type of allocator.
|
||||
* @param lhs A valid allocator.
|
||||
* @param rhs Another valid allocator.
|
||||
*/
|
||||
template<typename Allocator>
|
||||
constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
|
||||
if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) {
|
||||
lhs = rhs;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Utility function to design allocation-aware containers.
|
||||
* @tparam Allocator Type of allocator.
|
||||
* @param lhs A valid allocator.
|
||||
* @param rhs Another valid allocator.
|
||||
*/
|
||||
template<typename Allocator>
|
||||
constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
|
||||
if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) {
|
||||
lhs = std::move(rhs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Utility function to design allocation-aware containers.
|
||||
* @tparam Allocator Type of allocator.
|
||||
* @param lhs A valid allocator.
|
||||
* @param rhs Another valid allocator.
|
||||
*/
|
||||
template<typename Allocator>
|
||||
constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(std::allocator_traits<Allocator>::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers");
|
||||
|
||||
if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) {
|
||||
using std::swap;
|
||||
swap(lhs, rhs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept {
|
||||
return value && ((value & (value - 1)) == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Computes the smallest power of two greater than or equal to a value.
|
||||
* @brief Computes the smallest power of two greater than or equal to a value
|
||||
* (waiting for C++20 and `std::bit_ceil`).
|
||||
* @param value The value to use.
|
||||
* @return The smallest power of two greater than or equal to the given value.
|
||||
*/
|
||||
[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded");
|
||||
[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept {
|
||||
ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded");
|
||||
std::size_t curr = value - (value != 0u);
|
||||
|
||||
for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) {
|
||||
@@ -98,11 +44,240 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma
|
||||
* @param mod _Modulus_, it must be a power of two.
|
||||
* @return The common remainder.
|
||||
*/
|
||||
[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two");
|
||||
[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept {
|
||||
ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two");
|
||||
return value & (mod - 1u);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20).
|
||||
* @tparam Type Pointer type.
|
||||
* @param ptr Fancy or raw pointer.
|
||||
* @return A raw pointer that represents the address of the original pointer.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept {
|
||||
if constexpr(std::is_pointer_v<std::decay_t<Type>>) {
|
||||
return ptr;
|
||||
} else {
|
||||
return to_address(std::forward<Type>(ptr).operator->());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Utility function to design allocation-aware containers.
|
||||
* @tparam Allocator Type of allocator.
|
||||
* @param lhs A valid allocator.
|
||||
* @param rhs Another valid allocator.
|
||||
*/
|
||||
template<typename Allocator>
|
||||
constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept {
|
||||
if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) {
|
||||
lhs = rhs;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Utility function to design allocation-aware containers.
|
||||
* @tparam Allocator Type of allocator.
|
||||
* @param lhs A valid allocator.
|
||||
* @param rhs Another valid allocator.
|
||||
*/
|
||||
template<typename Allocator>
|
||||
constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept {
|
||||
if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) {
|
||||
lhs = std::move(rhs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Utility function to design allocation-aware containers.
|
||||
* @tparam Allocator Type of allocator.
|
||||
* @param lhs A valid allocator.
|
||||
* @param rhs Another valid allocator.
|
||||
*/
|
||||
template<typename Allocator>
|
||||
constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept {
|
||||
if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) {
|
||||
using std::swap;
|
||||
swap(lhs, rhs);
|
||||
} else {
|
||||
ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deleter for allocator-aware unique pointers (waiting for C++20).
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Allocator>
|
||||
struct allocation_deleter: private Allocator {
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Pointer type. */
|
||||
using pointer = typename std::allocator_traits<Allocator>::pointer;
|
||||
|
||||
/**
|
||||
* @brief Inherited constructors.
|
||||
* @param alloc The allocator to use.
|
||||
*/
|
||||
constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v<allocator_type>)
|
||||
: Allocator{alloc} {}
|
||||
|
||||
/**
|
||||
* @brief Destroys the pointed object and deallocates its memory.
|
||||
* @param ptr A valid pointer to an object of the given type.
|
||||
*/
|
||||
constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v<typename allocator_type::value_type>) {
|
||||
using alloc_traits = std::allocator_traits<Allocator>;
|
||||
alloc_traits::destroy(*this, to_address(ptr));
|
||||
alloc_traits::deallocate(*this, ptr, 1u);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Allows `std::unique_ptr` to use allocators (waiting for C++20).
|
||||
* @tparam Type Type of object to allocate for and to construct.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
* @tparam Args Types of arguments to use to construct the object.
|
||||
* @param allocator The allocator to use.
|
||||
* @param args Parameters to use to construct the object.
|
||||
* @return A properly initialized unique pointer with a custom deleter.
|
||||
*/
|
||||
template<typename Type, typename Allocator, typename... Args>
|
||||
ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) {
|
||||
static_assert(!std::is_array_v<Type>, "Array types are not supported");
|
||||
|
||||
using alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<Type>;
|
||||
using allocator_type = typename alloc_traits::allocator_type;
|
||||
|
||||
allocator_type alloc{allocator};
|
||||
auto ptr = alloc_traits::allocate(alloc, 1u);
|
||||
|
||||
ENTT_TRY {
|
||||
alloc_traits::construct(alloc, to_address(ptr), std::forward<Args>(args)...);
|
||||
}
|
||||
ENTT_CATCH {
|
||||
alloc_traits::deallocate(alloc, ptr, 1u);
|
||||
ENTT_THROW;
|
||||
}
|
||||
|
||||
return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc};
|
||||
}
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename Type>
|
||||
struct uses_allocator_construction {
|
||||
template<typename Allocator, typename... Params>
|
||||
static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept {
|
||||
if constexpr(!std::uses_allocator_v<Type, Allocator> && std::is_constructible_v<Type, Params...>) {
|
||||
return std::forward_as_tuple(std::forward<Params>(params)...);
|
||||
} else {
|
||||
static_assert(std::uses_allocator_v<Type, Allocator>, "Ill-formed request");
|
||||
|
||||
if constexpr(std::is_constructible_v<Type, std::allocator_arg_t, const Allocator &, Params...>) {
|
||||
return std::tuple<std::allocator_arg_t, const Allocator &, Params &&...>{std::allocator_arg, allocator, std::forward<Params>(params)...};
|
||||
} else {
|
||||
static_assert(std::is_constructible_v<Type, Params..., const Allocator &>, "Ill-formed request");
|
||||
return std::forward_as_tuple(std::forward<Params>(params)..., allocator);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Type, typename Other>
|
||||
struct uses_allocator_construction<std::pair<Type, Other>> {
|
||||
using type = std::pair<Type, Other>;
|
||||
|
||||
template<typename Allocator, typename First, typename Second>
|
||||
static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept {
|
||||
return std::make_tuple(
|
||||
std::piecewise_construct,
|
||||
std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Type>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<First>(first)),
|
||||
std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Other>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<Second>(second)));
|
||||
}
|
||||
|
||||
template<typename Allocator>
|
||||
static constexpr auto args(const Allocator &allocator) noexcept {
|
||||
return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{});
|
||||
}
|
||||
|
||||
template<typename Allocator, typename First, typename Second>
|
||||
static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept {
|
||||
return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward<First>(first)), std::forward_as_tuple(std::forward<Second>(second)));
|
||||
}
|
||||
|
||||
template<typename Allocator, typename First, typename Second>
|
||||
static constexpr auto args(const Allocator &allocator, const std::pair<First, Second> &value) noexcept {
|
||||
return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second));
|
||||
}
|
||||
|
||||
template<typename Allocator, typename First, typename Second>
|
||||
static constexpr auto args(const Allocator &allocator, std::pair<First, Second> &&value) noexcept {
|
||||
return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second)));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Uses-allocator construction utility (waiting for C++20).
|
||||
*
|
||||
* Primarily intended for internal use. Prepares the argument list needed to
|
||||
* create an object of a given type by means of uses-allocator construction.
|
||||
*
|
||||
* @tparam Type Type to return arguments for.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
* @tparam Args Types of arguments to use to construct the object.
|
||||
* @param allocator The allocator to use.
|
||||
* @param args Parameters to use to construct the object.
|
||||
* @return The arguments needed to create an object of the given type.
|
||||
*/
|
||||
template<typename Type, typename Allocator, typename... Args>
|
||||
constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept {
|
||||
return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Uses-allocator construction utility (waiting for C++20).
|
||||
*
|
||||
* Primarily intended for internal use. Creates an object of a given type by
|
||||
* means of uses-allocator construction.
|
||||
*
|
||||
* @tparam Type Type of object to create.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
* @tparam Args Types of arguments to use to construct the object.
|
||||
* @param allocator The allocator to use.
|
||||
* @param args Parameters to use to construct the object.
|
||||
* @return A newly created object of the given type.
|
||||
*/
|
||||
template<typename Type, typename Allocator, typename... Args>
|
||||
constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) {
|
||||
return std::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Uses-allocator construction utility (waiting for C++20).
|
||||
*
|
||||
* Primarily intended for internal use. Creates an object of a given type by
|
||||
* means of uses-allocator construction at an uninitialized memory location.
|
||||
*
|
||||
* @tparam Type Type of object to create.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
* @tparam Args Types of arguments to use to construct the object.
|
||||
* @param value Memory location in which to place the object.
|
||||
* @param allocator The allocator to use.
|
||||
* @param args Parameters to use to construct the object.
|
||||
* @return A pointer to the newly created object of the given type.
|
||||
*/
|
||||
template<typename Type, typename Allocator, typename... Args>
|
||||
constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) {
|
||||
return std::apply([value](auto &&...curr) { return new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
|
||||
@@ -25,7 +25,7 @@ struct monostate {
|
||||
* @param val User data to assign to the given key.
|
||||
*/
|
||||
template<typename Type>
|
||||
void operator=(Type val) const ENTT_NOEXCEPT {
|
||||
void operator=(Type val) const noexcept {
|
||||
value<Type> = val;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ struct monostate {
|
||||
* @return Stored value, if any.
|
||||
*/
|
||||
template<typename Type>
|
||||
operator Type() const ENTT_NOEXCEPT {
|
||||
operator Type() const noexcept {
|
||||
return value<Type>;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,36 @@
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename>
|
||||
struct is_tuple_impl: std::false_type {};
|
||||
|
||||
template<typename... Args>
|
||||
struct is_tuple_impl<std::tuple<Args...>>: std::true_type {};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if a given type is a
|
||||
* tuple, false otherwise.
|
||||
* @tparam Type The type to test.
|
||||
*/
|
||||
template<typename Type>
|
||||
struct is_tuple: internal::is_tuple_impl<std::remove_cv_t<Type>> {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type The type to test.
|
||||
*/
|
||||
template<typename Type>
|
||||
inline constexpr bool is_tuple_v = is_tuple<Type>::value;
|
||||
|
||||
/**
|
||||
* @brief Utility function to unwrap tuples of a single element.
|
||||
* @tparam Type Tuple type of any sizes.
|
||||
@@ -16,7 +42,7 @@ namespace entt {
|
||||
* element otherwise.
|
||||
*/
|
||||
template<typename Type>
|
||||
constexpr decltype(auto) unwrap_tuple(Type &&value) ENTT_NOEXCEPT {
|
||||
constexpr decltype(auto) unwrap_tuple(Type &&value) noexcept {
|
||||
if constexpr(std::tuple_size_v<std::remove_reference_t<Type>> == 1u) {
|
||||
return std::get<0>(std::forward<Type>(value));
|
||||
} else {
|
||||
@@ -24,6 +50,46 @@ constexpr decltype(auto) unwrap_tuple(Type &&value) ENTT_NOEXCEPT {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Utility class to forward-and-apply tuple objects.
|
||||
* @tparam Func Type of underlying invocable object.
|
||||
*/
|
||||
template<typename Func>
|
||||
struct forward_apply: private Func {
|
||||
/**
|
||||
* @brief Constructs a forward-and-apply object.
|
||||
* @tparam Args Types of arguments to use to construct the new instance.
|
||||
* @param args Parameters to use to construct the instance.
|
||||
*/
|
||||
template<typename... Args>
|
||||
constexpr forward_apply(Args &&...args) noexcept(std::is_nothrow_constructible_v<Func, Args...>)
|
||||
: Func{std::forward<Args>(args)...} {}
|
||||
|
||||
/**
|
||||
* @brief Forwards and applies the arguments with the underlying function.
|
||||
* @tparam Type Tuple-like type to forward to the underlying function.
|
||||
* @param args Parameters to forward to the underlying function.
|
||||
* @return Return value of the underlying function, if any.
|
||||
*/
|
||||
template<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<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));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
* @tparam Func Type of underlying invocable object.
|
||||
*/
|
||||
template<typename Func>
|
||||
forward_apply(Func) -> forward_apply<std::remove_reference_t<std::remove_cv_t<Func>>>;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
|
||||
@@ -11,22 +11,18 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
struct ENTT_API type_index final {
|
||||
[[nodiscard]] static id_type next() ENTT_NOEXCEPT {
|
||||
[[nodiscard]] static id_type next() noexcept {
|
||||
static ENTT_MAYBE_ATOMIC(id_type) value{};
|
||||
return value++;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr auto stripped_type_name() noexcept {
|
||||
#if defined ENTT_PRETTY_FUNCTION
|
||||
std::string_view pretty_function{ENTT_PRETTY_FUNCTION};
|
||||
auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1);
|
||||
@@ -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) ENTT_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) ENTT_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) ENTT_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) ENTT_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.
|
||||
@@ -81,13 +73,13 @@ struct ENTT_API type_index final {
|
||||
* @brief Returns the sequential identifier of a given type.
|
||||
* @return The sequential identifier of a given type.
|
||||
*/
|
||||
[[nodiscard]] static id_type value() ENTT_NOEXCEPT {
|
||||
[[nodiscard]] static id_type value() noexcept {
|
||||
static const id_type value = internal::type_index::next();
|
||||
return value;
|
||||
}
|
||||
|
||||
/*! @copydoc value */
|
||||
[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr operator id_type() const noexcept {
|
||||
return value();
|
||||
}
|
||||
};
|
||||
@@ -103,16 +95,16 @@ struct type_hash final {
|
||||
* @return The numeric representation of the given type.
|
||||
*/
|
||||
#if defined ENTT_PRETTY_FUNCTION
|
||||
[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
|
||||
[[nodiscard]] static constexpr id_type value() noexcept {
|
||||
return internal::type_hash<Type>(0);
|
||||
#else
|
||||
[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
|
||||
[[nodiscard]] static constexpr id_type value() noexcept {
|
||||
return type_index<Type>::value();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*! @copydoc value */
|
||||
[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr operator id_type() const noexcept {
|
||||
return value();
|
||||
}
|
||||
};
|
||||
@@ -127,33 +119,33 @@ struct type_name final {
|
||||
* @brief Returns the name of a given type.
|
||||
* @return The name of the given type.
|
||||
*/
|
||||
[[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT {
|
||||
[[nodiscard]] static constexpr std::string_view value() noexcept {
|
||||
return internal::type_name<Type>(0);
|
||||
}
|
||||
|
||||
/*! @copydoc value */
|
||||
[[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr operator std::string_view() const noexcept {
|
||||
return value();
|
||||
}
|
||||
};
|
||||
|
||||
/*! @brief Implementation specific information about a type. */
|
||||
class type_info final {
|
||||
struct type_info final {
|
||||
/**
|
||||
* @brief Constructs a type info object for a given type.
|
||||
* @tparam Type Type for which to construct a type info object.
|
||||
*/
|
||||
template<typename Type>
|
||||
friend const type_info &type_id() ENTT_NOEXCEPT;
|
||||
constexpr type_info(std::in_place_type_t<Type>) noexcept
|
||||
: seq{type_index<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
|
||||
identifier{type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
|
||||
alias{type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()} {}
|
||||
|
||||
template<typename Type>
|
||||
constexpr type_info(std::in_place_type_t<Type>) ENTT_NOEXCEPT
|
||||
: seq{type_index<std::remove_reference_t<std::remove_const_t<Type>>>::value()},
|
||||
identifier{type_hash<std::remove_reference_t<std::remove_const_t<Type>>>::value()},
|
||||
alias{type_name<std::remove_reference_t<std::remove_const_t<Type>>>::value()} {}
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Type index.
|
||||
* @return Type index.
|
||||
*/
|
||||
[[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr id_type index() const noexcept {
|
||||
return seq;
|
||||
}
|
||||
|
||||
@@ -161,7 +153,7 @@ public:
|
||||
* @brief Type hash.
|
||||
* @return Type hash.
|
||||
*/
|
||||
[[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr id_type hash() const noexcept {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
@@ -169,7 +161,7 @@ public:
|
||||
* @brief Type name.
|
||||
* @return Type name.
|
||||
*/
|
||||
[[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr std::string_view name() const noexcept {
|
||||
return alias;
|
||||
}
|
||||
|
||||
@@ -185,7 +177,7 @@ private:
|
||||
* @param rhs A type info object.
|
||||
* @return True if the two type info objects are identical, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept {
|
||||
return lhs.hash() == rhs.hash();
|
||||
}
|
||||
|
||||
@@ -195,7 +187,7 @@ private:
|
||||
* @param rhs A type info object.
|
||||
* @return True if the two type info objects differ, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
@@ -205,7 +197,7 @@ private:
|
||||
* @param rhs A valid type info object.
|
||||
* @return True if the first element is less than the second, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept {
|
||||
return lhs.index() < rhs.index();
|
||||
}
|
||||
|
||||
@@ -216,7 +208,7 @@ private:
|
||||
* @return True if the first element is less than or equal to the second, false
|
||||
* otherwise.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept {
|
||||
return !(rhs < lhs);
|
||||
}
|
||||
|
||||
@@ -227,7 +219,7 @@ private:
|
||||
* @return True if the first element is greater than the second, false
|
||||
* otherwise.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept {
|
||||
return rhs < lhs;
|
||||
}
|
||||
|
||||
@@ -238,30 +230,37 @@ private:
|
||||
* @return True if the first element is greater than or equal to the second,
|
||||
* false otherwise.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept {
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the type info object associated to a given type.
|
||||
*
|
||||
* The returned element refers to an object with static storage duration.<br/>
|
||||
* The type doesn't need to be a complete type. If the type is a reference, the
|
||||
* result refers to the referenced type. In all cases, top-level cv-qualifiers
|
||||
* are ignored.
|
||||
*
|
||||
* @tparam Type Type for which to generate a type info object.
|
||||
* @return A properly initialized type info object.
|
||||
* @return A reference to a properly initialized type info object.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT {
|
||||
[[nodiscard]] const type_info &type_id() noexcept {
|
||||
if constexpr(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>) {
|
||||
static type_info instance{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>};
|
||||
static type_info instance{std::in_place_type<Type>};
|
||||
return instance;
|
||||
} else {
|
||||
return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
|
||||
}
|
||||
}
|
||||
|
||||
/*! @copydoc type_id */
|
||||
template<typename Type>
|
||||
[[nodiscard]] const type_info &type_id(Type &&) noexcept {
|
||||
return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
|
||||
}
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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> {};
|
||||
@@ -121,22 +121,22 @@ struct type_list_element;
|
||||
/**
|
||||
* @brief Provides compile-time indexed access to the types of a type list.
|
||||
* @tparam Index Index of the type to return.
|
||||
* @tparam Type First type provided by the type list.
|
||||
* @tparam First First type provided by the type list.
|
||||
* @tparam Other Other types provided by the type list.
|
||||
*/
|
||||
template<std::size_t Index, typename Type, typename... Other>
|
||||
struct type_list_element<Index, type_list<Type, Other...>>
|
||||
template<std::size_t Index, typename First, typename... Other>
|
||||
struct type_list_element<Index, type_list<First, Other...>>
|
||||
: type_list_element<Index - 1u, type_list<Other...>> {};
|
||||
|
||||
/**
|
||||
* @brief Provides compile-time indexed access to the types of a type list.
|
||||
* @tparam Type First type provided by the type list.
|
||||
* @tparam First First type provided by the type list.
|
||||
* @tparam Other Other types provided by the type list.
|
||||
*/
|
||||
template<typename Type, typename... Other>
|
||||
struct type_list_element<0u, type_list<Type, Other...>> {
|
||||
template<typename First, typename... Other>
|
||||
struct type_list_element<0u, type_list<First, Other...>> {
|
||||
/*! @brief Searched type. */
|
||||
using type = Type;
|
||||
using type = First;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -147,6 +147,58 @@ struct type_list_element<0u, type_list<Type, Other...>> {
|
||||
template<std::size_t Index, typename List>
|
||||
using type_list_element_t = typename type_list_element<Index, List>::type;
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename, typename>
|
||||
struct type_list_index;
|
||||
|
||||
/**
|
||||
* @brief Provides compile-time type access to the types of a type list.
|
||||
* @tparam Type Type to look for and for which to return the index.
|
||||
* @tparam First First type provided by the type list.
|
||||
* @tparam Other Other types provided by the type list.
|
||||
*/
|
||||
template<typename Type, typename First, typename... Other>
|
||||
struct type_list_index<Type, type_list<First, Other...>> {
|
||||
/*! @brief Unsigned integer type. */
|
||||
using value_type = std::size_t;
|
||||
/*! @brief Compile-time position of the given type in the sublist. */
|
||||
static constexpr value_type value = 1u + type_list_index<Type, type_list<Other...>>::value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Provides compile-time type access to the types of a type list.
|
||||
* @tparam Type Type to look for and for which to return the index.
|
||||
* @tparam Other Other types provided by the type list.
|
||||
*/
|
||||
template<typename Type, typename... Other>
|
||||
struct type_list_index<Type, type_list<Type, Other...>> {
|
||||
static_assert(type_list_index<Type, type_list<Other...>>::value == sizeof...(Other), "Non-unique type");
|
||||
/*! @brief Unsigned integer type. */
|
||||
using value_type = std::size_t;
|
||||
/*! @brief Compile-time position of the given type in the sublist. */
|
||||
static constexpr value_type value = 0u;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Provides compile-time type access to the types of a type list.
|
||||
* @tparam Type Type to look for and for which to return the index.
|
||||
*/
|
||||
template<typename Type>
|
||||
struct type_list_index<Type, type_list<>> {
|
||||
/*! @brief Unsigned integer type. */
|
||||
using value_type = std::size_t;
|
||||
/*! @brief Compile-time position of the given type in the sublist. */
|
||||
static constexpr value_type value = 0u;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam List Type list.
|
||||
* @tparam Type Type to look for and for which to return the index.
|
||||
*/
|
||||
template<typename Type, typename List>
|
||||
inline constexpr std::size_t type_list_index_v = type_list_index<Type, List>::value;
|
||||
|
||||
/**
|
||||
* @brief Concatenates multiple type lists.
|
||||
* @tparam Type Types provided by the first type list.
|
||||
@@ -198,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::disjunction_v<std::is_same<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
|
||||
@@ -245,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.
|
||||
@@ -277,6 +333,29 @@ struct type_list_diff<type_list<Type...>, type_list<Other...>> {
|
||||
template<typename... List>
|
||||
using type_list_diff_t = typename type_list_diff<List...>::type;
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename, template<typename...> class>
|
||||
struct type_list_transform;
|
||||
|
||||
/**
|
||||
* @brief Applies a given _function_ to a type list and generate a new list.
|
||||
* @tparam Type Types provided by the type list.
|
||||
* @tparam Op Unary operation as template class with a type member named `type`.
|
||||
*/
|
||||
template<typename... Type, template<typename...> class Op>
|
||||
struct type_list_transform<type_list<Type...>, Op> {
|
||||
/*! @brief Resulting type list after applying the transform function. */
|
||||
using type = type_list<typename Op<Type>::type...>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam List Type list.
|
||||
* @tparam Op Unary operation as template class with a type member named `type`.
|
||||
*/
|
||||
template<typename List, template<typename...> class Op>
|
||||
using type_list_transform_t = typename type_list_transform<List, Op>::type;
|
||||
|
||||
/**
|
||||
* @brief A class to use to push around lists of constant values, nothing more.
|
||||
* @tparam Value Values provided by the value list.
|
||||
@@ -310,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.
|
||||
@@ -322,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.
|
||||
@@ -373,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 {};
|
||||
@@ -454,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>
|
||||
@@ -468,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_const_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> {};
|
||||
|
||||
/**
|
||||
@@ -486,33 +702,6 @@ struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<st
|
||||
template<typename Type>
|
||||
inline constexpr bool is_iterator_v = is_iterator<Type>::value;
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if a given type is of the
|
||||
* required iterator type, false otherwise.
|
||||
* @tparam Type The type to test.
|
||||
* @tparam It Required iterator type.
|
||||
*/
|
||||
template<typename Type, typename It, typename = void>
|
||||
struct is_iterator_type: std::false_type {};
|
||||
|
||||
/*! @copydoc is_iterator_type */
|
||||
template<typename Type, typename It>
|
||||
struct is_iterator_type<Type, It, std::enable_if_t<is_iterator_v<Type> && std::is_same_v<Type, It>>>
|
||||
: std::true_type {};
|
||||
|
||||
/*! @copydoc is_iterator_type */
|
||||
template<typename Type, typename It>
|
||||
struct is_iterator_type<Type, It, std::enable_if_t<!std::is_same_v<Type, It>, std::void_t<typename It::iterator_type>>>
|
||||
: is_iterator_type<Type, typename It::iterator_type> {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type The type to test.
|
||||
* @tparam It Required iterator type.
|
||||
*/
|
||||
template<typename Type, typename It>
|
||||
inline constexpr bool is_iterator_type_v = is_iterator_type<Type, It>::value;
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if a given type is both
|
||||
* an empty and non-final class, false otherwise.
|
||||
@@ -520,7 +709,7 @@ inline constexpr bool is_iterator_type_v = is_iterator_type<Type, It>::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.
|
||||
@@ -548,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>
|
||||
@@ -569,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_const_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.
|
||||
@@ -633,7 +832,7 @@ struct constness_as {
|
||||
template<typename To, typename From>
|
||||
struct constness_as<To, const From> {
|
||||
/*! @brief The type resulting from the transcription of the constness. */
|
||||
using type = std::add_const_t<To>;
|
||||
using type = const To;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -673,6 +872,50 @@ public:
|
||||
template<typename Member>
|
||||
using member_class_t = typename member_class<Member>::type;
|
||||
|
||||
/**
|
||||
* @brief Extracts the n-th argument of a given function or member function.
|
||||
* @tparam Index The index of the argument to extract.
|
||||
* @tparam Candidate A valid function, member function or data member type.
|
||||
*/
|
||||
template<std::size_t Index, typename Candidate>
|
||||
class nth_argument {
|
||||
template<typename Ret, typename... Args>
|
||||
static constexpr type_list<Args...> pick_up(Ret (*)(Args...));
|
||||
|
||||
template<typename Ret, typename Class, typename... Args>
|
||||
static constexpr type_list<Args...> pick_up(Ret (Class ::*)(Args...));
|
||||
|
||||
template<typename Ret, typename Class, typename... Args>
|
||||
static constexpr type_list<Args...> pick_up(Ret (Class ::*)(Args...) const);
|
||||
|
||||
template<typename Type, typename Class>
|
||||
static constexpr type_list<Type> pick_up(Type Class ::*);
|
||||
|
||||
public:
|
||||
/*! @brief N-th argument of the given function or member function. */
|
||||
using type = type_list_element_t<Index, decltype(pick_up(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 type.
|
||||
*/
|
||||
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
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#ifndef ENTT_CORE_UTILITY_HPP
|
||||
#define ENTT_CORE_UTILITY_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
|
||||
namespace entt {
|
||||
|
||||
@@ -17,8 +17,8 @@ struct identity {
|
||||
* @param value The actual argument.
|
||||
* @return The submitted value as-is.
|
||||
*/
|
||||
template<class Type>
|
||||
[[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT {
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept {
|
||||
return std::forward<Type>(value);
|
||||
}
|
||||
};
|
||||
@@ -31,7 +31,7 @@ struct identity {
|
||||
* @return Pointer to the member.
|
||||
*/
|
||||
template<typename Type, typename Class>
|
||||
[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr auto overload(Type Class::*member) noexcept {
|
||||
return member;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ template<typename Type, typename Class>
|
||||
* @return Pointer to the function.
|
||||
*/
|
||||
template<typename Func>
|
||||
[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr auto overload(Func *func) noexcept {
|
||||
return func;
|
||||
}
|
||||
|
||||
@@ -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,20 +59,20 @@ 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.
|
||||
* @param recursive A potentially recursive function.
|
||||
*/
|
||||
y_combinator(Func recursive)
|
||||
constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v<Func>)
|
||||
: func{std::move(recursive)} {}
|
||||
|
||||
/**
|
||||
@@ -81,14 +81,14 @@ struct y_combinator {
|
||||
* @param args Parameters to use to invoke the underlying function.
|
||||
* @return Return value of the underlying function, if any.
|
||||
*/
|
||||
template<class... Args>
|
||||
decltype(auto) operator()(Args &&...args) const {
|
||||
template<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>
|
||||
decltype(auto) operator()(Args &&...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)...);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,36 +1,55 @@
|
||||
#ifndef ENTT_ENTITY_COMPONENT_HPP
|
||||
#define ENTT_ENTITY_COMPONENT_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @brief Commonly used default traits for all types. */
|
||||
struct basic_component_traits {
|
||||
/*! @brief Pointer stability, default is `false`. */
|
||||
static constexpr auto in_place_delete = false;
|
||||
/*! @brief Empty type optimization, default is `ENTT_IGNORE_IF_EMPTY`. */
|
||||
static constexpr auto ignore_if_empty = ENTT_IGNORE_IF_EMPTY;
|
||||
/*! @brief Page size, default is `ENTT_PACKED_PAGE`. */
|
||||
static constexpr auto page_size = ENTT_PACKED_PAGE;
|
||||
};
|
||||
/*! @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 {};
|
||||
|
||||
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::void_t<decltype(Type::page_size)>>
|
||||
: std::integral_constant<std::size_t, Type::page_size> {};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Common way to access various properties of components.
|
||||
* @tparam Type Type of component.
|
||||
*/
|
||||
template<typename Type, typename = void>
|
||||
struct component_traits: basic_component_traits {
|
||||
struct component_traits {
|
||||
static_assert(std::is_same_v<std::decay_t<Type>, Type>, "Unsupported type");
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type Type of component.
|
||||
*/
|
||||
template<class Type>
|
||||
inline constexpr bool ignore_as_empty_v = component_traits<Type>::ignore_if_empty &&std::is_empty_v<Type>;
|
||||
/*! @brief Component type. */
|
||||
using type = Type;
|
||||
|
||||
/*! @brief Pointer stability, default is `false`. */
|
||||
static constexpr bool in_place_delete = internal::in_place_delete<Type>::value;
|
||||
/*! @brief Page size, default is `ENTT_PACKED_PAGE` for non-empty types. */
|
||||
static constexpr std::size_t page_size = internal::page_size<Type>::value;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
|
||||
@@ -9,77 +9,85 @@
|
||||
|
||||
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.
|
||||
* @param value The value to convert.
|
||||
* @return The integral representation of the given value.
|
||||
*/
|
||||
[[nodiscard]] static constexpr entity_type to_integral(const value_type value) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] static constexpr entity_type to_integral(const value_type value) noexcept {
|
||||
return static_cast<entity_type>(value);
|
||||
}
|
||||
|
||||
@@ -88,8 +96,8 @@ public:
|
||||
* @param value The value to convert.
|
||||
* @return The integral representation of the entity part.
|
||||
*/
|
||||
[[nodiscard]] static constexpr entity_type to_entity(const value_type value) ENTT_NOEXCEPT {
|
||||
return (to_integral(value) & base_type::entity_mask);
|
||||
[[nodiscard]] static constexpr entity_type to_entity(const value_type value) noexcept {
|
||||
return (to_integral(value) & entity_mask);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,8 +105,18 @@ public:
|
||||
* @param value The value to convert.
|
||||
* @return The integral representation of the version part.
|
||||
*/
|
||||
[[nodiscard]] static constexpr version_type to_version(const value_type value) ENTT_NOEXCEPT {
|
||||
return (to_integral(value) >> base_type::entity_shift);
|
||||
[[nodiscard]] static constexpr version_type to_version(const value_type value) noexcept {
|
||||
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)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,8 +129,8 @@ public:
|
||||
* @param version The version part of the identifier.
|
||||
* @return A properly constructed identifier.
|
||||
*/
|
||||
[[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) ENTT_NOEXCEPT {
|
||||
return value_type{(entity & base_type::entity_mask) | (static_cast<entity_type>(version) << base_type::entity_shift)};
|
||||
[[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) noexcept {
|
||||
return value_type{(entity & entity_mask) | (static_cast<entity_type>(version & version_mask) << length)};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,36 +143,53 @@ public:
|
||||
* @param rhs The identifier from which to take the version part.
|
||||
* @return A properly constructed identifier.
|
||||
*/
|
||||
[[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) ENTT_NOEXCEPT {
|
||||
constexpr auto mask = (base_type::version_mask << base_type::entity_shift);
|
||||
return value_type{(lhs & base_type::entity_mask) | (rhs & mask)};
|
||||
[[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept {
|
||||
return value_type{(lhs & entity_mask) | (rhs & (version_mask << length))};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @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) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_integral(const Entity value) noexcept {
|
||||
return entt_traits<Entity>::to_integral(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc entt_traits<Entity>::to_entity
|
||||
* @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) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_entity(const Entity value) noexcept {
|
||||
return entt_traits<Entity>::to_entity(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr typename entt_traits<Entity>::version_type to_version(const Entity value) noexcept {
|
||||
return entt_traits<Entity>::to_version(value);
|
||||
}
|
||||
|
||||
@@ -166,9 +201,10 @@ struct null_t {
|
||||
* @return The null representation for the given type.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT {
|
||||
using entity_traits = entt_traits<Entity>;
|
||||
return entity_traits::combine(entity_traits::reserved, entity_traits::reserved);
|
||||
[[nodiscard]] constexpr operator Entity() const noexcept {
|
||||
using traits_type = entt_traits<Entity>;
|
||||
constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -176,7 +212,7 @@ struct null_t {
|
||||
* @param other A null object.
|
||||
* @return True in all cases.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -185,7 +221,7 @@ struct null_t {
|
||||
* @param other A null object.
|
||||
* @return False in all cases.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -196,9 +232,9 @@ struct null_t {
|
||||
* @return False if the two elements differ, true otherwise.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT {
|
||||
using entity_traits = entt_traits<Entity>;
|
||||
return entity_traits::to_entity(entity) == entity_traits::to_entity(*this);
|
||||
[[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept {
|
||||
using traits_type = entt_traits<Entity>;
|
||||
return traits_type::to_entity(entity) == traits_type::to_entity(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -208,7 +244,7 @@ struct null_t {
|
||||
* @return True if the two elements differ, false otherwise.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept {
|
||||
return !(entity == *this);
|
||||
}
|
||||
};
|
||||
@@ -221,7 +257,7 @@ struct null_t {
|
||||
* @return False if the two elements differ, true otherwise.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr bool operator==(const Entity entity, const null_t other) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr bool operator==(const Entity entity, const null_t other) noexcept {
|
||||
return other.operator==(entity);
|
||||
}
|
||||
|
||||
@@ -233,7 +269,7 @@ template<typename Entity>
|
||||
* @return True if the two elements differ, false otherwise.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr bool operator!=(const Entity entity, const null_t other) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr bool operator!=(const Entity entity, const null_t other) noexcept {
|
||||
return !(other == entity);
|
||||
}
|
||||
|
||||
@@ -245,9 +281,10 @@ struct tombstone_t {
|
||||
* @return The tombstone representation for the given type.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT {
|
||||
using entity_traits = entt_traits<Entity>;
|
||||
return entity_traits::combine(entity_traits::reserved, entity_traits::reserved);
|
||||
[[nodiscard]] constexpr operator Entity() const noexcept {
|
||||
using traits_type = entt_traits<Entity>;
|
||||
constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -255,7 +292,7 @@ struct tombstone_t {
|
||||
* @param other A tombstone object.
|
||||
* @return True in all cases.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -264,7 +301,7 @@ struct tombstone_t {
|
||||
* @param other A tombstone object.
|
||||
* @return False in all cases.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -275,9 +312,9 @@ struct tombstone_t {
|
||||
* @return False if the two elements differ, true otherwise.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT {
|
||||
using entity_traits = entt_traits<Entity>;
|
||||
return entity_traits::to_version(entity) == entity_traits::to_version(*this);
|
||||
[[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept {
|
||||
using traits_type = entt_traits<Entity>;
|
||||
return traits_type::to_version(entity) == traits_type::to_version(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -287,7 +324,7 @@ struct tombstone_t {
|
||||
* @return True if the two elements differ, false otherwise.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept {
|
||||
return !(entity == *this);
|
||||
}
|
||||
};
|
||||
@@ -300,7 +337,7 @@ struct tombstone_t {
|
||||
* @return False if the two elements differ, true otherwise.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr bool operator==(const Entity entity, const tombstone_t other) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr bool operator==(const Entity entity, const tombstone_t other) noexcept {
|
||||
return other.operator==(entity);
|
||||
}
|
||||
|
||||
@@ -312,7 +349,7 @@ template<typename Entity>
|
||||
* @return True if the two elements differ, false otherwise.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr bool operator!=(const Entity entity, const tombstone_t other) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr bool operator!=(const Entity entity, const tombstone_t other) noexcept {
|
||||
return !(other == entity);
|
||||
}
|
||||
|
||||
@@ -320,7 +357,7 @@ template<typename Entity>
|
||||
* @brief Compile-time constant for null entities.
|
||||
*
|
||||
* There exist implicit conversions from this variable to identifiers of any
|
||||
* allowed type. Similarly, there exist comparision operators between the null
|
||||
* allowed type. Similarly, there exist comparison operators between the null
|
||||
* entity and any other identifier.
|
||||
*/
|
||||
inline constexpr null_t null{};
|
||||
@@ -329,7 +366,7 @@ inline constexpr null_t null{};
|
||||
* @brief Compile-time constant for tombstone entities.
|
||||
*
|
||||
* There exist implicit conversions from this variable to identifiers of any
|
||||
* allowed type. Similarly, there exist comparision operators between the
|
||||
* allowed type. Similarly, there exist comparison operators between the
|
||||
* tombstone entity and any other identifier.
|
||||
*/
|
||||
inline constexpr tombstone_t tombstone{};
|
||||
|
||||
@@ -1,31 +1,49 @@
|
||||
#ifndef ENTT_ENTITY_FWD_HPP
|
||||
#define ENTT_ENTITY_FWD_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include "../core/fwd.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
template<typename Entity, typename = std::allocator<Entity>>
|
||||
/*! @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, typename Type, typename = std::allocator<Type>, typename = void>
|
||||
template<typename Type, typename = entity, typename = std::allocator<Type>, typename = void>
|
||||
class basic_storage;
|
||||
|
||||
template<typename>
|
||||
template<typename, typename>
|
||||
class basic_sigh_mixin;
|
||||
|
||||
template<typename Entity = entity, typename = std::allocator<Entity>>
|
||||
class basic_registry;
|
||||
|
||||
template<typename, typename, typename, typename = void>
|
||||
template<typename, typename, typename = void>
|
||||
class basic_view;
|
||||
|
||||
template<typename>
|
||||
template<typename Type, typename = std::allocator<Type *>>
|
||||
class basic_runtime_view;
|
||||
|
||||
template<typename, typename, typename, typename>
|
||||
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>
|
||||
@@ -43,74 +61,205 @@ class basic_snapshot_loader;
|
||||
template<typename>
|
||||
class basic_continuous_loader;
|
||||
|
||||
/*! @brief Default entity identifier. */
|
||||
enum class entity : id_type {};
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using sparse_set = basic_sparse_set<>;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Type Type of objects assigned to the entities.
|
||||
*/
|
||||
template<typename Type>
|
||||
using storage = basic_storage<Type>;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @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 sparse_set = basic_sparse_set<entity>;
|
||||
using registry = basic_registry<>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using observer = basic_observer<registry>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using organizer = basic_organizer<registry>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using handle = basic_handle<registry>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using const_handle = basic_handle<const registry>;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Args Other template parameters.
|
||||
*/
|
||||
template<typename... Args>
|
||||
using storage = basic_storage<entity, Args...>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using registry = basic_registry<entity>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using observer = basic_observer<entity>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using organizer = basic_organizer<entity>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using handle = basic_handle<entity>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using const_handle = basic_handle<const entity>;
|
||||
using handle_view = basic_handle<registry, Args...>;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Args Other template parameters.
|
||||
*/
|
||||
template<typename... Args>
|
||||
using handle_view = basic_handle<entity, Args...>;
|
||||
using const_handle_view = basic_handle<const registry, Args...>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using snapshot = basic_snapshot<registry>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using snapshot_loader = basic_snapshot_loader<registry>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using continuous_loader = basic_continuous_loader<registry>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
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 Args Other template parameters.
|
||||
* @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 const_handle_view = basic_handle<const entity, Args...>;
|
||||
using storage_type_t = typename storage_type<Args...>::type;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using snapshot = basic_snapshot<entity>;
|
||||
/**
|
||||
* 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 Alias declaration for the most common use case. */
|
||||
using snapshot_loader = basic_snapshot_loader<entity>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using continuous_loader = basic_continuous_loader<entity>;
|
||||
/**
|
||||
* @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 components iterated by the view.
|
||||
* @tparam Exclude Types of components used to filter the view.
|
||||
* @tparam Get Types of storage iterated by the view.
|
||||
* @tparam Exclude Types of storage used to filter the view.
|
||||
*/
|
||||
template<typename Get, typename Exclude = exclude_t<>>
|
||||
using view = basic_view<entity, Get, Exclude>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using runtime_view = basic_runtime_view<entity>;
|
||||
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.
|
||||
* @tparam Args Other template parameters.
|
||||
* @tparam Owned Types of storage _owned_ by the group.
|
||||
* @tparam Get Types of storage _observed_ by the group.
|
||||
* @tparam Exclude Types of storage used to filter the group.
|
||||
*/
|
||||
template<typename... Args>
|
||||
using group = basic_group<entity, Args...>;
|
||||
template<typename Owned, typename Get, typename Exclude>
|
||||
using group = basic_group<type_list_transform_t<Owned, storage_for>, type_list_transform_t<Get, storage_for>, type_list_transform_t<Exclude, storage_for>>;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,28 +1,102 @@
|
||||
#ifndef ENTT_ENTITY_HANDLE_HPP
|
||||
#define ENTT_ENTITY_HANDLE_HPP
|
||||
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/iterator.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "registry.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename It>
|
||||
class handle_storage_iterator final {
|
||||
template<typename Other>
|
||||
friend class handle_storage_iterator;
|
||||
|
||||
using underlying_type = std::remove_reference_t<typename It::value_type::second_type>;
|
||||
using entity_type = typename underlying_type::entity_type;
|
||||
|
||||
public:
|
||||
using value_type = typename std::iterator_traits<It>::value_type;
|
||||
using pointer = input_iterator_pointer<value_type>;
|
||||
using reference = value_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using iterator_concept = std::forward_iterator_tag;
|
||||
|
||||
constexpr handle_storage_iterator() noexcept
|
||||
: entt{null},
|
||||
it{},
|
||||
last{} {}
|
||||
|
||||
constexpr handle_storage_iterator(entity_type value, It from, It to) noexcept
|
||||
: entt{value},
|
||||
it{from},
|
||||
last{to} {
|
||||
while(it != last && !it->second.contains(entt)) {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr handle_storage_iterator &operator++() noexcept {
|
||||
while(++it != last && !it->second.contains(entt)) {}
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr handle_storage_iterator operator++(int) noexcept {
|
||||
handle_storage_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr reference operator*() const noexcept {
|
||||
return *it;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr pointer operator->() const noexcept {
|
||||
return operator*();
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
friend constexpr bool operator==(const handle_storage_iterator<ILhs> &, const handle_storage_iterator<IRhs> &) noexcept;
|
||||
|
||||
private:
|
||||
entity_type entt;
|
||||
It it;
|
||||
It last;
|
||||
};
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] constexpr bool operator==(const handle_storage_iterator<ILhs> &lhs, const handle_storage_iterator<IRhs> &rhs) noexcept {
|
||||
return lhs.it == rhs.it;
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] constexpr bool operator!=(const handle_storage_iterator<ILhs> &lhs, const handle_storage_iterator<IRhs> &rhs) noexcept {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Non-owning handle to an entity.
|
||||
*
|
||||
* Tiny wrapper around a registry and an entity.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Type Types to which to restrict the scope of a handle.
|
||||
* @tparam Registry Basic registry type.
|
||||
* @tparam Scope Types to which to restrict the scope of a handle.
|
||||
*/
|
||||
template<typename Entity, typename... Type>
|
||||
template<typename Registry, typename... Scope>
|
||||
struct basic_handle {
|
||||
/*! @brief Type of registry accepted by the handle. */
|
||||
using registry_type = constness_as_t<basic_registry<std::remove_const_t<Entity>>, Entity>;
|
||||
using registry_type = Registry;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename registry_type::entity_type;
|
||||
/*! @brief Underlying version type. */
|
||||
@@ -31,7 +105,7 @@ struct basic_handle {
|
||||
using size_type = typename registry_type::size_type;
|
||||
|
||||
/*! @brief Constructs an invalid handle. */
|
||||
basic_handle() ENTT_NOEXCEPT
|
||||
basic_handle() noexcept
|
||||
: reg{},
|
||||
entt{null} {}
|
||||
|
||||
@@ -40,21 +114,37 @@ struct basic_handle {
|
||||
* @param ref An instance of the registry class.
|
||||
* @param value A valid identifier.
|
||||
*/
|
||||
basic_handle(registry_type &ref, entity_type value) ENTT_NOEXCEPT
|
||||
basic_handle(registry_type &ref, entity_type value) noexcept
|
||||
: reg{&ref},
|
||||
entt{value} {}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterable object to use to _visit_ a handle.
|
||||
*
|
||||
* The iterable object returns a pair that contains the name and a reference
|
||||
* to the current storage.<br/>
|
||||
* Returned storage are those that contain the entity associated with the
|
||||
* handle.
|
||||
*
|
||||
* @return An iterable object to use to _visit_ the handle.
|
||||
*/
|
||||
[[nodiscard]] auto storage() const noexcept {
|
||||
auto iterable = reg->storage();
|
||||
using iterator_type = internal::handle_storage_iterator<typename decltype(iterable)::iterator>;
|
||||
return iterable_adaptor{iterator_type{entt, iterable.begin(), iterable.end()}, iterator_type{entt, iterable.end(), iterable.end()}};
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
template<typename Other, typename... Args>
|
||||
operator basic_handle<Other, Args...>() const ENTT_NOEXCEPT {
|
||||
static_assert(std::is_same_v<Other, Entity> || std::is_same_v<std::remove_const_t<Other>, Entity>, "Invalid conversion between different handles");
|
||||
static_assert((sizeof...(Type) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Type)) && ... && (type_list_contains_v<type_list<Type...>, Args>))), "Invalid conversion between different handles");
|
||||
operator basic_handle<Other, Args...>() const noexcept {
|
||||
static_assert(std::is_same_v<Other, Registry> || std::is_same_v<std::remove_const_t<Other>, Registry>, "Invalid conversion between different handles");
|
||||
static_assert((sizeof...(Scope) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Scope)) && ... && (type_list_contains_v<type_list<Scope...>, Args>))), "Invalid conversion between different handles");
|
||||
|
||||
return reg ? basic_handle<Other, Args...>{*reg, entt} : basic_handle<Other, Args...>{};
|
||||
}
|
||||
@@ -63,7 +153,7 @@ struct basic_handle {
|
||||
* @brief Converts a handle to its underlying entity.
|
||||
* @return The contained identifier.
|
||||
*/
|
||||
[[nodiscard]] operator entity_type() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] operator entity_type() const noexcept {
|
||||
return entity();
|
||||
}
|
||||
|
||||
@@ -71,7 +161,7 @@ struct basic_handle {
|
||||
* @brief Checks if a handle refers to non-null registry pointer and entity.
|
||||
* @return True if the handle refers to non-null registry and entity, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] explicit operator bool() const noexcept {
|
||||
return reg && reg->valid(entt);
|
||||
}
|
||||
|
||||
@@ -87,7 +177,7 @@ struct basic_handle {
|
||||
* @brief Returns a pointer to the underlying registry, if any.
|
||||
* @return A pointer to the underlying registry, if any.
|
||||
*/
|
||||
[[nodiscard]] registry_type *registry() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] registry_type *registry() const noexcept {
|
||||
return reg;
|
||||
}
|
||||
|
||||
@@ -95,30 +185,25 @@ struct basic_handle {
|
||||
* @brief Returns the entity associated with a handle.
|
||||
* @return The entity associated with the handle.
|
||||
*/
|
||||
[[nodiscard]] entity_type entity() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] entity_type entity() const noexcept {
|
||||
return entt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroys the entity associated with a handle.
|
||||
* @sa basic_registry::destroy
|
||||
*/
|
||||
/*! @brief Destroys the entity associated with a handle. */
|
||||
void destroy() {
|
||||
reg->destroy(entt);
|
||||
reg->destroy(std::exchange(entt, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroys the entity associated with a handle.
|
||||
* @sa basic_registry::destroy
|
||||
* @param version A desired version upon destruction.
|
||||
*/
|
||||
void destroy(const version_type version) {
|
||||
reg->destroy(entt, version);
|
||||
reg->destroy(std::exchange(entt, null), version);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns the given component to a handle.
|
||||
* @sa basic_registry::emplace
|
||||
* @tparam Component Type of component to create.
|
||||
* @tparam Args Types of arguments to use to construct the component.
|
||||
* @param args Parameters to use to initialize the component.
|
||||
@@ -126,13 +211,12 @@ struct basic_handle {
|
||||
*/
|
||||
template<typename Component, typename... Args>
|
||||
decltype(auto) emplace(Args &&...args) const {
|
||||
static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
|
||||
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
|
||||
return reg->template emplace<Component>(entt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns or replaces the given component for a handle.
|
||||
* @sa basic_registry::emplace_or_replace
|
||||
* @tparam Component Type of component to assign or replace.
|
||||
* @tparam Args Types of arguments to use to construct the component.
|
||||
* @param args Parameters to use to initialize the component.
|
||||
@@ -140,13 +224,12 @@ struct basic_handle {
|
||||
*/
|
||||
template<typename Component, typename... Args>
|
||||
decltype(auto) emplace_or_replace(Args &&...args) const {
|
||||
static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
|
||||
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
|
||||
return reg->template emplace_or_replace<Component>(entt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Patches the given component for a handle.
|
||||
* @sa basic_registry::patch
|
||||
* @tparam Component Type of component to patch.
|
||||
* @tparam Func Types of the function objects to invoke.
|
||||
* @param func Valid function objects.
|
||||
@@ -154,13 +237,12 @@ struct basic_handle {
|
||||
*/
|
||||
template<typename Component, typename... Func>
|
||||
decltype(auto) patch(Func &&...func) const {
|
||||
static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
|
||||
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
|
||||
return reg->template patch<Component>(entt, std::forward<Func>(func)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Replaces the given component for a handle.
|
||||
* @sa basic_registry::replace
|
||||
* @tparam Component Type of component to replace.
|
||||
* @tparam Args Types of arguments to use to construct the component.
|
||||
* @param args Parameters to use to initialize the component.
|
||||
@@ -168,36 +250,33 @@ struct basic_handle {
|
||||
*/
|
||||
template<typename Component, typename... Args>
|
||||
decltype(auto) replace(Args &&...args) const {
|
||||
static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
|
||||
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
|
||||
return reg->template replace<Component>(entt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes the given components from a handle.
|
||||
* @sa basic_registry::remove
|
||||
* @tparam Component Types of components to remove.
|
||||
* @return The number of components actually removed.
|
||||
*/
|
||||
template<typename... Component>
|
||||
size_type remove() const {
|
||||
static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type");
|
||||
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Component> && ...), "Invalid type");
|
||||
return reg->template remove<Component...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erases the given components from a handle.
|
||||
* @sa basic_registry::erase
|
||||
* @tparam Component Types of components to erase.
|
||||
*/
|
||||
template<typename... Component>
|
||||
void erase() const {
|
||||
static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type");
|
||||
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Component> && ...), "Invalid type");
|
||||
reg->template erase<Component...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a handle has all the given components.
|
||||
* @sa basic_registry::all_of
|
||||
* @tparam Component Components for which to perform the check.
|
||||
* @return True if the handle has all the components, false otherwise.
|
||||
*/
|
||||
@@ -208,7 +287,6 @@ struct basic_handle {
|
||||
|
||||
/**
|
||||
* @brief Checks if a handle has at least one of the given components.
|
||||
* @sa basic_registry::any_of
|
||||
* @tparam Component Components for which to perform the check.
|
||||
* @return True if the handle has at least one of the given components,
|
||||
* false otherwise.
|
||||
@@ -220,19 +298,17 @@ struct basic_handle {
|
||||
|
||||
/**
|
||||
* @brief Returns references to the given components for a handle.
|
||||
* @sa basic_registry::get
|
||||
* @tparam Component Types of components to get.
|
||||
* @return References to the components owned by the handle.
|
||||
*/
|
||||
template<typename... Component>
|
||||
[[nodiscard]] decltype(auto) get() const {
|
||||
static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type");
|
||||
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Component> && ...), "Invalid type");
|
||||
return reg->template get<Component...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the given component for a handle.
|
||||
* @sa basic_registry::get_or_emplace
|
||||
* @tparam Component Type of component to get.
|
||||
* @tparam Args Types of arguments to use to construct the component.
|
||||
* @param args Parameters to use to initialize the component.
|
||||
@@ -240,19 +316,18 @@ struct basic_handle {
|
||||
*/
|
||||
template<typename Component, typename... Args>
|
||||
[[nodiscard]] decltype(auto) get_or_emplace(Args &&...args) const {
|
||||
static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
|
||||
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
|
||||
return reg->template get_or_emplace<Component>(entt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns pointers to the given components for a handle.
|
||||
* @sa basic_registry::try_get
|
||||
* @tparam Component Types of components to get.
|
||||
* @return Pointers to the components owned by the handle.
|
||||
*/
|
||||
template<typename... Component>
|
||||
[[nodiscard]] auto try_get() const {
|
||||
static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type");
|
||||
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Component> && ...), "Invalid type");
|
||||
return reg->template try_get<Component...>(entt);
|
||||
}
|
||||
|
||||
@@ -264,30 +339,6 @@ struct basic_handle {
|
||||
return reg->orphan(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Visits a handle and returns the pools for its components.
|
||||
*
|
||||
* The signature of the function should be equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(id_type, const basic_sparse_set<entity_type> &);
|
||||
* @endcode
|
||||
*
|
||||
* Returned pools are those that contain the entity associated with the
|
||||
* handle.
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
void visit(Func &&func) const {
|
||||
for(auto [id, storage]: reg->storage()) {
|
||||
if(storage.contains(entt)) {
|
||||
func(id, storage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
registry_type *reg;
|
||||
entity_type entt;
|
||||
@@ -303,7 +354,7 @@ private:
|
||||
* entity, false otherwise.
|
||||
*/
|
||||
template<typename... Args, typename... Other>
|
||||
[[nodiscard]] bool operator==(const basic_handle<Args...> &lhs, const basic_handle<Other...> &rhs) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool operator==(const basic_handle<Args...> &lhs, const basic_handle<Other...> &rhs) noexcept {
|
||||
return lhs.registry() == rhs.registry() && lhs.entity() == rhs.entity();
|
||||
}
|
||||
|
||||
@@ -317,24 +368,10 @@ template<typename... Args, typename... Other>
|
||||
* entity, true otherwise.
|
||||
*/
|
||||
template<typename... Args, typename... Other>
|
||||
[[nodiscard]] bool operator!=(const basic_handle<Args...> &lhs, const basic_handle<Other...> &rhs) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool operator!=(const basic_handle<Args...> &lhs, const basic_handle<Other...> &rhs) noexcept {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
basic_handle(basic_registry<Entity> &, Entity) -> basic_handle<Entity>;
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
basic_handle(const basic_registry<Entity> &, Entity) -> basic_handle<const Entity>;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,124 +1,113 @@
|
||||
#ifndef ENTT_ENTITY_HELPER_HPP
|
||||
#define ENTT_ENTITY_HELPER_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
#include <utility>
|
||||
#include "../core/fwd.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "../signal/delegate.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "registry.hpp"
|
||||
#include "group.hpp"
|
||||
#include "storage.hpp"
|
||||
#include "view.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @brief Converts a registry to a view.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Registry Basic registry type.
|
||||
*/
|
||||
template<typename Entity>
|
||||
struct as_view {
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = std::remove_const_t<Entity>;
|
||||
template<typename Registry>
|
||||
class as_view {
|
||||
template<typename... Get, typename... Exclude>
|
||||
auto dispatch(get_t<Get...>, exclude_t<Exclude...>) const {
|
||||
return reg.template view<constness_as_t<typename Get::value_type, Get>...>(exclude_t<constness_as_t<typename Exclude::value_type, Exclude>...>{});
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Type of registry to convert. */
|
||||
using registry_type = constness_as_t<basic_registry<entity_type>, Entity>;
|
||||
using registry_type = Registry;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename registry_type::entity_type;
|
||||
|
||||
/**
|
||||
* @brief Constructs a converter for a given registry.
|
||||
* @param source A valid reference to a registry.
|
||||
*/
|
||||
as_view(registry_type &source) ENTT_NOEXCEPT: reg{source} {}
|
||||
as_view(registry_type &source) noexcept
|
||||
: reg{source} {}
|
||||
|
||||
/**
|
||||
* @brief Conversion function from a registry to a view.
|
||||
* @tparam Exclude Types of components used to filter the view.
|
||||
* @tparam Component Type of components used to construct the view.
|
||||
* @tparam Get Type of storage used to construct the view.
|
||||
* @tparam Exclude Types of storage used to filter the view.
|
||||
* @return A newly created view.
|
||||
*/
|
||||
template<typename Exclude, typename... Component>
|
||||
operator basic_view<entity_type, get_t<Component...>, Exclude>() const {
|
||||
return reg.template view<Component...>(Exclude{});
|
||||
template<typename Get, typename Exclude>
|
||||
operator basic_view<Get, Exclude>() const {
|
||||
return dispatch(Get{}, Exclude{});
|
||||
}
|
||||
|
||||
private:
|
||||
registry_type ®
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
as_view(basic_registry<Entity> &) -> as_view<Entity>;
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
as_view(const basic_registry<Entity> &) -> as_view<const Entity>;
|
||||
|
||||
/**
|
||||
* @brief Converts a registry to a group.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Registry Basic registry type.
|
||||
*/
|
||||
template<typename Entity>
|
||||
struct as_group {
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = std::remove_const_t<Entity>;
|
||||
template<typename Registry>
|
||||
class as_group {
|
||||
template<typename... Owned, typename... Get, typename... Exclude>
|
||||
auto dispatch(owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>) const {
|
||||
if constexpr(std::is_const_v<registry_type>) {
|
||||
return reg.template group_if_exists<typename Owned::value_type...>(get_t<typename Get::value_type...>{}, exclude_t<typename Exclude::value_type...>{});
|
||||
} else {
|
||||
return reg.template group<constness_as_t<typename Owned::value_type, Owned>...>(get_t<constness_as_t<typename Get::value_type, Get>...>{}, exclude_t<constness_as_t<typename Exclude::value_type, Exclude>...>{});
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Type of registry to convert. */
|
||||
using registry_type = constness_as_t<basic_registry<entity_type>, Entity>;
|
||||
using registry_type = Registry;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename registry_type::entity_type;
|
||||
|
||||
/**
|
||||
* @brief Constructs a converter for a given registry.
|
||||
* @param source A valid reference to a registry.
|
||||
*/
|
||||
as_group(registry_type &source) ENTT_NOEXCEPT: reg{source} {}
|
||||
as_group(registry_type &source) noexcept
|
||||
: reg{source} {}
|
||||
|
||||
/**
|
||||
* @brief Conversion function from a registry to a group.
|
||||
* @tparam Get Types of components observed by the group.
|
||||
* @tparam Exclude Types of components used to filter the group.
|
||||
* @tparam Owned Types of components owned by the group.
|
||||
* @tparam Owned Types of _owned_ by the group.
|
||||
* @tparam Get Types of storage _observed_ by the group.
|
||||
* @tparam Exclude Types of storage used to filter the group.
|
||||
* @return A newly created group.
|
||||
*/
|
||||
template<typename Get, typename Exclude, typename... Owned>
|
||||
operator basic_group<entity_type, owned_t<Owned...>, Get, Exclude>() const {
|
||||
if constexpr(std::is_const_v<registry_type>) {
|
||||
return reg.template group_if_exists<Owned...>(Get{}, Exclude{});
|
||||
} else {
|
||||
return reg.template group<Owned...>(Get{}, Exclude{});
|
||||
}
|
||||
template<typename Owned, typename Get, typename Exclude>
|
||||
operator basic_group<Owned, Get, Exclude>() const {
|
||||
return dispatch(Owned{}, Get{}, Exclude{});
|
||||
}
|
||||
|
||||
private:
|
||||
registry_type ®
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
as_group(basic_registry<Entity> &) -> as_group<Entity>;
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
as_group(const basic_registry<Entity> &) -> as_group<const Entity>;
|
||||
|
||||
/**
|
||||
* @brief Helper to create a listener that directly invokes a member function.
|
||||
* @tparam Member Member function to invoke on a component of the given type.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Registry Basic registry type.
|
||||
* @param reg A registry that contains the given entity and its components.
|
||||
* @param entt Entity from which to get the component.
|
||||
*/
|
||||
template<auto Member, typename Entity = entity>
|
||||
void invoke(basic_registry<Entity> ®, const Entity entt) {
|
||||
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(basic_registry<Entity> &, const Entity)> func;
|
||||
delegate<void(Registry &, const typename Registry::entity_type)> func;
|
||||
func.template connect<Member>(reg.template get<member_class_t<decltype(Member)>>(entt));
|
||||
func(reg, entt);
|
||||
}
|
||||
@@ -127,23 +116,22 @@ void invoke(basic_registry<Entity> ®, const Entity 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 Entity A valid entity type (see entt_traits for more details).
|
||||
* @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 Entity, typename Component>
|
||||
Entity to_entity(const basic_registry<Entity> ®, const Component &instance) {
|
||||
const auto &storage = reg.template storage<Component>();
|
||||
const typename basic_registry<Entity>::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 += ENTT_PACKED_PAGE) {
|
||||
if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < ENTT_PACKED_PAGE) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -151,6 +139,135 @@ Entity to_entity(const basic_registry<Entity> ®, const Component &instance) {
|
||||
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
|
||||
@@ -6,14 +6,10 @@
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "../signal/delegate.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "registry.hpp"
|
||||
#include "storage.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
@@ -47,7 +43,7 @@ struct basic_collector<> {
|
||||
* @return The updated collector.
|
||||
*/
|
||||
template<typename... AllOf, typename... NoneOf>
|
||||
static constexpr auto group(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT {
|
||||
static constexpr auto group(exclude_t<NoneOf...> = exclude_t{}) noexcept {
|
||||
return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>>{};
|
||||
}
|
||||
|
||||
@@ -57,7 +53,7 @@ struct basic_collector<> {
|
||||
* @return The updated collector.
|
||||
*/
|
||||
template<typename AnyOf>
|
||||
static constexpr auto update() ENTT_NOEXCEPT {
|
||||
static constexpr auto update() noexcept {
|
||||
return basic_collector<matcher<type_list<>, type_list<>, AnyOf>>{};
|
||||
}
|
||||
};
|
||||
@@ -82,7 +78,7 @@ struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule
|
||||
* @return The updated collector.
|
||||
*/
|
||||
template<typename... AllOf, typename... NoneOf>
|
||||
static constexpr auto group(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT {
|
||||
static constexpr auto group(exclude_t<NoneOf...> = exclude_t{}) noexcept {
|
||||
return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>, current_type, Other...>{};
|
||||
}
|
||||
|
||||
@@ -92,7 +88,7 @@ struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule
|
||||
* @return The updated collector.
|
||||
*/
|
||||
template<typename AnyOf>
|
||||
static constexpr auto update() ENTT_NOEXCEPT {
|
||||
static constexpr auto update() noexcept {
|
||||
return basic_collector<matcher<type_list<>, type_list<>, AnyOf>, current_type, Other...>{};
|
||||
}
|
||||
|
||||
@@ -103,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...> = {}) ENTT_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...>{};
|
||||
}
|
||||
@@ -150,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
|
||||
@@ -159,11 +154,13 @@ inline constexpr basic_collector<> collector{};
|
||||
* from the registry before being destroyed to avoid crashes due to dangling
|
||||
* pointers.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Registry Basic registry type.
|
||||
* @tparam Mask Mask type.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Entity>
|
||||
class basic_observer {
|
||||
using payload_type = std::uint32_t;
|
||||
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;
|
||||
@@ -171,43 +168,43 @@ class basic_observer {
|
||||
template<typename... Reject, typename... Require, typename AnyOf>
|
||||
struct matcher_handler<matcher<type_list<Reject...>, type_list<Require...>, AnyOf>> {
|
||||
template<std::size_t Index>
|
||||
static void maybe_valid_if(basic_observer &obs, basic_registry<Entity> ®, const Entity entt) {
|
||||
static void maybe_valid_if(basic_observer &obs, Registry ®, const typename Registry::entity_type entt) {
|
||||
if(reg.template all_of<Require...>(entt) && !reg.template any_of<Reject...>(entt)) {
|
||||
if(!obs.storage.contains(entt)) {
|
||||
obs.storage.emplace(entt);
|
||||
if(!obs.contains(entt)) {
|
||||
obs.emplace(entt);
|
||||
}
|
||||
|
||||
obs.storage.get(entt) |= (1 << Index);
|
||||
obs.get(entt) |= (1 << Index);
|
||||
}
|
||||
}
|
||||
|
||||
template<std::size_t Index>
|
||||
static void discard_if(basic_observer &obs, basic_registry<Entity> &, const Entity entt) {
|
||||
if(obs.storage.contains(entt) && !(obs.storage.get(entt) &= (~(1 << Index)))) {
|
||||
obs.storage.erase(entt);
|
||||
static void discard_if(basic_observer &obs, Registry &, const typename Registry::entity_type entt) {
|
||||
if(obs.contains(entt) && !(obs.get(entt) &= (~(1 << Index)))) {
|
||||
obs.erase(entt);
|
||||
}
|
||||
}
|
||||
|
||||
template<std::size_t Index>
|
||||
static void connect(basic_observer &obs, basic_registry<Entity> ®) {
|
||||
static void connect(basic_observer &obs, Registry ®) {
|
||||
(reg.template on_destroy<Require>().template connect<&discard_if<Index>>(obs), ...);
|
||||
(reg.template on_construct<Reject>().template connect<&discard_if<Index>>(obs), ...);
|
||||
reg.template on_update<AnyOf>().template connect<&maybe_valid_if<Index>>(obs);
|
||||
reg.template on_destroy<AnyOf>().template connect<&discard_if<Index>>(obs);
|
||||
}
|
||||
|
||||
static void disconnect(basic_observer &obs, basic_registry<Entity> ®) {
|
||||
(reg.template on_destroy<Require>().disconnect(obs), ...);
|
||||
(reg.template on_construct<Reject>().disconnect(obs), ...);
|
||||
reg.template on_update<AnyOf>().disconnect(obs);
|
||||
reg.template on_destroy<AnyOf>().disconnect(obs);
|
||||
static void disconnect(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);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Reject, typename... Require, typename... NoneOf, typename... AllOf>
|
||||
struct matcher_handler<matcher<type_list<Reject...>, type_list<Require...>, type_list<NoneOf...>, AllOf...>> {
|
||||
template<std::size_t Index, typename... Ignore>
|
||||
static void maybe_valid_if(basic_observer &obs, basic_registry<Entity> ®, const Entity entt) {
|
||||
static void maybe_valid_if(basic_observer &obs, Registry ®, const typename Registry::entity_type entt) {
|
||||
auto condition = [®, entt]() {
|
||||
if constexpr(sizeof...(Ignore) == 0) {
|
||||
return reg.template all_of<AllOf..., Require...>(entt) && !reg.template any_of<NoneOf..., Reject...>(entt);
|
||||
@@ -217,23 +214,23 @@ class basic_observer {
|
||||
};
|
||||
|
||||
if(condition()) {
|
||||
if(!obs.storage.contains(entt)) {
|
||||
obs.storage.emplace(entt);
|
||||
if(!obs.contains(entt)) {
|
||||
obs.emplace(entt);
|
||||
}
|
||||
|
||||
obs.storage.get(entt) |= (1 << Index);
|
||||
obs.get(entt) |= (1 << Index);
|
||||
}
|
||||
}
|
||||
|
||||
template<std::size_t Index>
|
||||
static void discard_if(basic_observer &obs, basic_registry<Entity> &, const Entity entt) {
|
||||
if(obs.storage.contains(entt) && !(obs.storage.get(entt) &= (~(1 << Index)))) {
|
||||
obs.storage.erase(entt);
|
||||
static void discard_if(basic_observer &obs, Registry &, const typename Registry::entity_type entt) {
|
||||
if(obs.contains(entt) && !(obs.get(entt) &= (~(1 << Index)))) {
|
||||
obs.erase(entt);
|
||||
}
|
||||
}
|
||||
|
||||
template<std::size_t Index>
|
||||
static void connect(basic_observer &obs, basic_registry<Entity> ®) {
|
||||
static void connect(basic_observer &obs, Registry ®) {
|
||||
(reg.template on_destroy<Require>().template connect<&discard_if<Index>>(obs), ...);
|
||||
(reg.template on_construct<Reject>().template connect<&discard_if<Index>>(obs), ...);
|
||||
(reg.template on_construct<AllOf>().template connect<&maybe_valid_if<Index>>(obs), ...);
|
||||
@@ -242,43 +239,55 @@ class basic_observer {
|
||||
(reg.template on_construct<NoneOf>().template connect<&discard_if<Index>>(obs), ...);
|
||||
}
|
||||
|
||||
static void disconnect(basic_observer &obs, basic_registry<Entity> ®) {
|
||||
(reg.template on_destroy<Require>().disconnect(obs), ...);
|
||||
(reg.template on_construct<Reject>().disconnect(obs), ...);
|
||||
(reg.template on_construct<AllOf>().disconnect(obs), ...);
|
||||
(reg.template on_destroy<NoneOf>().disconnect(obs), ...);
|
||||
(reg.template on_destroy<AllOf>().disconnect(obs), ...);
|
||||
(reg.template on_construct<NoneOf>().disconnect(obs), ...);
|
||||
static void disconnect(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), ...);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Matcher>
|
||||
static void disconnect(basic_registry<Entity> ®, basic_observer &obs) {
|
||||
static void disconnect(Registry ®, basic_observer &obs) {
|
||||
(matcher_handler<Matcher>::disconnect(obs, reg), ...);
|
||||
}
|
||||
|
||||
template<typename... Matcher, std::size_t... Index>
|
||||
void connect(basic_registry<Entity> ®, std::index_sequence<Index...>) {
|
||||
static_assert(sizeof...(Matcher) < std::numeric_limits<payload_type>::digits, "Too many matchers");
|
||||
void connect(Registry ®, std::index_sequence<Index...>) {
|
||||
static_assert(sizeof...(Matcher) < std::numeric_limits<typename base_type::value_type>::digits, "Too many matchers");
|
||||
(matcher_handler<Matcher>::template connect<Index>(*this, reg), ...);
|
||||
release.template connect<&basic_observer::disconnect<Matcher...>>(reg);
|
||||
}
|
||||
|
||||
public:
|
||||
/*! Basic registry type. */
|
||||
using registry_type = Registry;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
using entity_type = typename registry_type::entity_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator = typename basic_sparse_set<Entity>::iterator;
|
||||
using iterator = typename registry_type::common_type::iterator;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
basic_observer()
|
||||
: release{},
|
||||
storage{} {}
|
||||
: 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;
|
||||
|
||||
@@ -286,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(basic_registry<entity_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.
|
||||
@@ -314,10 +321,10 @@ public:
|
||||
* @param reg A valid reference to a registry.
|
||||
*/
|
||||
template<typename... Matcher>
|
||||
void connect(basic_registry<entity_type> ®, basic_collector<Matcher...>) {
|
||||
void connect(registry_type ®, basic_collector<Matcher...>) {
|
||||
disconnect();
|
||||
connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{});
|
||||
storage.clear();
|
||||
base_type::clear();
|
||||
}
|
||||
|
||||
/*! @brief Disconnects an observer from the registry it keeps track of. */
|
||||
@@ -332,16 +339,16 @@ public:
|
||||
* @brief Returns the number of elements in an observer.
|
||||
* @return Number of elements.
|
||||
*/
|
||||
[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
|
||||
return storage.size();
|
||||
[[nodiscard]] size_type size() const noexcept {
|
||||
return base_type::size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether an observer is empty.
|
||||
* @return True if the observer is empty, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
|
||||
return storage.empty();
|
||||
[[nodiscard]] bool empty() const noexcept {
|
||||
return base_type::empty();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -356,39 +363,33 @@ public:
|
||||
*
|
||||
* @return A pointer to the array of entities.
|
||||
*/
|
||||
[[nodiscard]] const entity_type *data() const ENTT_NOEXCEPT {
|
||||
return storage.data();
|
||||
[[nodiscard]] const entity_type *data() const noexcept {
|
||||
return base_type::data();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
|
||||
return storage.basic_sparse_set<entity_type>::begin();
|
||||
[[nodiscard]] iterator begin() const noexcept {
|
||||
return base_type::base_type::begin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
|
||||
return storage.basic_sparse_set<entity_type>::end();
|
||||
[[nodiscard]] iterator end() const noexcept {
|
||||
return base_type::base_type::end();
|
||||
}
|
||||
|
||||
/*! @brief Clears the underlying container. */
|
||||
void clear() ENTT_NOEXCEPT {
|
||||
storage.clear();
|
||||
void clear() noexcept {
|
||||
base_type::clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -428,7 +429,6 @@ public:
|
||||
|
||||
private:
|
||||
delegate<void(basic_observer &)> release;
|
||||
basic_storage<entity_type, payload_type> storage;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -1,32 +1,28 @@
|
||||
#ifndef ENTT_ENTITY_ORGANIZER_HPP
|
||||
#define ENTT_ENTITY_ORGANIZER_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../container/dense_hash_map.hpp"
|
||||
#include "../core/type_info.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "../core/utility.hpp"
|
||||
#include "../graph/adjacency_matrix.hpp"
|
||||
#include "../graph/flow.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "helper.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename>
|
||||
struct is_view: std::false_type {};
|
||||
|
||||
template<typename Entity, typename... Component, typename... Exclude>
|
||||
struct is_view<basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>>>: std::true_type {};
|
||||
template<typename... Args>
|
||||
struct is_view<basic_view<Args...>>: std::true_type {};
|
||||
|
||||
template<typename Type>
|
||||
inline constexpr bool is_view_v = is_view<Type>::value;
|
||||
@@ -34,67 +30,60 @@ inline constexpr bool is_view_v = is_view<Type>::value;
|
||||
template<typename Type, typename Override>
|
||||
struct unpack_type {
|
||||
using ro = std::conditional_t<
|
||||
type_list_contains_v<Override, std::add_const_t<Type>> || (std::is_const_v<Type> && !type_list_contains_v<Override, std::remove_const_t<Type>>),
|
||||
type_list_contains_v<Override, const Type> || (std::is_const_v<Type> && !type_list_contains_v<Override, std::remove_const_t<Type>>),
|
||||
type_list<std::remove_const_t<Type>>,
|
||||
type_list<>>;
|
||||
|
||||
using rw = std::conditional_t<
|
||||
type_list_contains_v<Override, std::remove_const_t<Type>> || (!std::is_const_v<Type> && !type_list_contains_v<Override, std::add_const_t<Type>>),
|
||||
type_list_contains_v<Override, std::remove_const_t<Type>> || (!std::is_const_v<Type> && !type_list_contains_v<Override, const Type>),
|
||||
type_list<Type>,
|
||||
type_list<>>;
|
||||
};
|
||||
|
||||
template<typename Entity, typename... Override>
|
||||
struct unpack_type<basic_registry<Entity>, type_list<Override...>> {
|
||||
template<typename... Args, typename... Override>
|
||||
struct unpack_type<basic_registry<Args...>, type_list<Override...>> {
|
||||
using ro = type_list<>;
|
||||
using rw = type_list<>;
|
||||
};
|
||||
|
||||
template<typename Entity, typename... Override>
|
||||
struct unpack_type<const basic_registry<Entity>, type_list<Override...>>
|
||||
: unpack_type<basic_registry<Entity>, type_list<Override...>> {};
|
||||
template<typename... Args, typename... Override>
|
||||
struct unpack_type<const basic_registry<Args...>, type_list<Override...>>
|
||||
: unpack_type<basic_registry<Args...>, type_list<Override...>> {};
|
||||
|
||||
template<typename Entity, typename... Component, typename... Exclude, typename... Override>
|
||||
struct unpack_type<basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>>, type_list<Override...>> {
|
||||
using ro = type_list_cat_t<type_list<Exclude...>, typename unpack_type<Component, type_list<Override...>>::ro...>;
|
||||
using rw = type_list_cat_t<typename unpack_type<Component, type_list<Override...>>::rw...>;
|
||||
template<typename... Get, typename... Exclude, typename... Override>
|
||||
struct unpack_type<basic_view<get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>> {
|
||||
using ro = type_list_cat_t<type_list<typename Exclude::value_type...>, typename unpack_type<constness_as_t<typename Get::value_type, Get>, type_list<Override...>>::ro...>;
|
||||
using rw = type_list_cat_t<typename unpack_type<constness_as_t<typename Get::value_type, Get>, type_list<Override...>>::rw...>;
|
||||
};
|
||||
|
||||
template<typename Entity, typename... Component, typename... Exclude, typename... Override>
|
||||
struct unpack_type<const basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>>, type_list<Override...>>
|
||||
: unpack_type<basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>>, type_list<Override...>> {};
|
||||
template<typename... Get, typename... Exclude, typename... Override>
|
||||
struct unpack_type<const basic_view<get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>>
|
||||
: unpack_type<basic_view<get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>> {};
|
||||
|
||||
template<typename, typename>
|
||||
struct resource;
|
||||
struct resource_traits;
|
||||
|
||||
template<typename... Args, typename... Req>
|
||||
struct resource<type_list<Args...>, type_list<Req...>> {
|
||||
struct resource_traits<type_list<Args...>, type_list<Req...>> {
|
||||
using args = type_list<std::remove_const_t<Args>...>;
|
||||
using ro = type_list_cat_t<typename unpack_type<Args, type_list<Req...>>::ro..., typename unpack_type<Req, type_list<>>::ro...>;
|
||||
using rw = type_list_cat_t<typename unpack_type<Args, type_list<Req...>>::rw..., typename unpack_type<Req, type_list<>>::rw...>;
|
||||
};
|
||||
|
||||
template<typename... Req, typename Ret, typename... Args>
|
||||
resource<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> free_function_to_resource(Ret (*)(Args...));
|
||||
resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> free_function_to_resource_traits(Ret (*)(Args...));
|
||||
|
||||
template<typename... Req, typename Ret, typename Type, typename... Args>
|
||||
resource<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource(Ret (*)(Type &, Args...));
|
||||
resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (*)(Type &, Args...));
|
||||
|
||||
template<typename... Req, typename Ret, typename Class, typename... Args>
|
||||
resource<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource(Ret (Class::*)(Args...));
|
||||
resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...));
|
||||
|
||||
template<typename... Req, typename Ret, typename Class, typename... Args>
|
||||
resource<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource(Ret (Class::*)(Args...) const);
|
||||
|
||||
template<typename... Req>
|
||||
resource<type_list<>, type_list<Req...>> to_resource();
|
||||
resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...) const);
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Utility class for creating a static task graph.
|
||||
@@ -105,12 +94,12 @@ resource<type_list<>, type_list<Req...>> to_resource();
|
||||
* goal of the tool. Instead, they are returned to the user in the form of a
|
||||
* graph that allows for safe execution.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Registry Basic registry type.
|
||||
*/
|
||||
template<typename Entity>
|
||||
template<typename Registry>
|
||||
class basic_organizer final {
|
||||
using callback_type = void(const void *, basic_registry<Entity> &);
|
||||
using prepare_type = void(basic_registry<Entity> &);
|
||||
using callback_type = void(const void *, Registry &);
|
||||
using prepare_type = void(Registry &);
|
||||
using dependency_type = std::size_t(const bool, const type_info **, const std::size_t);
|
||||
|
||||
struct vertex_data final {
|
||||
@@ -125,18 +114,18 @@ class basic_organizer final {
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] static decltype(auto) extract(basic_registry<Entity> ®) {
|
||||
if constexpr(std::is_same_v<Type, basic_registry<Entity>>) {
|
||||
[[nodiscard]] static decltype(auto) extract(Registry ®) {
|
||||
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.template ctx_or_set<std::remove_reference_t<Type>>();
|
||||
return reg.ctx().template emplace<std::remove_reference_t<Type>>();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
[[nodiscard]] static auto to_args(basic_registry<Entity> ®, type_list<Args...>) {
|
||||
[[nodiscard]] static auto to_args(Registry ®, type_list<Args...>) {
|
||||
return std::tuple<decltype(extract<Args>(reg))...>(extract<Args>(reg)...);
|
||||
}
|
||||
|
||||
@@ -146,92 +135,29 @@ class basic_organizer final {
|
||||
return {};
|
||||
} else {
|
||||
const type_info *info[sizeof...(Type)]{&type_id<Type>()...};
|
||||
const auto length = (std::min)(count, sizeof...(Type));
|
||||
std::copy_n(info, length, buffer);
|
||||
const auto length = count < sizeof...(Type) ? count : sizeof...(Type);
|
||||
|
||||
for(std::size_t pos{}; pos < length; ++pos) {
|
||||
buffer[pos] = info[pos];
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... RO, typename... RW>
|
||||
void track_dependencies(std::size_t index, const bool requires_registry, type_list<RO...>, type_list<RW...>) {
|
||||
dependencies[type_hash<basic_registry<Entity>>::value()].emplace_back(index, requires_registry || (sizeof...(RO) + sizeof...(RW) == 0u));
|
||||
(dependencies[type_hash<RO>::value()].emplace_back(index, false), ...);
|
||||
(dependencies[type_hash<RW>::value()].emplace_back(index, true), ...);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<bool> adjacency_matrix() {
|
||||
const auto length = vertices.size();
|
||||
std::vector<bool> edges(length * length, false);
|
||||
|
||||
// creates the ajacency matrix
|
||||
for(const auto &deps: dependencies) {
|
||||
const auto last = deps.second.cend();
|
||||
auto it = deps.second.cbegin();
|
||||
|
||||
while(it != last) {
|
||||
if(it->second) {
|
||||
// rw item
|
||||
if(auto curr = it++; it != last) {
|
||||
if(it->second) {
|
||||
edges[curr->first * length + it->first] = true;
|
||||
} else {
|
||||
if(const auto next = std::find_if(it, last, [](const auto &elem) { return elem.second; }); next != last) {
|
||||
for(; it != next; ++it) {
|
||||
edges[curr->first * length + it->first] = true;
|
||||
edges[it->first * length + next->first] = true;
|
||||
}
|
||||
} else {
|
||||
for(; it != next; ++it) {
|
||||
edges[curr->first * length + it->first] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// ro item, possibly only on first iteration
|
||||
if(const auto next = std::find_if(it, last, [](const auto &elem) { return elem.second; }); next != last) {
|
||||
for(; it != next; ++it) {
|
||||
edges[it->first * length + next->first] = true;
|
||||
}
|
||||
} else {
|
||||
it = last;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// computes the transitive closure
|
||||
for(std::size_t vk{}; vk < length; ++vk) {
|
||||
for(std::size_t vi{}; vi < length; ++vi) {
|
||||
for(std::size_t vj{}; vj < length; ++vj) {
|
||||
edges[vi * length + vj] = edges[vi * length + vj] || (edges[vi * length + vk] && edges[vk * length + vj]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// applies the transitive reduction
|
||||
for(std::size_t vert{}; vert < length; ++vert) {
|
||||
edges[vert * length + vert] = false;
|
||||
}
|
||||
|
||||
for(std::size_t vj{}; vj < length; ++vj) {
|
||||
for(std::size_t vi{}; vi < length; ++vi) {
|
||||
if(edges[vi * length + vj]) {
|
||||
for(std::size_t vk{}; vk < length; ++vk) {
|
||||
if(edges[vj * length + vk]) {
|
||||
edges[vi * length + vk] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return edges;
|
||||
builder.bind(static_cast<id_type>(index));
|
||||
builder.set(type_hash<Registry>::value(), requires_registry || (sizeof...(RO) + sizeof...(RW) == 0u));
|
||||
(builder.ro(type_hash<RO>::value()), ...);
|
||||
(builder.rw(type_hash<RW>::value()), ...);
|
||||
}
|
||||
|
||||
public:
|
||||
/*! Basic registry type. */
|
||||
using registry_type = Registry;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
using entity_type = typename registry_type::entity_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Raw task function type. */
|
||||
@@ -257,7 +183,7 @@ public:
|
||||
* @param length The length of the user-supplied buffer.
|
||||
* @return The number of type info objects written to the buffer.
|
||||
*/
|
||||
size_type ro_dependency(const type_info **buffer, const std::size_t length) const ENTT_NOEXCEPT {
|
||||
size_type ro_dependency(const type_info **buffer, const std::size_t length) const noexcept {
|
||||
return node.dependency(false, buffer, length);
|
||||
}
|
||||
|
||||
@@ -268,7 +194,7 @@ public:
|
||||
* @param length The length of the user-supplied buffer.
|
||||
* @return The number of type info objects written to the buffer.
|
||||
*/
|
||||
size_type rw_dependency(const type_info **buffer, const std::size_t length) const ENTT_NOEXCEPT {
|
||||
size_type rw_dependency(const type_info **buffer, const std::size_t length) const noexcept {
|
||||
return node.dependency(true, buffer, length);
|
||||
}
|
||||
|
||||
@@ -276,7 +202,7 @@ public:
|
||||
* @brief Returns the number of read-only resources of a vertex.
|
||||
* @return The number of read-only resources of the vertex.
|
||||
*/
|
||||
size_type ro_count() const ENTT_NOEXCEPT {
|
||||
size_type ro_count() const noexcept {
|
||||
return node.ro_count;
|
||||
}
|
||||
|
||||
@@ -284,7 +210,7 @@ public:
|
||||
* @brief Returns the number of writable resources of a vertex.
|
||||
* @return The number of writable resources of the vertex.
|
||||
*/
|
||||
size_type rw_count() const ENTT_NOEXCEPT {
|
||||
size_type rw_count() const noexcept {
|
||||
return node.rw_count;
|
||||
}
|
||||
|
||||
@@ -292,7 +218,7 @@ public:
|
||||
* @brief Checks if a vertex is also a top-level one.
|
||||
* @return True if the vertex is a top-level one, false otherwise.
|
||||
*/
|
||||
bool top_level() const ENTT_NOEXCEPT {
|
||||
bool top_level() const noexcept {
|
||||
return is_top_level;
|
||||
}
|
||||
|
||||
@@ -300,7 +226,7 @@ public:
|
||||
* @brief Returns a type info object associated with a vertex.
|
||||
* @return A properly initialized type info object.
|
||||
*/
|
||||
const type_info &info() const ENTT_NOEXCEPT {
|
||||
const type_info &info() const noexcept {
|
||||
return *node.info;
|
||||
}
|
||||
|
||||
@@ -308,7 +234,7 @@ public:
|
||||
* @brief Returns a user defined name associated with a vertex, if any.
|
||||
* @return The user defined name associated with the vertex, if any.
|
||||
*/
|
||||
const char *name() const ENTT_NOEXCEPT {
|
||||
const char *name() const noexcept {
|
||||
return node.name;
|
||||
}
|
||||
|
||||
@@ -316,7 +242,7 @@ public:
|
||||
* @brief Returns the function associated with a vertex.
|
||||
* @return The function associated with the vertex.
|
||||
*/
|
||||
function_type *callback() const ENTT_NOEXCEPT {
|
||||
function_type *callback() const noexcept {
|
||||
return node.callback;
|
||||
}
|
||||
|
||||
@@ -324,7 +250,7 @@ public:
|
||||
* @brief Returns the payload associated with a vertex, if any.
|
||||
* @return The payload associated with the vertex, if any.
|
||||
*/
|
||||
const void *data() const ENTT_NOEXCEPT {
|
||||
const void *data() const noexcept {
|
||||
return node.payload;
|
||||
}
|
||||
|
||||
@@ -332,7 +258,7 @@ public:
|
||||
* @brief Returns the list of nodes reachable from a given vertex.
|
||||
* @return The list of nodes reachable from the vertex.
|
||||
*/
|
||||
const std::vector<std::size_t> &children() const ENTT_NOEXCEPT {
|
||||
const std::vector<std::size_t> &children() const noexcept {
|
||||
return reachable;
|
||||
}
|
||||
|
||||
@@ -341,7 +267,7 @@ public:
|
||||
* are properly instantiated before using them.
|
||||
* @param reg A valid registry.
|
||||
*/
|
||||
void prepare(basic_registry<entity_type> ®) const {
|
||||
void prepare(registry_type ®) const {
|
||||
node.prepare ? node.prepare(reg) : void();
|
||||
}
|
||||
|
||||
@@ -359,10 +285,10 @@ public:
|
||||
*/
|
||||
template<auto Candidate, typename... Req>
|
||||
void emplace(const char *name = nullptr) {
|
||||
using resource_type = decltype(internal::free_function_to_resource<Req...>(Candidate));
|
||||
constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, basic_registry<entity_type>>;
|
||||
using resource_type = decltype(internal::free_function_to_resource_traits<Req...>(Candidate));
|
||||
constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, registry_type>;
|
||||
|
||||
callback_type *callback = +[](const void *, basic_registry<entity_type> ®) {
|
||||
callback_type *callback = +[](const void *, registry_type ®) {
|
||||
std::apply(Candidate, to_args(reg, typename resource_type::args{}));
|
||||
};
|
||||
|
||||
@@ -373,7 +299,7 @@ public:
|
||||
nullptr,
|
||||
callback,
|
||||
+[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); },
|
||||
+[](basic_registry<entity_type> ®) { void(to_args(reg, typename resource_type::args{})); },
|
||||
+[](registry_type ®) { void(to_args(reg, typename resource_type::args{})); },
|
||||
&type_id<std::integral_constant<decltype(Candidate), Candidate>>()};
|
||||
|
||||
track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{});
|
||||
@@ -391,10 +317,10 @@ public:
|
||||
*/
|
||||
template<auto Candidate, typename... Req, typename Type>
|
||||
void emplace(Type &value_or_instance, const char *name = nullptr) {
|
||||
using resource_type = decltype(internal::constrained_function_to_resource<Req...>(Candidate));
|
||||
constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, basic_registry<entity_type>>;
|
||||
using resource_type = decltype(internal::constrained_function_to_resource_traits<Req...>(Candidate));
|
||||
constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, registry_type>;
|
||||
|
||||
callback_type *callback = +[](const void *payload, basic_registry<entity_type> ®) {
|
||||
callback_type *callback = +[](const void *payload, registry_type ®) {
|
||||
Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
|
||||
std::apply(Candidate, std::tuple_cat(std::forward_as_tuple(*curr), to_args(reg, typename resource_type::args{})));
|
||||
};
|
||||
@@ -406,7 +332,7 @@ public:
|
||||
&value_or_instance,
|
||||
callback,
|
||||
+[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); },
|
||||
+[](basic_registry<entity_type> ®) { void(to_args(reg, typename resource_type::args{})); },
|
||||
+[](registry_type ®) { void(to_args(reg, typename resource_type::args{})); },
|
||||
&type_id<std::integral_constant<decltype(Candidate), Candidate>>()};
|
||||
|
||||
track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{});
|
||||
@@ -423,7 +349,7 @@ public:
|
||||
*/
|
||||
template<typename... Req>
|
||||
void emplace(function_type *func, const void *payload = nullptr, const char *name = nullptr) {
|
||||
using resource_type = internal::resource<type_list<>, type_list<Req...>>;
|
||||
using resource_type = internal::resource_traits<type_list<>, type_list<Req...>>;
|
||||
track_dependencies(vertices.size(), true, typename resource_type::ro{}, typename resource_type::rw{});
|
||||
|
||||
vertex_data vdata{
|
||||
@@ -444,28 +370,19 @@ public:
|
||||
* @return The adjacency list of the task graph.
|
||||
*/
|
||||
std::vector<vertex> graph() {
|
||||
const auto edges = adjacency_matrix();
|
||||
|
||||
// creates the adjacency list
|
||||
std::vector<vertex> adjacency_list{};
|
||||
adjacency_list.reserve(vertices.size());
|
||||
auto adjacency_matrix = builder.graph();
|
||||
|
||||
for(std::size_t col{}, length = vertices.size(); col < length; ++col) {
|
||||
for(auto curr: adjacency_matrix.vertices()) {
|
||||
const auto iterable = adjacency_matrix.in_edges(curr);
|
||||
std::vector<std::size_t> reachable{};
|
||||
const auto row = col * length;
|
||||
bool is_top_level = true;
|
||||
|
||||
for(std::size_t next{}; next < length; ++next) {
|
||||
if(edges[row + next]) {
|
||||
reachable.push_back(next);
|
||||
}
|
||||
for(auto &&edge: adjacency_matrix.out_edges(curr)) {
|
||||
reachable.push_back(edge.second);
|
||||
}
|
||||
|
||||
for(std::size_t next{}; next < length && is_top_level; ++next) {
|
||||
is_top_level = !edges[next * length + col];
|
||||
}
|
||||
|
||||
adjacency_list.emplace_back(is_top_level, vertices[col], std::move(reachable));
|
||||
adjacency_list.emplace_back(iterable.cbegin() == iterable.cend(), vertices[curr], std::move(reachable));
|
||||
}
|
||||
|
||||
return adjacency_list;
|
||||
@@ -473,13 +390,13 @@ public:
|
||||
|
||||
/*! @brief Erases all elements from a container. */
|
||||
void clear() {
|
||||
dependencies.clear();
|
||||
builder.clear();
|
||||
vertices.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
dense_hash_map<id_type, std::vector<std::pair<std::size_t, bool>>, identity> dependencies;
|
||||
std::vector<vertex_data> vertices;
|
||||
flow builder;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,45 +2,46 @@
|
||||
#define ENTT_ENTITY_RUNTIME_VIEW_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../config/config.h"
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "sparse_set.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename Type>
|
||||
template<typename Set>
|
||||
class runtime_view_iterator final {
|
||||
using iterator_type = typename Set::iterator;
|
||||
|
||||
[[nodiscard]] bool valid() const {
|
||||
return std::all_of(pools->begin(), pools->end(), [entt = *it](const auto *curr) { return curr->contains(entt); })
|
||||
return (!tombstone_check || *it != tombstone)
|
||||
&& std::all_of(++pools->begin(), pools->end(), [entt = *it](const auto *curr) { return curr->contains(entt); })
|
||||
&& std::none_of(filter->cbegin(), filter->cend(), [entt = *it](const auto *curr) { return curr && curr->contains(entt); });
|
||||
}
|
||||
|
||||
public:
|
||||
using iterator_type = typename Type::iterator;
|
||||
using difference_type = typename iterator_type::difference_type;
|
||||
using value_type = typename iterator_type::value_type;
|
||||
using pointer = typename iterator_type::pointer;
|
||||
using reference = typename iterator_type::reference;
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
|
||||
runtime_view_iterator() ENTT_NOEXCEPT = default;
|
||||
constexpr runtime_view_iterator() noexcept
|
||||
: pools{},
|
||||
filter{},
|
||||
it{},
|
||||
tombstone_check{} {}
|
||||
|
||||
runtime_view_iterator(const std::vector<const Type *> &cpools, const std::vector<const Type *> &ignore, iterator_type curr) ENTT_NOEXCEPT
|
||||
runtime_view_iterator(const std::vector<Set *> &cpools, const std::vector<Set *> &ignore, iterator_type curr) noexcept
|
||||
: pools{&cpools},
|
||||
filter{&ignore},
|
||||
it{curr} {
|
||||
it{curr},
|
||||
tombstone_check{pools->size() == 1u && (*pools)[0u]->policy() == deletion_policy::in_place} {
|
||||
if(it != (*pools)[0]->end() && !valid()) {
|
||||
++(*this);
|
||||
}
|
||||
@@ -56,118 +57,178 @@ public:
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
runtime_view_iterator &operator--() ENTT_NOEXCEPT {
|
||||
runtime_view_iterator &operator--() {
|
||||
while(--it != (*pools)[0]->begin() && !valid()) {}
|
||||
return *this;
|
||||
}
|
||||
|
||||
runtime_view_iterator operator--(int) ENTT_NOEXCEPT {
|
||||
runtime_view_iterator operator--(int) {
|
||||
runtime_view_iterator orig = *this;
|
||||
return operator--(), orig;
|
||||
}
|
||||
|
||||
[[nodiscard]] pointer operator->() const {
|
||||
[[nodiscard]] pointer operator->() const noexcept {
|
||||
return it.operator->();
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator*() const {
|
||||
[[nodiscard]] reference operator*() const noexcept {
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const runtime_view_iterator &other) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr bool operator==(const runtime_view_iterator &other) const noexcept {
|
||||
return it == other.it;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator!=(const runtime_view_iterator &other) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr bool operator!=(const runtime_view_iterator &other) const noexcept {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
private:
|
||||
const std::vector<const Type *> *pools;
|
||||
const std::vector<const Type *> *filter;
|
||||
const std::vector<Set *> *pools;
|
||||
const std::vector<Set *> *filter;
|
||||
iterator_type it;
|
||||
bool tombstone_check;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Runtime view.
|
||||
* @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.
|
||||
* In all other cases, modifying the storage iterated by the view in any way
|
||||
* invalidates all the iterators.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Type Common base type.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Entity>
|
||||
class basic_runtime_view final {
|
||||
using basic_common_type = basic_sparse_set<Entity>;
|
||||
|
||||
[[nodiscard]] bool valid() const {
|
||||
return !pools.empty() && pools.front();
|
||||
}
|
||||
template<typename Type, typename Allocator>
|
||||
class basic_runtime_view {
|
||||
using alloc_traits = std::allocator_traits<Allocator>;
|
||||
static_assert(std::is_same_v<typename alloc_traits::value_type, Type *>, "Invalid value type");
|
||||
using container_type = std::vector<Type *, Allocator>;
|
||||
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
using entity_type = typename Type::entity_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Common type among all storage types. */
|
||||
using common_type = Type;
|
||||
/*! @brief Bidirectional iterator type. */
|
||||
using iterator = internal::runtime_view_iterator<basic_common_type>;
|
||||
using iterator = internal::runtime_view_iterator<common_type>;
|
||||
|
||||
/*! @brief Default constructor to use to create empty, invalid views. */
|
||||
basic_runtime_view() ENTT_NOEXCEPT
|
||||
: pools{},
|
||||
filter{} {}
|
||||
basic_runtime_view() noexcept
|
||||
: basic_runtime_view{allocator_type{}} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a runtime view from a set of storage classes.
|
||||
* @param cpools The storage for the types to iterate.
|
||||
* @param epools The storage for the types used to filter the view.
|
||||
* @brief Constructs an empty, invalid view with a given allocator.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
basic_runtime_view(std::vector<const basic_common_type *> cpools, std::vector<const basic_common_type *> epools) ENTT_NOEXCEPT
|
||||
: pools{std::move(cpools)},
|
||||
filter{std::move(epools)} {
|
||||
auto candidate = std::min_element(pools.begin(), pools.end(), [](const auto *lhs, const auto *rhs) {
|
||||
return (!lhs && rhs) || (lhs && rhs && lhs->size() < rhs->size());
|
||||
});
|
||||
explicit basic_runtime_view(const allocator_type &allocator)
|
||||
: pools{allocator},
|
||||
filter{allocator} {}
|
||||
|
||||
// brings the best candidate (if any) on front of the vector
|
||||
std::rotate(pools.begin(), candidate, pools.end());
|
||||
/*! @brief Default copy constructor. */
|
||||
basic_runtime_view(const basic_runtime_view &) = default;
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended copy constructor.
|
||||
* @param other The instance to copy from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
basic_runtime_view(const basic_runtime_view &other, const allocator_type &allocator)
|
||||
: pools{other.pools, allocator},
|
||||
filter{other.filter, allocator} {}
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
basic_runtime_view(basic_runtime_view &&) noexcept(std::is_nothrow_move_constructible_v<container_type>) = default;
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
basic_runtime_view(basic_runtime_view &&other, const allocator_type &allocator)
|
||||
: pools{std::move(other.pools), allocator},
|
||||
filter{std::move(other.filter), allocator} {}
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator.
|
||||
* @return This container.
|
||||
*/
|
||||
basic_runtime_view &operator=(const basic_runtime_view &) = default;
|
||||
|
||||
/**
|
||||
* @brief Default move assignment operator.
|
||||
* @return This container.
|
||||
*/
|
||||
basic_runtime_view &operator=(basic_runtime_view &&) noexcept(std::is_nothrow_move_assignable_v<container_type>) = default;
|
||||
|
||||
/**
|
||||
* @brief Exchanges the contents with those of a given view.
|
||||
* @param other View to exchange the content with.
|
||||
*/
|
||||
void swap(basic_runtime_view &other) {
|
||||
using std::swap;
|
||||
swap(pools, other.pools);
|
||||
swap(filter, other.filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the associated allocator.
|
||||
* @return The associated allocator.
|
||||
*/
|
||||
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
|
||||
return pools.get_allocator();
|
||||
}
|
||||
|
||||
/*! @brief Clears the view. */
|
||||
void clear() {
|
||||
pools.clear();
|
||||
filter.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Appends an opaque storage object to a runtime view.
|
||||
* @param base An opaque reference to a storage object.
|
||||
* @return This runtime view.
|
||||
*/
|
||||
basic_runtime_view &iterate(common_type &base) {
|
||||
if(pools.empty() || !(base.size() < pools[0u]->size())) {
|
||||
pools.push_back(&base);
|
||||
} else {
|
||||
pools.push_back(std::exchange(pools[0u], &base));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds an opaque storage object as a filter of a runtime view.
|
||||
* @param base An opaque reference to a storage object.
|
||||
* @return This runtime view.
|
||||
*/
|
||||
basic_runtime_view &exclude(common_type &base) {
|
||||
filter.push_back(&base);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,36 +236,29 @@ public:
|
||||
* @return Estimated number of entities iterated by the view.
|
||||
*/
|
||||
[[nodiscard]] size_type size_hint() const {
|
||||
return valid() ? pools.front()->size() : size_type{};
|
||||
return pools.empty() ? size_type{} : pools.front()->size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
[[nodiscard]] iterator begin() const {
|
||||
return valid() ? iterator{pools, filter, pools[0]->begin()} : iterator{};
|
||||
return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->begin()};
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
[[nodiscard]] iterator end() const {
|
||||
return valid() ? iterator{pools, filter, pools[0]->end()} : iterator{};
|
||||
return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->end()};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,7 +267,8 @@ public:
|
||||
* @return True if the view contains the given entity, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool contains(const entity_type entt) const {
|
||||
return valid() && std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *curr) { return curr->contains(entt); })
|
||||
return !pools.empty()
|
||||
&& std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *curr) { return curr->contains(entt); })
|
||||
&& std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); });
|
||||
}
|
||||
|
||||
@@ -221,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}
|
||||
@@ -240,8 +294,8 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<const basic_common_type *> pools;
|
||||
std::vector<const basic_common_type *> filter;
|
||||
container_type pools;
|
||||
container_type filter;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#ifndef ENTT_ENTITY_SNAPSHOT_HPP
|
||||
#define ENTT_ENTITY_SNAPSHOT_HPP
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
@@ -9,15 +8,31 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../config/config.h"
|
||||
#include "../container/dense_hash_map.hpp"
|
||||
#include "../container/dense_map.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "component.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "registry.hpp"
|
||||
#include "view.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @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.
|
||||
*
|
||||
@@ -26,125 +41,110 @@ namespace entt {
|
||||
* This type can be used in both cases if provided with a correctly configured
|
||||
* output archive.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Registry Basic registry type.
|
||||
*/
|
||||
template<typename Entity>
|
||||
template<typename Registry>
|
||||
class basic_snapshot {
|
||||
using entity_traits = entt_traits<Entity>;
|
||||
|
||||
template<typename Component, typename Archive, typename It>
|
||||
void get(Archive &archive, std::size_t sz, It first, It last) const {
|
||||
const auto view = reg->template view<std::add_const_t<Component>>();
|
||||
archive(typename 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. */
|
||||
using registry_type = Registry;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
using entity_type = typename registry_type::entity_type;
|
||||
|
||||
/**
|
||||
* @brief Constructs an instance that is bound to a given registry.
|
||||
* @param source A valid reference to a registry.
|
||||
*/
|
||||
basic_snapshot(const basic_registry<entity_type> &source) ENTT_NOEXCEPT
|
||||
basic_snapshot(const registry_type &source) noexcept
|
||||
: reg{&source} {}
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
basic_snapshot(basic_snapshot &&) = default;
|
||||
basic_snapshot(basic_snapshot &&) noexcept = default;
|
||||
|
||||
/*! @brief Default move assignment operator. @return This snapshot. */
|
||||
basic_snapshot &operator=(basic_snapshot &&) = default;
|
||||
basic_snapshot &operator=(basic_snapshot &&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Puts aside all the entities from the underlying registry.
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
|
||||
private:
|
||||
const basic_registry<entity_type> *reg;
|
||||
const registry_type *reg;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -155,100 +155,82 @@ private:
|
||||
* originally had.<br/>
|
||||
* An example of use is the implementation of a save/restore utility.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Registry Basic registry type.
|
||||
*/
|
||||
template<typename Entity>
|
||||
template<typename Registry>
|
||||
class basic_snapshot_loader {
|
||||
using entity_traits = entt_traits<Entity>;
|
||||
|
||||
template<typename Type, typename Archive>
|
||||
void assign(Archive &archive) const {
|
||||
typename entity_traits::entity_type length{};
|
||||
archive(length);
|
||||
|
||||
entity_type entt{};
|
||||
|
||||
if constexpr(ignore_as_empty_v<std::remove_const_t<Type>>) {
|
||||
while(length--) {
|
||||
archive(entt);
|
||||
const auto entity = reg->valid(entt) ? entt : reg->create(entt);
|
||||
ENTT_ASSERT(entity == entt, "Entity not available for use");
|
||||
reg->template emplace<Type>(entity);
|
||||
}
|
||||
} else {
|
||||
Type instance{};
|
||||
|
||||
while(length--) {
|
||||
archive(entt, instance);
|
||||
const auto entity = reg->valid(entt) ? entt : reg->create(entt);
|
||||
ENTT_ASSERT(entity == entt, "Entity not available for use");
|
||||
reg->template emplace<Type>(entity, std::move(instance));
|
||||
}
|
||||
}
|
||||
}
|
||||
static_assert(!std::is_const_v<Registry>, "Non-const registry type required");
|
||||
using traits_type = typename Registry::traits_type;
|
||||
|
||||
public:
|
||||
/*! Basic registry type. */
|
||||
using registry_type = Registry;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
using entity_type = typename registry_type::entity_type;
|
||||
|
||||
/**
|
||||
* @brief Constructs an instance that is bound to a given registry.
|
||||
* @param source A valid reference to a registry.
|
||||
*/
|
||||
basic_snapshot_loader(basic_registry<entity_type> &source) ENTT_NOEXCEPT
|
||||
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. */
|
||||
basic_snapshot_loader(basic_snapshot_loader &&) = default;
|
||||
basic_snapshot_loader(basic_snapshot_loader &&) noexcept = default;
|
||||
|
||||
/*! @brief Default move assignment operator. @return This loader. */
|
||||
basic_snapshot_loader &operator=(basic_snapshot_loader &&) = default;
|
||||
basic_snapshot_loader &operator=(basic_snapshot_loader &&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Restores entities that were in use during serialization.
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
|
||||
@@ -258,22 +240,17 @@ 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;
|
||||
}
|
||||
|
||||
private:
|
||||
basic_registry<entity_type> *reg;
|
||||
registry_type *reg;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -286,43 +263,29 @@ 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.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Registry Basic registry type.
|
||||
*/
|
||||
template<typename Entity>
|
||||
template<typename Registry>
|
||||
class basic_continuous_loader {
|
||||
using entity_traits = entt_traits<Entity>;
|
||||
static_assert(!std::is_const_v<Registry>, "Non-const registry type required");
|
||||
using traits_type = typename Registry::traits_type;
|
||||
|
||||
void destroy(Entity 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);
|
||||
}
|
||||
}
|
||||
|
||||
void restore(Entity 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();
|
||||
void restore(typename Registry::entity_type entt) {
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Container>
|
||||
auto update(int, Container &container)
|
||||
-> decltype(typename Container::mapped_type{}, void()) {
|
||||
auto update(int, Container &container) -> decltype(typename Container::mapped_type{}, void()) {
|
||||
// map like container
|
||||
Container other;
|
||||
|
||||
@@ -340,12 +303,12 @@ class basic_continuous_loader {
|
||||
}
|
||||
}
|
||||
|
||||
std::swap(container, other);
|
||||
using std::swap;
|
||||
swap(container, other);
|
||||
}
|
||||
|
||||
template<typename Container>
|
||||
auto update(char, Container &container)
|
||||
-> decltype(typename Container::value_type{}, void()) {
|
||||
auto update(char, Container &container) -> decltype(typename Container::value_type{}, void()) {
|
||||
// vector like container
|
||||
static_assert(std::is_same_v<typename Container::value_type, entity_type>, "Invalid value type");
|
||||
|
||||
@@ -354,9 +317,9 @@ class basic_continuous_loader {
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Other, typename Type, typename Member>
|
||||
void update([[maybe_unused]] Other &instance, [[maybe_unused]] Member Type::*member) {
|
||||
if constexpr(!std::is_same_v<Other, Type>) {
|
||||
template<typename Component, typename Other, typename Member>
|
||||
void update([[maybe_unused]] Component &instance, [[maybe_unused]] Member Other::*member) {
|
||||
if constexpr(!std::is_same_v<Component, Other>) {
|
||||
return;
|
||||
} else if constexpr(std::is_same_v<Member, entity_type>) {
|
||||
instance.*member = map(instance.*member);
|
||||
@@ -366,52 +329,19 @@ 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 Other, typename Archive, typename... Type, typename... Member>
|
||||
void assign(Archive &archive, [[maybe_unused]] Member Type::*...member) {
|
||||
typename entity_traits::entity_type length{};
|
||||
archive(length);
|
||||
|
||||
entity_type entt{};
|
||||
|
||||
if constexpr(ignore_as_empty_v<std::remove_const_t<Other>>) {
|
||||
while(length--) {
|
||||
archive(entt);
|
||||
restore(entt);
|
||||
reg->template emplace_or_replace<Other>(map(entt));
|
||||
}
|
||||
} else {
|
||||
Other instance{};
|
||||
|
||||
while(length--) {
|
||||
archive(entt, instance);
|
||||
(update(instance, member), ...);
|
||||
restore(entt);
|
||||
reg->template emplace_or_replace<Other>(map(entt), std::move(instance));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/*! Basic registry type. */
|
||||
using registry_type = Registry;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
using entity_type = typename registry_type::entity_type;
|
||||
|
||||
/**
|
||||
* @brief Constructs an instance that is bound to a given registry.
|
||||
* @param source A valid reference to a registry.
|
||||
*/
|
||||
basic_continuous_loader(basic_registry<entity_type> &source) ENTT_NOEXCEPT
|
||||
: reg{&source} {}
|
||||
basic_continuous_loader(registry_type &source) noexcept
|
||||
: remloc{source.get_allocator()},
|
||||
reg{&source} {}
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
basic_continuous_loader(basic_continuous_loader &&) = default;
|
||||
@@ -420,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 Type Types of components to update with local counterparts.
|
||||
* @tparam Member Types of members to update with their local counterparts.
|
||||
* @param archive A valid reference to an input archive.
|
||||
* @param member Members to update with their local counterparts.
|
||||
* @return A non-const reference to this loader.
|
||||
*/
|
||||
template<typename... Component, typename Archive, typename... Type, typename... Member>
|
||||
basic_continuous_loader &component(Archive &archive, Member Type::*...member) {
|
||||
(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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -513,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;
|
||||
}
|
||||
|
||||
@@ -532,8 +436,9 @@ public:
|
||||
* @param entt A valid identifier.
|
||||
* @return True if `entity` is managed by the loader, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool contains(entity_type entt) const ENTT_NOEXCEPT {
|
||||
return (remloc.find(entt) != remloc.cend());
|
||||
[[nodiscard]] bool contains(entity_type entt) const noexcept {
|
||||
const auto it = remloc.find(to_entity(entt));
|
||||
return it != remloc.cend() && it->second.first == entt;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -541,20 +446,17 @@ public:
|
||||
* @param entt A valid identifier.
|
||||
* @return The local identifier if any, the null entity otherwise.
|
||||
*/
|
||||
[[nodiscard]] entity_type map(entity_type entt) const ENTT_NOEXCEPT {
|
||||
const auto it = remloc.find(entt);
|
||||
entity_type other = null;
|
||||
|
||||
if(it != remloc.cend()) {
|
||||
other = it->second.first;
|
||||
[[nodiscard]] entity_type map(entity_type entt) const noexcept {
|
||||
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_hash_map<entity_type, std::pair<entity_type, bool>> remloc;
|
||||
basic_registry<entity_type> *reg;
|
||||
dense_map<typename traits_type::entity_type, std::pair<entity_type, entity_type>> remloc;
|
||||
registry_type *reg;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,52 +0,0 @@
|
||||
#ifndef ENTT_ENTITY_UTILITY_HPP
|
||||
#define ENTT_ENTITY_UTILITY_HPP
|
||||
|
||||
#include "../core/type_traits.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @brief Alias for exclusion lists.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
struct exclude_t: type_list<Type...> {};
|
||||
|
||||
/**
|
||||
* @brief Variable template for exclusion lists.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
inline constexpr exclude_t<Type...> exclude{};
|
||||
|
||||
/**
|
||||
* @brief Alias for lists of observed components.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
struct get_t: type_list<Type...> {};
|
||||
|
||||
/**
|
||||
* @brief Variable template for lists of observed components.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
inline constexpr get_t<Type...> get{};
|
||||
|
||||
/**
|
||||
* @brief Alias for lists of owned components.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
struct 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{};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,9 @@
|
||||
// IWYU pragma: begin_exports
|
||||
#include "config/config.h"
|
||||
#include "config/macro.h"
|
||||
#include "config/version.h"
|
||||
#include "container/dense_hash_map.hpp"
|
||||
#include "container/dense_hash_set.hpp"
|
||||
#include "container/dense_map.hpp"
|
||||
#include "container/dense_set.hpp"
|
||||
#include "core/algorithm.hpp"
|
||||
#include "core/any.hpp"
|
||||
#include "core/attribute.h"
|
||||
@@ -21,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"
|
||||
@@ -28,12 +32,14 @@
|
||||
#include "entity/snapshot.hpp"
|
||||
#include "entity/sparse_set.hpp"
|
||||
#include "entity/storage.hpp"
|
||||
#include "entity/utility.hpp"
|
||||
#include "entity/view.hpp"
|
||||
#include "graph/adjacency_matrix.hpp"
|
||||
#include "graph/dot.hpp"
|
||||
#include "graph/flow.hpp"
|
||||
#include "locator/locator.hpp"
|
||||
#include "meta/adl_pointer.hpp"
|
||||
#include "meta/container.hpp"
|
||||
#include "meta/ctx.hpp"
|
||||
#include "meta/context.hpp"
|
||||
#include "meta/factory.hpp"
|
||||
#include "meta/meta.hpp"
|
||||
#include "meta/node.hpp"
|
||||
@@ -49,9 +55,10 @@
|
||||
#include "process/process.hpp"
|
||||
#include "process/scheduler.hpp"
|
||||
#include "resource/cache.hpp"
|
||||
#include "resource/handle.hpp"
|
||||
#include "resource/loader.hpp"
|
||||
#include "resource/resource.hpp"
|
||||
#include "signal/delegate.hpp"
|
||||
#include "signal/dispatcher.hpp"
|
||||
#include "signal/emitter.hpp"
|
||||
#include "signal/sigh.hpp"
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
// IWYU pragma: begin_exports
|
||||
#include "container/fwd.hpp"
|
||||
#include "core/fwd.hpp"
|
||||
#include "entity/fwd.hpp"
|
||||
#include "graph/fwd.hpp"
|
||||
#include "meta/fwd.hpp"
|
||||
#include "poly/fwd.hpp"
|
||||
#include "process/fwd.hpp"
|
||||
#include "resource/fwd.hpp"
|
||||
#include "signal/fwd.hpp"
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
341
src/entt/graph/adjacency_matrix.hpp
Normal file
341
src/entt/graph/adjacency_matrix.hpp
Normal file
@@ -0,0 +1,341 @@
|
||||
#ifndef ENTT_GRAPH_ADJACENCY_MATRIX_HPP
|
||||
#define ENTT_GRAPH_ADJACENCY_MATRIX_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../config/config.h"
|
||||
#include "../core/iterator.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename It>
|
||||
class edge_iterator {
|
||||
using size_type = std::size_t;
|
||||
|
||||
public:
|
||||
using value_type = std::pair<size_type, size_type>;
|
||||
using pointer = input_iterator_pointer<value_type>;
|
||||
using reference = value_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using iterator_concept = std::forward_iterator_tag;
|
||||
|
||||
constexpr edge_iterator() noexcept
|
||||
: it{},
|
||||
vert{},
|
||||
pos{},
|
||||
last{},
|
||||
offset{} {}
|
||||
|
||||
constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept
|
||||
: it{std::move(base)},
|
||||
vert{vertices},
|
||||
pos{from},
|
||||
last{to},
|
||||
offset{step} {
|
||||
for(; pos != last && !it[pos]; pos += offset) {}
|
||||
}
|
||||
|
||||
constexpr edge_iterator &operator++() noexcept {
|
||||
for(pos += offset; pos != last && !it[pos]; pos += offset) {}
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr edge_iterator operator++(int) noexcept {
|
||||
edge_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr reference operator*() const noexcept {
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr pointer operator->() const noexcept {
|
||||
return std::make_pair<size_type>(pos / vert, pos % vert);
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
friend constexpr bool operator==(const edge_iterator<Type> &, const edge_iterator<Type> &) noexcept;
|
||||
|
||||
private:
|
||||
It it;
|
||||
size_type vert;
|
||||
size_type pos;
|
||||
size_type last;
|
||||
size_type offset{};
|
||||
};
|
||||
|
||||
template<typename Container>
|
||||
[[nodiscard]] inline constexpr bool operator==(const edge_iterator<Container> &lhs, const edge_iterator<Container> &rhs) noexcept {
|
||||
return lhs.pos == rhs.pos;
|
||||
}
|
||||
|
||||
template<typename Container>
|
||||
[[nodiscard]] inline constexpr bool operator!=(const edge_iterator<Container> &lhs, const edge_iterator<Container> &rhs) noexcept {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Basic implementation of a directed adjacency matrix.
|
||||
* @tparam Category Either a directed or undirected category tag.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Category, typename Allocator>
|
||||
class adjacency_matrix {
|
||||
using alloc_traits = std::allocator_traits<Allocator>;
|
||||
static_assert(std::is_base_of_v<directed_tag, Category>, "Invalid graph category");
|
||||
static_assert(std::is_same_v<typename alloc_traits::value_type, std::size_t>, "Invalid value type");
|
||||
using container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
|
||||
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Vertex type. */
|
||||
using vertex_type = size_type;
|
||||
/*! @brief Edge type. */
|
||||
using edge_type = std::pair<vertex_type, vertex_type>;
|
||||
/*! @brief Vertex iterator type. */
|
||||
using vertex_iterator = iota_iterator<vertex_type>;
|
||||
/*! @brief Edge iterator type. */
|
||||
using edge_iterator = internal::edge_iterator<typename container_type::const_iterator>;
|
||||
/*! @brief Out edge iterator type. */
|
||||
using out_edge_iterator = edge_iterator;
|
||||
/*! @brief In edge iterator type. */
|
||||
using in_edge_iterator = edge_iterator;
|
||||
/*! @brief Graph category tag. */
|
||||
using graph_category = Category;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
adjacency_matrix() noexcept(noexcept(allocator_type{}))
|
||||
: adjacency_matrix{0u} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an empty container with a given allocator.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit adjacency_matrix(const allocator_type &allocator) noexcept
|
||||
: adjacency_matrix{0u, allocator} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an empty container with a given allocator and user
|
||||
* supplied number of vertices.
|
||||
* @param vertices Number of vertices.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
adjacency_matrix(const size_type vertices, const allocator_type &allocator = allocator_type{})
|
||||
: matrix{vertices * vertices, allocator},
|
||||
vert{vertices} {}
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
* @param other The instance to copy from.
|
||||
*/
|
||||
adjacency_matrix(const adjacency_matrix &other)
|
||||
: adjacency_matrix{other, other.get_allocator()} {}
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended copy constructor.
|
||||
* @param other The instance to copy from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
adjacency_matrix(const adjacency_matrix &other, const allocator_type &allocator)
|
||||
: matrix{other.matrix, allocator},
|
||||
vert{other.vert} {}
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
adjacency_matrix(adjacency_matrix &&other) noexcept
|
||||
: adjacency_matrix{std::move(other), other.get_allocator()} {}
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator)
|
||||
: matrix{std::move(other.matrix), allocator},
|
||||
vert{std::exchange(other.vert, 0u)} {}
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator.
|
||||
* @param other The instance to copy from.
|
||||
* @return This container.
|
||||
*/
|
||||
adjacency_matrix &operator=(const adjacency_matrix &other) {
|
||||
matrix = other.matrix;
|
||||
vert = other.vert;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Default move assignment operator.
|
||||
* @param other The instance to move from.
|
||||
* @return This container.
|
||||
*/
|
||||
adjacency_matrix &operator=(adjacency_matrix &&other) noexcept {
|
||||
matrix = std::move(other.matrix);
|
||||
vert = std::exchange(other.vert, 0u);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the associated allocator.
|
||||
* @return The associated allocator.
|
||||
*/
|
||||
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
|
||||
return matrix.get_allocator();
|
||||
}
|
||||
|
||||
/*! @brief Clears the adjacency matrix. */
|
||||
void clear() noexcept {
|
||||
matrix.clear();
|
||||
vert = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exchanges the contents with those of a given adjacency matrix.
|
||||
* @param other Adjacency matrix to exchange the content with.
|
||||
*/
|
||||
void swap(adjacency_matrix &other) {
|
||||
using std::swap;
|
||||
swap(matrix, other.matrix);
|
||||
swap(vert, other.vert);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of vertices.
|
||||
* @return The number of vertices.
|
||||
*/
|
||||
[[nodiscard]] size_type size() const noexcept {
|
||||
return vert;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterable object to visit all vertices of a matrix.
|
||||
* @return An iterable object to visit all vertices of a matrix.
|
||||
*/
|
||||
[[nodiscard]] iterable_adaptor<vertex_iterator> vertices() const noexcept {
|
||||
return {0u, vert};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterable object to visit all edges of a matrix.
|
||||
* @return An iterable object to visit all edges of a matrix.
|
||||
*/
|
||||
[[nodiscard]] iterable_adaptor<edge_iterator> edges() const noexcept {
|
||||
const auto it = matrix.cbegin();
|
||||
const auto sz = matrix.size();
|
||||
return {{it, vert, 0u, sz, 1u}, {it, vert, sz, sz, 1u}};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterable object to visit all out edges of a vertex.
|
||||
* @param vertex The vertex of which to return all out edges.
|
||||
* @return An iterable object to visit all out edges of a vertex.
|
||||
*/
|
||||
[[nodiscard]] iterable_adaptor<out_edge_iterator> out_edges(const vertex_type vertex) const noexcept {
|
||||
const auto it = matrix.cbegin();
|
||||
const auto from = vertex * vert;
|
||||
const auto to = from + vert;
|
||||
return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterable object to visit all in edges of a vertex.
|
||||
* @param vertex The vertex of which to return all in edges.
|
||||
* @return An iterable object to visit all in edges of a vertex.
|
||||
*/
|
||||
[[nodiscard]] iterable_adaptor<in_edge_iterator> in_edges(const vertex_type vertex) const noexcept {
|
||||
const auto it = matrix.cbegin();
|
||||
const auto from = vertex;
|
||||
const auto to = vert * vert + from;
|
||||
return {{it, vert, from, to, vert}, {it, vert, to, to, vert}};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resizes an adjacency matrix.
|
||||
* @param vertices The new number of vertices.
|
||||
*/
|
||||
void resize(const size_type vertices) {
|
||||
adjacency_matrix other{vertices, get_allocator()};
|
||||
|
||||
for(auto [lhs, rhs]: edges()) {
|
||||
other.insert(lhs, rhs);
|
||||
}
|
||||
|
||||
other.swap(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inserts an edge into the adjacency matrix, if it does not exist.
|
||||
* @param lhs The left hand vertex of the edge.
|
||||
* @param rhs The right hand vertex of the edge.
|
||||
* @return A pair consisting of an iterator to the inserted element (or to
|
||||
* the element that prevented the insertion) and a bool denoting whether the
|
||||
* insertion took place.
|
||||
*/
|
||||
std::pair<edge_iterator, bool> insert(const vertex_type lhs, const vertex_type rhs) {
|
||||
const auto pos = lhs * vert + rhs;
|
||||
|
||||
if constexpr(std::is_same_v<graph_category, undirected_tag>) {
|
||||
const auto rev = rhs * vert + lhs;
|
||||
ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong");
|
||||
matrix[rev] = 1u;
|
||||
}
|
||||
|
||||
const auto inserted = !std::exchange(matrix[pos], 1u);
|
||||
return {edge_iterator{matrix.cbegin(), vert, pos, matrix.size(), 1u}, inserted};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes the edge associated with a pair of given vertices.
|
||||
* @param lhs The left hand vertex of the edge.
|
||||
* @param rhs The right hand vertex of the edge.
|
||||
* @return Number of elements removed (either 0 or 1).
|
||||
*/
|
||||
size_type erase(const vertex_type lhs, const vertex_type rhs) {
|
||||
const auto pos = lhs * vert + rhs;
|
||||
|
||||
if constexpr(std::is_same_v<graph_category, undirected_tag>) {
|
||||
const auto rev = rhs * vert + lhs;
|
||||
ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong");
|
||||
matrix[rev] = 0u;
|
||||
}
|
||||
|
||||
return std::exchange(matrix[pos], 0u);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if an adjacency matrix contains a given edge.
|
||||
* @param lhs The left hand vertex of the edge.
|
||||
* @param rhs The right hand vertex of the edge.
|
||||
* @return True if there is such an edge, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool contains(const vertex_type lhs, const vertex_type rhs) const {
|
||||
const auto pos = lhs * vert + rhs;
|
||||
return pos < matrix.size() && matrix[pos];
|
||||
}
|
||||
|
||||
private:
|
||||
container_type matrix;
|
||||
size_type vert;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
58
src/entt/graph/dot.hpp
Normal file
58
src/entt/graph/dot.hpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#ifndef ENTT_GRAPH_DOT_HPP
|
||||
#define ENTT_GRAPH_DOT_HPP
|
||||
|
||||
#include <ostream>
|
||||
#include <type_traits>
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @brief Outputs a graph in dot format.
|
||||
* @tparam Graph Graph type, valid as long as it exposes edges and vertices.
|
||||
* @tparam Writer Vertex decorator type.
|
||||
* @param out A standard output stream.
|
||||
* @param graph The graph to output.
|
||||
* @param writer Vertex decorator object.
|
||||
*/
|
||||
template<typename Graph, typename Writer>
|
||||
void dot(std::ostream &out, const Graph &graph, Writer writer) {
|
||||
static_assert(std::is_base_of_v<directed_tag, typename Graph::graph_category>, "Invalid graph category");
|
||||
|
||||
if constexpr(std::is_same_v<typename Graph::graph_category, undirected_tag>) {
|
||||
out << "graph{";
|
||||
} else {
|
||||
out << "digraph{";
|
||||
}
|
||||
|
||||
for(auto &&vertex: graph.vertices()) {
|
||||
out << vertex << "[";
|
||||
writer(out, vertex);
|
||||
out << "];";
|
||||
}
|
||||
|
||||
for(auto [lhs, rhs]: graph.edges()) {
|
||||
if constexpr(std::is_same_v<typename Graph::graph_category, undirected_tag>) {
|
||||
out << lhs << "--" << rhs << ";";
|
||||
} else {
|
||||
out << lhs << "->" << rhs << ";";
|
||||
}
|
||||
}
|
||||
|
||||
out << "}";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Outputs a graph in dot format.
|
||||
* @tparam Graph Graph type, valid as long as it exposes edges and vertices.
|
||||
* @param out A standard output stream.
|
||||
* @param graph The graph to output.
|
||||
*/
|
||||
template<typename Graph>
|
||||
void dot(std::ostream &out, const Graph &graph) {
|
||||
return dot(out, graph, [](auto &&...) {});
|
||||
}
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
341
src/entt/graph/flow.hpp
Normal file
341
src/entt/graph/flow.hpp
Normal file
@@ -0,0 +1,341 @@
|
||||
#ifndef ENTT_GRAPH_FLOW_HPP
|
||||
#define ENTT_GRAPH_FLOW_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../config/config.h"
|
||||
#include "../container/dense_map.hpp"
|
||||
#include "../container/dense_set.hpp"
|
||||
#include "../core/compressed_pair.hpp"
|
||||
#include "../core/fwd.hpp"
|
||||
#include "../core/iterator.hpp"
|
||||
#include "../core/utility.hpp"
|
||||
#include "adjacency_matrix.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @brief Utility class for creating task graphs.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Allocator>
|
||||
class basic_flow {
|
||||
using alloc_traits = std::allocator_traits<Allocator>;
|
||||
static_assert(std::is_same_v<typename alloc_traits::value_type, id_type>, "Invalid value type");
|
||||
using task_container_type = dense_set<id_type, identity, std::equal_to<id_type>, typename alloc_traits::template rebind_alloc<id_type>>;
|
||||
using ro_rw_container_type = std::vector<std::pair<std::size_t, bool>, typename alloc_traits::template rebind_alloc<std::pair<std::size_t, bool>>>;
|
||||
using deps_container_type = dense_map<id_type, ro_rw_container_type, identity, std::equal_to<id_type>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, ro_rw_container_type>>>;
|
||||
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");
|
||||
|
||||
if(!deps.contains(res) && sync_on != vertices.size()) {
|
||||
deps[res].emplace_back(sync_on, true);
|
||||
}
|
||||
|
||||
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;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Iterable task list. */
|
||||
using iterable = iterable_adaptor<typename task_container_type::const_iterator>;
|
||||
/*! @brief Adjacency matrix type. */
|
||||
using graph_type = adjacency_matrix_type;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
basic_flow()
|
||||
: basic_flow{allocator_type{}} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a flow builder with a given allocator.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit basic_flow(const allocator_type &allocator)
|
||||
: index{0u, allocator},
|
||||
vertices{allocator},
|
||||
deps{allocator},
|
||||
sync_on{} {}
|
||||
|
||||
/*! @brief Default copy constructor. */
|
||||
basic_flow(const basic_flow &) = default;
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended copy constructor.
|
||||
* @param other The instance to copy from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
basic_flow(const basic_flow &other, const allocator_type &allocator)
|
||||
: index{other.index.first(), allocator},
|
||||
vertices{other.vertices, allocator},
|
||||
deps{other.deps, allocator},
|
||||
sync_on{other.sync_on} {}
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
basic_flow(basic_flow &&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
basic_flow(basic_flow &&other, const allocator_type &allocator)
|
||||
: index{other.index.first(), allocator},
|
||||
vertices{std::move(other.vertices), allocator},
|
||||
deps{std::move(other.deps), allocator},
|
||||
sync_on{other.sync_on} {}
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator.
|
||||
* @return This flow builder.
|
||||
*/
|
||||
basic_flow &operator=(const basic_flow &) = default;
|
||||
|
||||
/**
|
||||
* @brief Default move assignment operator.
|
||||
* @return This flow builder.
|
||||
*/
|
||||
basic_flow &operator=(basic_flow &&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Returns the associated allocator.
|
||||
* @return The associated allocator.
|
||||
*/
|
||||
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
|
||||
return allocator_type{index.second()};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the identifier at specified location.
|
||||
* @param pos Position of the identifier to return.
|
||||
* @return The requested identifier.
|
||||
*/
|
||||
[[nodiscard]] id_type operator[](const size_type pos) const {
|
||||
return vertices.cbegin()[pos];
|
||||
}
|
||||
|
||||
/*! @brief Clears the flow builder. */
|
||||
void clear() noexcept {
|
||||
index.first() = {};
|
||||
vertices.clear();
|
||||
deps.clear();
|
||||
sync_on = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exchanges the contents with those of a given flow builder.
|
||||
* @param other Flow builder to exchange the content with.
|
||||
*/
|
||||
void swap(basic_flow &other) {
|
||||
using std::swap;
|
||||
std::swap(index, other.index);
|
||||
std::swap(vertices, other.vertices);
|
||||
std::swap(deps, other.deps);
|
||||
std::swap(sync_on, other.sync_on);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of tasks.
|
||||
* @return The number of tasks.
|
||||
*/
|
||||
[[nodiscard]] size_type size() const noexcept {
|
||||
return vertices.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Binds a task to a flow builder.
|
||||
* @param value Task identifier.
|
||||
* @return This flow builder.
|
||||
*/
|
||||
basic_flow &bind(const id_type value) {
|
||||
sync_on += (sync_on == vertices.size());
|
||||
const auto it = vertices.emplace(value).first;
|
||||
index.first() = size_type(it - vertices.begin());
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Turns the current task into a sync point.
|
||||
* @return This flow builder.
|
||||
*/
|
||||
basic_flow &sync() {
|
||||
ENTT_ASSERT(index.first() < vertices.size(), "Invalid node");
|
||||
sync_on = index.first();
|
||||
|
||||
for(const auto &elem: deps) {
|
||||
elem.second.emplace_back(sync_on, true);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns a resource to the current task with a given access mode.
|
||||
* @param res Resource identifier.
|
||||
* @param is_rw Access mode.
|
||||
* @return This flow builder.
|
||||
*/
|
||||
basic_flow &set(const id_type res, bool is_rw = false) {
|
||||
emplace(res, is_rw);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns a read-only resource to the current task.
|
||||
* @param res Resource identifier.
|
||||
* @return This flow builder.
|
||||
*/
|
||||
basic_flow &ro(const id_type res) {
|
||||
emplace(res, false);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns a range of read-only resources to the current task.
|
||||
* @tparam It Type of input iterator.
|
||||
* @param first An iterator to the first element of the range of elements.
|
||||
* @param last An iterator past the last element of the range of elements.
|
||||
* @return This flow builder.
|
||||
*/
|
||||
template<typename It>
|
||||
std::enable_if_t<std::is_same_v<std::remove_const_t<typename std::iterator_traits<It>::value_type>, id_type>, basic_flow &>
|
||||
ro(It first, It last) {
|
||||
for(; first != last; ++first) {
|
||||
emplace(*first, false);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns a writable resource to the current task.
|
||||
* @param res Resource identifier.
|
||||
* @return This flow builder.
|
||||
*/
|
||||
basic_flow &rw(const id_type res) {
|
||||
emplace(res, true);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns a range of writable resources to the current task.
|
||||
* @tparam It Type of input iterator.
|
||||
* @param first An iterator to the first element of the range of elements.
|
||||
* @param last An iterator past the last element of the range of elements.
|
||||
* @return This flow builder.
|
||||
*/
|
||||
template<typename It>
|
||||
std::enable_if_t<std::is_same_v<std::remove_const_t<typename std::iterator_traits<It>::value_type>, id_type>, basic_flow &>
|
||||
rw(It first, It last) {
|
||||
for(; first != last; ++first) {
|
||||
emplace(*first, true);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generates a task graph for the current content.
|
||||
* @return The adjacency matrix of the task graph.
|
||||
*/
|
||||
[[nodiscard]] graph_type graph() const {
|
||||
graph_type matrix{vertices.size(), get_allocator()};
|
||||
|
||||
setup_graph(matrix);
|
||||
transitive_closure(matrix);
|
||||
transitive_reduction(matrix);
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
private:
|
||||
compressed_pair<size_type, allocator_type> index;
|
||||
task_container_type vertices;
|
||||
deps_container_type deps;
|
||||
size_type sync_on;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
27
src/entt/graph/fwd.hpp
Normal file
27
src/entt/graph/fwd.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef ENTT_GRAPH_FWD_HPP
|
||||
#define ENTT_GRAPH_FWD_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include "../core/fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @brief Undirected graph category tag. */
|
||||
struct directed_tag {};
|
||||
|
||||
/*! @brief Directed graph category tag. */
|
||||
struct undirected_tag: directed_tag {};
|
||||
|
||||
template<typename, typename = std::allocator<std::size_t>>
|
||||
class adjacency_matrix;
|
||||
|
||||
template<typename = std::allocator<id_type>>
|
||||
class basic_flow;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using flow = basic_flow<>;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
@@ -10,95 +10,136 @@ namespace entt {
|
||||
/**
|
||||
* @brief Service locator, nothing more.
|
||||
*
|
||||
* A service locator can be used to do what it promises: locate services.<br/>
|
||||
* A service locator is used to do what it promises: locate services.<br/>
|
||||
* Usually service locators are tightly bound to the services they expose and
|
||||
* thus it's hard to define a general purpose class to do that. This template
|
||||
* based implementation tries to fill the gap and to get rid of the burden of
|
||||
* defining a different specific locator for each application.
|
||||
* thus it's hard to define a general purpose class to do that. This tiny class
|
||||
* tries to fill the gap and to get rid of the burden of defining a different
|
||||
* specific locator for each application.
|
||||
*
|
||||
* @tparam Service Type of service managed by the locator.
|
||||
* @note
|
||||
* Users shouldn't retain references to a service. The recommended way is to
|
||||
* retrieve the service implementation currently set each and every time the
|
||||
* need for it arises. The risk is to incur in unexpected behaviors otherwise.
|
||||
*
|
||||
* @tparam Service Service type.
|
||||
*/
|
||||
template<typename Service>
|
||||
struct service_locator {
|
||||
/*! @brief Type of service offered. */
|
||||
using service_type = Service;
|
||||
class locator final {
|
||||
class service_handle {
|
||||
friend class locator<Service>;
|
||||
std::shared_ptr<Service> value{};
|
||||
};
|
||||
|
||||
public:
|
||||
/*! @brief Service type. */
|
||||
using type = Service;
|
||||
/*! @brief Service node type. */
|
||||
using node_type = service_handle;
|
||||
|
||||
/*! @brief Default constructor, deleted on purpose. */
|
||||
service_locator() = delete;
|
||||
locator() = delete;
|
||||
/*! @brief Default destructor, deleted on purpose. */
|
||||
~service_locator() = delete;
|
||||
~locator() = delete;
|
||||
|
||||
/**
|
||||
* @brief Tests if a valid service implementation is set.
|
||||
* @return True if the service is set, false otherwise.
|
||||
* @brief Checks whether a service locator contains a value.
|
||||
* @return True if the service locator contains a value, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] static bool empty() ENTT_NOEXCEPT {
|
||||
return !static_cast<bool>(service);
|
||||
[[nodiscard]] static bool has_value() noexcept {
|
||||
return (service != nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a weak pointer to a service implementation, if any.
|
||||
*
|
||||
* Clients of a service shouldn't retain references to it. The recommended
|
||||
* way is to retrieve the service implementation currently set each and
|
||||
* every time the need of using it arises. Otherwise users can incur in
|
||||
* unexpected behaviors.
|
||||
*
|
||||
* @return A reference to the service implementation currently set, if any.
|
||||
*/
|
||||
[[nodiscard]] static std::weak_ptr<Service> get() ENTT_NOEXCEPT {
|
||||
return service;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a weak reference to a service implementation, if any.
|
||||
*
|
||||
* Clients of a service shouldn't retain references to it. The recommended
|
||||
* way is to retrieve the service implementation currently set each and
|
||||
* every time the need of using it arises. Otherwise users can incur in
|
||||
* unexpected behaviors.
|
||||
* @brief Returns a reference to a valid service, if any.
|
||||
*
|
||||
* @warning
|
||||
* In case no service implementation has been set, a call to this function
|
||||
* results in undefined behavior.
|
||||
* Invoking this function can result in undefined behavior if the service
|
||||
* hasn't been set yet.
|
||||
*
|
||||
* @return A reference to the service implementation currently set, if any.
|
||||
* @return A reference to the service currently set, if any.
|
||||
*/
|
||||
[[nodiscard]] static Service &ref() ENTT_NOEXCEPT {
|
||||
[[nodiscard]] static Service &value() noexcept {
|
||||
ENTT_ASSERT(has_value(), "Service not available");
|
||||
return *service;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a service if available or sets it from a fallback type.
|
||||
*
|
||||
* Arguments are used only if a service doesn't already exist. In all other
|
||||
* cases, they are discarded.
|
||||
*
|
||||
* @tparam Args Types of arguments to use to construct the fallback service.
|
||||
* @tparam Type Fallback service type.
|
||||
* @param args Parameters to use to construct the fallback service.
|
||||
* @return A reference to a valid service.
|
||||
*/
|
||||
template<typename Type = Service, typename... Args>
|
||||
[[nodiscard]] static Service &value_or(Args &&...args) {
|
||||
return service ? *service : emplace<Type>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets or replaces a service.
|
||||
* @tparam Impl Type of the new service to use.
|
||||
* @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>
|
||||
static void set(Args &&...args) {
|
||||
service = std::make_shared<Impl>(std::forward<Args>(args)...);
|
||||
template<typename Type = Service, typename... Args>
|
||||
static Service &emplace(Args &&...args) {
|
||||
service = std::make_shared<Type>(std::forward<Args>(args)...);
|
||||
return *service;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets or replaces a service.
|
||||
* @param ptr Service to use to replace the current one.
|
||||
* @brief Sets or replaces a service using a given allocator.
|
||||
* @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.
|
||||
*/
|
||||
static void set(std::shared_ptr<Service> ptr) {
|
||||
ENTT_ASSERT(static_cast<bool>(ptr), "Null service not allowed");
|
||||
service = std::move(ptr);
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resets a service.
|
||||
*
|
||||
* The service is no longer valid after a reset.
|
||||
* @brief Returns a handle to the underlying service.
|
||||
* @return A handle to the underlying service.
|
||||
*/
|
||||
static void reset() {
|
||||
service.reset();
|
||||
static node_type handle() noexcept {
|
||||
node_type node{};
|
||||
node.value = service;
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resets or replaces a service.
|
||||
* @param other Optional handle with which to replace the service.
|
||||
*/
|
||||
static void reset(const node_type &other = {}) noexcept {
|
||||
service = other.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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:
|
||||
inline static std::shared_ptr<Service> service = nullptr;
|
||||
// std::shared_ptr because of its type erased allocator which is useful here
|
||||
inline static std::shared_ptr<Service> service{};
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -2,272 +2,384 @@
|
||||
#define ENTT_META_CONTAINER_HPP
|
||||
|
||||
#include <array>
|
||||
#include <deque>
|
||||
#include <iterator>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#include "../container/dense_hash_map.hpp"
|
||||
#include "../container/dense_hash_set.hpp"
|
||||
#include "../container/dense_map.hpp"
|
||||
#include "../container/dense_set.hpp"
|
||||
#include "context.hpp"
|
||||
#include "meta.hpp"
|
||||
#include "type_traits.hpp"
|
||||
|
||||
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::reserve)>>: 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) ENTT_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 bool clear([[maybe_unused]] any &container) {
|
||||
if constexpr(is_dynamic_sequence_container<Type>::value) {
|
||||
if(auto *const cont = any_cast<Type>(&container); cont) {
|
||||
cont->clear();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] static iterator begin(any &container) {
|
||||
if(auto *const cont = any_cast<Type>(&container); cont) {
|
||||
return iterator{std::begin(*cont)};
|
||||
}
|
||||
|
||||
return iterator{std::begin(any_cast<const Type &>(container))};
|
||||
}
|
||||
|
||||
[[nodiscard]] static iterator end(any &container) {
|
||||
if(auto *const cont = any_cast<Type>(&container); cont) {
|
||||
return iterator{std::end(*cont)};
|
||||
}
|
||||
|
||||
return iterator{std::end(any_cast<const Type &>(container))};
|
||||
}
|
||||
|
||||
[[nodiscard]] static iterator insert([[maybe_unused]] any &container, [[maybe_unused]] iterator it, [[maybe_unused]] meta_any &value) {
|
||||
if constexpr(is_dynamic_sequence_container<Type>::value) {
|
||||
if(auto *const cont = any_cast<Type>(&container); cont) {
|
||||
// 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{cont->insert(any_cast<const typename Type::iterator &>(it.base()), element ? *element : value.cast<typename Type::value_type>())};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] static iterator erase([[maybe_unused]] any &container, [[maybe_unused]] iterator it) {
|
||||
if constexpr(is_dynamic_sequence_container<Type>::value) {
|
||||
if(auto *const cont = any_cast<Type>(&container); cont) {
|
||||
return iterator{cont->erase(any_cast<const typename Type::iterator &>(it.base()))};
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] static meta_any get(any &container, size_type pos) {
|
||||
if(auto *const cont = any_cast<Type>(&container); cont) {
|
||||
return meta_any{std::in_place_type<typename Type::reference>, (*cont)[pos]};
|
||||
}
|
||||
|
||||
return meta_any{std::in_place_type<typename Type::const_reference>, any_cast<const Type &>(container)[pos]};
|
||||
}
|
||||
};
|
||||
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) ENTT_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 begin(any &container) {
|
||||
if(auto *const cont = any_cast<Type>(&container); cont) {
|
||||
return iterator{std::integral_constant<bool, key_only>{}, cont->begin()};
|
||||
}
|
||||
|
||||
return iterator{std::integral_constant<bool, key_only>{}, std::begin(any_cast<const Type &>(container))};
|
||||
}
|
||||
|
||||
[[nodiscard]] static iterator end(any &container) {
|
||||
if(auto *const cont = any_cast<Type>(&container); cont) {
|
||||
return iterator{std::integral_constant<bool, key_only>{}, cont->end()};
|
||||
}
|
||||
|
||||
return iterator{std::integral_constant<bool, key_only>{}, std::end(any_cast<const Type &>(container))};
|
||||
}
|
||||
|
||||
[[nodiscard]] static bool insert(any &container, meta_any &key, [[maybe_unused]] meta_any &value) {
|
||||
if(auto *const cont = any_cast<Type>(&container); cont && key.allow_cast<const typename Type::key_type &>()) {
|
||||
if constexpr(is_key_only_meta_associative_container<Type>::value) {
|
||||
return cont->insert(key.cast<const typename Type::key_type &>()).second;
|
||||
} else {
|
||||
if(value.allow_cast<const typename Type::mapped_type &>()) {
|
||||
return cont->emplace(key.cast<const typename Type::key_type &>(), value.cast<const typename Type::mapped_type &>()).second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] static bool erase(any &container, meta_any &key) {
|
||||
if(auto *const cont = any_cast<Type>(&container); cont && key.allow_cast<const typename Type::key_type &>()) {
|
||||
return (cont->erase(key.cast<const typename Type::key_type &>()) != cont->size());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] static iterator find(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{std::integral_constant<bool, key_only>{}, cont->find(key.cast<const typename Type::key_type &>())};
|
||||
}
|
||||
|
||||
return iterator{std::integral_constant<bool, key_only>{}, any_cast<const Type &>(container).find(key.cast<const typename Type::key_type &>())};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
};
|
||||
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.
|
||||
* @tparam Type The type of elements.
|
||||
* @tparam Args Other arguments.
|
||||
* @tparam Args Template arguments for the container.
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
struct meta_sequence_container_traits<std::vector<Type, Args...>>
|
||||
: internal::basic_meta_sequence_container_traits<std::vector<Type, Args...>> {};
|
||||
template<typename... Args>
|
||||
struct 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.
|
||||
* @tparam Type The type of elements.
|
||||
* @tparam N The number of elements.
|
||||
* @tparam Type Template arguments for the container.
|
||||
* @tparam N Template arguments for the container.
|
||||
*/
|
||||
template<typename Type, auto N>
|
||||
struct meta_sequence_container_traits<std::array<Type, N>>
|
||||
: 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.
|
||||
* @tparam Args Template arguments for the container.
|
||||
*/
|
||||
template<typename... Args>
|
||||
struct 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.
|
||||
* @tparam Args Template arguments for the container.
|
||||
*/
|
||||
template<typename... Args>
|
||||
struct 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.
|
||||
* @tparam Key The key type of elements.
|
||||
* @tparam Value The value type of elements.
|
||||
* @tparam Args Other arguments.
|
||||
* @tparam Args Template arguments for the container.
|
||||
*/
|
||||
template<typename Key, typename Value, typename... Args>
|
||||
struct meta_associative_container_traits<std::map<Key, Value, Args...>>
|
||||
: internal::basic_meta_associative_container_traits<std::map<Key, Value, Args...>> {};
|
||||
template<typename... Args>
|
||||
struct 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
|
||||
* type.
|
||||
* @tparam Key The key type of elements.
|
||||
* @tparam Value The value type of elements.
|
||||
* @tparam Args Other arguments.
|
||||
* @tparam Args Template arguments for the container.
|
||||
*/
|
||||
template<typename Key, typename Value, typename... Args>
|
||||
struct meta_associative_container_traits<std::unordered_map<Key, Value, Args...>>
|
||||
: internal::basic_meta_associative_container_traits<std::unordered_map<Key, Value, Args...>> {};
|
||||
template<typename... Args>
|
||||
struct 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.
|
||||
* @tparam Key The type of elements.
|
||||
* @tparam Args Other arguments.
|
||||
* @tparam Args Template arguments for the container.
|
||||
*/
|
||||
template<typename Key, typename... Args>
|
||||
struct meta_associative_container_traits<std::set<Key, Args...>>
|
||||
: internal::basic_meta_associative_container_traits<std::set<Key, Args...>> {};
|
||||
template<typename... Args>
|
||||
struct 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
|
||||
* type.
|
||||
* @tparam Key The type of elements.
|
||||
* @tparam Args Other arguments.
|
||||
* @tparam Args Template arguments for the container.
|
||||
*/
|
||||
template<typename Key, typename... Args>
|
||||
struct meta_associative_container_traits<std::unordered_set<Key, Args...>>
|
||||
: internal::basic_meta_associative_container_traits<std::unordered_set<Key, Args...>> {};
|
||||
template<typename... Args>
|
||||
struct meta_associative_container_traits<std::unordered_set<Args...>>
|
||||
: basic_meta_associative_container_traits<std::unordered_set<Args...>> {};
|
||||
|
||||
/**
|
||||
* @brief Meta associative container traits for `dense_hash_map`s of any type.
|
||||
* @tparam Key The key type of the elements.
|
||||
* @tparam Type The value type of the elements.
|
||||
* @tparam Args Other arguments.
|
||||
* @brief Meta associative container traits for `dense_map`s of any type.
|
||||
* @tparam Args Template arguments for the container.
|
||||
*/
|
||||
template<typename Key, typename Type, typename... Args>
|
||||
struct meta_associative_container_traits<dense_hash_map<Key, Type, Args...>>
|
||||
: internal::basic_meta_associative_container_traits<dense_hash_map<Key, Type, Args...>> {};
|
||||
template<typename... Args>
|
||||
struct meta_associative_container_traits<dense_map<Args...>>
|
||||
: basic_meta_associative_container_traits<dense_map<Args...>> {};
|
||||
|
||||
/**
|
||||
* @brief Meta associative container traits for `dense_hash_set`s of any type.
|
||||
* @tparam Type The value type of the elements.
|
||||
* @tparam Args Other arguments.
|
||||
* @brief Meta associative container traits for `dense_set`s of any type.
|
||||
* @tparam Args Template arguments for the container.
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
struct meta_associative_container_traits<dense_hash_set<Type, Args...>>
|
||||
: internal::basic_meta_associative_container_traits<dense_hash_set<Type, Args...>> {};
|
||||
template<typename... Args>
|
||||
struct meta_associative_container_traits<dense_set<Args...>>
|
||||
: basic_meta_associative_container_traits<dense_set<Args...>> {};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user